From 1455dd60b68c9140db1b9977c9e5ce372b772ec8 Mon Sep 17 00:00:00 2001 From: Marius Date: Fri, 25 Mar 2011 19:20:25 +0200 Subject: beta 2011.03.25 18:03 --- context/data/scite/cont-cs-scite.properties | 2 +- context/data/scite/cont-de-scite.properties | 2 +- context/data/scite/cont-fr-scite.properties | 2 +- context/data/scite/cont-it-scite.properties | 2 +- context/data/scite/cont-nl-scite.properties | 2 +- context/data/scite/cont-pe-scite.properties | 2 +- context/data/scite/cont-ro-scite.properties | 2 +- metapost/context/base/mp-mlib.mp | 11 +- scripts/context/lua/mtx-context.lua | 532 +- scripts/context/lua/mtx-fonts.lua | 2 +- scripts/context/lua/mtx-interface.lua | 2 +- scripts/context/lua/mtx-server-ctx-fonttest.lua | 14 +- scripts/context/lua/mtxrun.lua | 99 +- scripts/context/stubs/mswin/mtxrun.lua | 99 +- scripts/context/stubs/unix/mtxrun | 99 +- tex/context/base/anch-pgr.mkiv | 41 - tex/context/base/attr-col.lua | 10 +- tex/context/base/attr-lay.lua | 2 + tex/context/base/attr-lay.mkiv | 9 +- tex/context/base/back-exp.lua | 2 +- tex/context/base/back-ini.lua | 194 +- tex/context/base/back-pdf.mkiv | 12 +- tex/context/base/bibl-tra.lua | 2 +- tex/context/base/blob-ini.lua | 40 +- tex/context/base/buff-ver.lua | 12 +- tex/context/base/buff-ver.mkiv | 2 +- tex/context/base/chem-str.lua | 22 +- tex/context/base/cldf-int.lua | 12 +- tex/context/base/colo-ext.mkiv | 2 +- tex/context/base/colo-icc.lua | 6 +- tex/context/base/colo-ini.lua | 38 +- tex/context/base/colo-ini.mkiv | 20 +- tex/context/base/cont-new.mkii | 2 +- tex/context/base/cont-new.mkiv | 2 +- tex/context/base/context.mkii | 2 +- tex/context/base/context.mkiv | 30 +- tex/context/base/context.todo | 4 + tex/context/base/core-con.lua | 4 +- tex/context/base/core-ini.mkiv | 2 +- tex/context/base/core-mis.mkiv | 66 - tex/context/base/data-ctx.lua | 36 +- tex/context/base/data-res.lua | 38 +- tex/context/base/data-tex.lua | 12 +- tex/context/base/font-afm.lua | 912 +- tex/context/base/font-age.lua | 13 +- tex/context/base/font-agl.lua | 4 +- tex/context/base/font-aux.lua | 105 + tex/context/base/font-chk.lua | 15 +- tex/context/base/font-cid.lua | 100 +- tex/context/base/font-clr.lua | 31 - tex/context/base/font-col.lua | 8 +- tex/context/base/font-con.lua | 1175 ++ tex/context/base/font-ctx.lua | 810 +- tex/context/base/font-def.lua | 336 +- tex/context/base/font-dum.lua | 354 - tex/context/base/font-enc.lua | 42 +- tex/context/base/font-enh.lua | 235 +- tex/context/base/font-ext.lua | 401 +- tex/context/base/font-fbk.lua | 282 +- tex/context/base/font-gds.lua | 170 +- tex/context/base/font-ini.lua | 110 +- tex/context/base/font-ini.mkiv | 172 +- tex/context/base/font-log.lua | 77 +- tex/context/base/font-lua.lua | 23 +- tex/context/base/font-map.lua | 86 +- tex/context/base/font-mis.lua | 33 +- tex/context/base/font-ota.lua | 169 +- tex/context/base/font-otb.lua | 783 +- tex/context/base/font-otc.lua | 157 +- tex/context/base/font-otd.lua | 209 +- tex/context/base/font-otf.lua | 2066 ++-- tex/context/base/font-oth.lua | 56 +- tex/context/base/font-oti.lua | 92 +- tex/context/base/font-otn.lua | 1341 +-- tex/context/base/font-otp.lua | 778 +- tex/context/base/font-ott.lua | 1353 +-- tex/context/base/font-pat.lua | 4 +- tex/context/base/font-syn.lua | 7 +- tex/context/base/font-tfm.lua | 841 +- tex/context/base/font-vf.lua | 214 +- tex/context/base/font-xtx.lua | 100 - tex/context/base/grph-inc.lua | 1 - tex/context/base/java-imp-ans.mkiv | 33 - tex/context/base/java-imp-fld.mkiv | 33 +- tex/context/base/java-ini.lua | 2 +- tex/context/base/l-dimen.lua | 22 +- tex/context/base/l-lpeg.lua | 2 + tex/context/base/l-table.lua | 19 +- tex/context/base/lang-txt.lua | 45 +- tex/context/base/lpdf-ano.lua | 53 +- tex/context/base/lpdf-col.lua | 42 +- tex/context/base/lpdf-fld.lua | 715 +- tex/context/base/lpdf-fmt.lua | 20 +- tex/context/base/lpdf-ini.lua | 50 +- tex/context/base/lpdf-mis.lua | 56 +- tex/context/base/lpdf-ren.lua | 24 +- tex/context/base/lpdf-wid.lua | 472 +- tex/context/base/lpdf-xmp.lua | 13 +- tex/context/base/luat-dum.lua | 207 - tex/context/base/luat-mac.lua | 140 +- tex/context/base/lxml-tex.lua | 15 +- tex/context/base/m-barcodes.mkiv | 2 +- tex/context/base/m-chart.mkiv | 37 +- tex/context/base/m-fields.mkiv | 70 + tex/context/base/m-newmat.tex | 269 +- tex/context/base/m-obsolete.tex | 2 +- tex/context/base/m-punk.mkiv | 58 +- tex/context/base/math-dim.lua | 4 +- tex/context/base/math-ext.lua | 65 +- tex/context/base/math-frc.mkii | 195 +- tex/context/base/math-frc.mkiv | 195 +- tex/context/base/math-ini.lua | 139 +- tex/context/base/math-ini.mkiv | 4 +- tex/context/base/math-noa.lua | 114 +- tex/context/base/math-vfu.lua | 240 +- tex/context/base/meta-imp-dum.mkiv | 42 +- tex/context/base/mlib-pps.lua | 35 +- tex/context/base/mlib-pps.mkiv | 8 +- tex/context/base/mult-aux.mkiv | 104 +- tex/context/base/mult-chk.lua | 22 +- tex/context/base/mult-de.mkii | 8 +- tex/context/base/mult-def.lua | 42 +- tex/context/base/mult-en.mkii | 8 +- tex/context/base/mult-fr.mkii | 8 +- tex/context/base/mult-it.mkii | 8 +- tex/context/base/mult-nl.mkii | 8 +- tex/context/base/mult-pe.mkii | 8 +- tex/context/base/mult-ro.mkii | 8 +- tex/context/base/mult-sys.mkiv | 35 +- tex/context/base/node-aux.lua | 2 +- tex/context/base/node-dum.lua | 140 - tex/context/base/node-fnt.lua | 77 +- tex/context/base/node-ini.mkiv | 2 +- tex/context/base/node-inj.lua | 40 +- tex/context/base/node-ref.lua | 35 +- tex/context/base/node-rul.lua | 52 +- tex/context/base/node-rul.mkiv | 145 +- tex/context/base/node-spl.lua | 52 +- tex/context/base/node-tra.lua | 34 +- tex/context/base/pack-lyr.mkiv | 23 - tex/context/base/pack-mis.mkvi | 84 + tex/context/base/pack-rul.mkiv | 278 +- tex/context/base/page-bck.mkiv | 2 +- tex/context/base/page-flt.lua | 2 +- tex/context/base/page-imp.mkii | 360 +- tex/context/base/page-imp.mkiv | 365 +- tex/context/base/page-lay.mkiv | 55 +- tex/context/base/page-run.mkiv | 4 +- tex/context/base/page-set.mkiv | 27 +- tex/context/base/ppchtex.mkiv | 28 +- tex/context/base/s-fnt-10.mkiv | 103 +- tex/context/base/s-fnt-23.mkiv | 35 +- tex/context/base/s-fnt-25.mkiv | 13 +- tex/context/base/s-fnt-29.mkiv | 6 +- tex/context/base/scrn-bar.mkiv | 400 - tex/context/base/scrn-bar.mkvi | 405 + tex/context/base/scrn-but.lua | 19 + tex/context/base/scrn-but.mkiv | 127 - tex/context/base/scrn-but.mkvi | 984 ++ tex/context/base/scrn-fld.lua | 76 + tex/context/base/scrn-fld.mkiv | 687 -- tex/context/base/scrn-fld.mkvi | 965 ++ tex/context/base/scrn-hlp.lua | 120 + tex/context/base/scrn-hlp.mkiv | 179 - tex/context/base/scrn-hlp.mkvi | 162 + tex/context/base/scrn-ini.lua | 21 + tex/context/base/scrn-ini.mkvi | 178 + tex/context/base/scrn-int.lua | 114 - tex/context/base/scrn-int.mkiv | 613 - tex/context/base/scrn-men.mkiv | 629 - tex/context/base/scrn-nav.mkiv | 258 - tex/context/base/scrn-pag.lua | 27 + tex/context/base/scrn-pag.mkvi | 180 + tex/context/base/scrn-ref.lua | 65 + tex/context/base/scrn-ref.mkvi | 90 + tex/context/base/scrn-wid.lua | 194 + tex/context/base/scrn-wid.mkvi | 700 ++ tex/context/base/scrp-cjk.lua | 27 +- tex/context/base/scrp-ini.lua | 23 +- tex/context/base/sort-ini.lua | 4 + tex/context/base/spac-ali.mkiv | 86 +- tex/context/base/spac-grd.mkiv | 19 +- tex/context/base/spac-hor.mkiv | 4 +- tex/context/base/spac-ver.mkiv | 246 - tex/context/base/status-files.pdf | Bin 23456 -> 23535 bytes tex/context/base/strc-blk.lua | 5 +- tex/context/base/strc-des.mkiv | 2 +- tex/context/base/strc-doc.mkiv | 2 +- tex/context/base/strc-itm.mkiv | 46 +- tex/context/base/strc-lst.mkiv | 8 +- tex/context/base/strc-num.lua | 4 +- tex/context/base/strc-ref.lua | 145 +- tex/context/base/strc-ref.mkiv | 197 +- tex/context/base/strc-sec.mkiv | 8 +- tex/context/base/strc-syn.mkiv | 6 +- tex/context/base/supp-fil.lua | 2 +- tex/context/base/symb-ini.lua | 24 +- tex/context/base/syst-aux.mkiv | 43 +- tex/context/base/tabl-ntb.mkiv | 1 + tex/context/base/trac-tex.lua | 29 +- tex/context/base/type-ini.mkiv | 6 +- tex/context/base/type-otf.mkiv | 2 +- tex/context/base/typo-brk.lua | 6 +- tex/context/base/typo-brk.mkiv | 2 +- tex/context/base/typo-cap.lua | 6 +- tex/context/base/typo-dig.lua | 10 +- tex/context/base/typo-dir.lua | 6 +- tex/context/base/typo-krn.lua | 7 +- tex/context/base/typo-mar.lua | 15 +- tex/context/base/typo-mar.mkiv | 8 +- tex/context/base/typo-rep.lua | 40 +- tex/context/base/typo-rep.mkiv | 2 +- tex/context/base/typo-spa.lua | 5 +- tex/context/base/util-mrg.lua | 10 +- tex/context/base/util-prs.lua | 7 +- tex/context/base/util-tab.lua | 24 +- tex/context/base/x-cals.lua | 6 +- tex/context/base/x-css.lua | 12 +- tex/context/base/x-mathml.lua | 2 +- tex/context/fonts/asana-math.lfg | 33 +- tex/context/fonts/cambria-math.lfg | 6 +- tex/context/fonts/lm-math.lfg | 10 +- tex/context/fonts/lucida-math.lfg | 4 +- tex/context/fonts/xits-math.lfg | 8 +- tex/context/interface/cont-cs.xml | 4 +- tex/context/interface/cont-de.xml | 4 +- tex/context/interface/cont-fr.xml | 4 +- tex/context/interface/cont-it.xml | 4 +- tex/context/interface/cont-nl.xml | 4 +- tex/context/interface/cont-pe.xml | 4 +- tex/context/interface/cont-ro.xml | 4 +- tex/context/interface/keys-cs.xml | 8 +- tex/context/interface/keys-de.xml | 8 +- tex/context/interface/keys-en.xml | 8 +- tex/context/interface/keys-fr.xml | 8 +- tex/context/interface/keys-it.xml | 8 +- tex/context/interface/keys-nl.xml | 8 +- tex/context/interface/keys-pe.xml | 8 +- tex/context/interface/keys-ro.xml | 8 +- tex/generic/context/luatex-basics-gen.lua | 220 + tex/generic/context/luatex-basics-nod.lua | 95 + tex/generic/context/luatex-fonts-cbk.lua | 68 + tex/generic/context/luatex-fonts-def.lua | 97 + tex/generic/context/luatex-fonts-demo-vf-1.lua | 14 +- tex/generic/context/luatex-fonts-enc.lua | 28 + tex/generic/context/luatex-fonts-ext.lua | 276 + tex/generic/context/luatex-fonts-lua.lua | 33 + tex/generic/context/luatex-fonts-merged.lua | 13403 +++++++--------------- tex/generic/context/luatex-fonts-syn.lua | 83 + tex/generic/context/luatex-fonts-tfm.lua | 38 + tex/generic/context/luatex-fonts.lua | 132 +- 251 files changed, 21165 insertions(+), 22601 deletions(-) create mode 100644 tex/context/base/context.todo create mode 100644 tex/context/base/font-aux.lua delete mode 100644 tex/context/base/font-clr.lua create mode 100644 tex/context/base/font-con.lua delete mode 100644 tex/context/base/font-dum.lua delete mode 100644 tex/context/base/font-xtx.lua delete mode 100644 tex/context/base/java-imp-ans.mkiv delete mode 100644 tex/context/base/luat-dum.lua create mode 100644 tex/context/base/m-fields.mkiv delete mode 100644 tex/context/base/node-dum.lua create mode 100644 tex/context/base/pack-mis.mkvi delete mode 100644 tex/context/base/scrn-bar.mkiv create mode 100644 tex/context/base/scrn-bar.mkvi create mode 100644 tex/context/base/scrn-but.lua delete mode 100644 tex/context/base/scrn-but.mkiv create mode 100644 tex/context/base/scrn-but.mkvi create mode 100644 tex/context/base/scrn-fld.lua delete mode 100644 tex/context/base/scrn-fld.mkiv create mode 100644 tex/context/base/scrn-fld.mkvi create mode 100644 tex/context/base/scrn-hlp.lua delete mode 100644 tex/context/base/scrn-hlp.mkiv create mode 100644 tex/context/base/scrn-hlp.mkvi create mode 100644 tex/context/base/scrn-ini.lua create mode 100644 tex/context/base/scrn-ini.mkvi delete mode 100644 tex/context/base/scrn-int.lua delete mode 100644 tex/context/base/scrn-int.mkiv delete mode 100644 tex/context/base/scrn-men.mkiv delete mode 100644 tex/context/base/scrn-nav.mkiv create mode 100644 tex/context/base/scrn-pag.lua create mode 100644 tex/context/base/scrn-pag.mkvi create mode 100644 tex/context/base/scrn-ref.lua create mode 100644 tex/context/base/scrn-ref.mkvi create mode 100644 tex/context/base/scrn-wid.lua create mode 100644 tex/context/base/scrn-wid.mkvi create mode 100644 tex/generic/context/luatex-basics-gen.lua create mode 100644 tex/generic/context/luatex-basics-nod.lua create mode 100644 tex/generic/context/luatex-fonts-cbk.lua create mode 100644 tex/generic/context/luatex-fonts-def.lua create mode 100644 tex/generic/context/luatex-fonts-enc.lua create mode 100644 tex/generic/context/luatex-fonts-ext.lua create mode 100644 tex/generic/context/luatex-fonts-lua.lua create mode 100644 tex/generic/context/luatex-fonts-syn.lua create mode 100644 tex/generic/context/luatex-fonts-tfm.lua diff --git a/context/data/scite/cont-cs-scite.properties b/context/data/scite/cont-cs-scite.properties index ddf42aacd..5c9cc3354 100644 --- a/context/data/scite/cont-cs-scite.properties +++ b/context/data/scite/cont-cs-scite.properties @@ -1 +1 @@ -keywordclass.macros.context.cs=CAP Cap Caps MESIC Rimskecislice SLOVA SLOVO Slova Slovo VSEDNIDEN Znak Znaky aktualnicislonadpisu aktualnidatum appendix barva bilemisto bublinkovanapoveda bypassblocks cap cernalinka cernelinky chapter chem cisla cislonadpisu citace citovat completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister coupledregister crlf datum definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetype definetypeface definuj definujbarvu definujblok definujbloksekce definujbuffer definujfont definujformatodkazu definujinterakcnimenu definujinterakcnimenu definujkombinovanyseznam definujkonverzi definujlogo definujnadpis definujobrazeksymbol definujodkaz definujodsazovani definujodstavce definujopis definujoramovani definujoramovanytext definujpaletu definujplvouciobjekt definujpodpole definujpole definujpopis definujpopisek definujprekryv definujpreskok definujprofil definujprogram definujprostredizakladnihofontu definujrejstrik definujsablonutabulky definujsekci definujseznam definujseznamodkazu definujskupinubarev definujstartstop definujsymbol definujsynonumumfontu definujsynonyma definujtabelaci definujtext definujtrideni definujupravu definujvelikostpapiru definujverzi definujvycet definujvystup definujzakladnifont definujzasobnikpoli definujznaceni description dodrzujprofil dodrzujverzi dodrzujverziprofilu dvoustrannypapir emptylines enumeration externiobraz footnotetext forceblocks framedtext hl hlavnijazyk indentation ininner inouter instalacejazyka interakcnilista interakcnitlacitka jazyk jdidolu jdina jdinabox klonujpole komentar konvertujcislo kopirujpole korekcebilehomista labeling listsymbol loadsorts loadsynonyms mapfontsize marginalnilinka marginalnitext matematika mediaeval meritko mesic mezera mrizka nadpis nadruhyokraj name naokraj nastavbarvu nastavbarvy nastavbilamista nastavblok nastavbloksekce nastavbuffer nastavcernelinky nastavcislonadpisu nastavcislostrany nastavcislovani nastavcislovaniodstavcu nastavcislovaniradku nastavcislovanistran nastavcitaci nastavdefinicipoznamekpodcarou nastavdeleniplvoucichobjektu nastavdelitko nastavdolnitexty nastavexterniobrazy nastavhorejsek nastavhornitexty nastavinterakci nastavinterakcnilistu nastavinterakcnimenu nastavinterakcniobrazovku nastavjazyk nastavkapitalky nastavkombinovanyseznam nastavkomentar nastavlegendu nastavmarginalie nastavmarginalniblok nastavmarginalnilinky nastavmeziradkovoumezeru nastavnadpis nastavnadpisy nastavodkazovani nastavodsazeni nastavodsazovani nastavodstavce nastavopis nastavoramovanetexty nastavoramovani nastavorez nastavotoceni nastavpaletu nastavplvouciobjekt nastavplvouciobjekty nastavpodcislostrany nastavpodtrzeni nastavpole nastavpolozky nastavpopisek nastavpopisky nastavpopisy nastavpozadi nastavpozadi nastavpoznamkypodcarou nastavprechodstrany nastavpreskok nastavprofily nastavprogramy nastavprostredizakladnihofontu nastavpublikace nastavradkovani nastavradky nastavrastr nastavrejstrik nastavrovnice nastavsadusymbolu nastavsekci nastavseznam nastavseznamodkazu nastavsirkucary nastavsloupce nastavspodek nastavspojeni nastavsynchronizaci nastavsynchronizacnilistu nastavsynonyma nastavsystem nastavtab nastavtabelaci nastavtabulky nastavtenkelinky nastavtext nastavtexthlavicky nastavtextovelinky nastavtextpopisku nastavtexttexty nastavtextyupati nastavtextyzahlavi nastavtlacitka nastavtoleranci nastavtrideni nastavtype nastavumisteniprotejsku nastavumistovani nastavupati nastavupravu nastavurl nastavusporadani nastavvelikostpapiru nastavverze nastavvsechnapole nastavvycty nastavvyplnovelinky nastavvyplnoveradky nastavvystup nastavvzhled nastavzahlavi nastavzakladnifont nastavzarovnani nastavznaceni nastavzuzeni nastrane nejakyradek nekde nextsection nivy nizky nocap nop obrazovka odkaz odkaz odkaznadatum odkaznastranu odkaznatext odsazovani okr opis opissoubor oramovani oref orez otocit overbar overbars overstrike overstrikes oznacverzi paragraph parovastrana part pis placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist pol pole polozka polozky popisky porovnejpaletu porovnejskupinubarev pozadi pozice poznamka poznamkapodcarou pref prelozit premistinamrizku prepninazakladnifont preskoc prizpusobivepole prizpusobvzhled program propojeneznaceni propojenydokument propojenyrejstrik publikace ran ref register reservefloat reset resettextcontent resetznaceni rimskecislice rozdelplvouciobjekt rozpojeneznaceni roztazene schovejbloky section sedabarva seeregister settextcontent setupfonthandling setupfontsynonym setupforms setupinterlinespace2 setupitemgroup setuplistalternative setuppaper setupstrut sloupec slovovpravo sort spodek stanovcharakteristickuseznamu stanovcislonadpisu startalignment startbarva startbuffer startbuffer startcitace startcolumns startcombination startdescription startdocument startenumeration startfakt startfigure startfloattext startformula startframedtext starthiding startinteraktivnimenu startitemgroup startkomentar startkomponenta startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginalnilinka startmarginblock startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startpozadi startprodukt startprofile startprojekt startprostredi startregister startsymbolset startsynchronization starttable starttables starttabulate starttextovalinka starttyping startunpacked startverze startzhustene stopalignment stopbarva stopbuffer stopbuffer stopcitace stopcolumns stopcombination stopdescription stopdocument stopenumeration stopfakt stopfigure stopfloattext stopformula stopframedtext stophiding stopinteraktivnimenu stopitemgroup stopkomentar stopkomponenta stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginalnilinka stopmarginblock stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stoppozadi stopprodukt stopprofile stopprojekt stopprostredi stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptextovalinka stoptyping stopunpacked stopverze stopzhustene strana sub subject subsection subsubject subsubsection subsubsubject sym symbol synchronizacnilista synchronizovat synonym tab tecky tenkalinka tenkelinky tex texthlavicky textovalinka textpopisku title tlacitko tref tvrdemezery typebuffer typstrany ukazbarvu ukazexterniobrazy ukazmrizku ukaznastaveni ukazpaletu ukazpodpery ukazpole ukazpostredizakladnihofontu ukazramecek ukazsadusymbolu ukazskupinubarev ukazupravu ukazvytisk ukazvzhled ukazzakladnifont umistikombinovanyseznam umistikombinovanyseznam umistilegendu umistiloga umistilokalnipoznamkypodcarou umistinadsebe umistinamrizku umistipodrovnici umistipoznamkypodcarou umistirejstrik umistirejstrik umistirovnici umistiseznam umistivedlesebe umistizalozky underbar underbars usedirectory usetypescript usetypescriptfile uzijURL uzijbloky uzijexternidokument uzijexterniobraz uzijexternisoubor uzijexternisoubory uzijexternizvuk uzijkodovani uzijmodul uzijodkazy uzijprikazy uzijspeciality uzijsymbol verze vl vlasovalinka vlevo vpravo vradku vsedniden vyberbloky vyberpapir vyberverzi vyplnenytext vyplnovelinky vyplnovepole vyplnovyradek vysoky zablokujinterakcnimenu zachovejbloky zadnamezera zadnebilemisto zadnedalsibloky zadnedalsisoubory zadnehorniadolniradky zadneodsazovani zadnezahlaviaupati zadneznaceni zadnyseznam zalozka zapisdorejstriku zapisdoseznamu zapisdoseznamuodkazu zapismeziseznam zaramovani zarovnanonastred zarovnanovlevo zarovnanovpravo zasobnikpoli zaznamovepole zhustene ziskejbuffer ziskejznaceni zlomek znaceni znak znaky zpracujbloky zpracujstranu zrcadlit zref \ No newline at end of file +keywordclass.macros.context.cs=CAP Cap Caps MESIC Rimskecislice SLOVA SLOVO Slova Slovo VSEDNIDEN Znak Znaky aktualnicislonadpisu aktualnidatum appendix barva bilemisto bublinkovanapoveda bypassblocks cap cernalinka cernelinky chapter chem cisla cislonadpisu citace citovat comment completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister coupledregister crlf datum definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetype definetypeface definuj definujbarvu definujblok definujbloksekce definujbuffer definujfont definujformatodkazu definujinterakcnimenu definujinterakcnimenu definujkombinovanyseznam definujkonverzi definujlogo definujnadpis definujobrazeksymbol definujodkaz definujodsazovani definujodstavce definujopis definujoramovani definujoramovanytext definujpaletu definujplvouciobjekt definujpodpole definujpole definujpopis definujpopisek definujprekryv definujpreskok definujprofil definujprogram definujprostredizakladnihofontu definujrejstrik definujsablonutabulky definujsekci definujseznam definujseznamodkazu definujskupinubarev definujstartstop definujsymbol definujsynonumumfontu definujsynonyma definujtabelaci definujtext definujtrideni definujupravu definujvelikostpapiru definujverzi definujvycet definujvystup definujzakladnifont definujzasobnikpoli definujznaceni description dodrzujprofil dodrzujverzi dodrzujverziprofilu dvoustrannypapir emptylines enumeration externiobraz footnotetext forceblocks framedtext hl hlavnijazyk indentation ininner inouter instalacejazyka interakcnilista interakcnitlacitka jazyk jdidolu jdina jdinabox klonujpole konvertujcislo kopirujpole korekcebilehomista labeling listsymbol loadsorts loadsynonyms mapfontsize marginalnilinka marginalnitext matematika mediaeval meritko mesic mezera mrizka nadpis nadruhyokraj name naokraj nastavbarvu nastavbarvy nastavbilamista nastavblok nastavbloksekce nastavbuffer nastavcernelinky nastavcislonadpisu nastavcislostrany nastavcislovani nastavcislovaniodstavcu nastavcislovaniradku nastavcislovanistran nastavcitaci nastavdefinicipoznamekpodcarou nastavdeleniplvoucichobjektu nastavdelitko nastavdolnitexty nastavexterniobrazy nastavhorejsek nastavhornitexty nastavinterakci nastavinterakcnilistu nastavinterakcnimenu nastavinterakcniobrazovku nastavjazyk nastavkapitalky nastavkombinovanyseznam nastavkomentar nastavlegendu nastavmarginalie nastavmarginalniblok nastavmarginalnilinky nastavmeziradkovoumezeru nastavnadpis nastavnadpisy nastavodkazovani nastavodsazeni nastavodsazovani nastavodstavce nastavopis nastavoramovanetexty nastavoramovani nastavorez nastavotoceni nastavpaletu nastavplvouciobjekt nastavplvouciobjekty nastavpodcislostrany nastavpodtrzeni nastavpole nastavpolozky nastavpopisek nastavpopisky nastavpopisy nastavpozadi nastavpozadi nastavpoznamkypodcarou nastavprechodstrany nastavpreskok nastavprofily nastavprogramy nastavprostredizakladnihofontu nastavpublikace nastavradkovani nastavradky nastavrastr nastavrejstrik nastavrovnice nastavsadusymbolu nastavsekci nastavseznam nastavseznamodkazu nastavsirkucary nastavsloupce nastavspodek nastavspojeni nastavsynchronizaci nastavsynchronizacnilistu nastavsynonyma nastavsystem nastavtab nastavtabelaci nastavtabulky nastavtenkelinky nastavtext nastavtexthlavicky nastavtextovelinky nastavtextpopisku nastavtexttexty nastavtextyupati nastavtextyzahlavi nastavtlacitka nastavtoleranci nastavtrideni nastavtype nastavumisteniprotejsku nastavumistovani nastavupati nastavupravu nastavurl nastavusporadani nastavvelikostpapiru nastavverze nastavvsechnapole nastavvycty nastavvyplnovelinky nastavvyplnoveradky nastavvystup nastavvzhled nastavzahlavi nastavzakladnifont nastavzarovnani nastavznaceni nastavzuzeni nastrane nejakyradek nekde nextsection nivy nizky nocap nop obrazovka odkaz odkaz odkaznadatum odkaznastranu odkaznatext odsazovani okr opis opissoubor oramovani oref orez otocit overbar overbars overstrike overstrikes oznacverzi paragraph parovastrana part pis placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist pol pole polozka polozky popisky porovnejpaletu porovnejskupinubarev pozadi pozice poznamka poznamkapodcarou pref prelozit premistinamrizku prepninazakladnifont preskoc prizpusobivepole prizpusobvzhled program propojeneznaceni propojenydokument propojenyrejstrik publikace ran ref register reservefloat reset resettextcontent resetznaceni rimskecislice rozdelplvouciobjekt rozpojeneznaceni roztazene schovejbloky section sedabarva seeregister settextcontent setupfonthandling setupfontsynonym setupforms setupinterlinespace2 setupitemgroup setuplistalternative setuppaper setupstrut sloupec slovovpravo sort spodek stanovcharakteristickuseznamu stanovcislonadpisu startalignment startbarva startbuffer startbuffer startcitace startcolumns startcombination startcomment startdescription startdocument startenumeration startfakt startfigure startfloattext startformula startframedtext starthiding startinteraktivnimenu startitemgroup startkomponenta startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginalnilinka startmarginblock startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startpozadi startprodukt startprofile startprojekt startprostredi startregister startsymbolset startsynchronization starttable starttables starttabulate starttextovalinka starttyping startunpacked startverze startzhustene stopalignment stopbarva stopbuffer stopbuffer stopcitace stopcolumns stopcombination stopcomment stopdescription stopdocument stopenumeration stopfakt stopfigure stopfloattext stopformula stopframedtext stophiding stopinteraktivnimenu stopitemgroup stopkomponenta stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginalnilinka stopmarginblock stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stoppozadi stopprodukt stopprofile stopprojekt stopprostredi stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptextovalinka stoptyping stopunpacked stopverze stopzhustene strana sub subject subsection subsubject subsubsection subsubsubject sym symbol synchronizacnilista synchronizovat synonym tab tecky tenkalinka tenkelinky tex texthlavicky textovalinka textpopisku title tlacitko tref tvrdemezery typebuffer typstrany ukazbarvu ukazexterniobrazy ukazmrizku ukaznastaveni ukazpaletu ukazpodpery ukazpole ukazpostredizakladnihofontu ukazramecek ukazsadusymbolu ukazskupinubarev ukazupravu ukazvytisk ukazvzhled ukazzakladnifont umistikombinovanyseznam umistikombinovanyseznam umistilegendu umistiloga umistilokalnipoznamkypodcarou umistinadsebe umistinamrizku umistipodrovnici umistipoznamkypodcarou umistirejstrik umistirejstrik umistirovnici umistiseznam umistivedlesebe umistizalozky underbar underbars usedirectory usetypescript usetypescriptfile uzijURL uzijbloky uzijexternidokument uzijexterniobraz uzijexternisoubor uzijexternisoubory uzijexternizvuk uzijkodovani uzijmodul uzijodkazy uzijprikazy uzijspeciality uzijsymbol verze vl vlasovalinka vlevo vpravo vradku vsedniden vyberbloky vyberpapir vyberverzi vyplnenytext vyplnovelinky vyplnovepole vyplnovyradek vysoky zablokujinterakcnimenu zachovejbloky zadnamezera zadnebilemisto zadnedalsibloky zadnedalsisoubory zadnehorniadolniradky zadneodsazovani zadnezahlaviaupati zadneznaceni zadnyseznam zalozka zapisdorejstriku zapisdoseznamu zapisdoseznamuodkazu zapismeziseznam zaramovani zarovnanonastred zarovnanovlevo zarovnanovpravo zasobnikpoli zaznamovepole zhustene ziskejbuffer ziskejznaceni zlomek znaceni znak znaky zpracujbloky zpracujstranu zrcadlit zref \ No newline at end of file diff --git a/context/data/scite/cont-de-scite.properties b/context/data/scite/cont-de-scite.properties index b0e05b91a..249fc6a59 100644 --- a/context/data/scite/cont-de-scite.properties +++ b/context/data/scite/cont-de-scite.properties @@ -1 +1 @@ -keywordclass.macros.context.de=Buchstabe Buchstaben CAP Cap Caps MONAT Roemischezahlen WOCHENTAG WOERTER WORT Woerter Wort amgitterausrichten amgitterneuausrichten appendix aufseite ausfuellfeld ausfuelltext ausschnitt bearbeitebloecke bearbeiteseite behaltebloecke bei benutzekodierung benutzespezielles beschrifteversion beschriftung bestimmekopfnummer bestimmelistencharakeristika bildschirm blanko bookmark bruch buchstabe buchstaben but bypassblocks cap chapter chem completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister coupledregister crlf datum definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling defineoutput defineschriftsynonym definetype definetypeface definiereabbsymbol definiereabsaetze definiereabschnitt definiereabschnittsblock definierebeschreibung definierebeschreibungen definierebeschriftung definiereblanko definiereblock definiereeinzug definierefarbe definierefarbengruppe definierefeld definierefeldstapel definierefliesstext definierefliesstextumgebung definieregleitobjekt definiereinteraktionsmenue definiereinteraktionsmenue definierekonversion definierelabel definiereliste definierelogo definieren definierenummerierung definiereoverlay definierepalette definierepapierformat definiereprofil definiereprogramme definierepuffer definierereferenz definierereferenzformat definierereferenzliste definiereregister definiereschrift definieresortieren definierestartstop definieresubfeld definieresymbol definieresynonyme definieretabellenvorlage definieretabulator definieretext definieretippen definiereueberschrift definiereumbruch definiereumrahmt definiereumrahmtertext definiereversion definierezusammengestellteliste description doppelseite doppelseitigespapier drehen duennelinie duennerumriss einezeile einziehen emptylines entknuepfebeschriftung enumeration externeabbildung farbe feld feldstapel festesspatium folgeprofil folgeprofilversion folgeversion footnotetext forceblocks format framedtext fussnote gefuelltesrechteck gefuelltezeile gestreckt gitter graufarbe haarlinie hauptsprache heutigesdatum heutigeskopfnummer hintergrund hl hoch holebeschriftung holepuffer imlinken imrechten imumriss in inaktiviereinteraktionsmenue inanderermarginale indentation ininner inmarginalie inouter installieresprache interaktionsbalken interaktionsknopfe inzeile irgendwo its keinebeschriftung keinebloeckemehr keinedateienmehr keinekopfundfusszeilen keineliste keinspatium keinzeilenobenundunten keinzwischenraum kleinerdurchschuss klonierefeld knopf kommentar konvertierezahl kopf kopfnummer kopierefeld korrigierezwischenraum labeling labels labeltext linksbuendig listsymbol loadsorts loadsynonyms mapfontsize mar marginallinie marginaltext mathematik mediaeval monat nachunten name nextsection nichteinziehen nocap nop notiz overbar overbars overstrike overstrikes paragraph part passelayoutan passendfeld placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist platzierebookmarks platziereformel platzierefussnoten platzierelegende platziereliste platzierelogo platzierelokalefussnoten platzierenebeneinander platziereregister platziereregister platziereuntereinander platziereunterformel platzierezusammengestellteliste platzierezusammengestellteliste pos position posten programm publikation punkt ran rechteck rechtecke rechtsbuendig ref referenz register registrierefelder reservefloat resettextcontent roemischezahlen ruecksetzten ruecksetztenbeschriftung schreibezumregister schreibezurliste schreibezurreferenzliste schreibezwischenliste section seeregister seite seitenreferenz seitentyp settext setupfonthandling setupfontsynonym setupforms setupinterlinespace2 setupitemgroup setuplistalternative setuppaper setupstrut showsymbolset sort spalte spatium spiegeln sprache startalignment startbuffer startbuffer startcolumns startcombination startdescription startdocument startenumeration startfarbe startfigure startfloattext startformula startframedtext startgeg starthiding starthintergrund startinteraktionsmenue startitemgroup startkleinerdurchschuss startkommentar startkomponente startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginallinie startmarginblock startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodukt startprofile startprojekt startregister startsymbolset startsynchronization starttable starttables starttabulate starttextlinie starttyping startumgebung startunpacked startversion startzitat stelleabsaetzeein stelleabsatznummerierungein stelleabschnittein stelleabschnittsblockein stelleanordnenein stelleausgabeein stelleausrichtungein stelleausschnittein stellebeschreibungein stellebeschriftungein stellebilderunterschriftein stellebildunterschriftein stellebindestrichein stelleblankoein stelleblockein stelledrehenein stelleduennerumrissein stelleeinziehenein stelleeinzuegein stelleengerein stelleexterneabbildungenein stellefarbeein stellefarbenein stellefeldein stellefelderin stellefliesstextein stellefliesstextumgebungein stelleformelnein stellefussnotendefinitionein stellefussnotenein stellefusszeileein stellefusszeilentextein stellegefuelltesrechteckein stellegefuelltezeileein stellegegenueberplatzierenein stellegleitobjekteein stellegleitobjektein stellehintergruendeein stellehintergrundein stelleinmarginalieein stelleinteraktionein stelleinteraktionsbalkenein stelleinteraktionsbildschirmein stelleinteraktionsmenueein stelleknopfein stellekombinationein stellekommentarein stellekopfzahlein stellekopfzeileein stellekopfzeilentextein stellelabeltextein stellelayoutein stellelegendeein stellelinienbreiteein stellelisteein stellemarginalblockein stellemarginallinieein stellenobenein stellenummerierungein stellepaletteein stellepapierformatein stelleplatziegeteiltegleitobjekt stellepositionierenein stellepostenein stelleprofilein stelleprogrammein stellepublikationein stellepufferein stellerasterein stellerechteckein stellereferenzierenein stellereferenzlisteein stelleregisterein stelleseitennummerein stelleseitennummeriernungein stelleseitenuebergangein stellesortierenein stellespaltenein stellespatiumein stellespracheein stellesymbolsetein stellesynchronisationein stellesynchronisationsbalkenein stellesynonymein stellesystemein stelletabein stelletabellenein stelletabulatorein stelletextein stelletextobenein stelletexttexteein stelletextumrissein stelletextuntenein stelletipein stelletippenein stelletoleranzein stelleueberschriftein stelleueberschriftenein stelleueberschrifttextein stelleumbruchein stelleumrahmtein stelleumrahmtetexteein stelleuntenein stelleunterseitennummerein stelleunterstreichenein stelleurlein stelleversalienein stelleversionein stellezeilenabstandein stellezeilenein stellezeilennumerierungein stellezitierenein stellezusammengestelltelisteein stellezwischenraumein stopalignment stopbuffer stopbuffer stopcolumns stopcombination stopdescription stopdocument stopenumeration stopfarbe stopfigure stopfloattext stopformula stopframedtext stopgeg stophiding stophintergrund stopinteraktionsmenue stopitemgroup stopkleinerdurchschuss stopkommentar stopkomponente stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginallinie stopmarginblock stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodukt stopprofile stopprojekt stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptextlinie stoptyping stopumgebung stopunpacked stopversion stopzitat sub subject subsection subsubject subsubsection subsubsubject sym symbol synchronisationsbalken synchronisieren synonym tab teilegleitobjekt tex textlinie textreferenz tief tiho tip tippedatei tippen tippepuffer title tooltip ueber ueberschrifttext uebersetzten umrahmt underbar underbars usedirectory usetypescript usetypescriptfile verbergebloecke vergleichefarbengruppe vergleichepalette verknuepfebeschriftung verknuepfedokument verknuepfregister version verweis verweisdatum verwendeURL verwendebefehl verwendebloecke verwendeexteresdokument verwendeexterneabbildung verwendeexternedatei verwendeexternedateien verwendeexternestonstueck verwendemodul verwendereferenzen verwendesymbole vl von waehlebloeckeaus waehlepapieraus waehleversionaus wechselezumfliesstext wochentag wortrechts zeigedruck zeigeeinstellungen zeigeexterneabbildungen zeigefarbe zeigefarbengruppe zeigefelder zeigefliesstext zeigefliesstextumgebung zeigegitter zeigelayout zeigepalette zeigerahmen zeigestruts zeigeumbruch zentriert ziffern zitat zitieren zu zurbox zwischenraum \ No newline at end of file +keywordclass.macros.context.de=Buchstabe Buchstaben CAP Cap Caps MONAT Roemischezahlen WOCHENTAG WOERTER WORT Woerter Wort amgitterausrichten amgitterneuausrichten appendix aufseite ausfuellfeld ausfuelltext ausschnitt bearbeitebloecke bearbeiteseite behaltebloecke bei benutzekodierung benutzespezielles beschrifteversion beschriftung bestimmekopfnummer bestimmelistencharakeristika bildschirm blanko bookmark bruch buchstabe buchstaben but bypassblocks cap chapter chem comment completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister coupledregister crlf datum definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling defineoutput defineschriftsynonym definetype definetypeface definiereabbsymbol definiereabsaetze definiereabschnitt definiereabschnittsblock definierebeschreibung definierebeschreibungen definierebeschriftung definiereblanko definiereblock definiereeinzug definierefarbe definierefarbengruppe definierefeld definierefeldstapel definierefliesstext definierefliesstextumgebung definieregleitobjekt definiereinteraktionsmenue definiereinteraktionsmenue definierekonversion definierelabel definiereliste definierelogo definieren definierenummerierung definiereoverlay definierepalette definierepapierformat definiereprofil definiereprogramme definierepuffer definierereferenz definierereferenzformat definierereferenzliste definiereregister definiereschrift definieresortieren definierestartstop definieresubfeld definieresymbol definieresynonyme definieretabellenvorlage definieretabulator definieretext definieretippen definiereueberschrift definiereumbruch definiereumrahmt definiereumrahmtertext definiereversion definierezusammengestellteliste description doppelseite doppelseitigespapier drehen duennelinie duennerumriss einezeile einziehen emptylines entknuepfebeschriftung enumeration externeabbildung farbe feld feldstapel festesspatium folgeprofil folgeprofilversion folgeversion footnotetext forceblocks format framedtext fussnote gefuelltesrechteck gefuelltezeile gestreckt gitter graufarbe haarlinie hauptsprache heutigesdatum heutigeskopfnummer hintergrund hl hoch holebeschriftung holepuffer imlinken imrechten imumriss in inaktiviereinteraktionsmenue inanderermarginale indentation ininner inmarginalie inouter installieresprache interaktionsbalken interaktionsknopfe inzeile irgendwo its keinebeschriftung keinebloeckemehr keinedateienmehr keinekopfundfusszeilen keineliste keinspatium keinzeilenobenundunten keinzwischenraum kleinerdurchschuss klonierefeld knopf konvertierezahl kopf kopfnummer kopierefeld korrigierezwischenraum labeling labels labeltext linksbuendig listsymbol loadsorts loadsynonyms mapfontsize mar marginallinie marginaltext mathematik mediaeval monat nachunten name nextsection nichteinziehen nocap nop notiz overbar overbars overstrike overstrikes paragraph part passelayoutan passendfeld placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist platzierebookmarks platziereformel platzierefussnoten platzierelegende platziereliste platzierelogo platzierelokalefussnoten platzierenebeneinander platziereregister platziereregister platziereuntereinander platziereunterformel platzierezusammengestellteliste platzierezusammengestellteliste pos position posten programm publikation punkt ran rechteck rechtecke rechtsbuendig ref referenz register registrierefelder reservefloat resettextcontent roemischezahlen ruecksetzten ruecksetztenbeschriftung schreibezumregister schreibezurliste schreibezurreferenzliste schreibezwischenliste section seeregister seite seitenreferenz seitentyp settext setupfonthandling setupfontsynonym setupforms setupinterlinespace2 setupitemgroup setuplistalternative setuppaper setupstrut showsymbolset sort spalte spatium spiegeln sprache startalignment startbuffer startbuffer startcolumns startcombination startcomment startdescription startdocument startenumeration startfarbe startfigure startfloattext startformula startframedtext startgeg starthiding starthintergrund startinteraktionsmenue startitemgroup startkleinerdurchschuss startkomponente startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginallinie startmarginblock startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodukt startprofile startprojekt startregister startsymbolset startsynchronization starttable starttables starttabulate starttextlinie starttyping startumgebung startunpacked startversion startzitat stelleabsaetzeein stelleabsatznummerierungein stelleabschnittein stelleabschnittsblockein stelleanordnenein stelleausgabeein stelleausrichtungein stelleausschnittein stellebeschreibungein stellebeschriftungein stellebilderunterschriftein stellebildunterschriftein stellebindestrichein stelleblankoein stelleblockein stelledrehenein stelleduennerumrissein stelleeinziehenein stelleeinzuegein stelleengerein stelleexterneabbildungenein stellefarbeein stellefarbenein stellefeldein stellefelderin stellefliesstextein stellefliesstextumgebungein stelleformelnein stellefussnotendefinitionein stellefussnotenein stellefusszeileein stellefusszeilentextein stellegefuelltesrechteckein stellegefuelltezeileein stellegegenueberplatzierenein stellegleitobjekteein stellegleitobjektein stellehintergruendeein stellehintergrundein stelleinmarginalieein stelleinteraktionein stelleinteraktionsbalkenein stelleinteraktionsbildschirmein stelleinteraktionsmenueein stelleknopfein stellekombinationein stellekommentarein stellekopfzahlein stellekopfzeileein stellekopfzeilentextein stellelabeltextein stellelayoutein stellelegendeein stellelinienbreiteein stellelisteein stellemarginalblockein stellemarginallinieein stellenobenein stellenummerierungein stellepaletteein stellepapierformatein stelleplatziegeteiltegleitobjekt stellepositionierenein stellepostenein stelleprofilein stelleprogrammein stellepublikationein stellepufferein stellerasterein stellerechteckein stellereferenzierenein stellereferenzlisteein stelleregisterein stelleseitennummerein stelleseitennummeriernungein stelleseitenuebergangein stellesortierenein stellespaltenein stellespatiumein stellespracheein stellesymbolsetein stellesynchronisationein stellesynchronisationsbalkenein stellesynonymein stellesystemein stelletabein stelletabellenein stelletabulatorein stelletextein stelletextobenein stelletexttexteein stelletextumrissein stelletextuntenein stelletipein stelletippenein stelletoleranzein stelleueberschriftein stelleueberschriftenein stelleueberschrifttextein stelleumbruchein stelleumrahmtein stelleumrahmtetexteein stelleuntenein stelleunterseitennummerein stelleunterstreichenein stelleurlein stelleversalienein stelleversionein stellezeilenabstandein stellezeilenein stellezeilennumerierungein stellezitierenein stellezusammengestelltelisteein stellezwischenraumein stopalignment stopbuffer stopbuffer stopcolumns stopcombination stopcomment stopdescription stopdocument stopenumeration stopfarbe stopfigure stopfloattext stopformula stopframedtext stopgeg stophiding stophintergrund stopinteraktionsmenue stopitemgroup stopkleinerdurchschuss stopkomponente stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginallinie stopmarginblock stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodukt stopprofile stopprojekt stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptextlinie stoptyping stopumgebung stopunpacked stopversion stopzitat sub subject subsection subsubject subsubsection subsubsubject sym symbol synchronisationsbalken synchronisieren synonym tab teilegleitobjekt tex textlinie textreferenz tief tiho tip tippedatei tippen tippepuffer title tooltip ueber ueberschrifttext uebersetzten umrahmt underbar underbars usedirectory usetypescript usetypescriptfile verbergebloecke vergleichefarbengruppe vergleichepalette verknuepfebeschriftung verknuepfedokument verknuepfregister version verweis verweisdatum verwendeURL verwendebefehl verwendebloecke verwendeexteresdokument verwendeexterneabbildung verwendeexternedatei verwendeexternedateien verwendeexternestonstueck verwendemodul verwendereferenzen verwendesymbole vl von waehlebloeckeaus waehlepapieraus waehleversionaus wechselezumfliesstext wochentag wortrechts zeigedruck zeigeeinstellungen zeigeexterneabbildungen zeigefarbe zeigefarbengruppe zeigefelder zeigefliesstext zeigefliesstextumgebung zeigegitter zeigelayout zeigepalette zeigerahmen zeigestruts zeigeumbruch zentriert ziffern zitat zitieren zu zurbox zwischenraum \ No newline at end of file diff --git a/context/data/scite/cont-fr-scite.properties b/context/data/scite/cont-fr-scite.properties index 606be2efa..5ecccf961 100644 --- a/context/data/scite/cont-fr-scite.properties +++ b/context/data/scite/cont-fr-scite.properties @@ -1 +1 @@ -keywordclass.macros.context.fr=CAP Cap Caps Caractere Caracteres Chiffresromains JOURSEMAINE MOIS MOT MOTS Mot Mots a adaptedisposition ajustechamp alaligne alapage aligneadroite aligneagauche aligneaumilieu appendix arriereplan baha barreinteraction barresynchronisation bas bouton boutonsinteraction but cacheblocs cap caractere caracteres champ changepolicecorps chapter chem chiffresromains citation citer clip clonechamp colonne commentaire comparegroupecouleur comparepalette completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregistre composeenalinea concernant convertitnumero copitchamp corrigeespaceblanc couleur couleurgrise coupledocument coupledregister couplemarquage couplepapier coupleregistre crlf dactylographier dans dansautremarge dansdroite dansgauche dansmarge date datecourante daterecommandation de decouplemarquage definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling defineframed defineframedtext definetypeface definit definitbloc definitblocsection definitbuffer definitchamp definitcompoalinea definitconversion definitcouleur definitdactylo definitdemarrestoppe definitdescription definitenumeration definitenvironnementpolicecorps definitetiquette definitflottant definitformatreference definitgroupecouleur definitliste definitlisteimbriquee definitlistereference definitlogo definitmakeup definitmarquage definitmenuinteraction definitmenuinteraction definitnotepdp definitpalette definitparagraphes definitpilechamp definitpolice definitpolicecorps definitprofil definitprogramme definitreference definitregistre definitrevetement definitsection definitsortie definitsouschamp definitsymbole definitsymbolefigure definitsynonymepolice definitsynonymes definittabulation definittaillepapier definittete definittexte definittrametableau definittri definittype definitversion definitvide definitvide demarreciter deplacesurgrille description determinecaracteristiqueliste determinenumerotete echelle ecran ecritdansliste ecritdanslistereference ecritentreliste ecritregistre el element elements emptylines enumeration espace espaceblanc espacesfixes etiquettes etire fichierdactylo figureexterne forceblocs fraction framed framedtext gardeblocs grille groupe haut hl indentation inframed inhibemenuinteraction ininner inouter installelangue joursemaine labeling labeltexte langue langueprincipale ligneh lignenoire ligneregleetexte lignesnoires listesymbole loadsorts loadsynonyms logchamp mapfontsize mar margereglee marquage marquageversion marquepage mathematique mediaeval mois montrecadre montrechamps montrecouleur montredisposition montreedition montreenvironnementpolicecorps montrefiguresexternes montregrille montregroupecouleur montrejeusymboles montremakeup montrepalette montrepolicecorps montrereglages montrestruts motdroit name nextsection nocap nop note notepdp numeros numerotete numerotetecourant obtientmarquage oriente overbar overbars overstrike overstrikes page pagedouble paragraph part pasplusdeblocs pasplusdefichiers periodes pilechamp placecoteacote placeflottant placeformule placelegende placelesunsaudessusdesautres placeliste placelisteinmbriquee placelisteinmbriquee placelistereference placelistoffloats placelistofsorts placelistofsynonyms placelogos placemarquespages placenotespdp placenotespdplocales placeregistre placeregistre placesousformule placesurgrille position prendbuffer programme publication qqpart ran raz razmarquage recommandation ref reference referencepage referencetexte reflete register reglealignement reglealineas reglearrangement reglearriereplan reglearriereplans reglebarreinteraction reglebarresynchronisation reglebloc regleblocmarge regleblocsection regleboutons reglebuffer reglecapitales reglechamp reglechamps regleclipping reglecolonnes reglecombinaisons reglecommentaire reglecompoetroite reglecomposeenalinea reglecouleur reglecouleurs regledactylo regledansmarge regledescriptions regledisposition regleecraninteraction regleecrans regleelements regleencadre regleentete regleenumerations regleenvironnementpolicecorps regleepaisseurligne regleespaceblanc regleespacement regleespacementinterligne reglefiguresexternes regleflottant regleflottants regleformulaires regleformules regleinf regleinteraction regleintitule regleintitules reglejeusymboles reglelabeltexte reglelangue reglelegende reglelignes reglelignesnoires reglelignesreglestexte regleliste reglelisteimbriquee reglelistereference reglemakeup reglemargereglee reglemarquage reglemarquagehyphenation reglemenuinteraction reglenotepdp reglenumeropage reglenumerotation reglenumerotationligne reglenumerotationpage reglenumerotationparagraphe reglenumerotete regleoriente reglepalette reglepapier regleparagraphes reglepdp regleplacementopposition reglepolicecorps reglepositionnement regleprofils regleprogrammes reglepublications reglereferencage regleregistre regleremplitligne regleremplitlignesreglees reglesection regleseparationflottant reglesortie reglesouslignage reglesousnumeropage reglestrut reglesup reglesynchronisation reglesynonymes reglesysteme regletab regletableaux regletabulation regletaillepapier regletete regletetes regletexte regletextesentete regletextesinf regletextespdp regletextessup regletextestexte regletextetete regletolerance regletraitsfins regletransitionspage regletri regletype regleurl regleversions remplitchamp remplitligne remplitlignesreglees remplittexte reservefloat resettextcontent sansalinea sansespace sansespaceblanc sanslignesenteteetpdp sanslignessupetinf sansliste sansmarquage sauteblocs section seeregister selectionneblocs selectionnepapier selectionneversion separeflottant settext setupfonthandling setupfontsynonym setupframedtexts setupinterlinespace2 setupitemgroup setuplistalternative sort startalignment startarriereplan startbuffer startbuffer startcitation startcolumns startcombination startcommentaire startcomposant startcouleur startdescription startdocument startenumeration startenvironement startfait startfigure startfloattext startformula startframedtext startgroupe starthiding startitemgroup startlegend startligneregleetexte startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmargereglee startmarginblock startmenuinteraction startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startproduit startprofile startprojet startregister startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startversion stopalignment stoparriereplan stopbuffer stopbuffer stopcitation stopcolumns stopcombination stopcommentaire stopcomposant stopcouleur stopdescription stopdocument stopenumeration stopenvironement stopfait stopfigure stopfloattext stopformula stopframedtext stopgroupe stophiding stopitemgroup stoplegend stopligneregleetexte stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmargereglee stopmarginblock stopmenuinteraction stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopproduit stopprofile stopprojet stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopversion sub subject subsection subsubject subsubsection subsubsubject suggestion suivantprofil suivantversion suivantversionprofil sym symbole synchronise synonym tab tapebuffer tapepage tete tex textemarge textenotepdp textetete title traduire traiteblocs traitepage traitfin traitsfins typ underbar underbars uneligne usedirectory usetypescript usetypescriptfile utiliseURL utiliseblocs utilisecommandes utilisedocumentexterne utiliseencodage utilisefichierexterne utilisefichiersexternes utilisefigureexterne utilisemodule utilisepsiteaudioexterne utilisereferences utilisespecialites utilisesymboles va vaalaboite vaenbas version vide vl \ No newline at end of file +keywordclass.macros.context.fr=CAP Cap Caps Caractere Caracteres Chiffresromains JOURSEMAINE MOIS MOT MOTS Mot Mots a adaptedisposition ajustechamp alaligne alapage aligneadroite aligneagauche aligneaumilieu appendix arriereplan baha barreinteraction barresynchronisation bas bouton boutonsinteraction but cacheblocs cap caractere caracteres champ changepolicecorps chapter chem chiffresromains citation citer clip clonechamp colonne comment comparegroupecouleur comparepalette completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregistre composeenalinea concernant convertitnumero copitchamp corrigeespaceblanc couleur couleurgrise coupledocument coupledregister couplemarquage couplepapier coupleregistre crlf dactylographier dans dansautremarge dansdroite dansgauche dansmarge date datecourante daterecommandation de decouplemarquage definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling defineframed defineframedtext definetypeface definit definitbloc definitblocsection definitbuffer definitchamp definitcompoalinea definitconversion definitcouleur definitdactylo definitdemarrestoppe definitdescription definitenumeration definitenvironnementpolicecorps definitetiquette definitflottant definitformatreference definitgroupecouleur definitliste definitlisteimbriquee definitlistereference definitlogo definitmakeup definitmarquage definitmenuinteraction definitmenuinteraction definitnotepdp definitpalette definitparagraphes definitpilechamp definitpolice definitpolicecorps definitprofil definitprogramme definitreference definitregistre definitrevetement definitsection definitsortie definitsouschamp definitsymbole definitsymbolefigure definitsynonymepolice definitsynonymes definittabulation definittaillepapier definittete definittexte definittrametableau definittri definittype definitversion definitvide definitvide demarreciter deplacesurgrille description determinecaracteristiqueliste determinenumerotete echelle ecran ecritdansliste ecritdanslistereference ecritentreliste ecritregistre el element elements emptylines enumeration espace espaceblanc espacesfixes etiquettes etire fichierdactylo figureexterne forceblocs fraction framed framedtext gardeblocs grille groupe haut hl indentation inframed inhibemenuinteraction ininner inouter installelangue joursemaine labeling labeltexte langue langueprincipale ligneh lignenoire ligneregleetexte lignesnoires listesymbole loadsorts loadsynonyms logchamp mapfontsize mar margereglee marquage marquageversion marquepage mathematique mediaeval mois montrecadre montrechamps montrecouleur montredisposition montreedition montreenvironnementpolicecorps montrefiguresexternes montregrille montregroupecouleur montrejeusymboles montremakeup montrepalette montrepolicecorps montrereglages montrestruts motdroit name nextsection nocap nop note notepdp numeros numerotete numerotetecourant obtientmarquage oriente overbar overbars overstrike overstrikes page pagedouble paragraph part pasplusdeblocs pasplusdefichiers periodes pilechamp placecoteacote placeflottant placeformule placelegende placelesunsaudessusdesautres placeliste placelisteinmbriquee placelisteinmbriquee placelistereference placelistoffloats placelistofsorts placelistofsynonyms placelogos placemarquespages placenotespdp placenotespdplocales placeregistre placeregistre placesousformule placesurgrille position prendbuffer programme publication qqpart ran raz razmarquage recommandation ref reference referencepage referencetexte reflete register reglealignement reglealineas reglearrangement reglearriereplan reglearriereplans reglebarreinteraction reglebarresynchronisation reglebloc regleblocmarge regleblocsection regleboutons reglebuffer reglecapitales reglechamp reglechamps regleclipping reglecolonnes reglecombinaisons reglecommentaire reglecompoetroite reglecomposeenalinea reglecouleur reglecouleurs regledactylo regledansmarge regledescriptions regledisposition regleecraninteraction regleecrans regleelements regleencadre regleentete regleenumerations regleenvironnementpolicecorps regleepaisseurligne regleespaceblanc regleespacement regleespacementinterligne reglefiguresexternes regleflottant regleflottants regleformulaires regleformules regleinf regleinteraction regleintitule regleintitules reglejeusymboles reglelabeltexte reglelangue reglelegende reglelignes reglelignesnoires reglelignesreglestexte regleliste reglelisteimbriquee reglelistereference reglemakeup reglemargereglee reglemarquage reglemarquagehyphenation reglemenuinteraction reglenotepdp reglenumeropage reglenumerotation reglenumerotationligne reglenumerotationpage reglenumerotationparagraphe reglenumerotete regleoriente reglepalette reglepapier regleparagraphes reglepdp regleplacementopposition reglepolicecorps reglepositionnement regleprofils regleprogrammes reglepublications reglereferencage regleregistre regleremplitligne regleremplitlignesreglees reglesection regleseparationflottant reglesortie reglesouslignage reglesousnumeropage reglestrut reglesup reglesynchronisation reglesynonymes reglesysteme regletab regletableaux regletabulation regletaillepapier regletete regletetes regletexte regletextesentete regletextesinf regletextespdp regletextessup regletextestexte regletextetete regletolerance regletraitsfins regletransitionspage regletri regletype regleurl regleversions remplitchamp remplitligne remplitlignesreglees remplittexte reservefloat resettextcontent sansalinea sansespace sansespaceblanc sanslignesenteteetpdp sanslignessupetinf sansliste sansmarquage sauteblocs section seeregister selectionneblocs selectionnepapier selectionneversion separeflottant settext setupfonthandling setupfontsynonym setupframedtexts setupinterlinespace2 setupitemgroup setuplistalternative sort startalignment startarriereplan startbuffer startbuffer startcitation startcolumns startcombination startcomment startcomposant startcouleur startdescription startdocument startenumeration startenvironement startfait startfigure startfloattext startformula startframedtext startgroupe starthiding startitemgroup startlegend startligneregleetexte startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmargereglee startmarginblock startmenuinteraction startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startproduit startprofile startprojet startregister startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startversion stopalignment stoparriereplan stopbuffer stopbuffer stopcitation stopcolumns stopcombination stopcomment stopcomposant stopcouleur stopdescription stopdocument stopenumeration stopenvironement stopfait stopfigure stopfloattext stopformula stopframedtext stopgroupe stophiding stopitemgroup stoplegend stopligneregleetexte stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmargereglee stopmarginblock stopmenuinteraction stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopproduit stopprofile stopprojet stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopversion sub subject subsection subsubject subsubsection subsubsubject suggestion suivantprofil suivantversion suivantversionprofil sym symbole synchronise synonym tab tapebuffer tapepage tete tex textemarge textenotepdp textetete title traduire traiteblocs traitepage traitfin traitsfins typ underbar underbars uneligne usedirectory usetypescript usetypescriptfile utiliseURL utiliseblocs utilisecommandes utilisedocumentexterne utiliseencodage utilisefichierexterne utilisefichiersexternes utilisefigureexterne utilisemodule utilisepsiteaudioexterne utilisereferences utilisespecialites utilisesymboles va vaalaboite vaenbas version vide vl \ No newline at end of file diff --git a/context/data/scite/cont-it-scite.properties b/context/data/scite/cont-it-scite.properties index da5df2400..699cd0f1e 100644 --- a/context/data/scite/cont-it-scite.properties +++ b/context/data/scite/cont-it-scite.properties @@ -1 +1 @@ -keywordclass.macros.context.it=CAP Cap Caps GIORNOSETTIMANA Lettera Lettere MESE Numeriromani PAROLA PAROLE Parola Parole accoppiacarta accoppiadocumento accoppiamarcatura accoppiapagina accoppiaregistro adattacampo adattalayout al allineacentro allineadestra allineasinistra ap apagina appendix barrainterazione barrasincronizzazione bastablocchi bastafile cambiaafontdeltesto campi camporiempimento cap capello chapter chim circondato citazione clip clonacampo colonna colore coloregrigio commento completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister confrontagruppocolori confrontatavolozza convertinumero copiacampo correggispaziobianco coupledregister crlf da daqualcheparte data datadioggi datareferral definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetypeface definisci definisciambientefontdeltesto definisciblocco definiscibloccosezione definiscibuffer definiscicampo definiscicapoversi definiscicolore definisciconversione definiscidescrizione definiscidimensionicarta definiscielenco definiscielencocombinato definiscienumerazione definiscietichetta definiscifigurasimbolo definiscifont definiscifontdeltesto definisciformatoriferimento definiscigruppocolori definisciincorniciato definisciiniziatermina definiscilistariferimenti definiscilogo definiscimakeup definiscimarcatura definiscimenuinterazione definiscimenuinterazione definiscimodellotabella definiscioggettomobile definisciordinamento definiscioutput definisciprofilo definisciprogramma definisciregistro definiscirientro definisciriferimento definiscirigovuoto definiscisezione definiscisimbolo definiscisinonimi definiscisinonimofont definiscisottocampo definiscisovrapposizione definiscistackcampi definiscitabulato definiscitavolozza definiscitesta definiscitesto definiscitestoincorniciato definiscitype definiscityping definisciversion description determinacarattersticheelenco determinanumerotesta disabilitamenuinterazione el elaborablocchi elaborapagina elementi elemento emptylines enumeration etichette figuraesterna fondo forzablocchi framedtext frazione giornosettimana griglia hl impaccato impostaallineamento impostaambientefontdeltesto impostaampiezzariga impostabarrainterazione impostabarrasincronizzazione impostablocchimargine impostablocco impostabloccosezione impostabuffer impostacampi impostacampo impostacapoversi impostacaption impostacaptions impostacima impostaclippling impostacolonne impostacolore impostacolori impostacombinazioni impostacommento impostadefinizionenotepdp impostadescrizioni impostadimensionicarta impostaelementi impostaelenco impostaelencocombinato impostaenumerazioni impostafigureesterne impostafondo impostafontdeltesto impostaforms impostaformule impostaincorniciato impostainmargine impostainstestazione impostainterazione impostainterlinea impostalayout impostalegenda impostalineemargine impostalineenere impostalineeriempimento impostalineesottili impostalineetesto impostalingua impostalistariferimenti impostamaiuscole impostamakeup impostamarcatura impostamenuinterazione impostamenzione impostanotepdp impostanumerazione impostanumerazionecapoversi impostanumerazionepagina impostanumerazionerighe impostanumeropagina impostanumerosottopagina impostanumerotesta impostaoggettimobili impostaoggettomobile impostaordinamento impostaoutput impostaparranging impostapdp impostapiustretto impostaposizionamento impostaposizionamentoopposti impostaprofili impostaprogrammi impostapubblicazioni impostapulsanti impostaregistro impostarientri impostarientro impostariferimento impostarighe impostarigheriempimento impostarigovuoto impostarotazione impostaschermi impostaschermointerazione impostasegnosillabazione impostasetsimboli impostasezione impostasfondi impostasfondo impostasincronizzazione impostasinonimi impostasistema impostasottolinea impostaspaziatura impostaspaziobianco impostaspezzamentooggettomobile impostastrut impostatab impostatabelle impostatabulato impostatavolozza impostatesta impostateste impostatesticima impostatestifondo impostatestiincorniciati impostatestiintestazioni impostatestipdp impostatesto impostatestoetichette impostatestointestazioni impostatestotesti impostatolleranza impostatransizionepagina impostatype impostatyping impostaurl impostaversioni in inaltromargine incorniciato indentation indestra ininner inmargine inouter inriga insinistra installalingua intorno labeling lettera lettere lineamargine lineanera lineasottile lineatesto lineenere lineeriempimento lineesottili lingua linguaprincipale listsymbol loadsorts loadsynonyms logcampi mapfontsize mar marcatura marcaversione matematica mediaeval menzione mese mettielenco mettielencocombinato mettielencocombinato mettifiancoafianco mettiformula mettiingriglia mettilegenda mettiloghi mettinotepdp mettinotepdplocali mettiregistro mettiregistro mettisegnalibro mettisottoformula mettiunosullaltro mostraambientefontdeltesto mostracampi mostracolore mostracornice mostrafiguresterne mostrafontdeltesto mostragriglia mostragruppocolori mostraimpostazioni mostralyout mostramakeup mostrasetsimboli mostrastampa mostrastruts mostratavolozza name nascondiblocchi nextsection nienteelenco nientelineecimafondo nientelineintestazionepdp nientemarcatura nienterientro nientespazio nientespaziobianco nocap nop nota notapdp numeri numeriromani numerotesta numerotestacorrente overbar overbars overstrike overstrikes pagina paragraph paroladestra part ped pedap placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist posizione prendibuffer prendimarcatura programma pubblicazione pulsante pulsantinterazione punti qualcheriga ran referral register reimposta reimpostamarcatura reservefloat resettextcontent rientro rif riferimento riferimentopagina riferimentotesto riflessione rigariempimento rigovuoto ruota saltablocchi scala schermo scrividentroelenco scriviinelenco scriviinlistariferimenti scriviinregistro section seeregister segnalibro seguiprofilo seguiversione seguiversioneprofilo selezionablocchi selezionacarta selezionaversione separamarcatura settext setupfonthandling setupfontsynonym setupinterlinespace2 setupitemgroup setuplistalternative setuppaper sfondo sim simbolo sincronizza sort spazifissi spazio spaziobianco spezzaoggettomobile spostaagriglia stackcampi startalignment startambiente startbuffer startbuffer startcitazione startcolore startcolumns startcombination startcommento startcomponenet startdescription startdocument startenumeration startfatto startfigure startfloattext startformula startframedtext starthiding startimpaccato startitemgroup startlegend startline startlineamargine startlineatesto startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startmenuinterattivo startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodotto startprofile startprogetto startregister startsfondo startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startversione stirato stopalignment stopambiente stopbuffer stopbuffer stopcitazione stopcolore stopcolumns stopcombination stopcommento stopcomponenet stopdescription stopdocument stopenumeration stopfatto stopfigure stopfloattext stopformula stopframedtext stophiding stopimpaccato stopitemgroup stoplegend stopline stoplineamargine stoplineatesto stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopmenuinterattivo stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodotto stopprofile stopprogetto stopsfondo stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopversione sub subject subsection subsubject subsubsection subsubsubject synonym tab testa testoetichetta testoinmargine testoinstestazioni testonotapdp testoriempimento tex tieniblocchi tipopagina title tooltip traduci typ type typebuffer typefile underbar underbars usaURL usablocco usacodifica usacolonnasonoraesterna usacomandi usadocumentoesterno usafiguraesterna usafileesterni usafileesterno usamodulo usariferimenti usasimboli usaspecialita usedirectory usetypescript usetypescriptfile vaia vaiabox vaigiu versione vl \ No newline at end of file +keywordclass.macros.context.it=CAP Cap Caps GIORNOSETTIMANA Lettera Lettere MESE Numeriromani PAROLA PAROLE Parola Parole accoppiacarta accoppiadocumento accoppiamarcatura accoppiapagina accoppiaregistro adattacampo adattalayout al allineacentro allineadestra allineasinistra ap apagina appendix barrainterazione barrasincronizzazione bastablocchi bastafile cambiaafontdeltesto campi camporiempimento cap capello chapter chim circondato citazione clip clonacampo colonna colore coloregrigio comment completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister confrontagruppocolori confrontatavolozza convertinumero copiacampo correggispaziobianco coupledregister crlf da daqualcheparte data datadioggi datareferral definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetypeface definisci definisciambientefontdeltesto definisciblocco definiscibloccosezione definiscibuffer definiscicampo definiscicapoversi definiscicolore definisciconversione definiscidescrizione definiscidimensionicarta definiscielenco definiscielencocombinato definiscienumerazione definiscietichetta definiscifigurasimbolo definiscifont definiscifontdeltesto definisciformatoriferimento definiscigruppocolori definisciincorniciato definisciiniziatermina definiscilistariferimenti definiscilogo definiscimakeup definiscimarcatura definiscimenuinterazione definiscimenuinterazione definiscimodellotabella definiscioggettomobile definisciordinamento definiscioutput definisciprofilo definisciprogramma definisciregistro definiscirientro definisciriferimento definiscirigovuoto definiscisezione definiscisimbolo definiscisinonimi definiscisinonimofont definiscisottocampo definiscisovrapposizione definiscistackcampi definiscitabulato definiscitavolozza definiscitesta definiscitesto definiscitestoincorniciato definiscitype definiscityping definisciversion description determinacarattersticheelenco determinanumerotesta disabilitamenuinterazione el elaborablocchi elaborapagina elementi elemento emptylines enumeration etichette figuraesterna fondo forzablocchi framedtext frazione giornosettimana griglia hl impaccato impostaallineamento impostaambientefontdeltesto impostaampiezzariga impostabarrainterazione impostabarrasincronizzazione impostablocchimargine impostablocco impostabloccosezione impostabuffer impostacampi impostacampo impostacapoversi impostacaption impostacaptions impostacima impostaclippling impostacolonne impostacolore impostacolori impostacombinazioni impostacommento impostadefinizionenotepdp impostadescrizioni impostadimensionicarta impostaelementi impostaelenco impostaelencocombinato impostaenumerazioni impostafigureesterne impostafondo impostafontdeltesto impostaforms impostaformule impostaincorniciato impostainmargine impostainstestazione impostainterazione impostainterlinea impostalayout impostalegenda impostalineemargine impostalineenere impostalineeriempimento impostalineesottili impostalineetesto impostalingua impostalistariferimenti impostamaiuscole impostamakeup impostamarcatura impostamenuinterazione impostamenzione impostanotepdp impostanumerazione impostanumerazionecapoversi impostanumerazionepagina impostanumerazionerighe impostanumeropagina impostanumerosottopagina impostanumerotesta impostaoggettimobili impostaoggettomobile impostaordinamento impostaoutput impostaparranging impostapdp impostapiustretto impostaposizionamento impostaposizionamentoopposti impostaprofili impostaprogrammi impostapubblicazioni impostapulsanti impostaregistro impostarientri impostarientro impostariferimento impostarighe impostarigheriempimento impostarigovuoto impostarotazione impostaschermi impostaschermointerazione impostasegnosillabazione impostasetsimboli impostasezione impostasfondi impostasfondo impostasincronizzazione impostasinonimi impostasistema impostasottolinea impostaspaziatura impostaspaziobianco impostaspezzamentooggettomobile impostastrut impostatab impostatabelle impostatabulato impostatavolozza impostatesta impostateste impostatesticima impostatestifondo impostatestiincorniciati impostatestiintestazioni impostatestipdp impostatesto impostatestoetichette impostatestointestazioni impostatestotesti impostatolleranza impostatransizionepagina impostatype impostatyping impostaurl impostaversioni in inaltromargine incorniciato indentation indestra ininner inmargine inouter inriga insinistra installalingua intorno labeling lettera lettere lineamargine lineanera lineasottile lineatesto lineenere lineeriempimento lineesottili lingua linguaprincipale listsymbol loadsorts loadsynonyms logcampi mapfontsize mar marcatura marcaversione matematica mediaeval menzione mese mettielenco mettielencocombinato mettielencocombinato mettifiancoafianco mettiformula mettiingriglia mettilegenda mettiloghi mettinotepdp mettinotepdplocali mettiregistro mettiregistro mettisegnalibro mettisottoformula mettiunosullaltro mostraambientefontdeltesto mostracampi mostracolore mostracornice mostrafiguresterne mostrafontdeltesto mostragriglia mostragruppocolori mostraimpostazioni mostralyout mostramakeup mostrasetsimboli mostrastampa mostrastruts mostratavolozza name nascondiblocchi nextsection nienteelenco nientelineecimafondo nientelineintestazionepdp nientemarcatura nienterientro nientespazio nientespaziobianco nocap nop nota notapdp numeri numeriromani numerotesta numerotestacorrente overbar overbars overstrike overstrikes pagina paragraph paroladestra part ped pedap placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist posizione prendibuffer prendimarcatura programma pubblicazione pulsante pulsantinterazione punti qualcheriga ran referral register reimposta reimpostamarcatura reservefloat resettextcontent rientro rif riferimento riferimentopagina riferimentotesto riflessione rigariempimento rigovuoto ruota saltablocchi scala schermo scrividentroelenco scriviinelenco scriviinlistariferimenti scriviinregistro section seeregister segnalibro seguiprofilo seguiversione seguiversioneprofilo selezionablocchi selezionacarta selezionaversione separamarcatura settext setupfonthandling setupfontsynonym setupinterlinespace2 setupitemgroup setuplistalternative setuppaper sfondo sim simbolo sincronizza sort spazifissi spazio spaziobianco spezzaoggettomobile spostaagriglia stackcampi startalignment startambiente startbuffer startbuffer startcitazione startcolore startcolumns startcombination startcomment startcomponenet startdescription startdocument startenumeration startfatto startfigure startfloattext startformula startframedtext starthiding startimpaccato startitemgroup startlegend startline startlineamargine startlineatesto startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startmenuinterattivo startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodotto startprofile startprogetto startregister startsfondo startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startversione stirato stopalignment stopambiente stopbuffer stopbuffer stopcitazione stopcolore stopcolumns stopcombination stopcomment stopcomponenet stopdescription stopdocument stopenumeration stopfatto stopfigure stopfloattext stopformula stopframedtext stophiding stopimpaccato stopitemgroup stoplegend stopline stoplineamargine stoplineatesto stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopmenuinterattivo stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodotto stopprofile stopprogetto stopsfondo stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopversione sub subject subsection subsubject subsubsection subsubsubject synonym tab testa testoetichetta testoinmargine testoinstestazioni testonotapdp testoriempimento tex tieniblocchi tipopagina title tooltip traduci typ type typebuffer typefile underbar underbars usaURL usablocco usacodifica usacolonnasonoraesterna usacomandi usadocumentoesterno usafiguraesterna usafileesterni usafileesterno usamodulo usariferimenti usasimboli usaspecialita usedirectory usetypescript usetypescriptfile vaia vaiabox vaigiu versione vl \ No newline at end of file diff --git a/context/data/scite/cont-nl-scite.properties b/context/data/scite/cont-nl-scite.properties index 688c8a7c0..a36e8f475 100644 --- a/context/data/scite/cont-nl-scite.properties +++ b/context/data/scite/cont-nl-scite.properties @@ -1 +1 @@ -keywordclass.macros.context.nl=CAP Cap Caps Letter Letters MAAND Romeins WEEKDAG WOORD WOORDEN Woord Woorden about achtergrond appendix bepaalkopnummer bepaallijstkenmerken blanko blokje blokjes blokkeerinteractiemenu bookmark breuk but button cap chapter chem cijfers citaat citeer clip commentaar completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms converteernummer copieerveld corrigeerwitruimte coupledregister crlf datum definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetypeface definieer definieeralineas definieerbeeldmerk definieerblanko definieerblok definieerbuffer definieerconversie definieerfiguursymbool definieerfont definieerfontsynoniem definieerinteractiemenu definieerinteractiemenu definieerkadertekst definieerkleur definieerkleurgroep definieerkop definieerkorps definieerkorpsomgeving definieerlijst definieermarkering definieeromlijnd definieeropmaak definieeroverlay definieerpalet definieerpapierformaat definieerplaatsblok definieerprofiel definieerprogramma definieerreferentie definieerreferentieformaat definieerreferentielijst definieerregister definieersamengesteldelijst definieersectie definieersectieblok definieersorteren definieerstartstop definieersubveld definieersymbool definieersynoniemen definieertabelvorm definieertabulatie definieertekst definieertype definieertypen definieeruitvoer definieerveld definieerveldstapel definieerversie description doordefinieren doorlabelen doornummeren doorspringen dunnelijn dunnelijnen eenregel enumeration ergens externfiguur forceerblokken framedtext gebruikURL gebruikblokken gebruikcommandos gebruikexterndocument gebruikexternefile gebruikexternefiles gebruikexternfiguur gebruikexterngeluidsfragment gebruikmodule gebruikreferenties gebruikspecials gebruiksymbolen gebruiktypescript gebruiktypescriptfile geenblokkenmeer geenbovenenonderregels geenfilesmeer geenhoofdenvoetregels geenlijst geenmarkering geenspatie geenwitruimte grijskleur haalbuffer haalmarkering haarlijn handhaafblokken hl hoofdtaal hoog huidigedatum huidigekopnummer in inanderemarge inbinnen inbuiten indentation inlijnd inlinker inmarge inrechter inregel inspringen installeertaal interactiebalk interactiebuttons invullijnen invulregel invultekst invulveld items its kantlijn kenmerk kenmerkdatum kleur kloonveld kolom kop kopnummer koppeldocument koppelmarkering koppelpagina koppelpapier koppelregister koptekst laag labeling labels labeltekst laho legeregels letter letters lijstsymbool loadsorts loadsynonyms maand mapfontsize mar margetekst markeer markeerversie mediaeval naar naarbox name nextsection nietinspringen nocap noot nop omlaag omlijnd ontkoppelmarkering op opelkaar oppagina overbar overbars overstrike overstrikes pagina paginareferentie paragraph part paslayoutaan passeerblokken passendveld plaatsbeeldmerken plaatsbookmarks plaatsformule plaatslegenda plaatslijst plaatslokalevoetnoten plaatsnaastelkaar plaatsonderelkaar plaatsopgrid plaatsplaatsblok plaatsreferentielijst plaatsregister plaatsregister plaatssamengesteldelijst plaatssamengesteldelijst plaatssubformule plaatsvoetnoten placelistoffloats placelistofsorts placelistofsynonyms positioneer programma publicatie punten ran ref referentie regellinks regelmidden regelrechts register registreervelden reservefloat reset resetmarkering resettextcontent romeins rooster roteer schaal scherm schrijfnaarlijst schrijfnaarreferentielijst schrijfnaarregister schrijftussenlijst section seeregister selecteerblokken selecteerpapier selecteerversie setupfonthandling setupfontsynonym setupinterlinespace2 setuplistalternative som soortpagina sort spatie spiegel splitsplaatsblok startachtergrond startalignment startbuffer startbuffer startcitaat startcolumns startcombination startcommentaar startdescription startdocument startenumeration startfigure startfloattext startformula startframedtext startgeg starthiding startinteractiemenu startitemgroup startkantlijn startkleur startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startnamemakeup startnarrower startomgeving startonderdeel startopelkaar startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodukt startprofile startprojekt startregister startsymbolset startsynchronization starttable starttables starttabulate starttekstlijn starttyping startunpacked startversie stelachtergrondenin stelachtergrondin stelalineasin stelarrangerenin stelblankoin stelblokin stelblokjesin stelblokkopjein stelblokkopjesin stelbovenin stelboventekstenin stelbufferin stelbuttonsin stelciterenin stelclipin stelcombinatiesin stelcommentaarin steldoordefinierenin steldoornummerenin steldoorspringenin steldunnelijnenin stelexternefigurenin stelformulesin stelformulierenin stelhoofdin stelhoofdtekstenin stelinmargein stelinspringenin stelinteractiebalkin stelinteractiein stelinteractiemenuin stelinteractieschermin stelinterliniein stelinvullijnenin stelinvulregelsin stelitemgroepin stelitemsin stelkadertekstenin stelkantlijnin stelkapitalenin stelkleurenin stelkleurin stelkolommenin stelkopin stelkopnummerin stelkoppeltekenin stelkoppenin stelkoptekstin stelkorpsin stelkorpsomgevingin stellabeltekstin stellayoutin stellegendain stellijndiktein stellijstin stelmargeblokkenin stelmarkeringin stelnaastplaatsenin stelnummerenin stelomlijndin stelonderin stelonderstrepenin stelondertekstenin stelopmaakin stelpaginanummerin stelpaginanummeringin stelpaginaovergangenin stelpaletin stelpapierformaatin stelpapierin stelparagraafnummerenin stelplaatsblokin stelplaatsblokkenin stelplaatsbloksplitsenin stelpositionerenin stelprofielenin stelprogrammasin stelpublicatiesin stelrastersin stelreferentielijstin stelrefererenin stelregelnummerenin stelregelsin stelregisterin stelroterenin stelsamengesteldelijstin stelsectieblokin stelsectiein stelsmallerin stelsorterenin stelspatieringin stelstrutin stelsubpaginanummerin stelsymboolsetin stelsynchronisatiebalkin stelsynchronisatiein stelsynoniemenin stelsysteemin steltaalin steltabellenin steltabin steltabulatiein steltekstin steltekstinhoudin steltekstlijnenin stelteksttekstenin steltolerantiein steltypein steltypenin steluitlijnenin steluitvoerin stelurlin stelveldenin stelveldin stelversiesin stelvoetin stelvoetnootdefinitiein stelvoetnotenin stelvoettekstenin stelwitruimtein stopachtergrond stopalignment stopbuffer stopbuffer stopcitaat stopcolumns stopcombination stopcommentaar stopdescription stopdocument stopenumeration stopfigure stopfloattext stopformula stopframedtext stopgeg stophiding stopinteractiemenu stopitemgroup stopkantlijn stopkleur stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopnamemakeup stopnarrower stopomgeving stoponderdeel stopopelkaar stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodukt stopprofile stopprojekt stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptekstlijn stoptyping stopunpacked stopversie sub subject subsection subsubject subsubsection subsubsubject suggestie switchnaarkorps sym symbool synchronisatiebalk synchroniseer synonym taal tab tekstlijn tekstreferentie tex title toonexternefiguren toongrid tooninstellingen toonkader toonkleur toonkleurgroep toonkorps toonkorpsomgeving toonlayout toonopmaak toonpalet toonprint toonstruts toonsymboolset toonvelden typ type typebuffer typefile uit uitgerekt underbar underbars usecodering usedirectory vastespaties veld veldstapel verbergblokken vergelijkkleurgroep vergelijkpalet verplaatsopgrid versie vertaal verwerkblokken verwerkpagina vl voetnoot voetnoottekst volgprofiel volgprofielversie volgversie volledigregister weekdag wiskunde witruimte woordrechts \ No newline at end of file +keywordclass.macros.context.nl=CAP Cap Caps Letter Letters MAAND Romeins WEEKDAG WOORD WOORDEN Woord Woorden about achtergrond appendix bepaalkopnummer bepaallijstkenmerken blanko blokje blokjes blokkeerinteractiemenu bookmark breuk but button cap chapter chem cijfers citaat citeer clip comment completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms converteernummer copieerveld corrigeerwitruimte coupledregister crlf datum definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetypeface definieer definieeralineas definieerbeeldmerk definieerblanko definieerblok definieerbuffer definieerconversie definieerfiguursymbool definieerfont definieerfontsynoniem definieerinteractiemenu definieerinteractiemenu definieerkadertekst definieerkleur definieerkleurgroep definieerkop definieerkorps definieerkorpsomgeving definieerlijst definieermarkering definieeromlijnd definieeropmaak definieeroverlay definieerpalet definieerpapierformaat definieerplaatsblok definieerprofiel definieerprogramma definieerreferentie definieerreferentieformaat definieerreferentielijst definieerregister definieersamengesteldelijst definieersectie definieersectieblok definieersorteren definieerstartstop definieersubveld definieersymbool definieersynoniemen definieertabelvorm definieertabulatie definieertekst definieertype definieertypen definieeruitvoer definieerveld definieerveldstapel definieerversie description doordefinieren doorlabelen doornummeren doorspringen dunnelijn dunnelijnen eenregel enumeration ergens externfiguur forceerblokken framedtext gebruikURL gebruikblokken gebruikcommandos gebruikexterndocument gebruikexternefile gebruikexternefiles gebruikexternfiguur gebruikexterngeluidsfragment gebruikmodule gebruikreferenties gebruikspecials gebruiksymbolen gebruiktypescript gebruiktypescriptfile geenblokkenmeer geenbovenenonderregels geenfilesmeer geenhoofdenvoetregels geenlijst geenmarkering geenspatie geenwitruimte grijskleur haalbuffer haalmarkering haarlijn handhaafblokken hl hoofdtaal hoog huidigedatum huidigekopnummer in inanderemarge inbinnen inbuiten indentation inlijnd inlinker inmarge inrechter inregel inspringen installeertaal interactiebalk interactiebuttons invullijnen invulregel invultekst invulveld items its kantlijn kenmerk kenmerkdatum kleur kloonveld kolom kop kopnummer koppeldocument koppelmarkering koppelpagina koppelpapier koppelregister koptekst laag labeling labels labeltekst laho legeregels letter letters lijstsymbool loadsorts loadsynonyms maand mapfontsize mar margetekst markeer markeerversie mediaeval naar naarbox name nextsection nietinspringen nocap noot nop omlaag omlijnd ontkoppelmarkering op opelkaar oppagina overbar overbars overstrike overstrikes pagina paginareferentie paragraph part paslayoutaan passeerblokken passendveld plaatsbeeldmerken plaatsbookmarks plaatsformule plaatslegenda plaatslijst plaatslokalevoetnoten plaatsnaastelkaar plaatsonderelkaar plaatsopgrid plaatsplaatsblok plaatsreferentielijst plaatsregister plaatsregister plaatssamengesteldelijst plaatssamengesteldelijst plaatssubformule plaatsvoetnoten placelistoffloats placelistofsorts placelistofsynonyms positioneer programma publicatie punten ran ref referentie regellinks regelmidden regelrechts register registreervelden reservefloat reset resetmarkering resettextcontent romeins rooster roteer schaal scherm schrijfnaarlijst schrijfnaarreferentielijst schrijfnaarregister schrijftussenlijst section seeregister selecteerblokken selecteerpapier selecteerversie setupfonthandling setupfontsynonym setupinterlinespace2 setuplistalternative som soortpagina sort spatie spiegel splitsplaatsblok startachtergrond startalignment startbuffer startbuffer startcitaat startcolumns startcombination startcomment startdescription startdocument startenumeration startfigure startfloattext startformula startframedtext startgeg starthiding startinteractiemenu startitemgroup startkantlijn startkleur startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startnamemakeup startnarrower startomgeving startonderdeel startopelkaar startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodukt startprofile startprojekt startregister startsymbolset startsynchronization starttable starttables starttabulate starttekstlijn starttyping startunpacked startversie stelachtergrondenin stelachtergrondin stelalineasin stelarrangerenin stelblankoin stelblokin stelblokjesin stelblokkopjein stelblokkopjesin stelbovenin stelboventekstenin stelbufferin stelbuttonsin stelciterenin stelclipin stelcombinatiesin stelcommentaarin steldoordefinierenin steldoornummerenin steldoorspringenin steldunnelijnenin stelexternefigurenin stelformulesin stelformulierenin stelhoofdin stelhoofdtekstenin stelinmargein stelinspringenin stelinteractiebalkin stelinteractiein stelinteractiemenuin stelinteractieschermin stelinterliniein stelinvullijnenin stelinvulregelsin stelitemgroepin stelitemsin stelkadertekstenin stelkantlijnin stelkapitalenin stelkleurenin stelkleurin stelkolommenin stelkopin stelkopnummerin stelkoppeltekenin stelkoppenin stelkoptekstin stelkorpsin stelkorpsomgevingin stellabeltekstin stellayoutin stellegendain stellijndiktein stellijstin stelmargeblokkenin stelmarkeringin stelnaastplaatsenin stelnummerenin stelomlijndin stelonderin stelonderstrepenin stelondertekstenin stelopmaakin stelpaginanummerin stelpaginanummeringin stelpaginaovergangenin stelpaletin stelpapierformaatin stelpapierin stelparagraafnummerenin stelplaatsblokin stelplaatsblokkenin stelplaatsbloksplitsenin stelpositionerenin stelprofielenin stelprogrammasin stelpublicatiesin stelrastersin stelreferentielijstin stelrefererenin stelregelnummerenin stelregelsin stelregisterin stelroterenin stelsamengesteldelijstin stelsectieblokin stelsectiein stelsmallerin stelsorterenin stelspatieringin stelstrutin stelsubpaginanummerin stelsymboolsetin stelsynchronisatiebalkin stelsynchronisatiein stelsynoniemenin stelsysteemin steltaalin steltabellenin steltabin steltabulatiein steltekstin steltekstinhoudin steltekstlijnenin stelteksttekstenin steltolerantiein steltypein steltypenin steluitlijnenin steluitvoerin stelurlin stelveldenin stelveldin stelversiesin stelvoetin stelvoetnootdefinitiein stelvoetnotenin stelvoettekstenin stelwitruimtein stopachtergrond stopalignment stopbuffer stopbuffer stopcitaat stopcolumns stopcombination stopcomment stopdescription stopdocument stopenumeration stopfigure stopfloattext stopformula stopframedtext stopgeg stophiding stopinteractiemenu stopitemgroup stopkantlijn stopkleur stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopnamemakeup stopnarrower stopomgeving stoponderdeel stopopelkaar stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodukt stopprofile stopprojekt stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptekstlijn stoptyping stopunpacked stopversie sub subject subsection subsubject subsubsection subsubsubject suggestie switchnaarkorps sym symbool synchronisatiebalk synchroniseer synonym taal tab tekstlijn tekstreferentie tex title toonexternefiguren toongrid tooninstellingen toonkader toonkleur toonkleurgroep toonkorps toonkorpsomgeving toonlayout toonopmaak toonpalet toonprint toonstruts toonsymboolset toonvelden typ type typebuffer typefile uit uitgerekt underbar underbars usecodering usedirectory vastespaties veld veldstapel verbergblokken vergelijkkleurgroep vergelijkpalet verplaatsopgrid versie vertaal verwerkblokken verwerkpagina vl voetnoot voetnoottekst volgprofiel volgprofielversie volgversie volledigregister weekdag wiskunde witruimte woordrechts \ No newline at end of file diff --git a/context/data/scite/cont-pe-scite.properties b/context/data/scite/cont-pe-scite.properties index a17695725..c89f52b8f 100644 --- a/context/data/scite/cont-pe-scite.properties +++ b/context/data/scite/cont-pe-scite.properties @@ -1 +1 @@ -keywordclass.macros.context.pe=CAP Cap Caps Character Characters MONTH Romannumerals WEEKDAY WORD WORDS Word Words appendix cap chapter chem completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms coupledregister crlf definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetypeface description enumeration framedtext indentation its labeling loadsorts loadsynonyms mapfontsize mediaeval name nextsection nocap overbar overbars overstrike overstrikes paragraph part placelistoffloats placelistofsorts placelistofsynonyms ran register reservefloat resettextcontent section seeregister setupcapitals setupfonthandling setupfontsynonym setupinterlinespace2 setuplistalternative setupurl sort startalignment startbuffer startbuffer startcolumns startcombination startdescription startdocument startenumeration startfigure startfloattext startformula startframedtext starthiding startitemgroup startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprofile startregister startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startتوضیح startتولید startحقیقت startخط‌حاشیه startخط‌متن startرنگ startفشرده startمحیط startمنوی‌پانل startمولفه startنسخه startنقل‌قول startپروژه startپس‌زمینه stopalignment stopbuffer stopbuffer stopcolumns stopcombination stopdescription stopdocument stopenumeration stopfigure stopfloattext stopformula stopframedtext stophiding stopitemgroup stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprofile stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopتوضیح stopتولید stopحقیقت stopخط‌حاشیه stopخط‌متن stopرنگ stopفشرده stopمحیط stopمنوی‌پانل stopمولفه stopنسخه stopنقل‌قول stopپروژه stopپس‌زمینه sub subject subsection subsubject subsubsection subsubsubject synonym title tooltip typ underbar underbars useURL usedirectory آیتم آیتمها آینه اجباربلوکها از ازکارانداختن‌منوی‌پانل استفاده‌بلوکها استفاده‌دستخط‌تایپ استفاده‌رمزینه استفاده‌شکل‌خارجی استفاده‌فرمانها استفاده‌قطعه‌موزیک‌خارجی استفاده‌مدول استفاده‌مرجعها استفاده‌نمادها استفاده‌نوشتارخارجی استفاده‌ویژگیها استفاده‌پرونده‌خارجی استفاده‌پرونده‌دستخط‌تایپ استفاده‌پرونده‌های‌خارجی اعدادلاتین اما انتخاب‌برگ انتخاب‌بلوکها انتخاب‌نسخه انتقال‌به‌توری بارگذاری‌آرایش بارگذاری‌آیتمها بارگذاری‌ارجاع بارگذاری‌اندازه‌برگ بارگذاری‌باریکتر بارگذاری‌بافر بارگذاری‌بالا بارگذاری‌بخش بارگذاری‌بردباری بارگذاری‌برنامه‌ها بارگذاری‌برگ بارگذاری‌بست بارگذاری‌بلوک بارگذاری‌بلوکهای‌حاشیه بارگذاری‌بلوک‌بخش بارگذاری‌تایپ بارگذاری‌تایپ‌کردن بارگذاری‌تب بارگذاری‌ترتیب بارگذاری‌ترتیب بارگذاری‌ترکیب‌ها بارگذاری‌تطابق بارگذاری‌تعریف‌پانوشت بارگذاری‌تنظیم بارگذاری‌ته‌برگ بارگذاری‌تورفتگی بارگذاری‌تورفتگیها بارگذاری‌توضیح بارگذاری‌ثبت بارگذاری‌جدولها بارگذاری‌جدول‌بندی بارگذاری‌خالی بارگذاری‌خروجی بارگذاری‌خطها بارگذاری‌خطهای‌حاشیه بارگذاری‌خطهای‌سیاه بارگذاری‌خطهای‌متن بارگذاری‌خطها‌ی‌نازک بارگذاری‌درج‌درخطها بارگذاری‌درج‌مخالف بارگذاری‌درون‌حاشیه بارگذاری‌دوران بارگذاری‌دکمه‌ها بارگذاری‌راهنما بارگذاری‌رنگ بارگذاری‌رنگها بارگذاری‌زبان بارگذاری‌ستونها بارگذاری‌سر بارگذاری‌سربرگ بارگذاری‌سرها بارگذاری‌سیستم بارگذاری‌شرح بارگذاری‌شرح بارگذاری‌شرحها بارگذاری‌شماره‌زیرصفحه بارگذاری‌شماره‌سر بارگذاری‌شماره‌صفحه بارگذاری‌شماره‌گذاری بارگذاری‌شماره‌گذاریها بارگذاری‌شماره‌گذاری‌صفحه بارگذاری‌شماره‌گذاری‌پاراگراف بارگذاری‌شماره‌‌گذاری‌خط بارگذاری‌شناور بارگذاری‌شناورها بارگذاری‌شکافتن‌شناورها بارگذاری‌شکلهای‌خارجی بارگذاری‌طرح بارگذاری‌طرح‌بندی بارگذاری‌عرض‌خط بارگذاری‌فاصله‌بین‌خط بارگذاری‌فرمولها بارگذاری‌فضای‌سفید بارگذاری‌فضا‌گذاری بارگذاری‌قالبی بارگذاری‌قلم‌متن بارگذاری‌لوح بارگذاری‌لیست بارگذاری‌لیست‌ترکیبی بارگذاری‌لیست‌مرجع بارگذاری‌مترادفها بارگذاری‌متن بارگذاری‌متنهای‌بالا بارگذاری‌متن‌سر بارگذاری‌متن‌سربرگ بارگذاری‌متن‌قالبی بارگذاری‌متن‌متنها بارگذاری‌متن‌پانوشت بارگذاری‌متن‌پایین بارگذاری‌مجموعه‌نماد بارگذاری‌محیط‌قلم‌متن بارگذاری‌منوی‌پانل بارگذاری‌مکان‌گذاری بارگذاری‌میدان بارگذاری‌میدانها بارگذاری‌میله‌تطابق بارگذاری‌میله‌زیر بارگذاری‌میله‌پانل بارگذاری‌نسخه‌ها بارگذاری‌نشانه‌شکستن بارگذاری‌نشانه‌گذاری بارگذاری‌نشرها بارگذاری‌نقل بارگذاری‌پاراگرافها بارگذاری‌پانل بارگذاری‌پانوشتها بارگذاری‌پایین بارگذاری‌پرده‌ها بارگذاری‌پرده‌پانل بارگذاری‌پروفایلها بارگذاری‌پرکردن‌خطها بارگذاری‌پس‌زمینه بارگذاری‌پس‌زمینه‌ها بارگذاری‌چیدن بارگذاری‌گذارصفحه بارگذاری‌گروه‌آیتم بازنشانی بازنشانی‌نشانه‌گذاری باگذاری‌متن‌برچسب بدون‌بلوکهای‌بیشتر بدون‌تورفتگی بدون‌خط‌بالاوپایین بدون‌خط‌سروته‌برگ بدون‌فایلهای‌بیشتر بدون‌فضا بدون‌فضای‌سفید بدون‌لیست بدون‌نشانه‌گذاری برنامه بروبه بروبه‌جعبه بروپایین برچسبها بلند بلوکهای‌پردازش بلوکها‌پنهان بنویس‌بین‌لیست بنویس‌درثبت بنویس‌درلیست‌مرجع بنویس‌در‌لیست تاریخ تاریخ‌جاری تاریخ‌رجوع تایپ تایپ‌بافر تایپ‌پرونده تب ترجمه تطابق تعریف تعریف‌آرایش تعریف‌آرم تعریف‌الگوی‌جدول تعریف‌اندازه‌برگ تعریف‌بافر تعریف‌بخش تعریف‌برنامه تعریف‌برچسب تعریف‌بلوک تعریف‌بلوک‌بخش تعریف‌تایپ تعریف‌تایپ‌کردن تعریف‌تبدیل تعریف‌ترتیب تعریف‌توده‌میدان تعریف‌تورفتگی تعریف‌ثبت تعریف‌جدول‌بندی تعریف‌خالی تعریف‌خروجی تعریف‌رنگ تعریف‌زیرمیدان تعریف‌سر تعریف‌شرح تعریف‌شروع‌پایان تعریف‌شماره‌بندی تعریف‌شمایل‌مرجع تعریف‌شناور تعریف‌قالبی تعریف‌قلم تعریف‌قلم‌متن تعریف‌لوح تعریف‌لیست تعریف‌لیست‌ترکیبی تعریف‌لیست‌مرجع تعریف‌مترادفها تعریف‌مترادف‌قلم تعریف‌متن تعریف‌متن‌قالبی تعریف‌محیط‌قلم‌بدنه تعریف‌مرجع تعریف‌منوی‌پانل تعریف‌منوی‌پانل تعریف‌میدان تعریف‌نسخه تعریف‌نشانه‌گذاری تعریف‌نماد تعریف‌نمادشکل تعریف‌پاراگرافها تعریف‌پروفایل تعریف‌پوشش تعریف‌گروه‌رنگ تعیین‌شماره‌سر تعیین‌محتوای‌متن تعیین‌مشخصات‌لیست تغییربه‌قلم‌بدنه تنظیم‌راست تنظیم‌طرح‌بندی تنظیم‌وسط تورفتگی توری توضیح تک ثبت‌زوج ثبت‌کامل جداسازی‌نشانه‌گذاری حاش حرف حرفها حفظ‌بلوکها خالی خطهای‌سیاه خطهای‌نازک خطها‌خالی خط‌حاشیه خط‌سیاه خط‌متن خط‌مو خط‌نازک خ‌ا خ‌ع در درج‌آرمها درج‌ثبت درج‌ثبت درج‌درخط درج‌درخطها درج‌درمتن درج‌درمیدان درج‌در‌بالای‌یکدیگر درج‌در‌توری درج‌راهنما درج‌زیرفرمول درج‌شناور درج‌فرمول درج‌لیست درج‌لیست‌مختلط درج‌لیست‌مختلط درج‌لیست‌مرجع درج‌پانوشتها درج‌پانوشتهای‌موضعی درج‌چوب‌خط درج‌کنار‌به‌کنار درحاشیه درحاشیه‌دیگر درخارجی درخط درداخلی درراست درصفحه درقالبی درمورد درون درچپ دریافت‌بافر دریافت‌نشانه دوران دکمه دکمه‌پانل رج رجوع رنگ رنگ‌خاکستری روزهفته ریاضی زبان زبان‌اصلی ستون سر شماره‌سر شماره‌سرجاری شماره‌مبدل شماره‌ها شکافتن‌شناور شکل‌خارجی صفحه صفحه‌زوج صفحه‌پردازش عبوربلوکها فشرده فضا فضاهای‌ثابت فضای‌سفید فضای‌سفیدصحیح قالبی لوح‌مقایسه ماه متن‌برچسب متن‌حاشیه متن‌سر متن‌پانوشت مرجع مرجع‌صفحه مرجع‌متن مقایسه‌گروه‌رنگ مقیاس منفی مکان میدان میدانهای‌گزارش میدان‌شبیه‌سازی میدان‌پشته میدان‌کپی میله‌تطابق میله‌پانل نسخه نسخه‌نشانه نشانه‌گذاری نشانه‌گذاری‌زوج نشر نصب‌زبان نقطه‌ها نقل نقل‌قول نم نماد نمادلیست نمایش‌آرایش نمایش‌بارگذاریها نمایش‌بستها نمایش‌توری نمایش‌رنگ نمایش‌شکلهای‌خارجی نمایش‌طرح‌بندی نمایش‌قالب نمایش‌قلم‌بدنه نمایش‌لوح نمایش‌مجموعه‌علامت نمایش‌محیط‌قلم‌بدنه نمایش‌میدانها نمایش‌چاپ نمایش‌گروه‌رنگ نوشتارزوج نوع‌صفحه پابا پانوشت پایین پرده پرکردن‌میدان پس‌زمینه پیروی‌نسخه پیروی‌نسخه‌پروفایل پیروی‌پروفایل چوبخط چپ‌چین کاغذزوج کسر کشیده کلمه‌راست گیره یادداشت یک‌جا یک‌خط \ No newline at end of file +keywordclass.macros.context.pe=CAP Cap Caps Character Characters MONTH Romannumerals WEEKDAY WORD WORDS Word Words appendix cap chapter chem comment completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms coupledregister crlf definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling definetypeface description enumeration framedtext indentation its labeling loadsorts loadsynonyms mapfontsize mediaeval name nextsection nocap overbar overbars overstrike overstrikes paragraph part placelistoffloats placelistofsorts placelistofsynonyms ran register reservefloat resettextcontent section seeregister setupcapitals setupfonthandling setupfontsynonym setupinterlinespace2 setuplistalternative setupurl sort startalignment startbuffer startbuffer startcolumns startcombination startcomment startdescription startdocument startenumeration startfigure startfloattext startformula startframedtext starthiding startitemgroup startlegend startline startlinecorrection startlinenumbering startlines startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprofile startregister startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startتولید startحقیقت startخط‌حاشیه startخط‌متن startرنگ startفشرده startمحیط startمنوی‌پانل startمولفه startنسخه startنقل‌قول startپروژه startپس‌زمینه stopalignment stopbuffer stopbuffer stopcolumns stopcombination stopcomment stopdescription stopdocument stopenumeration stopfigure stopfloattext stopformula stopframedtext stophiding stopitemgroup stoplegend stopline stoplinecorrection stoplinenumbering stoplines stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprofile stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopتولید stopحقیقت stopخط‌حاشیه stopخط‌متن stopرنگ stopفشرده stopمحیط stopمنوی‌پانل stopمولفه stopنسخه stopنقل‌قول stopپروژه stopپس‌زمینه sub subject subsection subsubject subsubsection subsubsubject synonym title tooltip typ underbar underbars useURL usedirectory آیتم آیتمها آینه اجباربلوکها از ازکارانداختن‌منوی‌پانل استفاده‌بلوکها استفاده‌دستخط‌تایپ استفاده‌رمزینه استفاده‌شکل‌خارجی استفاده‌فرمانها استفاده‌قطعه‌موزیک‌خارجی استفاده‌مدول استفاده‌مرجعها استفاده‌نمادها استفاده‌نوشتارخارجی استفاده‌ویژگیها استفاده‌پرونده‌خارجی استفاده‌پرونده‌دستخط‌تایپ استفاده‌پرونده‌های‌خارجی اعدادلاتین اما انتخاب‌برگ انتخاب‌بلوکها انتخاب‌نسخه انتقال‌به‌توری بارگذاری‌آرایش بارگذاری‌آیتمها بارگذاری‌ارجاع بارگذاری‌اندازه‌برگ بارگذاری‌باریکتر بارگذاری‌بافر بارگذاری‌بالا بارگذاری‌بخش بارگذاری‌بردباری بارگذاری‌برنامه‌ها بارگذاری‌برگ بارگذاری‌بست بارگذاری‌بلوک بارگذاری‌بلوکهای‌حاشیه بارگذاری‌بلوک‌بخش بارگذاری‌تایپ بارگذاری‌تایپ‌کردن بارگذاری‌تب بارگذاری‌ترتیب بارگذاری‌ترتیب بارگذاری‌ترکیب‌ها بارگذاری‌تطابق بارگذاری‌تعریف‌پانوشت بارگذاری‌تنظیم بارگذاری‌ته‌برگ بارگذاری‌تورفتگی بارگذاری‌تورفتگیها بارگذاری‌توضیح بارگذاری‌ثبت بارگذاری‌جدولها بارگذاری‌جدول‌بندی بارگذاری‌خالی بارگذاری‌خروجی بارگذاری‌خطها بارگذاری‌خطهای‌حاشیه بارگذاری‌خطهای‌سیاه بارگذاری‌خطهای‌متن بارگذاری‌خطها‌ی‌نازک بارگذاری‌درج‌درخطها بارگذاری‌درج‌مخالف بارگذاری‌درون‌حاشیه بارگذاری‌دوران بارگذاری‌دکمه‌ها بارگذاری‌راهنما بارگذاری‌رنگ بارگذاری‌رنگها بارگذاری‌زبان بارگذاری‌ستونها بارگذاری‌سر بارگذاری‌سربرگ بارگذاری‌سرها بارگذاری‌سیستم بارگذاری‌شرح بارگذاری‌شرح بارگذاری‌شرحها بارگذاری‌شماره‌زیرصفحه بارگذاری‌شماره‌سر بارگذاری‌شماره‌صفحه بارگذاری‌شماره‌گذاری بارگذاری‌شماره‌گذاریها بارگذاری‌شماره‌گذاری‌صفحه بارگذاری‌شماره‌گذاری‌پاراگراف بارگذاری‌شماره‌‌گذاری‌خط بارگذاری‌شناور بارگذاری‌شناورها بارگذاری‌شکافتن‌شناورها بارگذاری‌شکلهای‌خارجی بارگذاری‌طرح بارگذاری‌طرح‌بندی بارگذاری‌عرض‌خط بارگذاری‌فاصله‌بین‌خط بارگذاری‌فرمولها بارگذاری‌فضای‌سفید بارگذاری‌فضا‌گذاری بارگذاری‌قالبی بارگذاری‌قلم‌متن بارگذاری‌لوح بارگذاری‌لیست بارگذاری‌لیست‌ترکیبی بارگذاری‌لیست‌مرجع بارگذاری‌مترادفها بارگذاری‌متن بارگذاری‌متنهای‌بالا بارگذاری‌متن‌سر بارگذاری‌متن‌سربرگ بارگذاری‌متن‌قالبی بارگذاری‌متن‌متنها بارگذاری‌متن‌پانوشت بارگذاری‌متن‌پایین بارگذاری‌مجموعه‌نماد بارگذاری‌محیط‌قلم‌متن بارگذاری‌منوی‌پانل بارگذاری‌مکان‌گذاری بارگذاری‌میدان بارگذاری‌میدانها بارگذاری‌میله‌تطابق بارگذاری‌میله‌زیر بارگذاری‌میله‌پانل بارگذاری‌نسخه‌ها بارگذاری‌نشانه‌شکستن بارگذاری‌نشانه‌گذاری بارگذاری‌نشرها بارگذاری‌نقل بارگذاری‌پاراگرافها بارگذاری‌پانل بارگذاری‌پانوشتها بارگذاری‌پایین بارگذاری‌پرده‌ها بارگذاری‌پرده‌پانل بارگذاری‌پروفایلها بارگذاری‌پرکردن‌خطها بارگذاری‌پس‌زمینه بارگذاری‌پس‌زمینه‌ها بارگذاری‌چیدن بارگذاری‌گذارصفحه بارگذاری‌گروه‌آیتم بازنشانی بازنشانی‌نشانه‌گذاری باگذاری‌متن‌برچسب بدون‌بلوکهای‌بیشتر بدون‌تورفتگی بدون‌خط‌بالاوپایین بدون‌خط‌سروته‌برگ بدون‌فایلهای‌بیشتر بدون‌فضا بدون‌فضای‌سفید بدون‌لیست بدون‌نشانه‌گذاری برنامه بروبه بروبه‌جعبه بروپایین برچسبها بلند بلوکهای‌پردازش بلوکها‌پنهان بنویس‌بین‌لیست بنویس‌درثبت بنویس‌درلیست‌مرجع بنویس‌در‌لیست تاریخ تاریخ‌جاری تاریخ‌رجوع تایپ تایپ‌بافر تایپ‌پرونده تب ترجمه تطابق تعریف تعریف‌آرایش تعریف‌آرم تعریف‌الگوی‌جدول تعریف‌اندازه‌برگ تعریف‌بافر تعریف‌بخش تعریف‌برنامه تعریف‌برچسب تعریف‌بلوک تعریف‌بلوک‌بخش تعریف‌تایپ تعریف‌تایپ‌کردن تعریف‌تبدیل تعریف‌ترتیب تعریف‌توده‌میدان تعریف‌تورفتگی تعریف‌ثبت تعریف‌جدول‌بندی تعریف‌خالی تعریف‌خروجی تعریف‌رنگ تعریف‌زیرمیدان تعریف‌سر تعریف‌شرح تعریف‌شروع‌پایان تعریف‌شماره‌بندی تعریف‌شمایل‌مرجع تعریف‌شناور تعریف‌قالبی تعریف‌قلم تعریف‌قلم‌متن تعریف‌لوح تعریف‌لیست تعریف‌لیست‌ترکیبی تعریف‌لیست‌مرجع تعریف‌مترادفها تعریف‌مترادف‌قلم تعریف‌متن تعریف‌متن‌قالبی تعریف‌محیط‌قلم‌بدنه تعریف‌مرجع تعریف‌منوی‌پانل تعریف‌منوی‌پانل تعریف‌میدان تعریف‌نسخه تعریف‌نشانه‌گذاری تعریف‌نماد تعریف‌نمادشکل تعریف‌پاراگرافها تعریف‌پروفایل تعریف‌پوشش تعریف‌گروه‌رنگ تعیین‌شماره‌سر تعیین‌محتوای‌متن تعیین‌مشخصات‌لیست تغییربه‌قلم‌بدنه تنظیم‌راست تنظیم‌طرح‌بندی تنظیم‌وسط تورفتگی توری تک ثبت‌زوج ثبت‌کامل جداسازی‌نشانه‌گذاری حاش حرف حرفها حفظ‌بلوکها خالی خطهای‌سیاه خطهای‌نازک خطها‌خالی خط‌حاشیه خط‌سیاه خط‌متن خط‌مو خط‌نازک خ‌ا خ‌ع در درج‌آرمها درج‌ثبت درج‌ثبت درج‌درخط درج‌درخطها درج‌درمتن درج‌درمیدان درج‌در‌بالای‌یکدیگر درج‌در‌توری درج‌راهنما درج‌زیرفرمول درج‌شناور درج‌فرمول درج‌لیست درج‌لیست‌مختلط درج‌لیست‌مختلط درج‌لیست‌مرجع درج‌پانوشتها درج‌پانوشتهای‌موضعی درج‌چوب‌خط درج‌کنار‌به‌کنار درحاشیه درحاشیه‌دیگر درخارجی درخط درداخلی درراست درصفحه درقالبی درمورد درون درچپ دریافت‌بافر دریافت‌نشانه دوران دکمه دکمه‌پانل رج رجوع رنگ رنگ‌خاکستری روزهفته ریاضی زبان زبان‌اصلی ستون سر شماره‌سر شماره‌سرجاری شماره‌مبدل شماره‌ها شکافتن‌شناور شکل‌خارجی صفحه صفحه‌زوج صفحه‌پردازش عبوربلوکها فشرده فضا فضاهای‌ثابت فضای‌سفید فضای‌سفیدصحیح قالبی لوح‌مقایسه ماه متن‌برچسب متن‌حاشیه متن‌سر متن‌پانوشت مرجع مرجع‌صفحه مرجع‌متن مقایسه‌گروه‌رنگ مقیاس منفی مکان میدان میدانهای‌گزارش میدان‌شبیه‌سازی میدان‌پشته میدان‌کپی میله‌تطابق میله‌پانل نسخه نسخه‌نشانه نشانه‌گذاری نشانه‌گذاری‌زوج نشر نصب‌زبان نقطه‌ها نقل نقل‌قول نم نماد نمادلیست نمایش‌آرایش نمایش‌بارگذاریها نمایش‌بستها نمایش‌توری نمایش‌رنگ نمایش‌شکلهای‌خارجی نمایش‌طرح‌بندی نمایش‌قالب نمایش‌قلم‌بدنه نمایش‌لوح نمایش‌مجموعه‌علامت نمایش‌محیط‌قلم‌بدنه نمایش‌میدانها نمایش‌چاپ نمایش‌گروه‌رنگ نوشتارزوج نوع‌صفحه پابا پانوشت پایین پرده پرکردن‌میدان پس‌زمینه پیروی‌نسخه پیروی‌نسخه‌پروفایل پیروی‌پروفایل چوبخط چپ‌چین کاغذزوج کسر کشیده کلمه‌راست گیره یادداشت یک‌جا یک‌خط \ No newline at end of file diff --git a/context/data/scite/cont-ro-scite.properties b/context/data/scite/cont-ro-scite.properties index 47fd2b9c3..3f9cc57e1 100644 --- a/context/data/scite/cont-ro-scite.properties +++ b/context/data/scite/cont-ro-scite.properties @@ -1 +1 @@ -keywordclass.macros.context.ro=CAP CUVANT CUVINTE Cap Caps Cuvant Cuvinte LUNA Litera Litere Numereromane ZIDINSAPTAMANA adapteazaaspect adubuffer adumarcaje afiseazaaspect afiseazacampuri afiseazaculoare afiseazafiguriexterne afiseazafonttext afiseazagrid afiseazagrupculoare afiseazamakeup afiseazamediufonttext afiseazapaleta afiseazarama afiseazasetari afiseazasetsimboluri afiseazastruts afiseazatiparire aliniat aliniatcentru aliniatdreapta aliniatstanga appendix ascundeblocuri barainteractiune barasincronizare blanc but butoaneinteractiune buton camp campumplere cap chapter chem citat clip cloneazacamp coloana comentariu comparagrupculoare comparapaleta completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister convertestenumar copiazacamp corecteazaspatiualb coupledregister crlf culoare culoaregri cupleazadocument cupleazamarcaje cupleazaregistru cuvantdreapta data datacurenta datareferit decupleazamarcaje definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling defineste definestealiat definesteantet definesteblanc definestebloc definesteblocsectiune definestebuffer definestecamp definesteconversie definesteculoare definestedescriere definestedimensiunehartie definesteenumerare definesteeticheta definestefloat definestefont definestefonttext definesteformatreferinte definestegrupculori definesteinconjurare definestelista definestelistacombinata definestelistareferinte definestelogo definestemakeup definestemarcaje definestemediulfonttext definestemeniuinteractiune definestemeniuinteractiune definesteoutput definesteoverlay definestepaleta definesteparagraf definesteprofil definesteprogram definestereferinte definesteregistru definestesablontabel definestesectiune definestesimbol definestesimbolfigura definestesinonim definestesinonimfont definestesortare definestestartstop definestestivacampuri definestesubcamp definestetabulatori definestetext definestetexteinconjurate definestetextinconjurat definestetyping definesteversiune definetype definetypeface description despre determinacaracteristicilelistei determinanumartitlu dezactiveazameniuinteractiune din dute dutebox ecran el element element emptylines enumeration etichete faraaliniat farafisiere faraliniiantetsisubsol faraliniisussijos faralista faramarcaje faraspatiu faraspatiualb figuraexterna firdepar folosesteURL folosestebloc folosestecodificarea folosestecomenzi folosestedocumentextern folosestefiguraexterna folosestefisiereexterne folosestefisierextern folosestemodul folosestemuzicaexterna folosestereferinte folosestesimboluri folosestespeciale footnotetext forteazablocuri fractie framed framedtext fundal gatablocuri grid hartiedubla hl impachetat impartefloat in inalt inaltamargine indentation indreapta inframed ininner injos inlinie inouter instalarelimba instanga intins jos jossus la labeling lapagina limba limbaprincipala liniemargine linieneagra liniesubtire linieumplere liniinegre liniisubtiri listsymbol litera litere loadsorts loadsynonyms logcampuri luna mapfontsize mar marcaje marcheazaversiune marginal matematica mediaeval minicitat mutapegrid name nextsection nocap nop nota notasubsol numartitlu numartitlucurent numere numereromane olinie overbar overbars overstrike overstrikes pagina paginadubla paragraph part pastreazablocuri placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist plaseazapegrid plaseazasemnecarte potrivestecamp pozitie proceseazabloc proceseazapagina program publicatie puncte punedeasuprafiecareia punefatainfata puneformula punelegenda punelista punelistacombinata punelistacombinata punelogouri punenotesubsol punenotesubsollocale puneregistru puneregistru punesubformula ran ref referinta referintapagina referintatext referit reflexie register reservefloat reset reseteazamarcaje resettextcontent riglatext rigleumplere roteste saripesteblocuri scala scriebuffer scrieinlista scrieinlistareferinte scrieinregistru scrieintreliste section seeregister selecteazablocuri selecteazahartie selecteazaversiune semncarte setarebarasincronizare setarelimba setareoutput setarepozitie setaresincronizare seteazaaliniat seteazaaliniate seteazaalinierea seteazaantet seteazaaranjareapag seteazaaspect seteazabarainteractiune seteazablanc seteazabloc seteazablocsectiune seteazablocurimarginale seteazabuffer seteazabutoane seteazacamp seteazacampuri seteazaclipping seteazacoloane seteazacombinari seteazacomentariu seteazaculoare seteazaculori seteazadefinireanotasubsol seteazadescriere seteazadimensiunihartie seteazaecrane seteazaecraninteractiune seteazaelemente seteazaenumerare seteazafiguriexterne seteazafloat seteazafloats seteazafonttext seteazaformulare seteazaformule seteazafundal seteazafundaluri seteazagrosimelinie seteazaimpartireafloat seteazainconjurat seteazaingust seteazainteractiunea seteazajos seteazalegenda seteazalegenda seteazalegendele seteazaliniesilabe seteazaliniesubtire seteazalinii seteazaliniimargine seteazaliniinegre seteazaliniiumplere seteazalista seteazalistacombinata seteazalistareferinte seteazamajuscule seteazamakeup seteazamarcaje seteazamarginal seteazamediulfonttext seteazameniuinteractiune seteazaminicitat seteazanotasubsol seteazanumarpagina seteazanumarsubpagina seteazanumartitlu seteazanumerotare seteazanumerotarelinii seteazanumerotarepagina seteazanumerotareparagrafe seteazapaleta seteazaparagrafe seteazaplasareaopozita seteazaprofile seteazaprograme seteazapublicatii seteazareferinte seteazaregistru seteazarigletext seteazarigleumplere seteazarotare seteazasectiune seteazasimbol seteazasinonime seteazasistem seteazasortare seteazaspatiu seteazaspatiualb seteazaspatiuinterliniar seteazastrut seteazasublinie seteazasubsol seteazasus seteazatab seteazatabele seteazatabulatori seteazatext seteazatexteantet seteazatextejos seteazatextesubsol seteazatextesus seteazatextetext seteazatexteticheta seteazatexttitlu seteazatitlu seteazatitluri seteazatoleranta seteazatranzitiepagina seteazatype seteazatyping seteazaurl seteazaversiuni settextcontent setupfonthandling setupfontsynonym setupinterlinespace2 setupitemgroup setuplistalternative setuppaper sim simbol sincronizeaza sort spatiifixate spatiu spatiualb startalignment startbuffer startbuffer startcitat startcolumns startcombination startcomentariu startcomponenta startculoare startdescription startdocument startenumeration startfact startfigure startfloattext startformula startframedtext startfundal starthiding startimpachetat startitemgroup startlegend startline startlinecorrection startlinenumbering startlines startliniemargine startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startmediu startmeniuinteractiune startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodus startprofile startproiect startregister startriglatext startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startversiune stivacampuri stopalignment stopbuffer stopbuffer stopcitat stopcolumns stopcombination stopcomentariu stopcomponenta stopculoare stopdescription stopdocument stopenumeration stopfact stopfigure stopfloattext stopformula stopframedtext stopfundal stophiding stopimpachetat stopitemgroup stoplegend stopline stoplinecorrection stoplinenumbering stoplines stopliniemargine stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopmediu stopmeniuinteractiune stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodus stopprofile stopproiect stopriglatext stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopversiune sub subject subsection subsubject subsubsection subsubsubject synonym tab tex texteticheta textmarginal texttitlu textumplere tippagina title titlu tooltip traduce trecilafonttext typ type typefile underbar underbars undeva urmeazaprofil urmeazaversiune urmeazaversiuneprofil usedirectory usetypescript usetypescriptfile versiune vl zidinsaptamana \ No newline at end of file +keywordclass.macros.context.ro=CAP CUVANT CUVINTE Cap Caps Cuvant Cuvinte LUNA Litera Litere Numereromane ZIDINSAPTAMANA adapteazaaspect adubuffer adumarcaje afiseazaaspect afiseazacampuri afiseazaculoare afiseazafiguriexterne afiseazafonttext afiseazagrid afiseazagrupculoare afiseazamakeup afiseazamediufonttext afiseazapaleta afiseazarama afiseazasetari afiseazasetsimboluri afiseazastruts afiseazatiparire aliniat aliniatcentru aliniatdreapta aliniatstanga appendix ascundeblocuri barainteractiune barasincronizare blanc but butoaneinteractiune buton camp campumplere cap chapter chem citat clip cloneazacamp coloana comment comparagrupculoare comparapaleta completecombinedlist completelistoffloats completelistofsorts completelistofsynonyms completeregister convertestenumar copiazacamp corecteazaspatiualb coupledregister crlf culoare culoaregri cupleazadocument cupleazamarcaje cupleazaregistru cuvantdreapta data datacurenta datareferit decupleazamarcaje definebodyfontDEF definebodyfontREF definedfont definefontfeature definefonthandling defineste definestealiat definesteantet definesteblanc definestebloc definesteblocsectiune definestebuffer definestecamp definesteconversie definesteculoare definestedescriere definestedimensiunehartie definesteenumerare definesteeticheta definestefloat definestefont definestefonttext definesteformatreferinte definestegrupculori definesteinconjurare definestelista definestelistacombinata definestelistareferinte definestelogo definestemakeup definestemarcaje definestemediulfonttext definestemeniuinteractiune definestemeniuinteractiune definesteoutput definesteoverlay definestepaleta definesteparagraf definesteprofil definesteprogram definestereferinte definesteregistru definestesablontabel definestesectiune definestesimbol definestesimbolfigura definestesinonim definestesinonimfont definestesortare definestestartstop definestestivacampuri definestesubcamp definestetabulatori definestetext definestetexteinconjurate definestetextinconjurat definestetyping definesteversiune definetype definetypeface description despre determinacaracteristicilelistei determinanumartitlu dezactiveazameniuinteractiune din dute dutebox ecran el element element emptylines enumeration etichete faraaliniat farafisiere faraliniiantetsisubsol faraliniisussijos faralista faramarcaje faraspatiu faraspatiualb figuraexterna firdepar folosesteURL folosestebloc folosestecodificarea folosestecomenzi folosestedocumentextern folosestefiguraexterna folosestefisiereexterne folosestefisierextern folosestemodul folosestemuzicaexterna folosestereferinte folosestesimboluri folosestespeciale footnotetext forteazablocuri fractie framed framedtext fundal gatablocuri grid hartiedubla hl impachetat impartefloat in inalt inaltamargine indentation indreapta inframed ininner injos inlinie inouter instalarelimba instanga intins jos jossus la labeling lapagina limba limbaprincipala liniemargine linieneagra liniesubtire linieumplere liniinegre liniisubtiri listsymbol litera litere loadsorts loadsynonyms logcampuri luna mapfontsize mar marcaje marcheazaversiune marginal matematica mediaeval minicitat mutapegrid name nextsection nocap nop nota notasubsol numartitlu numartitlucurent numere numereromane olinie overbar overbars overstrike overstrikes pagina paginadubla paragraph part pastreazablocuri placefloat placelistoffloats placelistofsorts placelistofsynonyms placereferencelist plaseazapegrid plaseazasemnecarte potrivestecamp pozitie proceseazabloc proceseazapagina program publicatie puncte punedeasuprafiecareia punefatainfata puneformula punelegenda punelista punelistacombinata punelistacombinata punelogouri punenotesubsol punenotesubsollocale puneregistru puneregistru punesubformula ran ref referinta referintapagina referintatext referit reflexie register reservefloat reset reseteazamarcaje resettextcontent riglatext rigleumplere roteste saripesteblocuri scala scriebuffer scrieinlista scrieinlistareferinte scrieinregistru scrieintreliste section seeregister selecteazablocuri selecteazahartie selecteazaversiune semncarte setarebarasincronizare setarelimba setareoutput setarepozitie setaresincronizare seteazaaliniat seteazaaliniate seteazaalinierea seteazaantet seteazaaranjareapag seteazaaspect seteazabarainteractiune seteazablanc seteazabloc seteazablocsectiune seteazablocurimarginale seteazabuffer seteazabutoane seteazacamp seteazacampuri seteazaclipping seteazacoloane seteazacombinari seteazacomentariu seteazaculoare seteazaculori seteazadefinireanotasubsol seteazadescriere seteazadimensiunihartie seteazaecrane seteazaecraninteractiune seteazaelemente seteazaenumerare seteazafiguriexterne seteazafloat seteazafloats seteazafonttext seteazaformulare seteazaformule seteazafundal seteazafundaluri seteazagrosimelinie seteazaimpartireafloat seteazainconjurat seteazaingust seteazainteractiunea seteazajos seteazalegenda seteazalegenda seteazalegendele seteazaliniesilabe seteazaliniesubtire seteazalinii seteazaliniimargine seteazaliniinegre seteazaliniiumplere seteazalista seteazalistacombinata seteazalistareferinte seteazamajuscule seteazamakeup seteazamarcaje seteazamarginal seteazamediulfonttext seteazameniuinteractiune seteazaminicitat seteazanotasubsol seteazanumarpagina seteazanumarsubpagina seteazanumartitlu seteazanumerotare seteazanumerotarelinii seteazanumerotarepagina seteazanumerotareparagrafe seteazapaleta seteazaparagrafe seteazaplasareaopozita seteazaprofile seteazaprograme seteazapublicatii seteazareferinte seteazaregistru seteazarigletext seteazarigleumplere seteazarotare seteazasectiune seteazasimbol seteazasinonime seteazasistem seteazasortare seteazaspatiu seteazaspatiualb seteazaspatiuinterliniar seteazastrut seteazasublinie seteazasubsol seteazasus seteazatab seteazatabele seteazatabulatori seteazatext seteazatexteantet seteazatextejos seteazatextesubsol seteazatextesus seteazatextetext seteazatexteticheta seteazatexttitlu seteazatitlu seteazatitluri seteazatoleranta seteazatranzitiepagina seteazatype seteazatyping seteazaurl seteazaversiuni settextcontent setupfonthandling setupfontsynonym setupinterlinespace2 setupitemgroup setuplistalternative setuppaper sim simbol sincronizeaza sort spatiifixate spatiu spatiualb startalignment startbuffer startbuffer startcitat startcolumns startcombination startcomment startcomponenta startculoare startdescription startdocument startenumeration startfact startfigure startfloattext startformula startframedtext startfundal starthiding startimpachetat startitemgroup startlegend startline startlinecorrection startlinenumbering startlines startliniemargine startlocal startlocalenvironment startlocalfootnotes startmakeup startmarginblock startmediu startmeniuinteractiune startnamemakeup startnarrower startopposite startoverlay startoverview startparagraph startpositioning startpostponing startprodus startprofile startproiect startregister startriglatext startsymbolset startsynchronization starttable starttables starttabulate starttyping startunpacked startversiune stivacampuri stopalignment stopbuffer stopbuffer stopcitat stopcolumns stopcombination stopcomment stopcomponenta stopculoare stopdescription stopdocument stopenumeration stopfact stopfigure stopfloattext stopformula stopframedtext stopfundal stophiding stopimpachetat stopitemgroup stoplegend stopline stoplinecorrection stoplinenumbering stoplines stopliniemargine stoplocal stoplocalenvironment stoplocalfootnotes stopmakeup stopmarginblock stopmediu stopmeniuinteractiune stopnamemakeup stopnarrower stopopposite stopoverlay stopoverview stopparagraph stoppositioning stoppostponing stopprodus stopprofile stopproiect stopriglatext stopsymbolset stopsynchronization stoptable stoptables stoptabulate stoptyping stopunpacked stopversiune sub subject subsection subsubject subsubsection subsubsubject synonym tab tex texteticheta textmarginal texttitlu textumplere tippagina title titlu tooltip traduce trecilafonttext typ type typefile underbar underbars undeva urmeazaprofil urmeazaversiune urmeazaversiuneprofil usedirectory usetypescript usetypescriptfile versiune vl zidinsaptamana \ No newline at end of file diff --git a/metapost/context/base/mp-mlib.mp b/metapost/context/base/mp-mlib.mp index cedfa9f7b..e812df61e 100644 --- a/metapost/context/base/mp-mlib.mp +++ b/metapost/context/base/mp-mlib.mp @@ -406,14 +406,21 @@ enddef ; % fi ; % enddef ; +def withmask primary filename = + withprescript "fg_mask=" & filename +enddef ; + def externalfigure primary filename = - if true : - draw rawtextext("\externalfigure[" & filename & "]") + if false : + rawtextext("\externalfigure[" & filename & "]") else : image ( addto currentpicture doublepath unitsquare withprescript "fg_name=" & filename ; ) +% unitsquare +% withpen pencircle scaled 0 +% withprescript "fg_name=" & filename fi enddef ; diff --git a/scripts/context/lua/mtx-context.lua b/scripts/context/lua/mtx-context.lua index 39712af7e..36e3efb6b 100644 --- a/scripts/context/lua/mtx-context.lua +++ b/scripts/context/lua/mtx-context.lua @@ -117,334 +117,334 @@ function io.copydata(fromfile,tofile) io.savedata(tofile,io.loaddata(fromfile) or "") end --- ctx - -ctxrunner = { } - -do - - function ctxrunner.filtered(str,method) - str = tostring(str) - if method == 'name' then str = file.removesuffix(file.basename(str)) - elseif method == 'path' then str = file.dirname(str) - elseif method == 'suffix' then str = file.extname(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 str:gsub("\\","/") +-- ctx (will become util-ctx) + +local ctxrunner = { } + +function ctxrunner.filtered(str,method) + str = tostring(str) + if method == 'name' then str = file.removesuffix(file.basename(str)) + elseif method == 'path' then str = file.dirname(str) + elseif method == 'suffix' then str = file.extname(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 str:gsub("\\","/") +end - function ctxrunner.substitute(e,str) - local attributes = e.at - if str and attributes then - if attributes['method'] then - str = ctxrunner.filtered(str,attributes['method']) - end - if str == "" and attributes['default'] then - str = attributes['default'] - end +function ctxrunner.substitute(e,str) + local attributes = e.at + if str and attributes then + if attributes['method'] then + str = ctxrunner.filtered(str,attributes['method']) + end + if str == "" and attributes['default'] then + str = attributes['default'] end - return str end + return str +end - function ctxrunner.reflag(flags) - local t = { } - for _, flag in next, flags do - local key, value = match(flag,"^(.-)=(.+)$") - if key and value then - t[key] = value - else - t[flag] = true - end +function ctxrunner.reflag(flags) + local t = { } + for _, flag in next, flags do + local key, value = match(flag,"^(.-)=(.+)$") + if key and value then + t[key] = value + else + t[flag] = true end - return t end + return t +end - function ctxrunner.substitute(str) - return str - end +function ctxrunner.substitute(str) + return str +end - function ctxrunner.justtext(str) - str = xml.unescaped(tostring(str)) - str = xml.cleansed(str) - str = str:gsub("\\+",'/') - str = str:gsub("%s+",' ') - return str - end +function ctxrunner.justtext(str) + str = xml.unescaped(tostring(str)) + str = xml.cleansed(str) + str = str:gsub("\\+",'/') + str = str:gsub("%s+",' ') + return str +end - function ctxrunner.new() - return { - ctxname = "", - jobname = "", - xmldata = nil, - suffix = "prep", - locations = { '..', '../..' }, - variables = { }, - messages = { }, - environments = { }, - modules = { }, - filters = { }, - flags = { }, - modes = { }, - prepfiles = { }, - paths = { }, - } - end +function ctxrunner.new() + return { + ctxname = "", + jobname = "", + xmldata = nil, + suffix = "prep", + locations = { '..', '../..' }, + variables = { }, + messages = { }, + environments = { }, + modules = { }, + filters = { }, + flags = { }, + modes = { }, + prepfiles = { }, + paths = { }, + } +end - function ctxrunner.savelog(ctxdata,ctlname) - local function yn(b) - if b then return 'yes' else return 'no' end - end - if not ctlname or ctlname == "" or ctlname == ctxdata.jobname then - if ctxdata.jobname then - ctlname = file.replacesuffix(ctxdata.jobname,'ctl') - elseif ctxdata.ctxname then - ctlname = file.replacesuffix(ctxdata.ctxname,'ctl') - else - report("invalid ctl name: %s",ctlname or "?") - return - end +function ctxrunner.savelog(ctxdata,ctlname) + local function yn(b) + if b then return 'yes' else return 'no' end + end + if not ctlname or ctlname == "" or ctlname == ctxdata.jobname then + if ctxdata.jobname then + ctlname = file.replacesuffix(ctxdata.jobname,'ctl') + elseif ctxdata.ctxname then + ctlname = file.replacesuffix(ctxdata.ctxname,'ctl') + else + report("invalid ctl name: %s",ctlname or "?") + return end - local prepfiles = ctxdata.prepfiles - if prepfiles and next(prepfiles) then - report("saving logdata in: %s",ctlname) - f = io.open(ctlname,'w') - if f then - f:write("\n\n") - f:write(format("\n",yn(ctxdata.runlocal))) - local sorted = table.sortedkeys(prepfiles) - for i=1,#sorted do - local name = sorted[i] - f:write(format("\t%s\n",yn(prepfiles[name]),name)) - end - f:write("\n") - f:close() + end + local prepfiles = ctxdata.prepfiles + if prepfiles and next(prepfiles) then + report("saving logdata in: %s",ctlname) + f = io.open(ctlname,'w') + if f then + f:write("\n\n") + f:write(format("\n",yn(ctxdata.runlocal))) + local sorted = table.sortedkeys(prepfiles) + for i=1,#sorted do + local name = sorted[i] + f:write(format("\t%s\n",yn(prepfiles[name]),name)) end - else - report("nothing prepared, no ctl file saved") - os.remove(ctlname) + f:write("\n") + f:close() end + else + report("nothing prepared, no ctl file saved") + os.remove(ctlname) end +end - function ctxrunner.register_path(ctxdata,path) - -- test if exists - ctxdata.paths[ctxdata.paths+1] = path - end +function ctxrunner.register_path(ctxdata,path) + -- test if exists + ctxdata.paths[ctxdata.paths+1] = path +end - function ctxrunner.trace(ctxdata) - print(table.serialize(ctxdata.messages)) - print(table.serialize(ctxdata.flags)) - print(table.serialize(ctxdata.environments)) - print(table.serialize(ctxdata.modules)) - print(table.serialize(ctxdata.filters)) - print(table.serialize(ctxdata.modes)) - print(xml.tostring(ctxdata.xmldata)) - end +function ctxrunner.trace(ctxdata) + print(table.serialize(ctxdata.messages)) + print(table.serialize(ctxdata.flags)) + print(table.serialize(ctxdata.environments)) + print(table.serialize(ctxdata.modules)) + print(table.serialize(ctxdata.filters)) + print(table.serialize(ctxdata.modes)) + print(xml.tostring(ctxdata.xmldata)) +end - function ctxrunner.manipulate(ctxdata,ctxname,defaultname) +function ctxrunner.manipulate(ctxdata,ctxname,defaultname) - if not ctxdata.jobname or ctxdata.jobname == "" then - return - end + if not ctxdata.jobname or ctxdata.jobname == "" then + return + end - ctxdata.ctxname = ctxname or file.removesuffix(ctxdata.jobname) or "" + ctxdata.ctxname = ctxname or file.removesuffix(ctxdata.jobname) or "" - if ctxdata.ctxname == "" then - return - end + if ctxdata.ctxname == "" then + return + end - ctxdata.jobname = file.addsuffix(ctxdata.jobname,'tex') - ctxdata.ctxname = file.addsuffix(ctxdata.ctxname,'ctx') + ctxdata.jobname = file.addsuffix(ctxdata.jobname,'tex') + ctxdata.ctxname = file.addsuffix(ctxdata.ctxname,'ctx') - report("jobname: %s",ctxdata.jobname) - report("ctxname: %s",ctxdata.ctxname) + report("jobname: %s",ctxdata.jobname) + report("ctxname: %s",ctxdata.ctxname) - -- mtxrun should resolve kpse: and file: + -- mtxrun should resolve kpse: and file: - local usedname = ctxdata.ctxname - local found = lfs.isfile(usedname) + local usedname = ctxdata.ctxname + local found = lfs.isfile(usedname) - if not found then - for _, path in next, ctxdata.locations do - local fullname = file.join(path,ctxdata.ctxname) - if lfs.isfile(fullname) then - usedname, found = fullname, true - break - end + -- no futher test if qualified path + + if not found then + for _, path in next, ctxdata.locations do + local fullname = file.join(path,ctxdata.ctxname) + if lfs.isfile(fullname) then + usedname, found = fullname, true + break end end + end + if not found then usedname = resolvers.findfile(ctxdata.ctxname,"tex") found = usedname ~= "" + end - if not found and defaultname and defaultname ~= "" and lfs.isfile(defaultname) then - usedname, found = defaultname, true - end + if not found and defaultname and defaultname ~= "" and lfs.isfile(defaultname) then + usedname, found = defaultname, true + end - if not found then - return - end + if not found then + return + end - ctxdata.xmldata = xml.load(usedname) + ctxdata.xmldata = xml.load(usedname) - if not ctxdata.xmldata then - return - else - -- test for valid, can be text file - end + if not ctxdata.xmldata then + return + else + -- test for valid, can be text file + end - xml.include(ctxdata.xmldata,'ctx:include','name', table.append({'.', file.dirname(ctxdata.ctxname)},ctxdata.locations)) + xml.include(ctxdata.xmldata,'ctx:include','name', table.append({'.', file.dirname(ctxdata.ctxname)},ctxdata.locations)) - ctxdata.variables['job'] = ctxdata.jobname + ctxdata.variables['job'] = ctxdata.jobname - ctxdata.flags = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:flags/ctx:flag",true) - ctxdata.environments = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:environment",true) - ctxdata.modules = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:module",true) - ctxdata.filters = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:filter",true) - ctxdata.modes = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:mode",true) - ctxdata.messages = xml.collect_texts(ctxdata.xmldata,"ctx:message",true) + ctxdata.flags = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:flags/ctx:flag",true) + ctxdata.environments = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:environment",true) + ctxdata.modules = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:module",true) + ctxdata.filters = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:filter",true) + ctxdata.modes = xml.collect_texts(ctxdata.xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:mode",true) + ctxdata.messages = xml.collect_texts(ctxdata.xmldata,"ctx:message",true) - ctxdata.flags = ctxrunner.reflag(ctxdata.flags) + ctxdata.flags = ctxrunner.reflag(ctxdata.flags) - local messages = ctxdata.messages - for i=1,#messages do - report("ctx comment: %s", xml.tostring(messages[i])) - end + local messages = ctxdata.messages + for i=1,#messages do + report("ctx comment: %s", xml.tostring(messages[i])) + end - for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value[@name='job']") do - d[k] = ctxdata.variables['job'] or "" - end + for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value[@name='job']") do + d[k] = ctxdata.variables['job'] or "" + end - local commands = { } - for e in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor") do - commands[e.at and e.at['name'] or "unknown"] = e - end + local commands = { } + for e in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor") do + commands[e.at and e.at['name'] or "unknown"] = e + end - local suffix = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/attribute('suffix')") or ctxdata.suffix - local runlocal = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/attribute('local')") + local suffix = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/attribute('suffix')") or ctxdata.suffix + local runlocal = xml.filter(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:processors/attribute('local')") - runlocal = toboolean(runlocal) + runlocal = toboolean(runlocal) - for files in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:files") do - for pattern in xml.collected(files,"ctx:file") do + for files in xml.collected(ctxdata.xmldata,"/ctx:job/ctx:preprocess/ctx:files") do + for pattern in xml.collected(files,"ctx:file") do - preprocessor = pattern.at['processor'] or "" + preprocessor = pattern.at['processor'] or "" - if preprocessor ~= "" then + if preprocessor ~= "" then - ctxdata.variables['old'] = ctxdata.jobname - for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value") do - local ek = d[k] - local ekat = ek.at['name'] - if ekat == 'old' then - d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "") - end + ctxdata.variables['old'] = ctxdata.jobname + for r, d, k in xml.elements(ctxdata.xmldata,"ctx:value") do + local ek = d[k] + local ekat = ek.at['name'] + if ekat == 'old' then + d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "") end + end - pattern = ctxrunner.justtext(xml.tostring(pattern)) + pattern = ctxrunner.justtext(xml.tostring(pattern)) - local oldfiles = dir.glob(pattern) + local oldfiles = dir.glob(pattern) - local pluspath = false - if #oldfiles == 0 then - -- message: no files match pattern - local paths = ctxdata.paths - for i=1,#paths do - local p = paths[i] - local oldfiles = dir.glob(path.join(p,pattern)) - if #oldfiles > 0 then - pluspath = true - break - end + local pluspath = false + if #oldfiles == 0 then + -- message: no files match pattern + local paths = ctxdata.paths + for i=1,#paths do + local p = paths[i] + local oldfiles = dir.glob(path.join(p,pattern)) + if #oldfiles > 0 then + pluspath = true + break end end - if #oldfiles == 0 then - -- message: no old files - else - for i=1,#oldfiles do - local oldfile = oldfiles[i] - local newfile = oldfile .. "." .. suffix -- addsuffix will add one only - if ctxdata.runlocal then - newfile = file.basename(newfile) - end - if oldfile ~= newfile and file.needsupdate(oldfile,newfile) then - -- message: oldfile needs preprocessing - -- os.remove(newfile) - local splitted = preprocessor:split(',') - for i=1,#splitted do - local pp = splitted[i] - local command = commands[pp] - if command then - command = xml.copy(command) - local suf = (command.at and command.at['suffix']) or ctxdata.suffix - if suf then - newfile = oldfile .. "." .. suf - end - if ctxdata.runlocal then - newfile = file.basename(newfile) - end - for r, d, k in xml.elements(command,"ctx:old") do - d[k] = ctxrunner.substitute(oldfile) - end - for r, d, k in xml.elements(command,"ctx:new") do - d[k] = ctxrunner.substitute(newfile) - end - ctxdata.variables['old'] = oldfile - ctxdata.variables['new'] = newfile - for r, d, k in xml.elements(command,"ctx:value") do - local ek = d[k] - local ekat = ek.at and ek.at['name'] - if ekat then - d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "") - end - end - -- potential optimization: when mtxrun run internal - command = xml.content(command) - command = ctxrunner.justtext(command) - report("command: %s",command) - local result = os.spawn(command) or 0 - -- somehow we get the wrong return value - if result > 0 then - report("error, return code: %s",result) - end - if ctxdata.runlocal then - oldfile = file.basename(oldfile) + end + if #oldfiles == 0 then + -- message: no old files + else + for i=1,#oldfiles do + local oldfile = oldfiles[i] + local newfile = oldfile .. "." .. suffix -- addsuffix will add one only + if ctxdata.runlocal then + newfile = file.basename(newfile) + end + if oldfile ~= newfile and file.needsupdate(oldfile,newfile) then + -- message: oldfile needs preprocessing + -- os.remove(newfile) + local splitted = preprocessor:split(',') + for i=1,#splitted do + local pp = splitted[i] + local command = commands[pp] + if command then + command = xml.copy(command) + local suf = (command.at and command.at['suffix']) or ctxdata.suffix + if suf then + newfile = oldfile .. "." .. suf + end + if ctxdata.runlocal then + newfile = file.basename(newfile) + end + for r, d, k in xml.elements(command,"ctx:old") do + d[k] = ctxrunner.substitute(oldfile) + end + for r, d, k in xml.elements(command,"ctx:new") do + d[k] = ctxrunner.substitute(newfile) + end + ctxdata.variables['old'] = oldfile + ctxdata.variables['new'] = newfile + for r, d, k in xml.elements(command,"ctx:value") do + local ek = d[k] + local ekat = ek.at and ek.at['name'] + if ekat then + d[k] = ctxrunner.substitute(ctxdata.variables[ekat] or "") end end + -- potential optimization: when mtxrun run internal + command = xml.content(command) + command = ctxrunner.justtext(command) + report("command: %s",command) + local result = os.spawn(command) or 0 + -- somehow we get the wrong return value + if result > 0 then + report("error, return code: %s",result) + end + if ctxdata.runlocal then + oldfile = file.basename(oldfile) + end end - if lfs.isfile(newfile) then - file.syncmtimes(oldfile,newfile) - ctxdata.prepfiles[oldfile] = true - else - report("error, check target location of new file: %s", newfile) - ctxdata.prepfiles[oldfile] = false - end + end + if lfs.isfile(newfile) then + file.syncmtimes(oldfile,newfile) + ctxdata.prepfiles[oldfile] = true else - report("old file needs no preprocessing") - ctxdata.prepfiles[oldfile] = lfs.isfile(newfile) + report("error, check target location of new file: %s", newfile) + ctxdata.prepfiles[oldfile] = false end + else + report("old file needs no preprocessing") + ctxdata.prepfiles[oldfile] = lfs.isfile(newfile) end end end end end + end - ctxrunner.savelog(ctxdata) + ctxrunner.savelog(ctxdata) - end +end - function ctxrunner.preppedfile(ctxdata,filename) - if ctxdata.prepfiles[file.basename(filename)] then - return filename .. ".prep" - else - return filename - end +function ctxrunner.preppedfile(ctxdata,filename) + if ctxdata.prepfiles[file.basename(filename)] then + return filename .. ".prep" + else + return filename end - end -- rest @@ -575,7 +575,6 @@ function scripts.context.multipass.makeoptionfile(jobname,ctxdata,kindofrun,curr setalways("%% styles and modules") -- setalways("\\startsetups *runtime:modules") - setvalues("filter" , "\\useXMLfilter[%s]", true) setvalues("usemodule" , "\\usemodule[%s]", true) setvalues("environment" , "\\environment %s ", true) if ctxdata then @@ -875,9 +874,9 @@ function scripts.context.run(ctxdata,filename) --~ end --~ end -local directives = environment.directives -local trackers = environment.trackers -local experiments = environment.experiments + local directives = environment.directives + local trackers = environment.trackers + local experiments = environment.experiments -- if type(directives) == "string" then @@ -1214,7 +1213,7 @@ local persistent_runfiles = { } local special_runfiles = { - "-mpgraph*", "-mprun*" + "-mpgraph*", "-mprun*", "-temp-*" } local function purge_file(dfile,cfile) @@ -1324,6 +1323,7 @@ function scripts.context.touch() if environment.argument("expert") then touchfiles("mkii") touchfiles("mkiv") + touchfiles("mkvi") end end @@ -1355,7 +1355,7 @@ function scripts.context.modules(pattern) if not done[base] then done[base] = true local suffix = file.suffix(base) - if suffix == "tex" or suffix == "mkiv" then + if suffix == "tex" or suffix == "mkiv" or suffix == "mkvi" then local prefix = match(base,"^([xmst])%-") if prefix then v = resolvers.findfile(base) -- so that files on my dev path are seen diff --git a/scripts/context/lua/mtx-fonts.lua b/scripts/context/lua/mtx-fonts.lua index 0c315bc91..795fbe891 100644 --- a/scripts/context/lua/mtx-fonts.lua +++ b/scripts/context/lua/mtx-fonts.lua @@ -173,7 +173,7 @@ local function showfeatures(tag,specification) report("subfont : %s",subfont(specification.subfont)) report("fweight : %s",fontweight(specification.fontweight)) -- maybe more - local features = fonts.get_features(specification.filename,specification.format) + local features = fonts.helpers.getfeatures(specification.filename,specification.format) if features then for what, v in table.sortedhash(features) do local data = features[what] diff --git a/scripts/context/lua/mtx-interface.lua b/scripts/context/lua/mtx-interface.lua index 91ae16ce3..954cb4498 100644 --- a/scripts/context/lua/mtx-interface.lua +++ b/scripts/context/lua/mtx-interface.lua @@ -333,7 +333,7 @@ end function scripts.interface.preprocess() require("luat-mac.lua") - local newsuffix = environment.argument("suffix") or "tex" + local newsuffix = environment.argument("suffix") or "log" local force = environment.argument("force") for i=1,#environment.files do local oldname = environment.files[i] diff --git a/scripts/context/lua/mtx-server-ctx-fonttest.lua b/scripts/context/lua/mtx-server-ctx-fonttest.lua index d2d41fa39..b30cf0175 100644 --- a/scripts/context/lua/mtx-server-ctx-fonttest.lua +++ b/scripts/context/lua/mtx-server-ctx-fonttest.lua @@ -155,12 +155,12 @@ local function showfeatures(f) report("processing font '%s'",f) local features = cache[f] if features == nil then - features = fonts.get_features(resolvers.findfile(f)) + features = fonts.helpers.getfeatures(resolvers.findfile(f)) if not features then report("building cache for '%s'",f) io.savedata(file.join(temppath,file.addsuffix(tempname,"tex")),format(process_templates.cache,f,f)) os.execute(format("mtxrun --path=%s --script context --once --batchmode %s",temppath,tempname)) - features = fonts.get_features(f) + features = fonts.helpers.getfeatures(f) end cache[f] = features or false report("caching info of '%s'",f) @@ -305,7 +305,7 @@ local function edit_font(currentfont,detail,tempname) local sorted = table.sortedkeys(htmldata.scripts) for k=1,#sorted do local v = sorted[k] - local s = fonts.otf.tables.scripts[v] or v + local s = fonts.handlers.otf.tables.scripts[v] or v if detail and v == detail.script then scripts[#scripts+1] = format(" %s",s,v,v,v,v) else @@ -315,7 +315,7 @@ local function edit_font(currentfont,detail,tempname) local sorted = table.sortedkeys(htmldata.languages) for k=1,#sorted do local v = sorted[k] - local l = fonts.otf.tables.languages[v] or v + local l = fonts.handlers.otf.tables.languages[v] or v if detail and v == detail.language then languages[#languages+1] = format(" %s",l,v,v,v,v) else @@ -325,7 +325,7 @@ local function edit_font(currentfont,detail,tempname) local sorted = table.sortedkeys(htmldata.features) for k=1,#sorted do local v = sorted[k] - local f = fonts.otf.tables.features[v] or v + local f = fonts.handlers.otf.tables.features[v] or v if detail and detail["f-"..v] then features[#features+1] = format(" %s",f,v,v,v,v) else @@ -408,7 +408,7 @@ end local function show_font(currentfont,detail) local specification = get_specification(currentfont) - local features = fonts.get_features(specification.filename) + local features = fonts.helpers.getfeatures(specification.filename) local result = { } result[#result+1] = format("

names

",what) result[#result+1] = "" @@ -446,7 +446,7 @@ local function show_font(currentfont,detail) else done = true end - local title = fonts.otf.tables.features[f] or "" + local title = fonts.handlers.otf.tables.features[f] or "" result[#result+1] = format("",title,f,s,concat(table.sortedkeys(ss)," ")) end end diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index 0d9e071fe..38412f1e1 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -658,6 +658,7 @@ function lpeg.is_lpeg(p) end + end -- of closure do -- create closure to overcome 200 locals limit @@ -757,24 +758,24 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed + 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 kind == 3 then + if category == 3 then -- no further check else local tkey = type(key) if tkey == "string" then - kind = (kind == 2 and 3) or 1 + category = (category == 2 and 3) or 1 elseif tkey == "number" then - kind = (kind == 1 and 3) or 2 + category = (category == 1 and 3) or 2 else - kind = 3 + category = 3 end end end - if kind == 0 or kind == 3 then + if category == 0 or category == 3 then sort(srt,compare) else sort(srt) @@ -940,6 +941,13 @@ end table.fastcopy = fastcopy table.copy = copy +function table.derive(parent) + local child = { } + if parent then + setmetatable(child,{ __index = parent }) + end + return child +end function table.tohash(t,value) local h = { } @@ -3640,7 +3648,7 @@ local tables = utilities.tables local format, gmatch = string.format, string.gmatch local concat, insert, remove = table.concat, table.insert, table.remove -local setmetatable = setmetatable +local setmetatable, tonumber, tostring = setmetatable, tonumber, tostring function tables.definetable(target) -- defines undefined tables local composed, t, n = nil, { }, 0 @@ -3711,6 +3719,28 @@ function table.setemptymetatable(t) setmetatable(t,_empty_table_) end +-- experimental + +local function toxml(t,d,result) + for k, v in table.sortedpairs(t) do + if type(v) == "table" then + result[#result+1] = format("%s<%s>",d,k) + toxml(v,d.." ",result) + result[#result+1] = format("%s",d,k) + elseif tonumber(k) then + result[#result+1] = format("%s%s",d,k,v,k) + else + result[#result+1] = format("%s<%s>%s",d,k,tostring(v),k) + end + end +end + +function table.toxml(t,name) + local result = { "" } + toxml( { [name or "root"] = t }, "", result) + return concat(result,"\n") +end + end -- of closure @@ -3823,13 +3853,13 @@ local gsub, format = string.gsub, string.format local concat = table.concat local type, next = type, next -utilities = utilities or {} -utilities.merger = utilities.merger or { } -- maybe mergers -utilities.report = logs and logs.reporter("system") or print +utilities = utilities or {} +utilities.merger = utilities.merger or { } -- maybe mergers +utilities.report = logs and logs.reporter("system") or print -local merger = utilities.merger +local merger = utilities.merger -merger.strip_comment = true +merger.strip_comment = true local m_begin_merge = "begin library merge" local m_end_merge = "end library merge" @@ -4171,7 +4201,8 @@ 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,"%s*([^, ]+)") do -- space added + for s in gmatch(str,"[^, ]+") do -- space added t[s] = true end return t @@ -4214,6 +4245,10 @@ function parsers.getparameters(self,class,parentclass,settings) parsers.settings_to_hash(settings,sc) end +function parsers.listitem(str) + return gmatch(str,"[^, ]+") +end + end -- of closure @@ -11108,8 +11143,8 @@ local function load_configuration_files() local variables = data.variables or { } local warning = false for k, v in next, data do - local kind = type(v) - if kind == "table" then + local variant = type(v) + if variant == "table" then initializesetter(filename,k,v) elseif variables[k] == nil then if trace_locating and not warning then @@ -11381,7 +11416,7 @@ function resolvers.registerextrapath(paths,subpaths) end end elseif subpaths and subpaths ~= "" then - for i=1,n do + for i=1,oldn do -- we gmatch each step again, not that fast, but used seldom for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s @@ -11550,29 +11585,29 @@ local function collect_files(names) local blobroot = files.__path__ or blobpath if type(blobfile) == 'string' then if not dname or find(blobfile,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,blobfile,bname) - local search = filejoin(blobroot,blobfile,bname) - local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) + local variant = hash.type + -- local search = filejoin(blobpath,blobfile,bname) + local search = filejoin(blobroot,blobfile,bname) + local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end else for kk=1,#blobfile do local vv = blobfile[kk] if not dname or find(vv,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,vv,bname) - local search = filejoin(blobroot,vv,bname) - local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) + local variant = hash.type + -- local search = filejoin(blobpath,vv,bname) + local search = filejoin(blobroot,vv,bname) + local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end end end @@ -11950,14 +11985,14 @@ function resolvers.findgivenfile(filename) return findgivenfiles(filename,false)[1] or "" end -local function doit(path,blist,bname,tag,kind,result,allresults) +local function doit(path,blist,bname,tag,variant,result,allresults) local done = false - if blist and kind then + if blist and variant then local resolve = resolvers.resolve -- added if type(blist) == 'string' then -- make function and share code if find(lower(blist),path) then - local full = methodhandler('concatinators',kind,tag,blist,bname) or "" + local full = methodhandler('concatinators',variant,tag,blist,bname) or "" result[#result+1] = resolve(full) done = true end @@ -11965,7 +12000,7 @@ local function doit(path,blist,bname,tag,kind,result,allresults) for kk=1,#blist do local vv = blist[kk] if find(lower(vv),path) then - local full = methodhandler('concatinators',kind,tag,vv,bname) or "" + local full = methodhandler('concatinators',variant,tag,vv,bname) or "" result[#result+1] = resolve(full) done = true if not allresults then break end diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua index 0d9e071fe..38412f1e1 100644 --- a/scripts/context/stubs/mswin/mtxrun.lua +++ b/scripts/context/stubs/mswin/mtxrun.lua @@ -658,6 +658,7 @@ function lpeg.is_lpeg(p) end + end -- of closure do -- create closure to overcome 200 locals limit @@ -757,24 +758,24 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed + 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 kind == 3 then + if category == 3 then -- no further check else local tkey = type(key) if tkey == "string" then - kind = (kind == 2 and 3) or 1 + category = (category == 2 and 3) or 1 elseif tkey == "number" then - kind = (kind == 1 and 3) or 2 + category = (category == 1 and 3) or 2 else - kind = 3 + category = 3 end end end - if kind == 0 or kind == 3 then + if category == 0 or category == 3 then sort(srt,compare) else sort(srt) @@ -940,6 +941,13 @@ end table.fastcopy = fastcopy table.copy = copy +function table.derive(parent) + local child = { } + if parent then + setmetatable(child,{ __index = parent }) + end + return child +end function table.tohash(t,value) local h = { } @@ -3640,7 +3648,7 @@ local tables = utilities.tables local format, gmatch = string.format, string.gmatch local concat, insert, remove = table.concat, table.insert, table.remove -local setmetatable = setmetatable +local setmetatable, tonumber, tostring = setmetatable, tonumber, tostring function tables.definetable(target) -- defines undefined tables local composed, t, n = nil, { }, 0 @@ -3711,6 +3719,28 @@ function table.setemptymetatable(t) setmetatable(t,_empty_table_) end +-- experimental + +local function toxml(t,d,result) + for k, v in table.sortedpairs(t) do + if type(v) == "table" then + result[#result+1] = format("%s<%s>",d,k) + toxml(v,d.." ",result) + result[#result+1] = format("%s",d,k) + elseif tonumber(k) then + result[#result+1] = format("%s%s",d,k,v,k) + else + result[#result+1] = format("%s<%s>%s",d,k,tostring(v),k) + end + end +end + +function table.toxml(t,name) + local result = { "" } + toxml( { [name or "root"] = t }, "", result) + return concat(result,"\n") +end + end -- of closure @@ -3823,13 +3853,13 @@ local gsub, format = string.gsub, string.format local concat = table.concat local type, next = type, next -utilities = utilities or {} -utilities.merger = utilities.merger or { } -- maybe mergers -utilities.report = logs and logs.reporter("system") or print +utilities = utilities or {} +utilities.merger = utilities.merger or { } -- maybe mergers +utilities.report = logs and logs.reporter("system") or print -local merger = utilities.merger +local merger = utilities.merger -merger.strip_comment = true +merger.strip_comment = true local m_begin_merge = "begin library merge" local m_end_merge = "end library merge" @@ -4171,7 +4201,8 @@ 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,"%s*([^, ]+)") do -- space added + for s in gmatch(str,"[^, ]+") do -- space added t[s] = true end return t @@ -4214,6 +4245,10 @@ function parsers.getparameters(self,class,parentclass,settings) parsers.settings_to_hash(settings,sc) end +function parsers.listitem(str) + return gmatch(str,"[^, ]+") +end + end -- of closure @@ -11108,8 +11143,8 @@ local function load_configuration_files() local variables = data.variables or { } local warning = false for k, v in next, data do - local kind = type(v) - if kind == "table" then + local variant = type(v) + if variant == "table" then initializesetter(filename,k,v) elseif variables[k] == nil then if trace_locating and not warning then @@ -11381,7 +11416,7 @@ function resolvers.registerextrapath(paths,subpaths) end end elseif subpaths and subpaths ~= "" then - for i=1,n do + for i=1,oldn do -- we gmatch each step again, not that fast, but used seldom for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s @@ -11550,29 +11585,29 @@ local function collect_files(names) local blobroot = files.__path__ or blobpath if type(blobfile) == 'string' then if not dname or find(blobfile,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,blobfile,bname) - local search = filejoin(blobroot,blobfile,bname) - local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) + local variant = hash.type + -- local search = filejoin(blobpath,blobfile,bname) + local search = filejoin(blobroot,blobfile,bname) + local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end else for kk=1,#blobfile do local vv = blobfile[kk] if not dname or find(vv,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,vv,bname) - local search = filejoin(blobroot,vv,bname) - local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) + local variant = hash.type + -- local search = filejoin(blobpath,vv,bname) + local search = filejoin(blobroot,vv,bname) + local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end end end @@ -11950,14 +11985,14 @@ function resolvers.findgivenfile(filename) return findgivenfiles(filename,false)[1] or "" end -local function doit(path,blist,bname,tag,kind,result,allresults) +local function doit(path,blist,bname,tag,variant,result,allresults) local done = false - if blist and kind then + if blist and variant then local resolve = resolvers.resolve -- added if type(blist) == 'string' then -- make function and share code if find(lower(blist),path) then - local full = methodhandler('concatinators',kind,tag,blist,bname) or "" + local full = methodhandler('concatinators',variant,tag,blist,bname) or "" result[#result+1] = resolve(full) done = true end @@ -11965,7 +12000,7 @@ local function doit(path,blist,bname,tag,kind,result,allresults) for kk=1,#blist do local vv = blist[kk] if find(lower(vv),path) then - local full = methodhandler('concatinators',kind,tag,vv,bname) or "" + local full = methodhandler('concatinators',variant,tag,vv,bname) or "" result[#result+1] = resolve(full) done = true if not allresults then break end diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun index 0d9e071fe..38412f1e1 100644 --- a/scripts/context/stubs/unix/mtxrun +++ b/scripts/context/stubs/unix/mtxrun @@ -658,6 +658,7 @@ function lpeg.is_lpeg(p) end + end -- of closure do -- create closure to overcome 200 locals limit @@ -757,24 +758,24 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed + 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 kind == 3 then + if category == 3 then -- no further check else local tkey = type(key) if tkey == "string" then - kind = (kind == 2 and 3) or 1 + category = (category == 2 and 3) or 1 elseif tkey == "number" then - kind = (kind == 1 and 3) or 2 + category = (category == 1 and 3) or 2 else - kind = 3 + category = 3 end end end - if kind == 0 or kind == 3 then + if category == 0 or category == 3 then sort(srt,compare) else sort(srt) @@ -940,6 +941,13 @@ end table.fastcopy = fastcopy table.copy = copy +function table.derive(parent) + local child = { } + if parent then + setmetatable(child,{ __index = parent }) + end + return child +end function table.tohash(t,value) local h = { } @@ -3640,7 +3648,7 @@ local tables = utilities.tables local format, gmatch = string.format, string.gmatch local concat, insert, remove = table.concat, table.insert, table.remove -local setmetatable = setmetatable +local setmetatable, tonumber, tostring = setmetatable, tonumber, tostring function tables.definetable(target) -- defines undefined tables local composed, t, n = nil, { }, 0 @@ -3711,6 +3719,28 @@ function table.setemptymetatable(t) setmetatable(t,_empty_table_) end +-- experimental + +local function toxml(t,d,result) + for k, v in table.sortedpairs(t) do + if type(v) == "table" then + result[#result+1] = format("%s<%s>",d,k) + toxml(v,d.." ",result) + result[#result+1] = format("%s",d,k) + elseif tonumber(k) then + result[#result+1] = format("%s%s",d,k,v,k) + else + result[#result+1] = format("%s<%s>%s",d,k,tostring(v),k) + end + end +end + +function table.toxml(t,name) + local result = { "" } + toxml( { [name or "root"] = t }, "", result) + return concat(result,"\n") +end + end -- of closure @@ -3823,13 +3853,13 @@ local gsub, format = string.gsub, string.format local concat = table.concat local type, next = type, next -utilities = utilities or {} -utilities.merger = utilities.merger or { } -- maybe mergers -utilities.report = logs and logs.reporter("system") or print +utilities = utilities or {} +utilities.merger = utilities.merger or { } -- maybe mergers +utilities.report = logs and logs.reporter("system") or print -local merger = utilities.merger +local merger = utilities.merger -merger.strip_comment = true +merger.strip_comment = true local m_begin_merge = "begin library merge" local m_end_merge = "end library merge" @@ -4171,7 +4201,8 @@ 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,"%s*([^, ]+)") do -- space added + for s in gmatch(str,"[^, ]+") do -- space added t[s] = true end return t @@ -4214,6 +4245,10 @@ function parsers.getparameters(self,class,parentclass,settings) parsers.settings_to_hash(settings,sc) end +function parsers.listitem(str) + return gmatch(str,"[^, ]+") +end + end -- of closure @@ -11108,8 +11143,8 @@ local function load_configuration_files() local variables = data.variables or { } local warning = false for k, v in next, data do - local kind = type(v) - if kind == "table" then + local variant = type(v) + if variant == "table" then initializesetter(filename,k,v) elseif variables[k] == nil then if trace_locating and not warning then @@ -11381,7 +11416,7 @@ function resolvers.registerextrapath(paths,subpaths) end end elseif subpaths and subpaths ~= "" then - for i=1,n do + for i=1,oldn do -- we gmatch each step again, not that fast, but used seldom for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s @@ -11550,29 +11585,29 @@ local function collect_files(names) local blobroot = files.__path__ or blobpath if type(blobfile) == 'string' then if not dname or find(blobfile,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,blobfile,bname) - local search = filejoin(blobroot,blobfile,bname) - local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) + local variant = hash.type + -- local search = filejoin(blobpath,blobfile,bname) + local search = filejoin(blobroot,blobfile,bname) + local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end else for kk=1,#blobfile do local vv = blobfile[kk] if not dname or find(vv,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,vv,bname) - local search = filejoin(blobroot,vv,bname) - local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) + local variant = hash.type + -- local search = filejoin(blobpath,vv,bname) + local search = filejoin(blobroot,vv,bname) + local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end end end @@ -11950,14 +11985,14 @@ function resolvers.findgivenfile(filename) return findgivenfiles(filename,false)[1] or "" end -local function doit(path,blist,bname,tag,kind,result,allresults) +local function doit(path,blist,bname,tag,variant,result,allresults) local done = false - if blist and kind then + if blist and variant then local resolve = resolvers.resolve -- added if type(blist) == 'string' then -- make function and share code if find(lower(blist),path) then - local full = methodhandler('concatinators',kind,tag,blist,bname) or "" + local full = methodhandler('concatinators',variant,tag,blist,bname) or "" result[#result+1] = resolve(full) done = true end @@ -11965,7 +12000,7 @@ local function doit(path,blist,bname,tag,kind,result,allresults) for kk=1,#blist do local vv = blist[kk] if find(lower(vv),path) then - local full = methodhandler('concatinators',kind,tag,vv,bname) or "" + local full = methodhandler('concatinators',variant,tag,vv,bname) or "" result[#result+1] = resolve(full) done = true if not allresults then break end diff --git a/tex/context/base/anch-pgr.mkiv b/tex/context/base/anch-pgr.mkiv index 7c3ffdf43..afa881933 100644 --- a/tex/context/base/anch-pgr.mkiv +++ b/tex/context/base/anch-pgr.mkiv @@ -1243,47 +1243,6 @@ % % \setnewconstant\margincontentmethod \plusthree % textmethod % beware: 1 = old method % % \setnewconstant\marginpagecheckmethod \plusone % splitmethod -%D For a right menu, a sequence of calls to \type -%D {right_menu_button} is generated. -%D -%D \starttyping -%D right_menu_button (n, p, s=0/1/2, x, y, w, h, d) ; -%D \stoptyping -%D -%D Here, n is the number of the button, s a status variable, -%D while the rest is positional info. The status variable is -%D 0, 1 or~2: not found, found and found but current page. - -% 0=not found 1=found 2=current page - -% geen leeg - -\newtoks\MPmenutoks - -\def\MPmenubuttons#1{\the\MPmenutoks} - -\appendtoks \global\MPmenutoks\emptytoks \to \everyshipout - -\newconstant\currentamrealpagemode % 0=notfound 1=found 2=currentpage - -\def\domenuitemposition#1#2#3% - {\doifelsevalue{\??am#1\c!position}\v!yes - {\doglobal\increment\currentamposition - \doifreferencefoundelse{#2}% 0=not found, 1=same page, >1=elsewhere - {\currentamrealpagemode\ifnum\currentreferencerealpage=\realpageno\plusone\else\plustwo\fi}% - {\currentamrealpagemode\plustwo}% - \expanded % \doglobal\appendetoks - {\doglobal\noexpand\appendtoks - #1_menu_button(\number\currentamposition,\the\currentamrealpagemode,\MPpos{#1:\currentamposition}) ; - \to \MPmenutoks}% - \hpos{#1:\currentamposition}{#3}} - {#3}} - -\def\dowholemenuposition#1% - {\ifnum\currentamposition>0 - \dowithnextbox{\hpos{menu:#1:\the\realpageno}{\flushnextbox}}\hbox - \fi} - %D \macros %D {GFC, GTC, GSC} %D diff --git a/tex/context/base/attr-col.lua b/tex/context/base/attr-col.lua index fd69fb682..acabe62ac 100644 --- a/tex/context/base/attr-col.lua +++ b/tex/context/base/attr-col.lua @@ -269,17 +269,17 @@ local function reviver(data,n) d = { gray, gray, gray, gray } report_attributes("unable to revive color %s",n or "?") else - local kind = colors.forcedmodel(v[1]) - if kind == 2 then + local model = colors.forcedmodel(v[1]) + if model == 2 then local gray= graycolor(v[2]) d = { gray, gray, gray, gray } - elseif kind == 3 then + 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 kind == 4 then + 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 kind == 5 then + 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 } diff --git a/tex/context/base/attr-lay.lua b/tex/context/base/attr-lay.lua index 5e60fbed3..32812785a 100644 --- a/tex/context/base/attr-lay.lua +++ b/tex/context/base/attr-lay.lua @@ -175,3 +175,5 @@ function viewerlayers.define(settings) codeinjections.defineviewerlayer(settings) end end + +commands.defineviewerlayer = viewerlayers.define diff --git a/tex/context/base/attr-lay.mkiv b/tex/context/base/attr-lay.mkiv index efc9f4514..efea95bc9 100644 --- a/tex/context/base/attr-lay.mkiv +++ b/tex/context/base/attr-lay.mkiv @@ -44,11 +44,11 @@ \def\dodefineviewerlayer[#1][#2]% document wide properties {\begingroup \getparameters[\??lr][#2]% - \ctxlua{attributes.viewerlayers.define{ + \ctxcommand{defineviewerlayer{ tag = "#1", title = "\@@lrtitle", visible = "\@@lrstate", - kind = 0, % 1 == frozen + editable = "\v!yes", printable = "\@@lrprintable", }}% \doif\@@lrmethod\v!command @@ -73,11 +73,11 @@ % layout components are implemented rather directly (speed) \def\doinitializelayoutcomponent#1#2% - {\ctxlua{backends.codeinjections.defineviewerlayer{% this will move to the lua end i.e be merged with register + {\ctxcommand{defineviewerlayer{% this will move to the lua end i.e be merged with register tag = "#1:#2", title = "#1 #2", visible = "\v!start", - kind = 0, % 1 == frozen + editable = "\v!yes", printable = "\v!yes" }}% \edef\layoutcomponentboxattribute{attr \viewerlayerattribute \ctxlua{tex.write(attributes.viewerlayers.register('#1:#2',true))}\relax}% @@ -101,5 +101,4 @@ \let\setlayoutcomponentattribute \dosetlayoutcomponentattribute \let\resetlayoutcomponentattribute\doresetlayoutcomponentattribute} - \protect \endinput diff --git a/tex/context/base/back-exp.lua b/tex/context/base/back-exp.lua index d5bdc2b91..b9cfcefd4 100644 --- a/tex/context/base/back-exp.lua +++ b/tex/context/base/back-exp.lua @@ -57,7 +57,7 @@ local attributes = attributes local variables = interfaces.variables local tasks = nodes.tasks -local fontchar = fonts.characters +local fontchar = fonts.hashes.characters local languagenames = languages.numbers local nodecodes = nodes.nodecodes diff --git a/tex/context/base/back-ini.lua b/tex/context/base/back-ini.lua index 4e990aa38..5cabc16ac 100644 --- a/tex/context/base/back-ini.lua +++ b/tex/context/base/back-ini.lua @@ -6,10 +6,9 @@ if not modules then modules = { } end modules ['back-ini'] = { license = "see context related readme files" } --- I need to check what is actually needed, maybe some can become --- locals. +local setmetatable = setmetatable -backends = backends or { } +backends = backends or { } local backends = backends local trace_backend = false trackers.register("backend.initializers", function(v) trace_finalizers = v end) @@ -20,143 +19,35 @@ local function nothing() return nil end backends.nothing = nothing -backends.nodeinjections = { - - rgbcolor = nothing, - cmykcolor = nothing, - graycolor = nothing, - spotcolor = nothing, - - transparency = nothing, - - overprint = nothing, - knockout = nothing, - - positive = nothing, - negative = nothing, - - effect = nothing, - - startlayer = nothing, - stoplayer = nothing, - switchlayer = nothing, - - reference = nothing, - destination = nothing, - - addtags = nothing, - - insertu3d = nothing, - insertswf = nothing, - insertmovie = nothing, - insertsound = nothing, - - injectbitmap = nothing, - -} - -backends.codeinjections = { - - prerollreference = nothing, - - presetsymbol = nothing, - presetsymbollist = nothing, - registersymbol = nothing, - registeredsymbol = nothing, - - registercomment = nothing, - - embedfile = nothing, - attachfile = nothing, - attachmentid = nothing, - - adddocumentinfo = nothing, - setupidentity = nothing, - setupcanvas = nothing, - - setpagetransition = nothing, - - defineviewerlayer = nothing, - useviewerlayer = nothing, - - addbookmarks = nothing, - - addtransparencygroup = nothing, - - typesetfield = nothing, - doiffieldelse = nothing, - doiffieldgroupelse = nothing, - doiffieldsetelse = nothing, - definefield = nothing, - clonefield = nothing, - definefieldset = nothing, - setfieldcalculationset = nothing, - getfieldgroup = nothing, - getfieldset = nothing, - setformsmethod = nothing, - getdefaultfieldvalue = nothing, - - flushpageactions = nothing, - flushdocumentactions = nothing, - - insertrenderingwindow = nothing, - processrendering = nothing, - - setfigurecolorspace = nothing, - setfigurealternative = nothing, - setfiguremask = nothing, - - enabletags = nothing, - - mergereferences = nothing, - mergeviewerlayers = nothing, - - setformat = nothing, - getformatoption = nothing, - supportedformats = nothing, - - -- called in tex - - finalizepage = nothing, -- will go when we have a hook at the lua end - - finishreference = nothing, - - getoutputfilename = nothing, - +local mt = { + __index = function(t,k) + t[k] = nothing + return nothing + end } -backends.registrations = { - - grayspotcolor = nothing, - rgbspotcolor = nothing, - cmykspotcolor = nothing, - - grayindexcolor = nothing, - rgbindexcolor = nothing, - cmykindexcolor = nothing, - - spotcolorname = nothing, - - transparency = nothing, +local nodeinjections = { } setmetatable(nodeinjections, mt) +local codeinjections = { } setmetatable(codeinjections, mt) +local registrations = { } setmetatable(registrations, mt) +local tables = { } +local defaults = { + nodeinjections = nodeinjections, + codeinjections = codeinjections, + registrations = registrations, + tables = tables, } -local comment = { "comment", "" } +backends.defaults = defaults -backends.tables = { - vfspecials = { - red = comment, - green = comment, - blue = comment, - black = comment, - startslant = comment, - stopslant = comment, - } -} +backends.nodeinjections = { } setmetatable(backends.nodeinjections, { __index = nodeinjections }) +backends.codeinjections = { } setmetatable(backends.codeinjections, { __index = codeinjections }) +backends.registrations = { } setmetatable(backends.registrations, { __index = registrations }) +backends.tables = { } setmetatable(backends.tables, { __index = tables }) backends.current = "unknown" -function backends.install(what) -- these can become metatables +function backends.install(what) if type(what) == "string" then local backend = backends[what] if backend then @@ -164,32 +55,10 @@ function backends.install(what) -- these can become metatables report_backend("initializing backend %s (%s)",what,backend.comment or "no comment") end backends.current = what - for _, category in next, { "nodeinjections", "codeinjections", "registrations", "tables" } do - local plugin = backend[category] - local whereto = backends[category] - if plugin then - for name, meaning in next, whereto do - if plugin[name] then - whereto[name] = plugin[name] - -- report_backend("installing function %s in category %s of %s",name,category,what) - elseif trace_backend then - report_backend("no function %s in category %s of %s",name,category,what) - end - end - elseif trace_backend then - report_backend("no category %s in %s",category,what) - end - -- extra checks - for k, v in next, whereto do - if not plugin[k] then - report_backend("entry %s in %s is not set",k,category) - end - end - for k, v in next, plugin do - if not whereto[k] then - report_backend("entry %s in %s is not used",k,category) - end - end + for category, default in next, defaults do + local target, plugin = backends[category], backend[category] + setmetatable(plugin, { __index = default }) + setmetatable(target, { __index = plugin }) end elseif trace_backend then report_backend("no backend named %s",what) @@ -205,3 +74,14 @@ statistics.register("used backend", function() return nil end end) + +local comment = { "comment", "" } + +tables.vfspecials = { + red = comment, + green = comment, + blue = comment, + black = comment, + startslant = comment, + stopslant = comment, +} diff --git a/tex/context/base/back-pdf.mkiv b/tex/context/base/back-pdf.mkiv index 392dc8984..5b7de128d 100644 --- a/tex/context/base/back-pdf.mkiv +++ b/tex/context/base/back-pdf.mkiv @@ -56,11 +56,19 @@ \setjobsuffix{pdf} -%D PDF/X: +%D PDF/X (matbe combine the two lua calls) + +\setupbackend + [xmpfile=] + +\appendtoks + \doifsomething{\backendparameter{xmpfile}} + {\ctxcommand{setxmpfile("\backendparameter{xmpfile}")}}% +\to \everysetupbackend \appendtoks \doifsomething{\backendparameter\c!format} - {\ctxlua{backends.codeinjections.setformat { + {\ctxcommand{setformat { format = "\backendparameter\c!format", level = "\backendparameter\c!level", option = "\backendparameter\c!option", diff --git a/tex/context/base/bibl-tra.lua b/tex/context/base/bibl-tra.lua index 55d541fdc..e0eaf64b3 100644 --- a/tex/context/base/bibl-tra.lua +++ b/tex/context/base/bibl-tra.lua @@ -132,7 +132,7 @@ function hacks.resolve(prefix,block,reference) -- maybe already feed it split if subset then local result, nofresult, done = { }, 0, { } block = tonumber(block) - for rest in gmatch(reference,"([^,%s]+)") do + for rest in gmatch(reference,"[^, ]+") do local blk, tag, found = block, nil, nil if block then tag = blk .. ":" .. rest diff --git a/tex/context/base/blob-ini.lua b/tex/context/base/blob-ini.lua index 623325040..48cf4e393 100644 --- a/tex/context/base/blob-ini.lua +++ b/tex/context/base/blob-ini.lua @@ -23,13 +23,17 @@ if not modules then modules = { } end modules ['blob-ini'] = { -- collapse or new pars -- interline spacing etc +-- blob.char +-- blob.line +-- blob.paragraph +-- blob.page + local type = type +local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns local report_blobs = logs.reporter("blobs") -local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns - -local fontdata = fonts.identifiers +local fontdata = fonts.hashes.identifiers local nodepool = nodes.pool @@ -68,14 +72,14 @@ function blobs.new() end function blobs.append(t,str) -- will be link nodes.link - local kind = type(str) + local typ = type(str) local dummy = nil - if kind == "number" then + if typ == "number" then str = tostring(str) - kind = "string" + typ = "string" end local list = t.list - if kind == "string" then + if typ == "string" then local pars = lpegmatch(ctxtextcapture,str) local noflist = #list for p=1,#pars do @@ -131,3 +135,25 @@ function blobs.write(t) end 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 diff --git a/tex/context/base/buff-ver.lua b/tex/context/base/buff-ver.lua index 392c323be..a1f98ab0a 100644 --- a/tex/context/base/buff-ver.lua +++ b/tex/context/base/buff-ver.lua @@ -194,17 +194,17 @@ end local fallback = context.verbatim -local function makepattern(visualizer,kind,pattern) +local function makepattern(visualizer,replacement,pattern) if not pattern then - report_visualizers("error in visualizer: %s",kind) + report_visualizers("error in visualizer: %s",replacement) return patterns.alwaystrue else - if type(visualizer) == "table" and type(kind) == "string" then - kind = visualizer[kind] or fallback + if type(visualizer) == "table" and type(replacement) == "string" then + replacement = visualizer[replacement] or fallback else - kind = fallback + replacement = fallback end - return (C(pattern) * CargOne) / kind + return (C(pattern) * CargOne) / replacement end end diff --git a/tex/context/base/buff-ver.mkiv b/tex/context/base/buff-ver.mkiv index d92a5d07e..ab1d0cb23 100644 --- a/tex/context/base/buff-ver.mkiv +++ b/tex/context/base/buff-ver.mkiv @@ -304,7 +304,7 @@ {\dontleavehmode \bgroup \edef\currenttype{#1}% - \dolettypeparameter\v!lines\v!hyphenated + \lettypeparameter\v!lines\v!hyphenated \let\specialobeyedspace\specialstretchedspace \doifnextoptionalelse\redotype\dodotype} diff --git a/tex/context/base/chem-str.lua b/tex/context/base/chem-str.lua index 44849c067..1a6ed4dc2 100644 --- a/tex/context/base/chem-str.lua +++ b/tex/context/base/chem-str.lua @@ -161,7 +161,7 @@ function chemicals.define(name,spec,text) } end -local metacode, kind, keys, bonds, max, txt, textsize, rot, pstack +local metacode, variant, keys, bonds, max, txt, textsize, rot, pstack local molecule = chemicals.molecule -- or use lpegmatch(chemicals.moleculeparser,...) local function fetch(txt) @@ -226,7 +226,7 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) else local rep, operation, special, index, upto, set, text = lpegmatch(pattern,s) if operation == "pb" then - insert(pstack,kind) + 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 ... @@ -236,19 +236,19 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) end end elseif operation == "save" then - insert(pstack,kind) + insert(pstack,variant) m = m + 1 ; metacode[m] = syntax.save.direct elseif operation == "pe" or operation == "restore" then - kind = remove(pstack) - local ss = syntax[kind] + variant = remove(pstack) + local ss = syntax[variant] local prev = bonds or 6 keys, bonds, max, rot = ss.keys, ss.n, ss.max, 1 m = m + 1 ; metacode[m] = syntax[operation].direct m = m + 1 ; metacode[m] = format("chem_set(%s,%s) ;",prev,bonds) elseif operation == "front" then - if syntax[kind .. "_front"] then - kind = kind .. "_front" - local ss = syntax[kind] + if syntax[variant .. "_front"] then + variant = variant .. "_front" + local ss = syntax[variant] local prev = bonds or 6 keys, bonds, max, rot = ss.keys, ss.n, ss.max, 1 m = m + 1 ; metacode[m] = format("chem_set(%s,%s) ;",prev,bonds) @@ -271,7 +271,7 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) end elseif ss.keys then local prev = bonds or 6 - kind, keys, bonds, max, rot = s, ss.keys, ss.n, ss.max, 1 + variant, keys, bonds, max, rot = s, ss.keys, ss.n, ss.max, 1 m = m + 1 ; metacode[m] = format("chem_set(%s,%s) ;",prev,bonds) end else @@ -308,7 +308,7 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) end end elseif what == "text" then - local align = syntax[kind].align + local align = syntax[variant].align align = align and align[operation] align = align and align[rot] if set then @@ -440,7 +440,7 @@ function chemicals.start(settings) l/25, r/25, t/25, b/25, scale, tostring(settings.axis == variables.on), tostring(width), tostring(height), tostring(offset) ) } - kind, keys, bonds, stack, rot, pstack = "six", { }, 6, { }, 1, { } + variant, keys, bonds, stack, rot, pstack = "six", { }, 6, { }, 1, { } end function chemicals.stop() diff --git a/tex/context/base/cldf-int.lua b/tex/context/base/cldf-int.lua index e2fff18dc..4e2e7b0e6 100644 --- a/tex/context/base/cldf-int.lua +++ b/tex/context/base/cldf-int.lua @@ -75,8 +75,8 @@ function mkiv.define(name,specification) -- name is optional texsprint(ctxcatcodes,"\\def",mkivdo) for i=1,na do local a = arguments[i] - local kind = a[1] - if kind == "option" then + local variant = a[1] + if variant == "option" then texsprint(ctxcatcodes,"[#",i,"]") if not done then opt = opt + 1 @@ -93,12 +93,12 @@ function mkiv.define(name,specification) -- name is optional end for i=1,na do local a = arguments[i] - local kind = a[2] - if kind == "list" then + local variant = a[2] + if variant == "list" then texsprint(ctxcatcodes,",mkiv.a([[#",i,"]])") - elseif kind == "hash" then + elseif variant == "hash" then texsprint(ctxcatcodes,",mkiv.h([[#",i,"]])") - elseif kind == "number" then + elseif variant == "number" then texsprint(ctxcatcodes,",mkiv.n([[#",i,"]])") else texsprint(ctxcatcodes,",[[#",i,"]]") diff --git a/tex/context/base/colo-ext.mkiv b/tex/context/base/colo-ext.mkiv index 343b062af..771974d4d 100644 --- a/tex/context/base/colo-ext.mkiv +++ b/tex/context/base/colo-ext.mkiv @@ -77,7 +77,7 @@ % A goodie that replaces the startMPcolor hackery -%\definecolor[red-t] [r=1,t=0.5,a=1] +% \definecolor[red-t] [r=1,t=0.5,a=1] % \definecolor[green-t][g=1,t=0.5,a=1] % % \defineintermediatecolor[mycolora][0.5,red,green] diff --git a/tex/context/base/colo-icc.lua b/tex/context/base/colo-icc.lua index 2ce34e8cd..904d42143 100644 --- a/tex/context/base/colo-icc.lua +++ b/tex/context/base/colo-icc.lua @@ -90,8 +90,8 @@ function colors.iccprofile(filename,verbose) for tag, spec in next, tags do if tag then local offset, length = spec.offset, spec.length - local kind = readstring(f,offset,4) - if kind == "text" or kind == "desc" then + local variant = readstring(f,offset,4) + if variant == "text" or variant == "desc" then local str = readstring(f,length-4) tags[tag] = { data = str, @@ -99,7 +99,7 @@ function colors.iccprofile(filename,verbose) } else if verbose then - report_colors("ignoring tag '%s' or type '%s' in profile '%s'",tag,kind,fullname) + report_colors("ignoring tag '%s' or type '%s' in profile '%s'",tag,variant,fullname) end tags[tag] = nil end diff --git a/tex/context/base/colo-ini.lua b/tex/context/base/colo-ini.lua index 75cee7b23..635a13ec5 100644 --- a/tex/context/base/colo-ini.lua +++ b/tex/context/base/colo-ini.lua @@ -209,16 +209,16 @@ local function do_registerspotcolor(parent,name,parentnumber,e,f,d,p) if not registered[parentnumber] then local v = colors.values[parentnumber] if v then - local kind = colors.default -- else problems with shading etc - if kind == 1 then kind = v[1] end + local model = colors.default -- else problems with shading etc + if model == 1 then model = v[1] end if e and e ~= "" then registrations.spotcolorname(parent,e) -- before registration of the color end - if kind == 2 then -- name noffractions names p's r g b + if model == 2 then -- name noffractions names p's r g b registrations.grayspotcolor(parent,f,d,p,v[2]) - elseif kind == 3 then + elseif model == 3 then registrations.rgbspotcolor (parent,f,d,p,v[3],v[4],v[5]) - elseif kind == 4 then + elseif model == 4 then registrations.cmykspotcolor(parent,f,d,p,v[6],v[7],v[8],v[9]) end end @@ -230,13 +230,13 @@ local function do_registermultitonecolor(parent,name,parentnumber,e,f,d,p) -- sa if not registered[parentnumber] then local v = colors.values[parentnumber] if v then - local kind = colors.default -- else problems with shading etc - if kind == 1 then kind = v[1] end - if kind == 2 then + local model = colors.default -- else problems with shading etc + if model == 1 then model = v[1] end + if model == 2 then registrations.grayindexcolor(parent,f,d,p,v[2]) - elseif kind == 3 then + elseif model == 3 then registrations.rgbindexcolor (parent,f,d,p,v[3],v[4],v[5]) - elseif kind == 4 then + elseif model == 4 then registrations.cmykindexcolor(parent,f,d,p,v[6],v[7],v[8],v[9]) end end @@ -551,7 +551,7 @@ function colors.defineintermediatecolor(name,fraction,c_one,c_two,a_one,a_two,sp if one then if two then local csone, cstwo = one[1], two[1] - if csone == cstwo then + -- if csone == cstwo then -- actually we can set all 8 values at once here but this is cleaner as we avoid -- problems with weighted gray conversions and work with original values local ca @@ -565,7 +565,7 @@ function colors.defineintermediatecolor(name,fraction,c_one,c_two,a_one,a_two,sp ca = register_color(name,'gray',f(one,two,2,fraction)) end definecolor(name,ca,global,freeze) - end + -- end else local csone = one[1] local ca @@ -627,13 +627,13 @@ commands.definemultitonecolor = colors.definemultitonecolor commands.definetransparency = colors.definetransparency commands.defineintermediatecolor = colors.defineintermediatecolor -function commands.spotcolorname (a) context(colors.spotcolorname (a)) end -function commands.spotcolorparent (a) context(colors.spotcolorparent (a)) end -function commands.spotcolorvalue (a) context(colors.spotcolorvalue (a)) end -function commands.colorcomponents (a) context(colors.colorcomponents (a)) end -function commands.transparencycomponents(a) context(colors.transparencycomponents(a)) end -function commands.formatcolor (a) context(colors.formatcolor (a)) end -function commands.formatgray (a) context(colors.formatgray (a)) end +function commands.spotcolorname (a) context(colors.spotcolorname (a)) end +function commands.spotcolorparent (a) context(colors.spotcolorparent (a)) end +function commands.spotcolorvalue (a) context(colors.spotcolorvalue (a)) end +function commands.colorcomponents (a) context(colors.colorcomponents (a)) end +function commands.transparencycomponents(a) context(colors.transparencycomponents(a)) end +function commands.formatcolor (...) context(colors.formatcolor (...)) end +function commands.formatgray (...) context(colors.formatgray (...)) end function commands.mpcolor(model,ca,ta,default) context(colors.mpcolor(model,ca,ta,default)) diff --git a/tex/context/base/colo-ini.mkiv b/tex/context/base/colo-ini.mkiv index b842337a9..24627b68c 100644 --- a/tex/context/base/colo-ini.mkiv +++ b/tex/context/base/colo-ini.mkiv @@ -140,15 +140,15 @@ %D \showsetup{color} %D \showsetup{graycolor} - \def\switchtocolor [#1]{\csname#1\endcsname} +\unexpanded\def\switchtocolor [#1]{\csname#1\endcsname} \unexpanded\def\color [#1]{\groupedcommand{\doactivatecolor{#1}}{}} \unexpanded\def\startcolor [#1]{\begingroup\doactivatecolor{#1}} \unexpanded\def\stopcolor {\endgroup} \unexpanded\def\graycolor [#1]{\groupedcommand{\dosetcolormodel{gray}\getvalue{#1}}{}} \unexpanded\def\colored [#1]{\groupedcommand{\definecolor[@colored@][#1]\doactivatecolor{@colored@}}{}} \unexpanded\def\fastcolored [#1]#2{\begingroup\dodefinefastcolor[@colored@][#1]\doactivatecolor{@colored@}#2\endgroup} - \def\predefinecolor [#1]{\flushatshipout{\hbox{\color[#1]{}}}} - \def\predefineindexcolor[#1]{\flushatshipout{\hbox{\color[#1]{}}}} +\unexpanded\def\predefinecolor [#1]{\flushatshipout{\hbox{\color[#1]{}}}} +\unexpanded\def\predefineindexcolor[#1]{\flushatshipout{\hbox{\color[#1]{}}}} % some of this will go away @@ -156,11 +156,17 @@ \unexpanded\def\stopcolorpage {\stopcolor} \unexpanded\def\startraster [#1]{\dosetrastercolor{#1}} \unexpanded\def\stopraster {} - \def\raster [#1]{\groupedcommand{\dosetrastercolor{#1}}{}} - \def\faststartcolor [#1]{\doactivatecolor{#1}} - \def\faststopcolor {} +\unexpanded\def\raster [#1]{\groupedcommand{\dosetrastercolor{#1}}{}} +\unexpanded\def\faststartcolor [#1]{\doactivatecolor{#1}} +\unexpanded\def\faststopcolor {} \unexpanded\def\dosetcolorattribute#1#2{\ifcsname#1#2\endcsname\doactivatecolor{\csname#1#2\endcsname}\fi} +\def\getcolorattributevalue#1#2% color macro (obsolete again, we have a better method) + {\begingroup + \doactivatecolor{#1}% + \normalexpanded{\endgroup\edef\noexpand#2% + {\ifnum\attribute\colorattribute=\attributeunsetvalue\else\number\attribute\colorattribute\fi}}} + \let\grey\graycolor %D Stacking: @@ -749,7 +755,7 @@ \letvalueempty{(cs:-} \letvalueempty{(ts:-} -\def\doactivatecolor#1% : in currentpalet, maybe not, ugly +\def\doactivatecolor#1% : in currentpalet, maybe not, ugly (some day at the lua end) {\def\currentcolorname{#1}% \ifcsname(cs:\currentpalet#1)\endcsname \csname(cs:\currentpalet#1)\endcsname diff --git a/tex/context/base/cont-new.mkii b/tex/context/base/cont-new.mkii index c5ffe80c9..dd6fb1bf7 100644 --- a/tex/context/base/cont-new.mkii +++ b/tex/context/base/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2011.02.25 22:03} +\newcontextversion{2011.03.25 18:03} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv index b47ff0ef2..7a4533c56 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{2011.02.25 22:03} +\newcontextversion{2011.03.25 18:03} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/context.mkii b/tex/context/base/context.mkii index 9d4a81588..c9f510415 100644 --- a/tex/context/base/context.mkii +++ b/tex/context/base/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2011.02.25 22:03} +\edef\contextversion{2011.03.25 18:03} %D For those who want to use this: diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv index 1906ffea4..cba0f10f5 100644 --- a/tex/context/base/context.mkiv +++ b/tex/context/base/context.mkiv @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2011.02.25 22:03} +\edef\contextversion{2011.03.25 18:03} %D For those who want to use this: @@ -37,7 +37,8 @@ \def\loadcorefile#1{\normalinput#1\relax} \def\loadmarkfile#1{\normalinput#1.\mksuffix\relax} \def\loadmkiifile#1{} -\def\loadmkivfile#1{\normalinput#1\relax} +\def\loadmkivfile#1{\normalinput#1.mkiv\relax} +\def\loadmkvifile#1{\normalinput#1.mkvi\relax} %D First we load the system modules. These implement a lot of %D manipulation macros. We start with setting up some basic \TEX\ @@ -134,7 +135,7 @@ \loadmarkfile{trac-tex} \loadmarkfile{trac-deb} -\loadmarkfile{blob-ini} % not to be used, we only use a helper +%loadmarkfile{blob-ini} % not to be used, we only use a helper \loadmarkfile{supp-box} @@ -183,6 +184,7 @@ \loadmarkfile{sort-ini} +\loadmkvifile{pack-mis} \loadmarkfile{pack-rul} \loadmarkfile{lxml-ini} @@ -216,7 +218,9 @@ \loadmarkfile{anch-pos} -\loadmarkfile{scrn-nav} +\loadmkvifile{scrn-ini} +\loadmkvifile{scrn-ref} + \loadmarkfile{pack-obj} \loadmarkfile{strc-itm} @@ -268,10 +272,11 @@ \loadmarkfile{page-sel} % optional \loadmarkfile{page-com} % optional -\loadmarkfile{scrn-int} -\loadmarkfile{scrn-men} -\loadmarkfile{scrn-but} -\loadmarkfile{scrn-bar} +\loadmkvifile{scrn-pag} +\loadmkvifile{scrn-wid} +\loadmkvifile{scrn-but} +\loadmkvifile{scrn-bar} + \loadmarkfile{strc-bkm} % bookmarks \loadmarkfile{tabl-com} @@ -287,8 +292,8 @@ \loadmarkfile{java-ini} -\loadmarkfile{scrn-fld} -\loadmarkfile{scrn-hlp} +\loadmkvifile{scrn-fld} +\loadmkvifile{scrn-hlp} \loadmarkfile{char-enc} \loadmarkfile{font-ini} @@ -298,6 +303,8 @@ \loadmarkfile{font-col} \loadmarkfile{font-gds} +\loadmarkfile{blob-ini} % not to be used, we only use a helper + \loadmarkfile{typo-cln} \loadmarkfile{typo-spa} \loadmarkfile{typo-krn} @@ -315,9 +322,6 @@ \loadmarkfile{scrp-ini} \loadmarkfile{prop-ini} % only for downward compatibility -%loadmarkfile{prop-lay} -%loadmarkfile{prop-neg} -%loadmarkfile{prop-eff} \loadmarkfile{mlib-ctx} diff --git a/tex/context/base/context.todo b/tex/context/base/context.todo new file mode 100644 index 000000000..111243f96 --- /dev/null +++ b/tex/context/base/context.todo @@ -0,0 +1,4 @@ +% marginrules + +% consistently use label/name/tag +% consistently use type/kind diff --git a/tex/context/base/core-con.lua b/tex/context/base/core-con.lua index ea1f6635d..3ad9f6c98 100644 --- a/tex/context/base/core-con.lua +++ b/tex/context/base/core-con.lua @@ -142,7 +142,9 @@ local function chrs(n,m,t) chrs(floor((n-1)/26),m,t) n = (n-1)%26 + 1 end - t[#t+1] = utfchar(n+m) + if n ~= 0 then + t[#t+1] = utfchar(n+m) + end if n <= 26 then return concat(t) end diff --git a/tex/context/base/core-ini.mkiv b/tex/context/base/core-ini.mkiv index 34e20d3f1..c823c9594 100644 --- a/tex/context/base/core-ini.mkiv +++ b/tex/context/base/core-ini.mkiv @@ -28,7 +28,7 @@ \appendtoks \showparagraphnumber \to \everypar \appendtoks \restoreinterlinepenalty \to \everypar %appendtoks \flushmargincontents \to \everypar -\appendtoks \flushcommentanchors \to \everypar +%appendtoks \flushcommentanchors \to \everypar \appendtoks \flushnotes \to \everypar \appendtoks \synchronizenotes \to \everypar \appendtoks \OTRSETshowstatus \to \everypar diff --git a/tex/context/base/core-mis.mkiv b/tex/context/base/core-mis.mkiv index 2214d77c1..06ee5b617 100644 --- a/tex/context/base/core-mis.mkiv +++ b/tex/context/base/core-mis.mkiv @@ -196,72 +196,6 @@ \c!inbetween={\blank[\v!medium]}, \c!after=\blank] -% \definieerplaats[naam][instellingen] -% \stelplaatsin[naam][instellingen] -% \plaats[[instellingen]] -% -% - still undocumented and also not in setupb yet -% - kan ook intern/direct (scheelt duplicatie), zie \framedtext - -\def\dodefineplacement[#1][#2]% - {\getparameters - [\??pl#1] - [\c!left=\hss, - \c!right=\hss, - \c!linecorrection=\v!off, - \c!depthcorrection=\v!off, - \c!margin=\v!standard, - \c!grid=\v!middle, - %\c!before=, - %\c!after=, - #2]% - \setvalue{\e!place#1}{\doplacement[\??pl#1]}} - -\unexpanded\def\defineplacement - {\dodoubleempty\dodefineplacement} - -\unexpanded\def\setupplacement - {\dodoubleempty\dosetupplacement} - -\def\dosetupplacement[#1]% - {\dodoubleempty\getparameters[\??pl#1]} - -\def\doplacement - {\dodoubleempty\dodoplacement} - -\def\dodoplacement[#1][#2]% correctie moet mooier - {\bgroup - \dowithnextboxcontent - {\forgetall} - {\setlocalhsize - \getparameters[#1][#2]% - \getvalue{#1\c!before}% - \begingroup - \disableparpositions - \setbox\nextbox\hbox to \localhsize - {\getvalue{#1\c!left}% - \flushnextbox - \getvalue{#1\c!right}}% - \ifinsidefloat \else - \addlocalbackgroundtobox\nextbox - \fi - \ifgridsnapping - \doifundefined{#1\c!grid}{\letvalue{#1\c!grid}\v!middle}% - % unchecked - \doifinset{\getvalue{#1\c!margin}}{\v!standard,\v!yes}\noindent - \snaptogrid[\getvalue{#1\c!grid}]\hbox{\flushnextbox}% - \else - \doifvalue{#1\c!linecorrection}\v!on \startbaselinecorrection - \doifinset{\getvalue{#1\c!margin}}{\v!standard,\v!yes}\noindent - \flushnextbox - \doifvalue{#1\c!depthcorrection}\v!on\baselinecorrection - \doifvalue{#1\c!linecorrection }\v!on\stopbaselinecorrection - \fi - \endgroup - \getvalue{#1\c!after}% - \egroup} - \vbox} - % Te zijner tijd [plaats=boven,onder,midden] implementeren, % in dat geval moet eerst de maximale hoogte worden bepaald. % diff --git a/tex/context/base/data-ctx.lua b/tex/context/base/data-ctx.lua index adf334d8e..345e9c741 100644 --- a/tex/context/base/data-ctx.lua +++ b/tex/context/base/data-ctx.lua @@ -6,38 +6,4 @@ if not modules then modules = { } end modules ['data-ctx'] = { license = "see context related readme files" } -local format = string.format - -local report_dump = logs.reporter("resolvers","dump") - -local resolvers = resolvers - -local function saveusedfilesin_trees() - local jobname = environment.jobname - if not jobname or jobname == "" then jobname = "luatex" end - local filename = file.replacesuffix(jobname,'jlg') - local f = io.open(filename,'w') - if f then - f:write("\n") - f:write("\n") - f:write(format("\t%s\n",jobname)) - f:write(format("\t%s\n",environment.version)) - local found = resolvers.instance.foundintrees - local sorted = table.sortedkeys(found) - if #sorted > 0 then - f:write("\t\n") - for k=1,#sorted do - local v = sorted[k] - f:write(format("\t\t%s\n",found[v],v)) - end - f:write("\t\n") - else - f:write("\t\n") - end - f:write("\n") - f:close() - report_dump("saving used tree files in '%s'",filename) - end -end - -directives.register("system.dumpfiles", function() saveusedfilesintrees() end) +-- empty diff --git a/tex/context/base/data-res.lua b/tex/context/base/data-res.lua index 7d4e102fe..82066a90e 100644 --- a/tex/context/base/data-res.lua +++ b/tex/context/base/data-res.lua @@ -320,8 +320,8 @@ local function load_configuration_files() local variables = data.variables or { } local warning = false for k, v in next, data do - local kind = type(v) - if kind == "table" then + local variant = type(v) + if variant == "table" then initializesetter(filename,k,v) elseif variables[k] == nil then if trace_locating and not warning then @@ -593,7 +593,7 @@ function resolvers.registerextrapath(paths,subpaths) end end elseif subpaths and subpaths ~= "" then - for i=1,n do + for i=1,oldn do -- we gmatch each step again, not that fast, but used seldom for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s @@ -762,29 +762,29 @@ local function collect_files(names) local blobroot = files.__path__ or blobpath if type(blobfile) == 'string' then if not dname or find(blobfile,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,blobfile,bname) - local search = filejoin(blobroot,blobfile,bname) - local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) + local variant = hash.type + -- local search = filejoin(blobpath,blobfile,bname) + local search = filejoin(blobroot,blobfile,bname) + local result = methodhandler('concatinators',hash.type,blobroot,blobfile,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end else for kk=1,#blobfile do local vv = blobfile[kk] if not dname or find(vv,dname) then - local kind = hash.type - -- local search = filejoin(blobpath,vv,bname) - local search = filejoin(blobroot,vv,bname) - local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) + local variant = hash.type + -- local search = filejoin(blobpath,vv,bname) + local search = filejoin(blobroot,vv,bname) + local result = methodhandler('concatinators',hash.type,blobroot,vv,bname) if trace_detail then - report_resolving("match: kind '%s', search '%s', result '%s'",kind,search,result) + report_resolving("match: variant '%s', search '%s', result '%s'",variant,search,result) end noffiles = noffiles + 1 - filelist[noffiles] = { kind, search, result } + filelist[noffiles] = { variant, search, result } end end end @@ -1453,14 +1453,14 @@ function resolvers.findgivenfile(filename) return findgivenfiles(filename,false)[1] or "" end -local function doit(path,blist,bname,tag,kind,result,allresults) +local function doit(path,blist,bname,tag,variant,result,allresults) local done = false - if blist and kind then + if blist and variant then local resolve = resolvers.resolve -- added if type(blist) == 'string' then -- make function and share code if find(lower(blist),path) then - local full = methodhandler('concatinators',kind,tag,blist,bname) or "" + local full = methodhandler('concatinators',variant,tag,blist,bname) or "" result[#result+1] = resolve(full) done = true end @@ -1468,7 +1468,7 @@ local function doit(path,blist,bname,tag,kind,result,allresults) for kk=1,#blist do local vv = blist[kk] if find(lower(vv),path) then - local full = methodhandler('concatinators',kind,tag,vv,bname) or "" + local full = methodhandler('concatinators',variant,tag,vv,bname) or "" result[#result+1] = resolve(full) done = true if not allresults then break end diff --git a/tex/context/base/data-tex.lua b/tex/context/base/data-tex.lua index fddd04bcc..14148b20e 100644 --- a/tex/context/base/data-tex.lua +++ b/tex/context/base/data-tex.lua @@ -60,17 +60,17 @@ function helpers.textopener(tag,filename,filehandle) filehandle:close() end if type(lines) == "string" then - local kind = utffiletype(lines) + local coding = utffiletype(lines) if trace_locating then - report_tex("%s opener, '%s' opened using method '%s'",tag,filename,kind) + report_tex("%s opener, '%s' opened using method '%s'",tag,filename,coding) end - if kind == "utf-16-be" then + if coding == "utf-16-be" then lines = unicode.utf16_to_utf8_be(lines) - elseif kind == "utf-16-le" then + elseif coding == "utf-16-le" then lines = unicode.utf16_to_utf8_le(lines) - elseif kind == "utf-32-be" then + elseif coding == "utf-32-be" then lines = unicode.utf32_to_utf8_be(lines) - elseif kind == "utf-32-le" then + elseif coding == "utf-32-le" then lines = unicode.utf32_to_utf8_le(lines) else -- utf8 or unknown if textfileactions.dirty then -- maybe use autocompile diff --git a/tex/context/base/font-afm.lua b/tex/context/base/font-afm.lua index e9450f88d..8d35be9d6 100644 --- a/tex/context/base/font-afm.lua +++ b/tex/context/base/font-afm.lua @@ -24,39 +24,49 @@ local trace_defining = false trackers.register("fonts.defining", function(v) tr local report_afm = logs.reporter("fonts","afm loading") -local next, type = next, type +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 lpegmatch = lpeg.match local abs = math.abs +local P, S, C, R, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.R, lpeg.match -local findbinfile = resolvers.findbinfile +local fonts = fonts +local afm = { } +local pfb = { } +fonts.handlers.afm = afm +fonts.handlers.pfb = pfb -local fonts = fonts -fonts.afm = fonts.afm or { } +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.*) -local afm = fonts.afm -local tfm = fonts.tfm +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 --- so far +local definers = fonts.definers +local readers = fonts.readers +local constructors = fonts.constructors -afm.version = 1.403 -- incrementing this number one up will force a re-cache -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 -afm.cache = containers.define("fonts", "afm", afm.version, true) +local findbinfile = resolvers.findbinfile -local definers = fonts.definers -local readers = fonts.tfm.readers +local afmfeatures = constructors.newfeatures("afm") +local registerafmfeature = afmfeatures.register -local afmfeatures = { - aux = { }, - data = { }, - list = { }, - default = { }, -} +local function setmode(tfmdata,value) + if value then + tfmdata.properties.mode = lower(value) + end +end -afm.features = afmfeatures +registerafmfeature { + name = "mode", + description = "mode", + initializers = { + base = setmode, + node = setmode, + } +} --[[ldx--

We start with the basic reader which we give a name similar to the @@ -79,42 +89,46 @@ built in and reader.

--~ Comment DELIM 2390 1010 --~ Comment AXISHEIGHT 250 -local P, S, C = lpeg.P, lpeg.S, lpeg.C - -local c = P("Comment") -local s = S(" \t") -local l = S("\n\r") -local w = C((1 - l)^1) -local n = C((lpeg.R("09") + S("."))^1) / tonumber * s^0 - -local fd = { } - -local pattern = ( c * s^1 * ( - ("CODINGSCHEME" * s^1 * w ) / function(a) end + - ("DESIGNSIZE" * s^1 * n * w ) / function(a) fd[ 1] = a end + - ("CHECKSUM" * s^1 * n * w ) / function(a) fd[ 2] = a end + - ("SPACE" * s^1 * n * "plus" * n * "minus" * n) / function(a,b,c) fd[ 3], fd[ 4], fd[ 5] = a, b, c end + - ("QUAD" * s^1 * n ) / function(a) fd[ 6] = a end + - ("EXTRASPACE" * s^1 * n ) / function(a) fd[ 7] = a end + - ("NUM" * s^1 * n * n * n ) / function(a,b,c) fd[ 8], fd[ 9], fd[10] = a, b, c end + - ("DENOM" * s^1 * n * n ) / function(a,b ) fd[11], fd[12] = a, b end + - ("SUP" * s^1 * n * n * n ) / function(a,b,c) fd[13], fd[14], fd[15] = a, b, c end + - ("SUB" * s^1 * n * n ) / function(a,b) fd[16], fd[17] = a, b end + - ("SUPDROP" * s^1 * n ) / function(a) fd[18] = a end + - ("SUBDROP" * s^1 * n ) / function(a) fd[19] = a end + - ("DELIM" * s^1 * n * n ) / function(a,b) fd[20], fd[21] = a, b end + - ("AXISHEIGHT" * s^1 * n ) / function(a) fd[22] = a end + - (1-l)^0 -) + (1-c)^1)^0 +local comment = P("Comment") +local spacing = S(" \t")^1 +local lineend = 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) - fd = { } - lpegmatch(pattern,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. +-- as in now supports afm/pfb loading but it's not too bad to have different methods +-- for testing approaches. local keys = { } @@ -136,35 +150,35 @@ end local function get_charmetrics(data,charmetrics,vector) local characters = data.characters - local chr, str, ind = { }, "", 0 + local chr, ind = { }, 0 for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do if k == 'C' then - if str ~= "" then characters[str] = chr end - chr = { } - str = "" v = tonumber(v) if v < 0 then - ind = ind + 1 + ind = ind + 1 -- ? else ind = v end - chr.index = ind + chr = { + index = ind + } elseif k == 'WX' then - chr.width = v + chr.width = tonumber(v) elseif k == 'N' then - str = v + 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,"^(.-) +(.-)$") - if not chr.ligatures then chr.ligatures = { } end - chr.ligatures[plus] = becomes + local ligatures = chr.ligatures + if ligatures then + ligatures[plus] = becomes + else + chr.ligatures = { [plus] = becomes } + end end end - if str ~= "" then - characters[str] = chr - end end local function get_kernpairs(data,kernpairs) @@ -172,32 +186,37 @@ local function get_kernpairs(data,kernpairs) for one, two, value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do local chr = characters[one] if chr then - if not chr.kerns then chr.kerns = { } end - chr.kerns[two] = tonumber(value) + 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 - if keys[key] then keys[key](data,rest) end + local keyhandler = keys[key] + if keyhandler then + keyhandler(data,rest) + end end end local function get_indexes(data,pfbname) - data.luatex.filename = resolvers.unresolve(pfbname) -- no shortcut + 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) - --~ print(table.serialize(pfbdata)) if pfbdata then local glyphs = pfbdata.glyphs if glyphs then if trace_loading then report_afm("getting index data from %s",pfbname) end - -- local offset = (glyphs[0] and glyphs[0] != .notdef) or 0 for index, glyph in next, glyphs do local name = glyph.name if name then @@ -224,14 +243,27 @@ end local function readafm(filename) local ok, afmblob, size = resolvers.loadbinfile(filename) -- has logging --- local ok, afmblob = true, file.readdata(filename) if ok and afmblob then local data = { - characters = { }, - metadata = { - version = version or '0', -- hm + resources = { + filename = resolvers.unresolve(filename), + version = afm.version, + creator = "context mkiv", + }, + properties = { + italic_correction = 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 @@ -256,7 +288,6 @@ local function readafm(filename) data.fontdimens = scan_comment(fontmetrics) -- todo: all lpeg, no time now return "" end) - data.luatex = { } return data else if trace_loading then @@ -272,7 +303,7 @@ by adding ligatures and kern information to the afm derived data. That way we can set them faster when defining a font.

--ldx]]-- -local addkerns, addligatures, addtexligatures, unify -- we will implement these later +local addkerns, addligatures, addtexligatures, unify, normalize -- we will implement these later function afm.load(filename) -- hm, for some reasons not resolved yet @@ -291,14 +322,13 @@ function afm.load(filename) local pfbsize, pfbtime = 0, 0 if pfbname ~= "" then local attr = lfs.attributes(pfbname) - pfbsize, pfbtime = attr.size or 0, attr.modification or 0 + pfbsize = attr.size or 0 + pfbtime = attr.modification or 0 end - if not data or data.verbose ~= fonts.verbose - or data.size ~= size or data.time ~= time or data.pfbsize ~= pfbsize or data.pfbtime ~= pfbtime then + if not data or data.size ~= size or data.time ~= time or data.pfbsize ~= pfbsize or data.pfbtime ~= pfbtime then report_afm( "reading %s",filename) data = readafm(filename) if data then - -- data.luatex = data.luatex or { } if pfbname ~= "" then get_indexes(data,pfbname) elseif trace_loading then @@ -318,13 +348,13 @@ function afm.load(filename) report_afm( "add extra kerns") addkerns(data) end + normalize(data) report_afm( "add tounicode data") - fonts.map.addtounicode(data,filename) + fonts.mappings.addtounicode(data,filename) data.size = size data.time = time data.pfbsize = pfbsize data.pfbtime = pfbtime - data.verbose = fonts.verbose report_afm("saving: %s in cache",name) data = containers.write(afm.cache, name, data) data = containers.read(afm.cache,name) @@ -336,17 +366,16 @@ function afm.load(filename) end end -local uparser = fonts.map.makenameparser() +local uparser = fonts.mappings.makenameparser() unify = function(data, filename) - local unicodevector = fonts.enc.agl.unicodes -- loaded runtime in context - local glyphs, indices, unicodes, names = { }, { }, { }, { } - local verbose, private = fonts.verbose, fonts.privateoffset + 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 - -- local u = match(name,"^uni(%x+)$") - -- code = u and tonumber(u,16) code = lpegmatch(uparser,name) if not code then code = private @@ -356,33 +385,42 @@ unify = function(data, filename) end local index = blob.index unicodes[name] = code - indices[code] = index - glyphs[index] = blob names[name] = index blob.name = name - if verbose then - local bu = blob.unicode - if not bu then - blob.unicode = code - elseif type(bu) == "table" then - bu[#bu+1] = code - else - blob.unicode = { bu, code } + 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 - else - blob.index = nil + description.kerns = krn end end - data.glyphs = glyphs data.characters = nil - local luatex = data.luatex - local filename = luatex.filename or file.removesuffix(file.basename(filename)) - luatex.filename = resolvers.unresolve(filename) -- no shortcut - luatex.unicodes = unicodes -- name to unicode - luatex.indices = indices -- unicode to index - luatex.marks = { } -- todo - luatex.names = names -- name to index - luatex.private = private + 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-- @@ -397,8 +435,8 @@ is that a character can be in an encoding twice but is hashed once.

--ldx]]-- -local ligatures = { -- okay, nowadays we could parse the name - ['f'] = { +local ligatures = { -- 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' }, @@ -445,18 +483,20 @@ local texligatures = { } } -local addthem = function(afmdata,ligatures) - local glyphs, luatex = afmdata.glyphs, afmdata.luatex - local indices, unicodes, names = luatex.indices, luatex.unicodes, luatex.names +local addthem = function(rawdata,ligatures) + 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 = glyphs[names[ligname]] + local one = descriptions[unicodes[ligname]] if one then for _, pair in next, ligdata do - local two, three = pair[1], pair[2] - if two and three and names[two] and names[three] then + 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 -- was one.ligatures ... bug + if not ol[two] then ol[two] = three end else @@ -468,8 +508,8 @@ local addthem = function(afmdata,ligatures) end end -addligatures = function(afmdata) addthem(afmdata,ligatures ) end -addtexligatures = function(afmdata) addthem(afmdata,texligatures) end +addligatures = function(rawdata) addthem(rawdata,ligatures ) end +addtexligatures = function(rawdata) addthem(rawdata,texligatures) end --[[ldx--

We keep the extra kerns in separate kerning tables so that we can use @@ -479,7 +519,11 @@ them selectively.

-- 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. +-- 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). + +-- we can make them numbers local left = { AEligature = "A", aeligature = "a", @@ -620,44 +664,67 @@ local both = { } -addkerns = function(afmdata) -- using shcodes is not robust here - local glyphs = afmdata.glyphs - local names = afmdata.luatex.names +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) - for index, glyph in next, glyphs do - local kerns = glyph.kerns + for unicode, description in next, descriptions do + local kerns = description.kerns if kerns then - local extrakerns = glyph.extrakerns or { } + local extrakerns for complex, simple in next, what do - if names[complex] then + complex = unicodes[complex] + simple = unicodes[simple] + if complex and simple then local ks = kerns[simple] if ks and not kerns[complex] then - extrakerns[complex] = ks + if extrakerns then + extrakerns[complex] = ks + else + extrakerns = { [complex] = ks } + end end end end - if next(extrakerns) then - glyph.extrakerns = extrakerns + if extrakerns then + description.extrakerns = extrakerns end end end end local function do_it_copy(what) for complex, simple in next, what do - local c = glyphs[names[complex]] - if c then -- optional - local s = glyphs[names[simple]] - if s then - if not c.kerns then - c.extrakerns = s.kerns or { } - end - if s.extrakerns then - local extrakerns = c.extrakerns or { } - for k, v in next, s.extrakerns do - extrakerns[k] = v + 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 - if next(extrakerns) then - s.extrakerns = extrakerns + 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 @@ -676,24 +743,21 @@ end

The copying routine looks messy (and is indeed a bit messy).

--ldx]]-- --- once we have otf sorted out (new format) we can try to make the afm --- cache similar to it (similar tables) - local function adddimensions(data) -- we need to normalize afm to otf i.e. indexed table instead of name if data then - for index, glyph in next, data.glyphs do - local bb = glyph.boundingbox + 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 - glyph.height = ht + description.height = ht end if dp == 0 or dp < 0 then -- no negative depths and no negative depths, nil == 0 else - glyph.depth = dp + description.depth = dp end end end @@ -701,115 +765,124 @@ local function adddimensions(data) -- we need to normalize afm to otf i.e. index end local function copytotfm(data) - if data then - local glyphs = data.glyphs - if glyphs then - local metadata, luatex = data.metadata, data.luatex - local unicodes, indices = luatex.unicodes, luatex.indices - local characters, parameters, descriptions = { }, { }, { } - local mode = data.mode or "base" - -- todo : merge into tfm - for u, i in next, indices do - local d = glyphs[i] - characters[u] = { } - descriptions[u] = d + if data and data.descriptions then + local metadata = data.metadata + local resources = data.resources + local properties = table.derive(data.properties) + local descriptions = table.derive(data.descriptions) + local goodies = table.derive(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 - local filename = fonts.tfm.checkedfilename(luatex) -- was metadata.filename - local fontname = metadata.fontname or metadata.fullname - local fullname = metadata.fullname or metadata.fontname - local endash, emdash, spacer, spaceunits = unicodes['space'], unicodes['emdash'], "space", 500 - -- same as otf - if metadata.isfixedpitch 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 metadata.charwidth then - spaceunits, spacer = metadata.charwidth, "charwidth" - end - else - if descriptions[endash] then - spaceunits, spacer = descriptions[endash].width, "space" - end - if not spaceunits and metadata.charwidth then - spaceunits, spacer = metadata.charwidth, "charwidth" - end + if not spaceunits and descriptions[emdash] then + spaceunits, spacer = descriptions[emdash].width, "emdash" end - spaceunits = tonumber(spaceunits) - if spaceunits < 200 then - -- todo: warning + if not spaceunits and charwidth then + spaceunits, spacer = charwidth, "charwidth" end - -- - parameters.slant = 0 - parameters.space = spaceunits - parameters.space_stretch = 500 - parameters.space_shrink = 333 - parameters.x_height = 400 - parameters.quad = 1000 - local italicangle = data.metadata.italicangle - if italicangle then - parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) + else + if descriptions[endash] then + spaceunits, spacer = descriptions[endash].width, "space" end - if metadata.isfixedpitch then - parameters.space_stretch = 0 - parameters.space_shrink = 0 - elseif afm.syncspace then - parameters.space_stretch = spaceunits/2 - parameters.space_shrink = spaceunits/3 + if not spaceunits and charwidth then + spaceunits, spacer = charwidth, "charwidth" end - parameters.extra_space = parameters.space_shrink - if metadata.xheight and metadata.xheight > 0 then - parameters.x_height = metadata.xheight - else - -- same as otf - local x = unicodes['x'] + 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 - 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 + parameters.x_height = x.height end end -- - if next(characters) then - return { - characters = characters, - parameters = parameters, - descriptions = descriptions, - indices = indices, - unicodes = unicodes, - luatex = luatex, - encodingbytes = 2, - mode = mode, - filename = filename, - fontname = fontname, - fullname = fullname, - psname = fullname, -- in otf: tfm.fontname or tfm.fullname - name = filename or fullname or fontname, - format = fonts.fontformat(filename,"type1"), - type = 'real', - units = 1000, - direction = 0, - boundarychar_label = 0, - boundarychar = 65536, - --~ false_boundarychar = 65536, -- produces invalid tfm in luatex - designsize = (metadata.designsize or 10)*65536, - spacer = spacer, - ascender = abs(metadata.ascender or 0), - descender = abs(metadata.descender or 0), - italicangle = italicangle, - } + 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 @@ -821,79 +894,16 @@ to treat this fontformat like any other and handle features in a more configurable way.

--ldx]]-- -local function register_feature(name,default) - afmfeatures.list[#afmfeatures.list+1] = name - afmfeatures.default[name] = default -end - -afmfeatures.register = register_feature - -local function setfeatures(tfmdata) - local shared = tfmdata.shared - local afmdata = shared.afmdata - local features = shared.features - if features and next(features) then - local mode = tfmdata.mode or features.mode or "base" - local initializers = fonts.initializers - local fi = initializers[mode] - local fiafm = fi and fi.afm - if fiafm then - local lists = { - fonts.triggers, - afmfeatures.list, - fonts.manipulators, - } - for l=1,3 do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - local value = features[f] - if value and fiafm[f] then -- brr - if trace_features then - report_afm("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') - end - fiafm[f](tfmdata,value) - mode = tfmdata.mode or features.mode or "base" - fiafm = initializers[mode].afm - end - end - end - end - end - local fm = fonts.methods[mode] - local fmafm = fm and fm.afm - if fmafm then - local lists = { - afmfeatures.list, - } - local sp = shared.processors - for l=1,1 do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - if features[f] and fmafm[f] then -- brr - if not sp then - sp = { fmafm[f] } - shared.processors = sp - else - sp[#sp+1] = fmafm[f] - end - end - end - end - end - end +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) - local features, done = definers.check(specification.features.normal,afmfeatures.default) - if done then - specification.features.normal = features - tfm.hashinstance(specification,true) - end end local function afmtotfm(specification) @@ -908,34 +918,37 @@ local function afmtotfm(specification) if trace_loading then report_afm("fallback from afm to tfm for %s",afmname) end - afmname = "" + return -- just that end end - if afmname == "" then - return nil - else - checkfeatures(specification) + 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 features = specification.features.normal local cache_id = specification.hash - local tfmdata = containers.read(tfm.cache, cache_id) -- cache with features applied + local tfmdata = containers.read(constructors.cache, cache_id) -- cache with features applied if not tfmdata then - local afmdata = afm.load(afmname) - if afmdata and next(afmdata) then - adddimensions(afmdata) - tfmdata = copytotfm(afmdata) + 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 - local unique = tfmdata.unique - if not shared then shared = { } tfmdata.shared = shared end - if not unique then unique = { } tfmdata.unique = unique end - shared.afmdata, shared.features = afmdata, features - setfeatures(tfmdata) + 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 %s",afmname) end - tfmdata = containers.write(tfm.cache,cache_id,tfmdata) + tfmdata = containers.write(constructors.cache,cache_id,tfmdata) end return tfmdata end @@ -949,33 +962,15 @@ those cases, but now that we can handle directly we no longer need this features.

--ldx]]-- --- tfm.default_encoding = 'unicode' --- --- function tfm.setnormalfeature(specification,name,value) --- if specification and name then --- local features = specification.features --- if not features then --- features = { } --- specification.features = features --- end --- local normalfeatures = features.normal --- if normalfeatures then --- normalfeatures[name] = value --- else --- features.normal = { [name] = value } --- end --- end --- end - local function read_from_afm(specification) - local tfmtable = afmtotfm(specification) - if tfmtable then - tfmtable.name = specification.name - tfmtable = tfm.scale(tfmtable, specification.size, specification.relativeid) - local afmdata = tfmtable.shared.afmdata - fonts.logger.save(tfmtable,'afm',specification) + local tfmdata = afmtotfm(specification) + if tfmdata then + tfmdata.properties.name = specification.name + tfmdata = constructors.scale(tfmdata, specification) + constructors.applymanipulators("afm",tfmdata,specification.features.normal,trace_features,report_afm) + fonts.loggers.register(tfmdata,'afm',specification) end - return tfmtable + return tfmdata end --[[ldx-- @@ -983,40 +978,34 @@ end those that make sense for this format.

--ldx]]-- -local function prepare_ligatures(tfmdata,ligatures,value) +local function prepareligatures(tfmdata,ligatures,value) if value then - local afmdata = tfmdata.shared.afmdata - local luatex = afmdata.luatex - local unicodes = luatex.unicodes local descriptions = tfmdata.descriptions - for u, chr in next, tfmdata.characters do - local d = descriptions[u] - local l = d[ligatures] - if l then - local ligatures = chr.ligatures - if not ligatures then - ligatures = { } - chr.ligatures = ligatures + 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 k, v in next, l do - local uk, uv = unicodes[k], unicodes[v] - if uk and uv then - ligatures[uk] = { - char = uv, - type = 0 - } - end + for unicode, ligature in next, dligatures do + cligatures[unicode] = { + char = ligature, + type = 0 + } end end end end end -local function prepare_kerns(tfmdata,kerns,value) +local function preparekerns(tfmdata,kerns,value) if value then - local afmdata = tfmdata.shared.afmdata - local luatex = afmdata.luatex - local unicodes = luatex.unicodes + 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] @@ -1038,66 +1027,70 @@ local function prepare_kerns(tfmdata,kerns,value) end end --- hm, register? - -local base_initializers = fonts.initializers.base.afm -local node_initializers = fonts.initializers.node.afm -local common_initializers = fonts.initializers.common - -local function ligatures (tfmdata,value) prepare_ligatures(tfmdata,'ligatures', value) end -local function texligatures(tfmdata,value) prepare_ligatures(tfmdata,'texligatures',value) end -local function kerns (tfmdata,value) prepare_kerns (tfmdata,'kerns', value) end -local function extrakerns (tfmdata,value) prepare_kerns (tfmdata,'extrakerns', value) end - -register_feature('liga') -- was true -register_feature('kern') -- was true -register_feature('extrakerns') -- needed? - -base_initializers.ligatures = ligatures -node_initializers.ligatures = ligatures -base_initializers.texligatures = texligatures -node_initializers.texligatures = texligatures -base_initializers.kern = kerns -node_initializers.kerns = kerns -node_initializers.extrakerns = extrakerns -base_initializers.extrakerns = extrakerns - -base_initializers.liga = ligatures -node_initializers.liga = ligatures -base_initializers.tlig = texligatures -node_initializers.tlig = texligatures -base_initializers.trep = tfm.replacements -node_initializers.trep = tfm.replacements - -register_feature('tlig') -- was true -- todo: also proper features for afm -register_feature('trep') -- was true -- todo: also proper features for afm - --- tfm features - -base_initializers.equaldigits = common_initializers.equaldigits -node_initializers.equaldigits = common_initializers.equaldigits -base_initializers.lineheight = common_initializers.lineheight -node_initializers.lineheight = common_initializers.lineheight - --- vf features - -base_initializers.compose = common_initializers.compose -node_initializers.compose = common_initializers.compose - --- afm specific, obsolete --- --- register_feature('encoding') --- --- base_initializers.encoding = common_initializers.encoding --- node_initializers.encoding = common_initializers.encoding --- --- base_initializers.onum = common_initializers.oldstyle --- base_initializers.smcp = common_initializers.smallcaps --- base_initializers.fkcp = common_initializers.fakecaps --- --- register_feature('onum',false) --- register_feature('smcp',false) --- register_feature('fkcp',false) +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 @@ -1111,9 +1104,9 @@ local function check_afm(specification,fullname) if foundname == "" then foundname = fonts.names.getfilename(fullname,"afm") end - if foundname == "" and tfm.autoprefixedafm then + if foundname == "" and afm.autoprefixed then local encoding, shortname = match(fullname,"^(.-)%-(.*)$") -- context: encoding-name.* - if encoding and shortname and fonts.enc.known[encoding] then + if encoding and shortname and fonts.encodings.known[encoding] then shortname = findbinfile(shortname,'afm') or "" -- just to be sure if shortname ~= "" then foundname = shortname @@ -1124,34 +1117,35 @@ local function check_afm(specification,fullname) end end if foundname ~= "" then - specification.filename, specification.format = foundname, "afm" + specification.filename = foundname + specification.format = "afm" return read_from_afm(specification) end end function readers.afm(specification,method) - local fullname, tfmtable = specification.filename or "", nil + local fullname, tfmdata = specification.filename or "", nil if fullname == "" then local forced = specification.forced or "" if forced ~= "" then - tfmtable = check_afm(specification,specification.name .. "." .. forced) + tfmdata = check_afm(specification,specification.name .. "." .. forced) end - if not tfmtable then + if not tfmdata then method = method or definers.method or "afm or tfm" if method == "tfm" then - tfmtable = check_tfm(specification,specification.name) + tfmdata = check_tfm(specification,specification.name) elseif method == "afm" then - tfmtable = check_afm(specification,specification.name) + tfmdata = check_afm(specification,specification.name) elseif method == "tfm or afm" then - tfmtable = check_tfm(specification,specification.name) or check_afm(specification,specification.name) + tfmdata = check_tfm(specification,specification.name) or check_afm(specification,specification.name) else -- method == "afm or tfm" or method == "" then - tfmtable = check_afm(specification,specification.name) or check_tfm(specification,specification.name) + tfmdata = check_afm(specification,specification.name) or check_tfm(specification,specification.name) end end else - tfmtable = check_afm(specification,fullname) + tfmdata = check_afm(specification,fullname) end - return tfmtable + return tfmdata end function readers.pfb(specification,method) -- only called when forced diff --git a/tex/context/base/font-age.lua b/tex/context/base/font-age.lua index 5c19d41b1..741bb475a 100644 --- a/tex/context/base/font-age.lua +++ b/tex/context/base/font-age.lua @@ -1,16 +1,17 @@ -if not modules then modules = { } end modules ['font-map'] = { +if not modules then modules = { } end modules ['font-age'] = { version = 1.001, - comment = "companion to font-ini.mkiv", + comment = "companion to font-gee.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", } -fonts = fonts or { } -fonts.enc = fonts.enc or { } -fonts.enc.agl = fonts.enc.agl or { } +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end -fonts.enc.agl.unicodes = { -- generated +return { -- generated ["A"]=65, ["AE"]=198, ["AEacute"]=508, diff --git a/tex/context/base/font-agl.lua b/tex/context/base/font-agl.lua index 45aae9507..a81d3503c 100644 --- a/tex/context/base/font-agl.lua +++ b/tex/context/base/font-agl.lua @@ -274,9 +274,9 @@ end -- We load this table only when needed. We coul duse a loading mechanism -- return the table but there are no more vectors like this so why bother. -fonts.enc = fonts.enc or { } +fonts.encodings = fonts.encodings or { } -fonts.enc.agl = { +fonts.encodings.agl = { names = names, -- unicode -> name unicodes = unicodes, -- name -> unicode extras = extras, -- merged into the other two diff --git a/tex/context/base/font-aux.lua b/tex/context/base/font-aux.lua new file mode 100644 index 000000000..a3e717b44 --- /dev/null +++ b/tex/context/base/font-aux.lua @@ -0,0 +1,105 @@ +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 number = tonumber +local wrap, yield = coroutine.wrap, coroutine.yield + +local fonts, font = fonts, font + +local iterators = { } +fonts.interators = iterators + +local currentfont = font.current +local identifiers = fonts.hashes.identifiers +local sortedkeys = table.sortedkeys + +-- for unicode, character in fonts.iterators.characters () do print(k,v) end +-- for unicode, description in fonts.iterators.descriptions() do print(k,v) end +-- for index, glyph in fonts.iterators.glyphs () do print(k,v) 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 + +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 + +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.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 diff --git a/tex/context/base/font-chk.lua b/tex/context/base/font-chk.lua index d6a0cdcc8..819586532 100644 --- a/tex/context/base/font-chk.lua +++ b/tex/context/base/font-chk.lua @@ -7,6 +7,7 @@ if not modules then modules = { } end modules ['font-chk'] = { } -- possible optimization: delayed initialization of vectors +-- move to the nodes namespace local report_fonts = logs.reporter("fonts","checking") @@ -15,7 +16,7 @@ local fonts = fonts fonts.checkers = fonts.checkers or { } local checkers = fonts.checkers -local fontdata = fonts.identifiers +local fontdata = fonts.hashes.identifiers local is_character = characters.is_character local chardata = characters.data local tasks = nodes.tasks @@ -30,7 +31,9 @@ local remove_node = nodes.remove checkers.enabled = false checkers.delete = false -local function registermessage(font,char,message) +-- to tfmdata.properties ? + +local function onetimemessage(font,char,message) local tfmdata = fontdata[font] local shared = tfmdata.shared local messages = shared.messages @@ -44,12 +47,12 @@ local function registermessage(font,char,message) messages[message] = category end if not category[char] then - report_fonts("char U+%04X in font '%s' with id %s: %s",char,tfmdata.fullname,font,message) + report_fonts("char U+%04X in font '%s' with id %s: %s",char,tfmdata.properties.fullname,font,message) category[char] = true end end -fonts.registermessage = registermessage +fonts.loggers.onetimemessage = onetimemessage function checkers.missing(head) if checkers.enabled then @@ -61,9 +64,9 @@ function checkers.missing(head) end if not characters[char] and is_character[chardata[char].category] then if checkers.delete then - registermessage(font,char,"missing (will be deleted)") + onetimemessage(font,char,"missing (will be deleted)") else - registermessage(font,char,"missing") + onetimemessage(font,char,"missing") end if not found then found = { n } diff --git a/tex/context/base/font-cid.lua b/tex/context/base/font-cid.lua index a9bd3c56e..4a4c4d209 100644 --- a/tex/context/base/font-cid.lua +++ b/tex/context/base/font-cid.lua @@ -8,18 +8,19 @@ if not modules then modules = { } end modules ['font-cid'] = { local format, match, lower = string.format, string.match, string.lower local tonumber = tonumber -local lpegmatch = lpeg.match +local P, S, R, C, V, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.match -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +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 fonts = fonts -fonts.cid = fonts.cid or { } -local cid = fonts.cid -cid.map = cid.map or { } -cid.max = cid.max or 10 +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 -- @@ -28,8 +29,6 @@ cid.max = cid.max or 10 -- 1..95 0020 -- 99 3000 -local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C - local number = C(R("09","af","AF")^1) local space = S(" \n\r\t") local spaces = space^0 @@ -37,7 +36,7 @@ local period = P(".") local periods = period * period local name = P("/") * C((1-space)^1) -local unicodes, names = { }, { } +local unicodes, names = { }, { } -- we could use Carg now local function do_one(a,b) unicodes[tonumber(a)] = tonumber(b,16) @@ -55,15 +54,15 @@ local function do_name(a,b) names[tonumber(a)] = b end -local grammar = lpeg.P { "start", - start = number * spaces * number * lpeg.V("series"), - series = (spaces * (lpeg.V("one") + lpeg.V("range") + lpeg.V("named")) )^1, +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 } -function cid.load(filename) +local function loadcidfile(filename) local data = io.loaddata(filename) if data then unicodes, names = { }, { } @@ -77,75 +76,90 @@ function cid.load(filename) unicodes = unicodes, names = names } - else - return nil 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 cidmap = cid.map[hashname] - if not cidmap then + local found = cidmap[hashname] + if not found then if trace_loading then report_otf("checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) end local fullname = resolvers.findfile(filename,'cid') or "" if fullname ~= "" then - cidmap = cid.load(fullname) - if cidmap then + found = loadcidfile(fullname) + if found then if trace_loading then report_otf("using cidmap file %s",filename) end - cid.map[hashname] = cidmap - cidmap.usedname = file.basename(filename) - return cidmap + cidmap[hashname] = found + found.usedname = file.basename(filename) end end end - return cidmap + return found end -function cid.getmap(registry,ordering,supplement) - -- cf Arthur R. we can safely scan upwards since cids are downward compatible - local supplement = tonumber(supplement) +-- 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("needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) end - local cidmap = locate(registry,ordering,supplement) - if not cidmap then + found = locate(registry,ordering,supplement) + if not found then + local supnum = tonumber(supplement) local cidnum = nil -- next highest (alternatively we could start high) - if supplement < cid.max then - for supplement=supplement+1,cid.max do - local c = locate(registry,ordering,supplement) + if supnum < cidmax then + for s=supnum+1,cidmax do + local c = locate(registry,ordering,s) if c then - cidmap, cidnum = c, supplement + found, cidnum = c, s break end end end -- next lowest (least worse fit) - if not cidmap and supplement > 0 then - for supplement=supplement-1,0,-1 do - local c = locate(registry,ordering,supplement) + if not found and supnum > 0 then + for s=supnum-1,0,-1 do + local c = locate(registry,ordering,s) if c then - cidmap, cidnum = c, supplement + found, cidnum = c, s break end end end - -- prevent further lookups - if cidmap and cidnum > 0 then + -- prevent further lookups -- somewhat tricky + registry = lower(registry) + ordering = lower(ordering) + if found and cidnum > 0 then for s=0,cidnum-1 do - filename = format(template,registry,ordering,s) - if not cid.map[filename] then - cid.map[filename] = cidmap -- copy of ref + local filename = format(template,registry,ordering,s) + if not cidmap[filename] then + cidmap[filename] = found end end end end - return cidmap + return found end diff --git a/tex/context/base/font-clr.lua b/tex/context/base/font-clr.lua deleted file mode 100644 index 7d19aadd5..000000000 --- a/tex/context/base/font-clr.lua +++ /dev/null @@ -1,31 +0,0 @@ -if not modules then modules = { } end modules ['font-clr'] = { - 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" -} - --- moved from ini ... will become inline - -fonts.colors = fonts.colors or { } -- dummy in ini -local colors = fonts.colors - -local set_attribute = node.set_attribute -local unset_attribute = node.unset_attribute - -local attribute = attributes.private('color') -local mapping = attributes and attributes.list[attribute] or { } - -function colors.set(n,c) - local mc = mapping[c] - if not mc then - unset_attribute(n,attribute) - else - set_attribute(n,attribute,mc) - end -end - -function colors.reset(n) - unset_attribute(n,attribute) -end diff --git a/tex/context/base/font-col.lua b/tex/context/base/font-col.lua index 4b888d6cb..bf24737ba 100644 --- a/tex/context/base/font-col.lua +++ b/tex/context/base/font-col.lua @@ -28,7 +28,7 @@ local definitions = collections.definitions collections.vectors = collections.vectors or { } local vectors = collections.vectors -local fontdata = fonts.identifiers +local fontdata = fonts.hashes.identifiers local glyph = node.id('glyph') @@ -67,7 +67,7 @@ function collections.define(name,font,ranges,details) end details = settings_to_hash(details) -- todo, combine per font start/stop as arrays - for s in gmatch(ranges,"([^, ]+)") do + for s in gmatch(ranges,"[^, ]+") do local start, stop, description = characters.getrange(s) if start and stop then if trace_collecting then @@ -162,7 +162,7 @@ function collections.prepare(name) local d = definitions[name] if d then if trace_collecting then - local filename = file.basename(fontdata[current].filename or "?") + local filename = file.basename(fontdata[current].properties.filename or "?") report_fonts("def: applying collection %s to %s (file: %s)",name,current,filename) end list = { } @@ -181,7 +181,7 @@ function collections.prepare(name) context.doclonefontstagetwo(name) -- preparing clone vectors context.dostopcloningfonts() elseif trace_collecting then - local filename = file.basename(fontdata[current].filename or "?") + local filename = file.basename(fontdata[current].properties.filename or "?") report_fonts("def: error in applying collection %s to %s (file: %s)",name,current,filename) end end diff --git a/tex/context/base/font-con.lua b/tex/context/base/font-con.lua new file mode 100644 index 000000000..230136929 --- /dev/null +++ b/tex/context/base/font-con.lua @@ -0,0 +1,1175 @@ +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" +} + + +local utf = unicode.utf8 + +local next, tostring, setmetatable, rawget = next, tostring, setmetatable, 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 allocate = utilities.storage.allocate + +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-- +

Here we only implement a few helper functions.

+--ldx]]-- + +local fonts = fonts +local constructors = { } +fonts.constructors = constructors +local handlers = { } +fonts.handlers = handlers + +local specifiers = fonts.specifiers +local contextsetups = specifiers.contextsetups +local contextnumbers = specifiers.contextnumbers + +-- will be directives + +constructors.sharebasekerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too) +constructors.dontembed = allocate() +constructors.mathactions = { } +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 + +-- This might become an interface; + +local designsizes = allocate() +constructors.designsizes = designsizes +local loadedfonts = allocate() +constructors.loadedfonts = loadedfonts + +--[[ldx-- +

We need to normalize the scale factor (in scaled points). This has to +do with the fact that uses a negative multiple of 1000 as +a signal for a font scaled based on the design size.

+--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-- +

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.)

+--ldx]]-- + +-- the following hack costs a bit of runtime but safes memory +-- +-- basekerns are scaled and will be hashed by table id +-- sharedkerns are unscaled and are be hashed by concatenated indexes + +--~ function constructors.check_base_kerns(tfmdata) +--~ if constructors.sharebasekerns then +--~ local sharedkerns = tfmdata.sharedkerns +--~ if sharedkerns then +--~ local basekerns = { } +--~ tfmdata.basekerns = basekerns +--~ return sharedkerns, basekerns +--~ end +--~ end +--~ return nil, nil +--~ end + +--~ function constructors.prepare_base_kerns(tfmdata) +--~ if constructors.sharebasekerns and not tfmdata.sharedkerns then +--~ local sharedkerns = { } +--~ tfmdata.sharedkerns = sharedkerns +--~ for u, chr in next, tfmdata.characters do +--~ local kerns = chr.kerns +--~ if kerns then +--~ local hash = concat(sortedkeys(kerns), " ") +--~ local base = sharedkerns[hash] +--~ if not base then +--~ sharedkerns[hash] = kerns +--~ else +--~ chr.kerns = base +--~ end +--~ end +--~ end +--~ end +--~ end + +-- we can cache scaled characters when we are in node mode and don't have +-- protruding and expansion: hash == fullname @ size @ protruding @ expansion +-- but in practice (except from mk) the otf hash will be enough already so it +-- makes no sense to mess up the code now + +-- The scaler is only used for otf and afm and virtual fonts. If +-- a virtual font has italic correction make sure to set the +-- italic_correction flag. Some more flags will be added in +-- the future. + +--[[ldx-- +

The reason why the scaler was originally split, is that for a while we experimented +with a helper function. However, in practice the calls are too slow to +make this profitable and the based variant was just faster. A days +wasted day but an experience richer.

+--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 soem 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 + +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 MathConstants = tfmdata.mathconstants or { } + -- + local targetcharacters = { } + local targetdescriptions = table.derive(descriptions) + local targetparameters = table.derive(parameters) + local targetmathparameters = table.derive(mathparameters) + local targetproperties = table.derive(properties) + local targetgoodies = table.derive(goodies) + target.characters = targetcharacters + target.descriptions = targetdescriptions + target.parameters = targetparameters + target.mathparameters = targetmathparameters + target.properties = targetproperties + target.goodies = targetgoodies + target.shared = shared + target.resources = resources + target.unscaled = tfmdata -- the original unscaled one (temp) + -- + -- 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 + 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 + -- + 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 + -- + 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 extend_factor = parameters.extend_factor or 0 + if extend_factor ~= 0 and extend_factor ~= 1 then + hdelta = hdelta * extend_factor + target.extend = extend_factor * 1000 -- extent ? + else + target.extend = 1000 -- extent ? + end + -- slanting + local slant_factor = parameters.slant_factor or 0 + if slant_factor ~= 0 then + target.slant = slant_factor * 1000 + else + target.slant = 0 + end + -- + targetparameters.hfactor = hdelta + targetparameters.vfactor = vdelta + targetparameters.factor = delta + 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 hasitalic = properties.italic_correction + local stackmath = not properties.no_stackmath + 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" + -- more extensive test + local hasmath = (properties.math or next(mathparameters) or next(MathConstants)) and true + if hasmath then + properties.has_math = true + target.nomath = false + target.MathConstants = MathConstants + target.mathconstants = MathConstants + else + properties.has_math = false + target.nomath = true + target.mathparameters = nil -- nop + end + -- this will move to some subtable so that it is copied at once + 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 + 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 + -- + if hasmath then + local ma = constructors.mathactions + local ta = type(ma) + if ta == "function" then -- context + ma(target,tfmdata) + elseif ta == "table" then -- generic (we keep the deltas) + for i=1,#ma do + ma[i](target,tfmdata,delta,hdelta,vdelta) + end + end + if not targetparameters[13] then targetparameters[13] = .86*targetx_height end -- mathsupdisplay + if not targetparameters[14] then targetparameters[14] = .86*targetx_height end -- mathsupnormal + if not targetparameters[15] then targetparameters[15] = .86*targetx_height end -- mathsupcramped + if not targetparameters[16] then targetparameters[16] = .48*targetx_height end -- mathsubnormal + if not targetparameters[17] then targetparameters[17] = .48*targetx_height end -- mathsubcombined + if not targetparameters[22] then targetparameters[22] = 0 end -- mathaxisheight + if target.MathConstants then target.MathConstants.AccentBaseHeight = nil end -- safeguard + if trace_defining then + report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'", + name or "noname",fullname or "nofullname",filename or "nofilename") + end + else + if trace_defining then + report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'", + name or "noname",fullname or "nofullname",filename or "nofilename") + end + end + -- + targetparameters.xheight = targetparameters.xheight or parameters.x_height + targetparameters.extraspace = targetparameters.extraspace or parameters.extra_space + targetparameters.spacestretch = targetparameters.spacestretch or parameters.space_stretch + targetparameters.spaceshrink = targetparameters.spaceshrink or parameters.space_shrink + -- + local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0 + local scaledwidth = defaultwidth * hdelta + local scaledheight = defaultheight * vdelta + local scaleddepth = defaultdepth * vdelta + -- + local sharedkerns = { } + -- + for unicode, character in next, characters do + local chr, description, index + if changed then + -- basemode hack + local c = changed[unicode] + if c then + description = descriptions[c] or character + character = characters[c] or character + index = description.index or c + else + description = descriptions[unicode] or character + index = description.index or unicode + end + else + description = descriptions[unicode] or character + index = description.index or unicode + end + 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 trace_scaling then + -- report_defining("t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or "",index or 0,description.name or '-',description.class or '-') + -- end + if tounicode then + local tu = tounicode[index] -- nb: index! + if tu then + chr.tounicode = tu + end + end + if hasquality then + -- we could move these calculations elsewhere (saves calculations) + local ve = character.expansion_factor + if ve then + chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere + end + local vl = character.left_protruding + if vl then + chr.left_protruding = protrusionfactor*width*vl + end + local vr = character.right_protruding + if vr then + chr.right_protruding = protrusionfactor*width*vr + end + end + -- todo: hasitalic + if hasitalic then + local vi = description.italic or character.italic + if vi and vi ~= 0 then + chr.italic = 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 + -- if character.vert_variants or character.horiz_variants then + -- report_defining("glyph 0x%05X has combination of next, vert_variants and horiz_variants",index) + -- end + 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 + -- if sharedkerns then + -- local base = basekerns[vk] -- hashed by table id, not content + -- if not base then + -- base = {} + -- for k,v in next, vk do base[k] = v*hdelta end + -- basekerns[vk] = base + -- end + -- chr.kerns = base + -- else + -- local tt = {} + -- for k,v in next, vk do tt[k] = v*hdelta end + -- chr.kerns = tt + -- end + 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 + 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.extend_factor then + parameters.extend_factor = tfmdata.extend or 0 + end + -- + if not parameters.slant_factor then + parameters.slant_factor = 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 + setmetatable(descriptions, { __index = 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 + -- tfmdata.mathconstants + -- + if not properties.has_math then + properties.has_math = 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-- +

A unique hash value is generated by:

+--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 k ~= "number" and k ~= "features" then -- I need to figure this out, features + 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-- +

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 .

+--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: '%s'",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 '%s' is not found",askedfilename) + foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or "" + if foundfilename ~= "" then + report_defining("using source file '%s' (cache mismatch)",foundfilename) + end + end + end + data.foundfilename = foundfilename + end + return foundfilename +end + +local formats = allocate() +fonts.formats = formats + +setmetatable(formats, { + __index = function(t,k) + local l = lower(k) + if rawget(t,k) then + t[k] = l + return l + end + return rawget(t,file.extname(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 '%s', group '%s', mode '%s'",name or "?",group or "?",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 '%s', group '%s'",name or "?",group or "?") + os.exit() + end + local source = source[group] + if not source then + report_defining("fatal source error in setting feature '%s', group '%s'",name or "?",group or "?") + 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.newfeatures(what) + local features = handlers[what].features + if not features then + local tables = handlers[what].tables -- can be preloaded + features = { + defaults = { }, + descriptions = tables and tables.features or { }, + initializers = { base = { }, node = { } }, + processors = { base = { }, node = { } }, + manipulators = { base = { }, node = { } }, + } + features.register = function(specification) return register(features,specification) end + handlers[what].features = features -- will also become hidden + end + return features +end + +--[[ldx-- +

We need to check for default features. For this we provide +a helper function.

+--ldx]]-- + +function constructors.checkedfeatures(what,features) + if features and next(features) then + local done = false + for key, value in next, handlers[what].features.defaults do + if features[key] == nil then + features[key] = value + done = true + end + end + return features, done -- done signals a change + else + return fastcopy(defaults), true + 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 + local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features)) or features.mode or "base" + properties.mode = mode -- also status + 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 + 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 %s to %s for mode %s for font %s",feature, + tostring(value),mode or 'unknown', tfmdata.properties.fullname or 'unknown') + end + action(tfmdata,value,features) -- can set mode (e.g. goodies) so it can trigger a restart + if mode ~= properties.mode then + mode = properties.mode + redo = true + 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 %s for mode %s for font %s",feature, + mode or 'unknown', tfmdata.properties.fullname or 'unknown') + end + if action then + nofprocesses = nofprocesses + 1 + processes[nofprocesses] = action + end + end + end + 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 %s for mode %s for font %s",feature, + mode or 'unknown', tfmdata.properties.fullname or 'unknown') + 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 4a88d3527..9b1dc03ec 100644 --- a/tex/context/base/font-ctx.lua +++ b/tex/context/base/font-ctx.lua @@ -6,32 +6,45 @@ if not modules then modules = { } end modules ['font-ctx'] = { license = "see context related readme files" } --- needs a cleanup: merge of replace, lang/script etc +-- split in definition and specifiers (as these need to come before goodies) -local texcount, texsetcount, write_nl = tex.count, tex.setcount, texio.write_nl +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 = table.concat, table.serialize +local concat, serialize, sort = table.concat, table.serialize, table.sort local settings_to_hash, hash_to_string = utilities.parsers.settings_to_hash, utilities.parsers.hash_to_string local formatcolumns = utilities.formatters.formatcolumns local tostring, next, type = tostring, next, type -local lpegmatch = lpeg.match +local utfchar, utfbyte = utf.char, utf.byte local round = math.round -local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = 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 report_defining = logs.reporter("fonts","defining") -local report_status = logs.reporter("fonts","status") -local report_mapfiles = logs.reporter("fonts","mapfiles") - -local fonts = fonts -local tfm = fonts.tfm -local definers = fonts.definers -local specifiers = definers.specifiers -local currentfont = font.current -local texattribute = tex.attribute +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_defining = false trackers.register("fonts.defining", function(v) trace_defining = 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_defining = logs.reporter("fonts","defining") +local report_status = logs.reporter("fonts","status") +local report_mapfiles = logs.reporter("fonts","mapfiles") + +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 helpers = fonts.helpers +local hashes = fonts.hashes +local fontdata = hashes.identifiers +local currentfont = font.current +local texattribute = tex.attribute + +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register specifiers.contextsetups = specifiers.contextsetups or { } specifiers.contextnumbers = specifiers.contextnumbers or { } @@ -43,49 +56,20 @@ local numbers = specifiers.contextnumbers local merged = specifiers.contextmerged local synonyms = specifiers.synonyms -local triggers = fonts.triggers -local names = fonts.names +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") -local allocate, mark = utilities.storage.allocate, utilities.storage.mark +constructors.resolvevirtualtoo = true -- context specific (due to resolver) -fonts.internalized = allocate() -- internal tex numbers - -fonts.characters = mark(fonts.characters or { }) -- chardata -fonts.csnames = mark(fonts.csnames or { }) -- namedata -fonts.quads = mark(fonts.quads or { }) -- quaddata -fonts.xheights = mark(fonts.xheights or { }) -- xheightdata - -local fontdata = fonts.identifiers -local chardata = fonts.characters -local quaddata = fonts.quads -local xheightdata = fonts.xheights - -fonts.ids = fontdata -- we keep this one for a while (as it is used in mk etc) - --- todo: give parameters at the lua end a metatable - ---~ function parameters(t,k) ---~ local v = 0 ---~ if k == "x_height" then v = t.xheight ---~ elseif k == "space_stretch" then v = t.spacestretch ---~ elseif k == "space_shrink" then v = t.spaceshrink ---~ elseif k == "extra_space" then v = t.extraspace ---~ elseif k == 1 then v = t.slant ---~ elseif k == 2 then v = t.space ---~ elseif k == 3 then v = t.spacestretch ---~ elseif k == 4 then v = t.spaceshrink ---~ elseif k == 5 then v = t.xheight ---~ elseif k == 6 then v = t.quad ---~ elseif k == 7 then v = t.extraspace ---~ end ---~ t[k] = v ---~ return v ---~ end +local allocate, mark = utilities.storage.allocate, utilities.storage.mark local nulldata = { name = "nullfont", characters = { }, descriptions = { }, + properties = { }, parameters = { -- lmromanregular @ 12pt slant = 0, -- 1 space = 256377, -- 2 @@ -99,14 +83,14 @@ local nulldata = { function definers.resetnullfont() -- resetting is needed because tikz misuses nullfont - local p = nulldata.parameters - p.slant = 0 -- 1 - p.space = 0 -- 2 - p.spacestretch = 0 -- 3 - p.spaceshrink = 0 -- 4 - p.xheight = 0 -- 5 - p.quad = 0 -- 6 - p.extraspace = 0 -- 7 + local parameters = nulldata.parameters + parameters.slant = 0 -- 1 + parameters.space = 0 -- 2 + parameters.spacestretch = 0 -- 3 + parameters.spaceshrink = 0 -- 4 + parameters.xheight = 0 -- 5 + parameters.quad = 0 -- 6 + parameters.extraspace = 0 -- 7 definers.resetnullfont = function() end end @@ -114,75 +98,102 @@ setmetatablekey(fontdata, "__index", function(t,k) return nulldata end) +local chardata = allocate() -- chardata +local csnames = allocate() -- namedata +local quaddata = allocate() -- quaddata +local xheightdata = allocate() -- xheightdata + +hashes.characters = chardata +hashes.quads = quaddata +hashes.xheights = xheightdata + setmetatablekey(chardata, "__index", function(t,k) local characters = fontdata[k].characters - chardata[k] = characters + t[k] = characters return characters end) setmetatablekey(quaddata, "__index", function(t,k) local parameters = fontdata[k].parameters local quad = parameters and parameters.quad or 0 - quaddata[k] = quad + t[k] = quad return quad end) setmetatablekey(xheightdata, "__index", function(t,k) local parameters = fontdata[k].parameters local xheight = parameters and parameters.xheight or 0 - xheightdata[k] = xheight + t[k] = xheight return quad end) --- local function enhancetfmdata(tfmdata) --- local characters = tfmdata.characters --- local parameters = tfmdata.parameters --- local shared = tfmdata.shared --- setmetatablekey(chardata, "__index", function(t,k) --- if type(k) == "number" then --- return characters[k] --- else --- -- t[k] = v -- can be option --- return parameters[k] or shared[k] --- end --- return v --- end) --- end - --- Here we overload the registration code. - -function definers.registered(hash) - local id = fonts.internalized[hash] - return id, id and fontdata[id] -end - -function definers.register(tfmdata,id) - if tfmdata and id then - local hash = tfmdata.hash - if not fonts.internalized[hash] then - fonts.internalized[hash] = id - if trace_defining then - report_defining("registering font, id: %s, hash: %s",id or "?",hash or "?") +-- this cannot be a feature initializer as there is no auto namespace +-- so we never enter the loop then + +local function modechecker(tfmdata,features) -- we cannot adapt features as they are shared! + local mode = features.mode + if mode == "auto" then + local script = features.script + local language = features.language + local rawdata = tfmdata.shared.rawdata + local sequences = rawdata and rawdata.resources.sequences + if script and language and sequences and #sequences > 0 then + for feature, value in next, features do + if value then + local found = false + for i=1,#sequences do + local features = sequences[i].features + if features then + local scripts = features[feature] + if scripts then + local languages = scripts[script] + if languages and languages[language] then + if found then + features.mode = "node" + if trace_automode then + report_defining("forcing node mode due to features %s, script %s, language %s",feature,script,language) + end + return "node" + else + found = true + end + end + end + end + end + end end - -- enhancetfmdata(tfmdata) - local characters = tfmdata.characters - local parameters = tfmdata.parameters - fontdata[id] = tfmdata - -- chardata [id] = characters - -- quaddata [id] = parameters and parameters.quad or 0 - -- xheightdata[id] = parameters and parameters.xheight or 0 - -- - tfmdata.mathconstants = tfmdata.mathconstants or tfmdata.MathMonstants - -- - parameters.xheight = parameters.xheight or parameters.x_height - parameters.extraspace = parameters.extraspace or parameters.extra_space - parameters.spacestretch = parameters.spacestretch or parameters.space_stretch - parameters.spaceshrink = parameters.spaceshrink or parameters.space_shrink end + return "base" + else + return mode end end --- End of overload. +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, +-- }, +-- } --[[ldx--

So far we haven't really dealt with features (or whatever we want @@ -197,25 +208,45 @@ name*context specification --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 definers.methods.variants[detail] then - specification.features.vtf = { preset = detail } + if detail ~= "" and variants[detail] then + specification.features.virtual = { preset = detail } end return specification end definers.registersplit("@", predefined,"virtual") -storage.register("fonts/setups" , setups , "fonts.definers.specifiers.contextsetups" ) -storage.register("fonts/numbers", numbers, "fonts.definers.specifiers.contextnumbers") -storage.register("fonts/merged", merged, "fonts.definers.specifiers.contextmerged") -storage.register("fonts/synonyms", synonyms, "fonts.definers.specifiers.synonyms") - -local normalize_meanings = fonts.otf.meanings.normalize -local default_features = fonts.otf.features.default +local normalize_features = otffeatures.normalize -- should be general -local function presetcontext(name,parent,features) -- currently otf only +local function presetcontext(name,parent,features) -- will go to con and shared if features == "" and find(parent,"=") then features = parent parent = "" @@ -223,9 +254,9 @@ local function presetcontext(name,parent,features) -- currently otf only if features == "" then features = { } elseif type(features) == "string" then - features = normalize_meanings(settings_to_hash(features)) + features = normalize_features(settings_to_hash(features)) else - features = normalize_meanings(features) + features = normalize_features(features) end -- todo: synonyms, and not otf bound if parent ~= "" then @@ -243,10 +274,18 @@ local function presetcontext(name,parent,features) -- currently otf only -- 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 +--~ 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 @@ -362,9 +401,30 @@ specifiers.contextnumber = contextnumber specifiers.mergecontext = mergecontext specifiers.registercontext = registercontext +-- 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 + 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 cache = { } -- concat might be less efficient than nested tables -function fonts.withset(name,what) +local function withset(name,what) local zero = texattribute[0] local hash = zero .. "+" .. name .. "*" .. what local done = cache[hash] @@ -375,7 +435,7 @@ function fonts.withset(name,what) texattribute[0] = done end -function fonts.withfnt(name,what) +local function withfnt(name,what) local font = currentfont() local hash = font .. "*" .. name .. "*" .. what local done = cache[hash] @@ -440,7 +500,7 @@ end specifiers.splitcontext = splitcontext function specifiers.contexttostring(name,kind,separator,yes,no,strict,omit) -- not used - return hash_to_string(table.merged(fonts[kind].features.default or {},setups[name] or {}),separator,yes,no,strict,omit) + return hash_to_string(table.merged(handlers[kind].features.defaults or {},setups[name] or {}),separator,yes,no,strict,omit) end local function starred(features) -- no longer fallbacks here @@ -455,9 +515,27 @@ end definers.registersplit('*',starred,"featureset") --- define (two steps) +-- 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.specification)) + return specification +end -local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc +definers.registersplit(":",colonized,"direct") + +-- define (two steps) local space = P(" ") local spaces = space^0 @@ -475,7 +553,7 @@ 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 -local specification -- +local specification -- still needed as local ? local getspecification = definers.getspecification @@ -564,7 +642,7 @@ function definers.stage_two(global,cs,str,size,classfeatures,fontfeatures,classf local tfmdata = definers.read(specification,size) -- id not yet known local cs = specification.cs if cs then - fonts.csnames[cs] = tfmdata -- new (beware: locals can be forgotten) + csnames[cs] = tfmdata -- new (beware: locals can be forgotten) end if not tfmdata then report_defining("unable to define %s as \\%s",name,cs) @@ -577,21 +655,22 @@ function definers.stage_two(global,cs,str,size,classfeatures,fontfeatures,classf end tex.definefont(global,cs,tfmdata) -- resolved (when designsize is used): - setsomefontsize(fontdata[tfmdata].size .. "sp") + setsomefontsize(fontdata[tfmdata].parameters.size .. "sp") texsetcount("global","lastfontid",tfmdata) else -- local t = os.clock(t) local id = font.define(tfmdata) -- print(name,os.clock()-t) - tfmdata.id = id + tfmdata.properties.id = id definers.register(tfmdata,id) -- to be sure, normally already done tex.definefont(global,cs,id) - tfm.cleanuptable(tfmdata) + constructors.cleanuptable(tfmdata) + constructors.finalize(tfmdata) if trace_defining then report_defining("defining %s with id %s as \\%s (features: %s/%s, fallbacks: %s/%s)",name,id,cs,classfeatures,fontfeatures,classfallbacks,fontfallbacks) end -- resolved (when designsize is used): - setsomefontsize((tfmdata.size or 655360) .. "sp") + setsomefontsize((tfmdata.parameters.size or 655360) .. "sp") --~ if specification.fallbacks then --~ fonts.collections.prepare(specification.fallbacks) --~ end @@ -647,12 +726,13 @@ function definers.define(specification) return tfmdata, fontdata[tfmdata] else local id = font.define(tfmdata) - tfmdata.id = id + tfmdata.properties.id = id definers.register(tfmdata,id) if specification.cs then tex.definefont(specification.global,specification.cs,id) end - tfm.cleanuptable(tfmdata) + constructors.cleanuptable(tfmdata) + constructors.finalize(tfmdata) return id, tfmdata end statistics.stoptiming(fonts) @@ -665,94 +745,32 @@ experiments.register("fonts.autorscale", function(v) enable_auto_r_scale = v end) -local calculatescale = fonts.tfm.calculatescale - -- Not ok, we can best use a database for this. The problem is that we -- have delayed definitions and so we never know what style is taken -- as start. -function fonts.tfm.calculatescale(tfmtable, scaledpoints, relativeid) - local scaledpoints, delta, units = calculatescale(tfmtable,scaledpoints) +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 rfmtable = relativedata and relativedata.unscaled and relativedata.unscaled ---~ local id_x_height = rfmtable and rfmtable.parameters and rfmtable.parameters.x_height ---~ local tf_x_height = tfmtable and tfmtable.parameters and tfmtable.parameters.x_height +--~ 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, units + return scaledpoints, delta end ---~ table.insert(readers.sequence,1,'vtf') - ---~ function readers.vtf(specification) ---~ if specification.features.vtf and specification.features.vtf.preset then ---~ return tfm.make(specification) ---~ else ---~ return nil ---~ end ---~ end - --- we need a place for this .. outside the generic scope - -local dimenfactors = number.dimenfactors - -function fonts.dimenfactor(unit,tfmdata) - if unit == "ex" then - return (tfmdata and tfmdata.parameters.x_height) or 655360 - elseif unit == "em" then - return (tfmdata and tfmdata.parameters.em_height) or 655360 - else - return dimenfactors[unit] or unit - end -end - -function fonts.cleanname(name) - context(names.cleanname(name)) -end - -local p, f = 1, "%0.1fpt" -- normally this value is changed only once - -local stripper = lpeg.patterns.stripzeros - -function fonts.nbfs(amount,precision) - if precision ~= p then - p = precision - f = "%0." .. p .. "fpt" - end - context(lpegmatch(stripper,format(f,amount/65536))) -end - --- for the moment here, this will become a chain of extras that is --- hooked into the ctx registration (or scaler or ...) - -local function digitwidth(font) -- max(quad/2,wd(0..9)) - local tfmtable = fontdata[font] - local parameters = tfmtable.parameters - local width = parameters.digitwidth - if not width then - width = round(parameters.quad/2) -- maybe tex.scale - local characters = tfmtable.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 - -fonts.getdigitwidth = digitwidth -fonts.setdigitwidth = digitwidth - -- 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, @@ -760,7 +778,7 @@ local loaded = { -- prevent loading (happens in cont-sys files) ["original-public-lm.map"] = true, } -function fonts.map.loadfile(name) +function mappings.loadfile(name) name = file.addsuffix(name,"map") if not loaded[name] then if trace_mapfiles then @@ -774,7 +792,7 @@ end local loaded = { -- prevent double loading } -function fonts.map.loadline(how,line) +function mappings.loadline(how,line) if line then how = how .. " " .. line elseif how == "" then @@ -789,109 +807,49 @@ function fonts.map.loadline(how,line) end end -function fonts.map.reset() +function mappings.reset() pdf.mapfile("") end -fonts.map.reset() -- resets the default file +mappings.reset() -- resets the default file -- we need an 'do after the banner hook' --- pdf.mapfile("mkiv-base.map") -- loads the default file - -local nounicode = byte("?") - -local function nametoslot(name,all) -- maybe some day rawdata - local tfmdata = fontdata[currentfont()] - local shared = tfmdata and tfmdata.shared - local fntdata = shared and (shared.otfdata or shared.afmdata) - if fntdata then - local unicode = fntdata.luatex.unicodes[name] - if not unicode then - return nounicode - elseif type(unicode) == "number" then - return unicode - elseif all then - return unicode - else - return unicode[1] - end - end - return nounicode -end - -fonts.nametoslot = nametoslot - -function fonts.char(n,all) -- todo: afm en tfm - if type(n) == "string" then - n = nametoslot(n,all) - end - -- if type(n) == "number" then - if n then - context.char(n) +-- => commands + +function nametoslot(name) + local t = type(name) + if t == "string" then + local tfmdata = fonts.hashes.identifiers[currentfont()] + local shared = tfmdata and tfmdata.shared + local fntdata = shared and shared.rawdata + return fntdata and fntdata.resources.unicodes[name] + elseif t == "number" then + return n end end --- this will become obsolete: - -fonts.otf.nametoslot = nametoslot -fonts.afm.nametoslot = nametoslot - -fonts.otf.char = fonts.char -fonts.afm.char = fonts.char +helpers.nametoslot = nametoslot -- this will change ... -function fonts.showchardata(n) - local tfmdata = fontdata[currentfont()] - if tfmdata then - if type(n) == "string" then - n = utf.byte(n) - end - local chr = tfmdata.characters[n] - if chr then - write_nl(format("%s @ %s => U%04X => %s => ",tfmdata.fullname,tfmdata.size,n,utf.char(n)) .. serialize(chr,false)) - end - end -end - -function fonts.showfontparameters() - local tfmdata = fontdata[currentfont()] - if tfmdata then - local parameters, mathconstants = tfmdata.parameters, tfmdata.MathConstants - local hasparameters, hasmathconstants = parameters and next(parameters), mathconstants and next(mathconstants) - if hasparameters then - write_nl(format("%s @ %s => parameters => ",tfmdata.fullname,tfmdata.size) .. serialize(parameters,false)) - end - if hasmathconstants then - write_nl(format("%s @ %s => math constants => ",tfmdata.fullname,tfmdata.size) .. serialize(mathconstants,false)) - end - if not hasparameters and not hasmathconstants then - write_nl(format("%s @ %s => no parameters and/or mathconstants",tfmdata.fullname,tfmdata.size)) - end - end -end - -function fonts.reportdefinedfonts() +function loggers.reportdefinedfonts() if trace_usage then local t, tn = { }, 0 for id, data in table.sortedhash(fontdata) do + local properties = data.properties or { } + local parameters = data.parameters or { } tn = tn + 1 t[tn] = { - format("%03i",id), - format("%09i",data.size or 0), - data.type or "real", - (data.mode or "base") .. "mode", - data.auto_expand and "expanded" or "", - data.auto_protrude and "protruded" or "", - data.has_math and "math" or "", - data.extend_factor and "extended" or "", - data.slant_factor and "slanted" or "", - data.name or "", - data.psname or "", - data.fullname or "", - data.hash or "", + 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: %s",properties.name,concat(table.sortedkeys(data)," ")) end formatcolumns(t," ") report_status() @@ -903,9 +861,9 @@ function fonts.reportdefinedfonts() end end -luatex.registerstopactions(fonts.reportdefinedfonts) +luatex.registerstopactions(loggers.reportdefinedfonts) -function fonts.reportusedfeatures() +function loggers.reportusedfeatures() -- numbers, setups, merged if trace_usage then local t, n = { }, #numbers @@ -927,7 +885,11 @@ function fonts.reportusedfeatures() end end -luatex.registerstopactions(fonts.reportusedfeatures) +luatex.registerstopactions(loggers.reportusedfeatures) + +statistics.register("fonts load time", function() + return statistics.elapsedseconds(fonts) +end) -- experimental mechanism for Mojca: -- @@ -997,11 +959,255 @@ 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.otfdata - f = f and f.luatex + 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, "%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 = "%0." .. p .. "fpt" + end + context(lpegmatch(stripper,format(f,amount/65536))) +end + +function commands.featureattribute(tag) + tex.write(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 +function commands.subff(tag) withfnt(tag,-2) end + +-- function commands.addfontfeaturetoset (tag) withset(tag, 1) end +-- function commands.subtractfontfeaturefromset (tag) withset(tag,-1) end +-- function commands.addfontfeaturetofont (tag) withfnt(tag, 2) end +-- function commands.subtractfontfeaturefromfont(tag) withfnt(tag,-2) end + +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%04X => %s => %s",tfmdata.properties.fullname,tfmdata.parameters.size,n,utfchar(n),serialize(chr,false)) + end + end +end + +function commands.showfontparameters() + local tfmdata = fontdata[currentfont()] + if tfmdata then + local parameters = tfmdata.parameters + local mathconstants = tfmdata.MathConstants + local properties = tfmdata.properties + local hasparameters = parameters and next(parameters) + local hasmathconstants = mathconstants and next(mathconstants) + if hasparameters then + report_status("%s @ %s => parameters => %s",properties.fullname,parameters.size,serialize(parameters,false)) + end + if hasmathconstants then + report_status("%s @ %s => math constants => %s",properties.fullname,parameters.size,serialize(mathconstants,false)) + end + if not hasparameters and not hasmathconstants then + report_status("%s @ %s => no parameters and/or mathconstants",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 + return dimenfactors[unit] or unit + 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 + local t = { } + t[#t+1] = "[" + t[#t+1] = what + t[#t+1] = format("(base initializers: %s)",names(features.initializers.base)) + t[#t+1] = format("(base processors: %s)", names(features.processors .base)) + t[#t+1] = format("(base manipulators: %s)",names(features.manipulators.base)) + t[#t+1] = format("(node initializers: %s)",names(features.initializers.node)) + t[#t+1] = format("(node processors: %s)", names(features.processors .node)) + t[#t+1] = format("(node manipulators: %s)",names(features.manipulators.node)) + t[#t+1] = "]" + l[#l+1] = concat(t, " ") + end + end + return concat(l, " | ") + end) + +end + +-- redefinition + +local quads = hashes.quads +local xheights = hashes.xheights +local currentfont = font.current +local texdimen = tex.dimen + +setmetatable(number.dimenfactors, { + __index = 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-- +

Before a font is passed to we scale it. Here we also need +to scale virtual characters.

+--ldx]]-- + +-- 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 diff --git a/tex/context/base/font-def.lua b/tex/context/base/font-def.lua index 3a09b6664..ec9a059df 100644 --- a/tex/context/base/font-def.lua +++ b/tex/context/base/font-def.lua @@ -27,44 +27,32 @@ default loader that only handles .

--ldx]]-- local fonts = fonts -local tfm = fonts.tfm -local vf = fonts.vf - -fonts.used = allocate() - -tfm.readers = tfm.readers or { } -tfm.fonts = allocate() - -local readers = tfm.readers -local sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -readers.sequence = sequence - -tfm.version = 1.01 -tfm.cache = containers.define("fonts", "tfm", tfm.version, false) -- better in font-tfm -tfm.autoprefixedafm = true -- this will become false some day (catches texnansi-blabla.*) - -fonts.definers = fonts.definers or { } +local fontdata = fonts.hashes.identifiers +local readers = fonts.readers local definers = fonts.definers +local specifiers = fonts.specifiers +local constructors = fonts.constructors -definers.specifiers = definers.specifiers or { } -local specifiers = definers.specifiers +readers.sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -- dfont ttc -specifiers.variants = allocate() -local variants = specifiers.variants +local variants = allocate() +specifiers.variants = variants -definers.method = "afm or tfm" -- afm, tfm, afm or tfm, tfm or afm definers.methods = definers.methods or { } -local findbinfile = resolvers.findbinfile +local internalized = allocate() -- internal tex numbers (private) + + +local loadedfonts = constructors.loadedfonts +local designsizes = constructors.designsizes --[[ldx--

We hardly gain anything when we cache the final (pre scaled) - table. But it can be handy for debugging.

+ 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.

--ldx]]-- -fonts.version = 1.05 -fonts.cache = containers.define("fonts", "def", fonts.version, false) - --[[ldx--

We can prefix a font specification by name: or file:. The first case will result in a lookup in the @@ -162,84 +150,6 @@ function definers.analyze(specification, size) return definers.makespecification(specification, lookup, name, sub, method, detail, size) end ---[[ldx-- -

A unique hash value is generated by:

---ldx]]-- - -local sortedhashkeys = table.sortedhashkeys - -function tfm.hashfeatures(specification) - local features = specification.features - if features then - local t, tn = { }, 0 - local normal = features.normal - if normal and next(normal) then - local f = sortedhashkeys(normal) - for i=1,#f do - local v = f[i] - if v ~= "number" and v ~= "features" then -- i need to figure this out, features - tn = tn + 1 - t[tn] = v .. '=' .. tostring(normal[v]) - end - end - end - local vtf = features.vtf - if vtf and next(vtf) then - local f = sortedhashkeys(vtf) - for i=1,#f do - local v = f[i] - tn = tn + 1 - t[tn] = v .. '=' .. tostring(vtf[v]) - end - end - -- if specification.mathsize then - -- tn = tn + 1 - -- t[tn] = "mathsize=" .. specification.mathsize - -- end - if tn > 0 then - return concat(t,"+") - end - end - return "unknown" -end - -fonts.designsizes = allocate() - ---[[ldx-- -

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 .

---ldx]]-- - -function tfm.hashinstance(specification,force) - local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks - if force or not hash then - hash = tfm.hashfeatures(specification) - specification.hash = hash - end - if size < 1000 and fonts.designsizes[hash] then - size = math.round(tfm.scaled(size,fonts.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 - --[[ldx--

We can resolve the filename using the next function:

--ldx]]-- @@ -310,7 +220,7 @@ function definers.resolve(specification) end end -- - specification.hash = lower(specification.name .. ' @ ' .. tfm.hashfeatures(specification)) + specification.hash = lower(specification.name .. ' @ ' .. constructors.hashfeatures(specification)) if specification.sub and specification.sub ~= "" then specification.hash = specification.sub .. ' @ ' .. specification.hash end @@ -333,26 +243,48 @@ features (esp in virtual fonts) so let's not do that now.

specification yet.

--ldx]]-- -function tfm.read(specification) - local hash = tfm.hashinstance(specification) - local tfmtable = tfm.fonts[hash] -- hashes by size ! - if not tfmtable then +-- not in context, at least not now: +-- +-- function definers.applypostprocessors(tfmdata) +-- local postprocessors = tfmdata.postprocessors +-- if postprocessors then +-- 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]","-") +-- tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) +-- end +-- end +-- end +-- return tfmdata +-- end + +function definers.applypostprocessors(tfmdata) + return tfmdata +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)] - tfmtable = reader and reader(specification) - if not tfmtable then + tfmdata = reader and reader(specification) + if not tfmdata then report_defining("forced type %s of %s not found",forced,specification.name) end else - for s=1,#sequence do -- reader sequence + 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 -- not really needed + if readers[reader] then -- we skip not loaded readers if trace_defining then report_defining("trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") end - tfmtable = readers[reader](specification) - if tfmtable then + tfmdata = readers[reader](specification) + if tfmdata then break else specification.filename = nil @@ -360,82 +292,56 @@ function tfm.read(specification) end end end - if tfmtable then + if tfmdata then + local properties = tfmdata.properties + local embedding if directive_embedall then - tfmtable.embedding = "full" - elseif tfmtable.filename and fonts.dontembed[tfmtable.filename] then - tfmtable.embedding = "no" + embedding = "full" + elseif properties.filename and constructors.dontembed[properties.filename] then + embedding = "no" else - tfmtable.embedding = "subset" + embedding = "subset" end - -- fonts.goodies.postprocessors.apply(tfmdata) -- only here - local postprocessors = tfmtable.postprocessors - if postprocessors then - for i=1,#postprocessors do - local extrahash = postprocessors[i](tfmtable) -- after scaling etc - if type(extrahash) == "string" and extrahash ~= "" then - -- e.g. a reencoding needs this - extrahash = gsub(lower(extrahash),"[^a-z]","-") - tfmtable.fullname = format("%s-%s",tfmtable.fullname,extrahash) - end - end + if properties then + properties.embedding = embedding + else + tfmdata.properties = { embedding = embedding } end - -- - tfm.fonts[hash] = tfmtable - fonts.designsizes[specification.hash] = tfmtable.designsize -- we only know this for sure after loading once - --~ tfmtable.mode = specification.features.normal.mode or "base" + tfmdata = definers.applypostprocessors(tfmdata) + loadedfonts[hash] = tfmdata + designsizes[specification.hash] = tfmdata.parameters.designsize end end - if not tfmtable then + if not tfmdata then report_defining("font with asked name '%s' is not found using lookup '%s'",specification.name,specification.lookup) end - return tfmtable + return tfmdata end --[[ldx--

For virtual fonts we need a slightly different approach:

--ldx]]-- -function tfm.readanddefine(name,size) -- no id +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 = tfm.hashinstance(specification) + local hash = constructors.hashinstance(specification) local id = definers.registered(hash) if not id then - local tfmdata = tfm.read(specification) + local tfmdata = definers.loadfont(specification) if tfmdata then - tfmdata.hash = hash + tfmdata.properties.hash = hash id = font.define(tfmdata) definers.register(tfmdata,id) - tfm.cleanuptable(tfmdata) else id = 0 -- signal end end - return fonts.identifiers[id], id -end - ---[[ldx-- -

We need to check for default features. For this we provide -a helper function.

---ldx]]-- - -function definers.check(features,defaults) -- nb adapts features ! - local done = false - if features and next(features) then - for k,v in next, defaults do - if features[k] == nil then - features[k], done = v, true - end - end - else - features, done = table.fastcopy(defaults), true - end - return features, done -- done signals a change + return fontdata[id], id end --[[ldx-- @@ -457,42 +363,24 @@ function definers.current() -- or maybe current return lastdefined end -function definers.register(tfmdata,id) -- will be overloaded +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.hash + local hash = tfmdata.properties.hash if not internalized[hash] then + internalized[hash] = id if trace_defining then report_defining("registering font, id: %s, hash: %s",id or "?",hash or "?") end - fonts.identifiers[id] = tfmdata - internalized[hash] = id + fontdata[id] = tfmdata end end end -function definers.registered(hash) -- will be overloaded - local id = internalized[hash] - return id, id and fonts.identifiers[id] -end - -local cache_them = false - -function tfm.make(specification) - -- 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.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.tfm.scale (and set the - -- factor there) - local fvm = definers.methods.variants[specification.features.vtf.preset] - if fvm then - return fvm(specification) - else - return nil - 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 @@ -503,26 +391,13 @@ function definers.read(specification,size,id) -- id can be optional, name can al specification = variants[method](specification) end specification = definers.resolve(specification) - local hash = tfm.hashinstance(specification) - if cache_them then - local tfmdata = containers.read(fonts.cache,hash) -- for tracing purposes - end + local hash = constructors.hashinstance(specification) local tfmdata = definers.registered(hash) -- id if not tfmdata then - if specification.features.vtf and specification.features.vtf.preset then - tfmdata = tfm.make(specification) - else - tfmdata = tfm.read(specification) - if tfmdata then - tfm.checkvirtualid(tfmdata) - end - end - if cache_them then - tfmdata = containers.write(fonts.cache,hash,tfmdata) -- for tracing purposes - end + tfmdata = definers.loadfont(specification) -- can be overloaded if tfmdata then - tfmdata.hash = hash - tfmdata.cache = "no" +--~ constructors.checkvirtualid(tfmdata) -- interferes + tfmdata.properties.hash = hash if id then definers.register(tfmdata,id) end @@ -532,46 +407,25 @@ function definers.read(specification,size,id) -- id can be optional, name can al if not tfmdata then -- or id? report_defining( "unknown font %s, loading aborted",specification.name) elseif trace_defining and type(tfmdata) == "table" then + constructors.finalize(tfmdata) + -- local properties = tfmdata.properties or { } + -- local parameters = tfmdata.parameters or { } report_defining("using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", - tfmdata.type or "unknown", - id or "?", - tfmdata.name or "?", - tfmdata.size or "default", - tfmdata.encodingbytes or "?", - tfmdata.encodingname or "unicode", - tfmdata.fullname or "?", - file.basename(tfmdata.filename or "?")) + properties.type or "unknown", + id or "?", + properties.name or "?", + parameters.size or "default", + properties.encodingbytes or "?", + properties.encodingname or "unicode", + properties.fullname or "?", + file.basename(properties.filename or "?")) end statistics.stoptiming(fonts) return tfmdata end -function vf.find(name) - name = file.removesuffix(file.basename(name)) - if tfm.resolvevirtualtoo then - local format = fonts.logger.format(name) - if format == 'tfm' or format == 'ofm' then - if trace_defining then - report_defining("locating vf for %s",name) - end - return findbinfile(name,"ovf") - else - if trace_defining then - report_defining("vf for %s is already taken care of",name) - end - return nil -- "" - end - else - if trace_defining then - report_defining("locating vf for %s",name) - end - return findbinfile(name,"ovf") - end -end - --[[ldx-- -

We overload both the and readers.

+

We overload the reader.

--ldx]]-- -callbacks.register('define_font' , definers.read, "definition of fonts (tfmtable preparation)") -callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more +callbacks.register('define_font' , definers.read, "definition of fonts (tfmdata preparation)") diff --git a/tex/context/base/font-dum.lua b/tex/context/base/font-dum.lua deleted file mode 100644 index 54b631d55..000000000 --- a/tex/context/base/font-dum.lua +++ /dev/null @@ -1,354 +0,0 @@ -if not modules then modules = { } end modules ['font-dum'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -fonts = fonts or { } - --- general - -fonts.otf.pack = false -- only makes sense in context -fonts.tfm.resolvevirtualtoo = false -- context specific (due to resolver) -fonts.tfm.fontnamemode = "specification" -- somehow latex needs this (changed name!) - --- readers - -fonts.tfm.readers = fonts.tfm.readers or { } -fonts.tfm.readers.sequence = { 'otf', 'ttf', 'tfm', 'lua' } -fonts.tfm.readers.afm = nil - --- define - -fonts.definers = fonts.definers or { } -fonts.definers.specifiers = fonts.definers.specifiers or { } - -fonts.definers.specifiers.colonizedpreference = "name" -- is "file" in context - -function fonts.definers.getspecification(str) - return "", str, "", ":", str -end - -fonts.definers.registersplit("",fonts.definers.specifiers.variants[":"]) -- we add another one for catching lone [names] - --- logger - -fonts.logger = fonts.logger or { } - -function fonts.logger.save() -end - --- names --- --- Watch out, the version number is the same as the one used in --- the mtx-fonts.lua function scripts.fonts.names as we use a --- simplified font database in the plain solution and by using --- a different number we're less dependent on context. - -fonts.names = fonts.names or { } - -fonts.names.version = 1.001 -- not the same as in context -fonts.names.basename = "luatex-fonts-names.lua" -fonts.names.new_to_old = { } -fonts.names.old_to_new = { } - -local data, loaded = nil, false - -local fileformats = { "lua", "tex", "other text files" } - -function fonts.names.resolve(name,sub) - if not loaded then - local basename = fonts.names.basename - if basename and basename ~= "" then - for i=1,#fileformats do - local format = fileformats[i] - local foundname = resolvers.findfile(basename,format) or "" - if foundname ~= "" then - data = dofile(foundname) - break - end - end - end - loaded = true - end - if type(data) == "table" and data.version == fonts.names.version then - local condensed = string.gsub(string.lower(name),"[^%a%d]","") - local found = data.mappings and data.mappings[condensed] - if found then - local fontname, filename, subfont = found[1], found[2], found[3] - if subfont then - return filename, fontname - else - return filename, false - end - else - return name, false -- fallback to filename - end - end -end - -fonts.names.resolvespec = fonts.names.resolve -- only supported in mkiv - -function fonts.names.getfilename(askedname,suffix) -- only supported in mkiv - return "" -end - --- For the moment we put this (adapted) pseudo feature here. - -table.insert(fonts.triggers,"itlc") - -local function itlc(tfmdata,value) - if value then - -- the magic 40 and it formula come from Dohyun Kim - local metadata = tfmdata.shared.otfdata.metadata - if metadata then - local italicangle = metadata.italicangle - if italicangle and italicangle ~= 0 then - local uwidth = (metadata.uwidth or 40)/2 - for unicode, d in next, tfmdata.descriptions do - local it = d.boundingbox[3] - d.width + uwidth - if it ~= 0 then - d.italic = it - end - end - tfmdata.has_italic = true - end - end - end -end - -fonts.initializers.base.otf.itlc = itlc -fonts.initializers.node.otf.itlc = itlc - --- slant and extend - -function fonts.initializers.common.slant(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.slant_factor = value -end - -function fonts.initializers.common.extend(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.extend_factor = value -end - -table.insert(fonts.triggers,"slant") -table.insert(fonts.triggers,"extend") - -fonts.initializers.base.otf.slant = fonts.initializers.common.slant -fonts.initializers.node.otf.slant = fonts.initializers.common.slant -fonts.initializers.base.otf.extend = fonts.initializers.common.extend -fonts.initializers.node.otf.extend = fonts.initializers.common.extend - --- expansion and protrusion - -fonts.protrusions = fonts.protrusions or { } -fonts.protrusions.setups = fonts.protrusions.setups or { } - -local setups = fonts.protrusions.setups - -function fonts.initializers.common.protrusion(tfmdata,value) - if value then - local setup = setups[value] - if setup then - local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 - local emwidth = tfmdata.parameters.quad - tfmdata.auto_protrude = true - for i, chr in next, tfmdata.characters do - local v, pl, pr = setup[i], nil, nil - if v then - pl, pr = v[1], v[2] - 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 - end - end -end - -fonts.expansions = fonts.expansions or { } -fonts.expansions.setups = fonts.expansions.setups or { } - -local setups = fonts.expansions.setups - -function fonts.initializers.common.expansion(tfmdata,value) - if value then - local setup = setups[value] - if setup then - local stretch, shrink, step, factor = setup.stretch or 0, setup.shrink or 0, setup.step or 0, setup.factor or 1 - tfmdata.stretch, tfmdata.shrink, tfmdata.step, tfmdata.auto_expand = stretch * 10, shrink * 10, step * 10, true - for i, chr in next, tfmdata.characters do - local v = setup[i] - if v and v ~= 0 then - chr.expansion_factor = v*factor - else -- can be option - chr.expansion_factor = factor - end - end - end - end -end - -table.insert(fonts.manipulators,"protrusion") -table.insert(fonts.manipulators,"expansion") - -fonts.initializers.base.otf.protrusion = fonts.initializers.common.protrusion -fonts.initializers.node.otf.protrusion = fonts.initializers.common.protrusion -fonts.initializers.base.otf.expansion = fonts.initializers.common.expansion -fonts.initializers.node.otf.expansion = fonts.initializers.common.expansion - --- left over - -function fonts.registermessage() -end - --- example vectors - -local byte = string.byte - -fonts.expansions.setups['default'] = { - - stretch = 2, shrink = 2, step = .5, factor = 1, - - [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, -} - -fonts.protrusions.setups['default'] = { - - factor = 1, left = 1, right = 1, - - [0x002C] = { 0, 1 }, -- comma - [0x002E] = { 0, 1 }, -- period - [0x003A] = { 0, 1 }, -- colon - [0x003B] = { 0, 1 }, -- semicolon - [0x002D] = { 0, 1 }, -- 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 ۔ - -} - --- normalizer - -fonts.otf.meanings = fonts.otf.meanings or { } - -fonts.otf.meanings.normalize = fonts.otf.meanings.normalize or function(t) - if t.rand then - t.rand = "random" - end -end - --- needed (different in context) - -function fonts.otf.scriptandlanguage(tfmdata) - return tfmdata.script, tfmdata.language -end - --- bonus - -function fonts.otf.nametoslot(name) - local tfmdata = fonts.identifiers[font.current()] - if tfmdata and tfmdata.shared then - local otfdata = tfmdata.shared.otfdata - local unicode = otfdata.luatex.unicodes[name] - return unicode and (type(unicode) == "number" and unicode or unicode[1]) - end -end - -function fonts.otf.char(n) - if type(n) == "string" then - n = fonts.otf.nametoslot(n) - end - if type(n) == "number" then - tex.sprint("\\char" .. n) - end -end - --- another one: - -fonts.strippables = 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, -} - --- \font\test=file:somefont:reencode=mymessup --- --- fonts.enc.reencodings.mymessup = { --- [109] = 110, -- m --- [110] = 109, -- n --- } - -fonts.enc = fonts.enc or {} -local reencodings = { } -fonts.enc.reencodings = reencodings - -local function specialreencode(tfmdata,value) - -- we forget about kerns as we assume symbols and we - -- could issue a message if ther are kerns but it's - -- a hack anyway so we odn't care too much here - local encoding = value and reencodings[value] - if encoding then - local temp = { } - local char = tfmdata.characters - for k, v in next, encoding do - temp[k] = char[v] - end - for k, v in next, temp do - char[k] = temp[k] - end - -- if we use the font otherwise luatex gets confused so - -- we return an additional hash component for fullname - return string.format("reencoded:%s",value) - end -end - -local function reencode(tfmdata,value) - tfmdata.postprocessors = tfmdata.postprocessors or { } - table.insert(tfmdata.postprocessors, - function(tfmdata) - return specialreencode(tfmdata,value) - end - ) -end - -table.insert(fonts.manipulators,"reencode") -fonts.initializers.base.otf.reencode = reencode diff --git a/tex/context/base/font-enc.lua b/tex/context/base/font-enc.lua index ad1c2ec64..7e62b1eec 100644 --- a/tex/context/base/font-enc.lua +++ b/tex/context/base/font-enc.lua @@ -15,15 +15,14 @@ local match, gmatch, gsub = string.match, string.gmatch, string.gsub them in tables. But we may do so some day, for consistency.

--ldx]]-- -fonts.enc = fonts.enc or { } -local enc = fonts.enc - local report_encoding = logs.reporter("fonts","encoding") -enc.version = 1.03 -enc.cache = containers.define("fonts", "enc", fonts.enc.version, true) +local encodings = { } +fonts.encodings = encodings -enc.known = utilities.storage.allocate { -- sort of obsolete +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, @@ -34,8 +33,8 @@ enc.known = utilities.storage.allocate { -- sort of obsolete unicode = true, } -function enc.is_known(encoding) - return containers.is_valid(enc.cache,encoding) +function encodings.is_known(encoding) + return containers.is_valid(encodings.cache,encoding) end --[[ldx-- @@ -57,16 +56,16 @@ Latin Modern or Gyre) come in OpenType variants too, so these will be used.

--ldx]]-- -local enccodes = characters.enccodes +local enccodes = characters.enccodes or { } -function enc.load(filename) +function encodings.load(filename) local name = file.removesuffix(filename) - local data = containers.read(enc.cache,name) + local data = containers.read(encodings.cache,name) if data then return data end if name == "unicode" then - data = enc.make_unicode_vector() -- special case, no tex file for this + data = encodings.make_unicode_vector() -- special case, no tex file for this end if data then return data @@ -102,7 +101,7 @@ function enc.load(filename) hash = hash, unicodes = unicodes } - return containers.write(enc.cache, name, data) + return containers.write(encodings.cache, name, data) end --[[ldx-- @@ -112,12 +111,13 @@ one.

-- maybe make this a function: -function enc.make_unicode_vector() +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], hash[name] = name, code + vector[code] = name + hash[name] = code else vector[code] = '.notdef' end @@ -125,21 +125,21 @@ function enc.make_unicode_vector() for name, code in next, characters.synonyms do vector[code], hash[name] = name, code end - return containers.write(enc.cache, 'unicode', { name='unicode', tag='unicode', vector=vector, hash=hash }) + return containers.write(encodings.cache, 'unicode', { name='unicode', tag='unicode', vector=vector, hash=hash }) end -if not enc.agl then +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). - enc.agl = { } + encodings.agl = { } - setmetatable(enc.agl, { __index = function(t,k) + setmetatable(encodings.agl, { __index = function(t,k) report_encoding("loading (extended) adobe glyph list") - dofile(resolvers.findfile("font-agl.lua")) - return rawget(enc.agl,k) + dofile(resolvers.findfile("font-age.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 34cdd9264..3e3535f87 100644 --- a/tex/context/base/font-enh.lua +++ b/tex/context/base/font-enh.lua @@ -6,172 +6,56 @@ if not modules then modules = { } end modules ['font-enh'] = { license = "see context related readme files" } --- todo: optimize a bit +local next = next -local next, match = next, string.match +local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +local report_defining = logs.reporter("fonts","defining") -local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +local fonts = fonts +local constructors = fonts.constructors -local report_defining = logs.reporter("fonts","defining") +local tfmfeatures = constructors.newfeatures("tfm") +local registertfmfeature = tfmfeatures.register --- tfmdata has also fast access to indices and unicodes --- to be checked: otf -> tfm -> tfmscaled --- --- watch out: no negative depths and negative eights permitted in regular fonts +local fontencodings = fonts.encodings +fontencodings.remappings = fontencodings.remappings or { } ---[[ldx-- -

Here we only implement a few helper functions.

---ldx]]-- - -local fonts = fonts -local tfm = fonts.tfm - ---[[ldx-- -

The next function encapsulates the standard loader as -supplied by .

---ldx]]-- - --- auto complete font with missing composed characters - --- tfm features, experimental - -tfm.features = tfm.features or { } -tfm.features.list = tfm.features.list or { } -tfm.features.default = tfm.features.default or { } - -local initializers = fonts.initializers -local triggers = fonts.triggers -local manipulators = fonts.manipulators -local featurelist = tfm.features.list -local defaultfeaturelist = tfm.features.default - -table.insert(manipulators,"compose") - -function initializers.common.compose(tfmdata,value) - if value then - fonts.vf.aux.compose_characters(tfmdata) - end -end - -function tfm.enhance(tfmdata,specification) - -- we don't really share tfm data because we always reload - -- but this is more in sycn with afm and such - local features = (specification.features and specification.features.normal ) or { } - tfmdata.shared = tfmdata.shared or { } - tfmdata.shared.features = features - -- tfmdata.shared.tfmdata = tfmdata -- circular - tfmdata.filename = specification.name - if not features.encoding then - local name, size = specification.name, specification.size - local encoding, filename = match(name,"^(.-)%-(.*)$") -- context: encoding-name.* - if filename and encoding and fonts.enc.known[encoding] then - features.encoding = encoding - end - end - tfm.setfeatures(tfmdata) -end - -function tfm.setfeatures(tfmdata) - -- todo: no local functions - local shared = tfmdata.shared --- local tfmdata = shared.tfmdata - local features = shared.features - if features and next(features) then - local mode = tfmdata.mode or features.mode or "base" - local fi = initializers[mode] - if fi and fi.tfm then - local function initialize(list) -- using tex lig and kerning - if list then - -- fi adapts ! - for i=1,#list do - local f = list[i] - local value = features[f] - if value then - local fitfmf = fi.tfm[f] -- brr - if fitfmf then - if tfm.trace_features then - report_defining("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') - end - fitfmf(tfmdata,value) - mode = tfmdata.mode or features.mode or "base" - fi = initializers[mode] - end - end - end - end - end - initialize(triggers) - initialize(featurelist) - initialize(manipulators) - end - local fm = fonts.methods[mode] - if fm then - local fmtfm = fm.tfm - if fmtfm then - local function register(list) -- node manipulations - if list then - local sp = shared.processors - local ns = sp and #sp - for i=1,#list do - local f = list[i] - if features[f] then - local fmtfmf = fmtfm[f] - if not fmtfmf then - -- brr - elseif not sp then - sp = { fmtfmf } - ns = 1 - shared.processors = sp - else - ns = ns + 1 - sp[ns] = fmtfmf - end - end - end - end - end - register(featurelist) - end - end - end -end - -function tfm.features.register(name,default) - featurelist[#tfm.features.list+1] = name - defaultfeaturelist[name] = default -end - -function tfm.reencode(tfmdata,encoding) - if encoding and fonts.enc.known[encoding] then - local data = fonts.enc.load(encoding) +local function reencode(tfmdata,encoding) + if encoding and fontencodings.known[encoding] then + local data = fontencodings.load(encoding) if data then - local characters, original, vector = tfmdata.characters, { }, data.vector - tfmdata.encoding = encoding -- not needed - for k, v in next, characters do - v.name, v.index, original[k] = vector[k], k, v + 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 k,v in next, data.unicodes do - if k ~= v then + for newcode, oldcode in next, data.unicodes do + if newcode ~= oldcode then if trace_defining then - report_defining("reencoding U+%04X to U+%04X",k,v) + report_defining("reencoding U+%04X to U+%04X",newcode,oldcode) end - characters[k] = original[v] + characters[newcode] = original[oldcode] end end end end end -tfm.features.register('reencode') - -initializers.base.tfm.reencode = tfm.reencode -initializers.node.tfm.reencode = tfm.reencode - -fonts.enc = fonts.enc or { } -fonts.enc.remappings = fonts.enc.remappings or { } +registertfmfeature { + name = "reencode", + description = "reencode", + manipulators = { + base = reencode, + node = reencode, + } +} -function tfm.remap(tfmdata,remapping) - local vector = remapping and fonts.enc.remappings[remapping] +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 @@ -187,41 +71,22 @@ function tfm.remap(tfmdata,remapping) c.index = k end end - tfmdata.encodingbytes = 2 - tfmdata.format = tfmdata.format or 'type1' + 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 -tfm.features.register('remap') - -initializers.base.tfm.remap = tfm.remap -initializers.node.tfm.remap = tfm.remap - ---~ obsolete ---~ ---~ function tfm.enhance(tfmdata,specification) ---~ local name, size = specification.name, specification.size ---~ local encoding, filename = match(name,"^(.-)%-(.*)$") -- context: encoding-name.* ---~ if filename and encoding and fonts.enc.known[encoding] then ---~ local data = fonts.enc.load(encoding) ---~ if data then ---~ local characters = tfmdata.characters ---~ tfmdata.encoding = encoding ---~ local vector = data.vector ---~ local original = { } ---~ for k, v in next, characters do ---~ v.name = vector[k] ---~ v.index = k ---~ original[k] = v ---~ end ---~ for k,v in next, data.unicodes do ---~ if k ~= v then ---~ if trace_defining then ---~ report_defining("mapping %s onto %s",k,v) ---~ end ---~ characters[k] = original[v] ---~ end ---~ end ---~ end ---~ end ---~ end +registertfmfeature { + name = "remap", + description = "remap", + manipulators = { + base = remap, + node = remap, + } +} diff --git a/tex/context/base/font-ext.lua b/tex/context/base/font-ext.lua index 16a0008cc..acabdb465 100644 --- a/tex/context/base/font-ext.lua +++ b/tex/context/base/font-ext.lua @@ -20,95 +20,21 @@ local trace_expansion = false trackers.register("fonts.expansion", function(v local report_expansions = logs.reporter("fonts","expansions") local report_protrusions = logs.reporter("fonts","protrusions") -commands = commands or { } - --[[ldx--

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.

--ldx]]-- -local fonts = fonts - -fonts.triggers = fonts.triggers or { } -local triggers = fonts.triggers - -fonts.methods = fonts.methods or { } -local methods = fonts.methods - -fonts.manipulators = fonts.manipulators or { } -local manipulators = fonts.manipulators - -fonts.initializers = fonts.initializers or { } -local initializers = fonts.initializers -initializers.common = initializers.common or { } - -local otf = fonts.otf - ---[[ldx-- -

This feature will remove inter-digit kerns.

---ldx]]-- - --- old code, no kerns set at this point, so this has to be done afterwards --- --- table.insert(triggers,"equaldigits") --- --- function initializers.common.equaldigits(tfmdata,value) --- if value then --- local chr = tfmdata.characters --- for i = utfbyte('0'), utfbyte('9') do --- local c = chr[i] --- if c then --- c.kerns = nil --- end --- end --- end --- end +local fonts = fonts +local handlers = fonts.handlers +local otf = handlers.otf ---[[ldx-- -

This feature will give all glyphs an equal height and/or depth. Valid -values are none, height, depth and -both.

---ldx]]-- +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register --- old code, no dimensions set at this point, so this has to be done afterwards --- --- table.insert(triggers,"lineheight") --- --- function initializers.common.lineheight(tfmdata,value) --- if value and type(value) == "string" then --- if value == "none" then --- for _,v in next, tfmdata.characters do --- v.height, v.depth = 0, 0 --- end --- else --- local ascender, descender = tfmdata.ascender, tfmdata.descender --- if ascender and descender then --- local ht, dp = ascender or 0, descender or 0 --- if value == "height" then --- dp = 0 --- elseif value == "depth" then --- ht = 0 --- end --- if ht > 0 then --- if dp > 0 then --- for _,v in next, tfmdata.characters do --- v.height, v.depth = ht, dp --- end --- else --- for _,v in next, tfmdata.characters do --- v.height = ht --- end --- end --- elseif dp > 0 then --- for _,v in next, tfmdata.characters do --- v.depth = dp --- end --- end --- end --- end --- end --- end +local afmfeatures = fonts.constructors.newfeatures("afm") +local registerafmfeature = afmfeatures.register -- -- -- -- -- -- -- shared @@ -116,11 +42,10 @@ values are none, height, depth and 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 f_where = handlers[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]) ---~ print(value,class,f_where,f_classes) if class then local class_vector = class.vector local g_vectors = g_where and g_where.vectors @@ -169,17 +94,26 @@ vectors['default'] = { vectors['quality'] = vectors['default'] -- metatable ? -function initializers.common.expansion(tfmdata,value) +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, shrink, step, factor = class.stretch or 0, class.shrink or 0, class.step or 0, class.factor or 1 + 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 %s, vector: %s, factor: %s, stretch: %s, shrink: %s, step: %s", value,class.vector,factor,stretch,shrink,step) end - tfmdata.stretch, tfmdata.shrink, tfmdata.step, tfmdata.auto_expand = stretch * 10, shrink * 10, step * 10, true + 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] @@ -211,13 +145,23 @@ function initializers.common.expansion(tfmdata,value) end end -table.insert(manipulators,"expansion") - -initializers.base.otf.expansion = initializers.common.expansion -initializers.node.otf.expansion = initializers.common.expansion +registerotffeature { + name = "expansion", + description = "apply hz optimization", + initializers = { + base = initializeexpansion, + node = initializeexpansion, + } +} -initializers.base.afm.expansion = initializers.common.expansion -initializers.node.afm.expansion = initializers.common.expansion +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) @@ -374,10 +318,13 @@ classes['double'] = { -- for testing opbd } local function map_opbd_onto_protrusion(tfmdata,value,opbd) - local characters, descriptions = tfmdata.characters, tfmdata.descriptions - local otfdata = tfmdata.shared.otfdata - local singles = otfdata.shared.featuredata.gpos_single - local script, language = tfmdata.script, tfmdata.language + 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 @@ -388,11 +335,11 @@ local function map_opbd_onto_protrusion(tfmdata,value,opbd) factor = tonumber(value) or 1 end if opbd ~= "right" then - local validlookups, lookuplist = otf.collectlookups(otfdata,"lfbd",script,language) + local validlookups, lookuplist = otf.collectlookups(rawdata,"lfbd",script,language) if validlookups then for i=1,#lookuplist do local lookup = lookuplist[i] - local data = singles[lookup] + local data = lookuphash[lookup] if data then if trace_protrusion then report_protrusions("setting left using lfbd lookup '%s'",lookup) @@ -411,11 +358,11 @@ local function map_opbd_onto_protrusion(tfmdata,value,opbd) end end if opbd ~= "left" then - local validlookups, lookuplist = otf.collectlookups(otfdata,"rtbd",script,language) + local validlookups, lookuplist = otf.collectlookups(rawdata,"rtbd",script,language) if validlookups then for i=1,#lookuplist do local lookup = lookuplist[i] - local data = singles[lookup] + local data = lookuphash[lookup] if data then if trace_protrusion then report_protrusions("setting right using rtbd lookup '%s'",lookup) @@ -441,7 +388,7 @@ end -- only has some kerns for digits. So, consider this feature not -- supported till we have a proper test font. -function initializers.common.protrusion(tfmdata,value) +local function initializeprotrusion(tfmdata,value) if value then local opbd = tfmdata.shared.features.opbd if opbd then @@ -460,7 +407,12 @@ function initializers.common.protrusion(tfmdata,value) end local data = characters.data local emwidth = tfmdata.parameters.quad - tfmdata.auto_protrude = true + 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 @@ -500,61 +452,80 @@ function initializers.common.protrusion(tfmdata,value) end end -table.insert(manipulators,"protrusion") - -initializers.base.otf.protrusion = initializers.common.protrusion -initializers.node.otf.protrusion = initializers.common.protrusion +registerotffeature { + name = "protrusion", + description = "shift characters into the left and or right margin", + initializers = { + base = initializeprotrusion, + node = initializeprotrusion, + } +} -initializers.base.afm.protrusion = initializers.common.protrusion -initializers.node.afm.protrusion = initializers.common.protrusion +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) -- -- -- -function initializers.common.nostackmath(tfmdata,value) - tfmdata.ignore_stack_math = value +local function initializenostackmath(tfmdata,value) + tfmdata.properties.no_stackmath = value and true end -table.insert(manipulators,"nostackmath") - -initializers.base.otf.nostackmath = initializers.common.nostackmath -initializers.node.otf.nostackmath = initializers.common.nostackmath - -table.insert(triggers,"itlc") +registerotffeature { + name = "nostackmath", + description = "disable math stacking mechanism", + initializers = { + base = initializenostackmath, + node = initializenostackmath, + } +} -function initializers.common.itlc(tfmdata,value) +local function initializeitlc(tfmdata,value) if value then -- the magic 40 and it formula come from Dohyun Kim - local fontdata = tfmdata.shared.otfdata or tfmdata.shared.afmdata - local metadata = fontdata and fontdata.metadata - if metadata then - local italicangle = metadata.italicangle - if italicangle and italicangle ~= 0 then - local uwidth = (metadata.uwidth or 40)/2 - for unicode, d in next, tfmdata.descriptions do - local it = d.boundingbox[3] - d.width + uwidth - if it ~= 0 then - d.italic = it - end + local parameters = tfmdata.parameters + local italicangle = parameters.italicangle + if italicangle and italicangle ~= 0 then + local uwidth = (parameters.uwidth or 40)/2 + for unicode, d in next, tfmdata.descriptions do + local it = d.boundingbox[3] - d.width + uwidth + if it ~= 0 then + d.italic = it end - tfmdata.has_italic = true end + tfmdata.properties.italic_correction = true end end end -initializers.base.otf.itlc = initializers.common.itlc -initializers.node.otf.itlc = initializers.common.itlc +registerotffeature { + name = "itlc", + description = "italic correction", + initializers = { + base = initializeitlc, + node = initializeitlc, + } +} -initializers.base.afm.itlc = initializers.common.itlc -initializers.node.afm.itlc = initializers.common.itlc +registerafmfeature { + name = "itlc", + description = "italic correction", + initializers = { + base = initializeitlc, + node = initializeitlc, + } +} -- slanting -table.insert(triggers,"slant") - -function initializers.common.slant(tfmdata,value) +local function initializeslant(tfmdata,value) value = tonumber(value) if not value then value = 0 @@ -563,18 +534,28 @@ function initializers.common.slant(tfmdata,value) elseif value < -1 then value = -1 end - tfmdata.slant_factor = value + tfmdata.parameters.slant_factor = value end -initializers.base.otf.slant = initializers.common.slant -initializers.node.otf.slant = initializers.common.slant - -initializers.base.afm.slant = initializers.common.slant -initializers.node.afm.slant = initializers.common.slant +registerotffeature { + name = "slant", + description = "slant glyphs", + initializers = { + base = initializeslant, + node = initializeslant, + } +} -table.insert(triggers,"extend") +registerafmfeature { + name = "slant", + description = "slant glyphs", + initializers = { + base = initializeslant, + node = initializeslant, + } +} -function initializers.common.extend(tfmdata,value) +local function initializeextend(tfmdata,value) value = tonumber(value) if not value then value = 0 @@ -583,58 +564,90 @@ function initializers.common.extend(tfmdata,value) elseif value < -10 then value = -10 end - tfmdata.extend_factor = value + tfmdata.parameters.extend_factor = value end -initializers.base.otf.extend = initializers.common.extend -initializers.node.otf.extend = initializers.common.extend - -initializers.base.afm.extend = initializers.common.extend -initializers.node.afm.extend = initializers.common.extend - --- historic stuff, move from font-ota - -local delete_node = nodes.delete -local fontdata = fonts.identifiers - -local nodecodes = nodes.nodecodes -local glyph_code = nodecodes.glyph - -fonts.strippables = fonts.strippables or { -- just a placeholder - [0x200C] = true, -- zwnj - [0x200D] = true, -- zwj +registerotffeature { + name = "extend", + description = "scale glyphs horizontally", + initializers = { + base = initializeextend, + node = initializeextend, + } } -local strippables = fonts.strippables - -local function processformatters(head,font) - local how = fontdata[font].shared.features.formatters - if how == nil or how == "strip" then -- nil when forced - 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 strippables[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 - -methods.node.otf.formatters = processformatters -methods.base.otf.formatters = processformatters - -otf.tables.features['formatters'] = 'Hide Formatting Characters' - -otf.features.register("formatters") +registerafmfeature { + name = "extend", + description = "scale glyphs horizontally", + initializers = { + base = initializeextend, + node = initializeextend, + } +} -table.insert(manipulators,"formatters") -- at end +-- -- 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, +-- } +-- } diff --git a/tex/context/base/font-fbk.lua b/tex/context/base/font-fbk.lua index 586af127b..d5cec59c9 100644 --- a/tex/context/base/font-fbk.lua +++ b/tex/context/base/font-fbk.lua @@ -12,6 +12,8 @@ local utfbyte, utfchar = utf.byte, utf.char local trace_combining = false trackers.register("fonts.combining", function(v) trace_combining = v end) local trace_combining_all = false trackers.register("fonts.combining.all", function(v) trace_combining_all = v end) +local force_combining = false -- just for demo purposes (see mk) + trackers.register("fonts.composing", "fonts.combining") local report_combining = logs.reporter("fonts","combining") @@ -22,66 +24,59 @@ local allocate = utilities.storage.allocate

This is very experimental code!

--ldx]]-- -local fonts = fonts -local vf = fonts.vf -local tfm = fonts.tfm - -fonts.fallbacks = allocate() -local fallbacks = fonts.fallbacks -local commands = vf.aux.combine.commands - -local push, pop = { "push" }, { "pop" } - -commands["enable-tracing"] = function(g,v) - trace_combining = true -end - -commands["disable-tracing"] = function(g,v) - trace_combining = false -end +local fonts = fonts +local handlers = fonts.handlers +local constructors = fonts.constructors +local vf = handlers.vf +local commands = vf.combiner.commands -commands["set-tracing"] = function(g,v) - if v[2] == nil then - trace_combining = true - else - trace_combining = v[2] - end -end +local otffeatures = constructors.newfeatures("otf") +local registerotffeature = otffeatures.register --- maybe store llx etc instead of bbox in tfm blob / more efficient +local afmfeatures = constructors.newfeatures("afm") +local registerafmfeature = afmfeatures.register -local force_composed = false +local unicodecharacters = characters.data +local unicodefallbacks = characters.fallbacks -local cache = { } -- we could make these weak -local fraction = 0.15 -- 30 units for lucida +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 -function vf.aux.compose_characters(g) -- todo: scaling depends on call location +local function composecharacters(tfmdata) -- this assumes that slot 1 is self, there will be a proper self some day - local chars, descs = g.characters, g.descriptions - local Xdesc, xdesc = descs[utfbyte("X")], descs[utfbyte("x")] + 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 = g.factor or 1 + local scale = parameters.factor or 1 local deltaxheight = scale * (Xdesc.boundingbox[4] - xdesc.boundingbox[4]) local extraxheight = fraction * deltaxheight -- maybe use compose value - -- local cap_ury = scale*xdesc.boundingbox[4] - local ita_cor = cos(rad(90+(g.italicangle or 0))) - local fallbacks = characters.fallbacks - local vfspecials = backends.tables.vfspecials + local italicfactor = parameters.italicfactor or 0 + local vfspecials = backends.tables.vfspecials --brr local red, green, blue, black if trace_combining then - red, green, blue, black = vfspecials.red, vfspecials.green, vfspecials.blue, vfspecials.black + red = vfspecials.red + green = vfspecials.green + blue = vfspecials.blue + black = vfspecials.black end - local compose = fonts.goodies.getcompositions(g) + local compose = fonts.goodies.getcompositions(tfmdata) if compose and trace_combining then report_combining("using compose information from goodies file") end local done = false - for i,c in next, characters.data do -- loop over all characters ... not that efficient but a specials hash takes memory - if force_composed or not chars[i] then + 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 = chars[chr] + 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] @@ -92,7 +87,7 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location t[k] = v end end - local charsacc = chars[acc] + local charsacc = characters[acc] --~ local ca = charsacc.category --~ if ca == "mn" then --~ -- mark nonspacing @@ -102,8 +97,8 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location --~ -- mark enclosing --~ else if not charsacc then -- fallback accents - acc = fallbacks[acc] - charsacc = acc and chars[acc] + acc = unicodefallbacks[acc] + charsacc = acc and characters[acc] end local chr_t = cache[chr] if not chr_t then @@ -119,15 +114,15 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location acc_t = {"slot", 1, acc} cache[acc] = acc_t end - local cb = descs[chr].boundingbox - local ab = descs[acc].boundingbox + local cb = descriptions[chr].boundingbox + local ab = descriptions[acc].boundingbox -- todo: adapt height if cb and ab then -- can be sped up for scale == 1 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 dx = (c_urx - a_urx - a_llx + c_llx)/2 - local dd = (c_urx - c_llx)*ita_cor + local dd = (c_urx - c_llx)*italicfactor if a_ury < 0 then if trace_combining then t.commands = { push, {"right", dx-dd}, red, acc_t, black, pop, chr_t } @@ -135,7 +130,6 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location t.commands = { push, {"right", dx-dd}, acc_t, pop, chr_t } end elseif c_ury > a_lly then -- messy test - -- local dy = cap_ury - a_lly local dy if compose then -- experimental: we could use sx but all that testing @@ -187,180 +181,78 @@ function vf.aux.compose_characters(g) -- todo: scaling depends on call location t.commands = { chr_t } -- else index mess end done = true - chars[i] = t + characters[i] = t local d = { } - for k, v in next, descs[chr] do + for k, v in next, descriptions[chr] do d[k] = v end - -- d.name = c.adobename or "unknown" -- TOO TRICKY ! CAN CLASH WITH THE SUBSETTER - -- d.unicode = i - descs[i] = d + descriptions[i] = d end end end end end if done then - g.type = "virtual" -- for the moment, to be sure - g.virtualized = true + properties.virtualized = true end end end -commands["complete-composed-characters"] = function(g,v) - vf.aux.compose_characters(g) -end +registerotffeature { + name = "compose", + description = "additional composed characters", + manipulators = { + base = composecharacters, + node = composecharacters, + } +} --- {'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'}, +registerafmfeature { + name = "compose", + description = "additional composed characters", + manipulators = { + base = composecharacters, + node = composecharacters, + } +} -local force_fallback = false +vf.helpers.composecharacters = composecharacters -commands["fake-character"] = function(g,v) -- g, nr, fallback_id - local index, fallback = v[2], v[3] - if (force_fallback or not g.characters[index]) and fallbacks[fallback] then - g.characters[index], g.descriptions[index] = fallbacks[fallback](g) - end -end +-- This installs the builder into the regular virtual font builder, +-- which only makes sense as demo. -commands["enable-force"] = function(g,v) - force_composed = true - force_fallback = true +commands["compose.trace.enable"] = function() + trace_combining = true end -commands["disable-force"] = function(g,v) - force_composed = false - force_fallback = false +commands["compose.trace.disable"] = function() + trace_combining = false end -local install = fonts.definers.methods.install - --- these are just examples used in the manuals, so they will end up in --- modules eventually +commands["compose.force.enable"] = function() + force_combining = true +end -fallbacks['textcent'] = function (g) - local c = utfbyte("c") - local t = table.fastcopy(g.characters[c],true) - local a = - tan(rad(g.italicangle or 0)) - local vfspecials = backends.tables.vfspecials - local green, black - if trace_combining then - green, black = vfspecials.green, vfspecials.black - end - local startslant, stopslant = vfspecials.startslant, vfspecials.stopslant - local quad = g.parameters.quad - if a == 0 then - if trace_combining then - t.commands = { - push, {"slot", 1, c}, pop, - {"right", .5*t.width}, - {"down", .2*t.height}, - green, - {"rule", 1.4*t.height, .02*quad}, - black, - } - else - t.commands = { - push, {"slot", 1, c}, pop, - {"right", .5*t.width}, - {"down", .2*t.height}, - {"rule", 1.4*t.height, .02*quad}, - } - end - else - if trace_combining then - t.commands = { - push, - {"right", .5*t.width-.025*quad}, - {"down", .2*t.height}, - startslant(a), - green, - {"rule", 1.4*t.height, .025*quad}, - black, - stopslant, - pop, - {"slot", 1, c} -- last else problems with cm - } - else - t.commands = { - push, - {"right", .5*t.width-.025*quad}, - {"down", .2*t.height}, - startslant(a), - {"rule", 1.4*t.height, .025*quad}, - stopslant, - pop, - {"slot", 1, c} -- last else problems with cm - } - end - end - -- somehow the width is messed up now - -- todo: set height - t.height = 1.2*t.height - t.depth = 0.2*t.height - g.virtualized = true - local d = g.descriptions - return t, d and d[c] +commands["compose.force.disable"] = function() + force_combining = false end -fallbacks['texteuro'] = function (g) - local c = utfbyte("C") - local t = table.fastcopy(g.characters[c],true) - local d = cos(rad(90+(g.italicangle))) - local vfspecials = backends.tables.vfspecials - local green, black - if trace_combining then - green, black = vfspecials.green, vfspecials.black - end - local quad = g.parameters.quad - t.width = 1.05*t.width - if trace_combining then - t.commands = { - {"right", .05*t.width}, - push, {"slot", 1, c}, pop, - {"right", .5*t.width*d}, - {"down", -.5*t.height}, - green, - {"rule", .05*quad, .4*quad}, - black, - } +commands["compose.trace.set"] = function(g,v) + if v[2] == nil then + trace_combining = true else - t.commands = { - {"right", .05*t.width}, - push, {"slot", 1, c}, pop, - {"right", .5*t.width*d}, - {"down", -.5*t.height}, - {"rule", .05*quad, .4*quad}, - } + trace_combining = v[2] end - g.virtualized = true - return t, g.descriptions[c] end +commands["compose.apply"] = function(g,v) + composecharacters(g) +end -install("fallback", { -- todo: auto-fallback with loop over data.characters - { "fake-character", 0x00A2, 'textcent' }, - { "fake-character", 0x20AC, 'texteuro' } -}) - -install("demo-2", { - { "enable-tracing" }, - { "enable-force" }, - { "initialize" }, - { "include-method", "fallback" }, - { "complete-composed-characters" }, - { "disable-tracing" }, - { "disable-force" }, -}) - -install("demo-3", { - { "enable-tracing" }, - { "initialize" }, - { "complete-composed-characters" }, - { "disable-tracing" }, -}) +-- vf builder --- end of examples +-- {'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 f5c5a3127..c2ff92fbf 100644 --- a/tex/context/base/font-gds.lua +++ b/tex/context/base/font-gds.lua @@ -6,30 +6,32 @@ if not modules then modules = { } end modules ['font-gds'] = { license = "see context related readme files" } +-- depends on ctx + local type, next = type, next local gmatch = string.gmatch -local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end) +local fonts, nodes, attributes, node = fonts, nodes, attributes, node -local report_fonts = logs.reporter("fonts","goodies") +local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end) +local report_fonts = logs.reporter("fonts","goodies") -local allocate = utilities.storage.allocate +local allocate = utilities.storage.allocate --- goodies=name,colorscheme=,featureset= --- --- goodies=auto +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -local fonts, nodes, attributes = fonts, nodes, attributes -local node = node +local fontgoodies = { } +fonts.goodies = fontgoodies -fonts.goodies = fonts.goodies or { } -local fontgoodies = fonts.goodies +local typefaces = allocate() +fonts.typefaces = typefaces -fontgoodies.data = allocate() -- fontgoodies.data or { } -local data = fontgoodies.data +local data = allocate() +fontgoodies.data = fontgoodies.data -fontgoodies.list = fontgoodies.list or { } -- no allocate as we want to see what is there -local list = fontgoodies.list +local list = { } +fontgoodies.list = list -- no allocate as we want to see what is there function fontgoodies.report(what,trace,goodies) if trace_goodies or trace then @@ -71,7 +73,7 @@ local function getgoodies(filename) -- maybe a merge is better return goodies end -function fontgoodies.register(name,fnc) +function fontgoodies.register(name,fnc) -- will be a proper sequencer list[name] = fnc end @@ -79,10 +81,12 @@ fontgoodies.get = getgoodies -- register goodies file -local presetcontext = fonts.definers.specifiers.presetcontext - local function setgoodies(tfmdata,value) - local goodies = tfmdata.goodies or { } -- future versions might store goodies in the cached instance + 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 = getgoodies(filename) @@ -90,7 +94,6 @@ local function setgoodies(tfmdata,value) goodies[#goodies+1] = ok end end - tfmdata.goodies = goodies -- shared ? end -- this will be split into good-* files and this file might become good-ini.lua @@ -120,13 +123,13 @@ local function flattenedfeatures(t,tt) return tt end -fonts.flattenedfeatures = flattenedfeatures +-- fonts.features.flattened = flattenedfeatures function fontgoodies.prepare_features(goodies,name,set) if set then local ff = flattenedfeatures(set) local fullname = goodies.name .. "::" .. name - local n, s = presetcontext(fullname,"",ff) + local n, s = fonts.specifiers.presetcontext(fullname,"",ff) goodies.featuresets[name] = s -- set if trace_goodies then report_fonts("feature set '%s' gets number %s and name '%s'",name,n,fullname) @@ -154,6 +157,7 @@ local function setfeatureset(tfmdata,set) local goodies = tfmdata.goodies -- shared ? if goodies then local features = tfmdata.shared.features + local properties = tfmdata.properties local what for i=1,#goodies do -- last one counts @@ -166,7 +170,7 @@ local function setfeatureset(tfmdata,set) features[feature] = value end end - tfmdata.mode = features.mode or tfmdata.mode + properties.mode = features.mode or properties.mode end end end @@ -207,7 +211,7 @@ end -- fontgoodies.postprocessors = fontgoodies.postprocessors or { } -- local postprocessors = fontgoodies.postprocessors - +-- -- function postprocessors.apply(tfmdata) -- local postprocessors = tfmdata.postprocessors -- if postprocessors then @@ -216,12 +220,17 @@ end -- end -- end -- end +-- +-- function definers.applypostprocessors(tfmdata) +-- fonts.goodies.postprocessors.apply(tfmdata) -- only here +-- return tfmdata +-- end -- colorschemes -fontgoodies.colorschemes = fontgoodies.colorschemes or { } -local colorschemes = fontgoodies.colorschemes -colorschemes.data = colorschemes.data or { } +local colorschemes = { } +fontgoodies.colorschemes = colorschemes +colorschemes.data = { } local function setcolorscheme(tfmdata,scheme) if type(scheme) == "string" then @@ -237,7 +246,7 @@ local function setcolorscheme(tfmdata,scheme) if what then -- this is font bound but we can share them if needed -- just as we could hash the conversions (per font) - local hash, reverse = tfmdata.luatex.unicodes, { } + local hash, reverse = tfmdata.resources.unicodes, { } for i=1,#what do local w = what[i] for j=1,#w do @@ -248,16 +257,16 @@ local function setcolorscheme(tfmdata,scheme) end end end - tfmdata.colorscheme = reverse + tfmdata.properties.colorscheme = reverse return end end end - tfmdata.colorscheme = false + tfmdata.properties.colorscheme = false end -local fontdata = fonts.identifiers -local fcs = fonts.colors.set +local fontdata = fonts.hashes.identifiers +local setnodecolor = nodes.tracers.colors.set local has_attribute = node.has_attribute local traverse_id = node.traverse_id local a_colorscheme = attributes.private('colorscheme') @@ -271,13 +280,13 @@ function colorschemes.coloring(head) if a then local f = n.font if f ~= lastfont then - lastscheme, lastfont = fontdata[f].colorscheme, f + lastscheme, lastfont = fontdata[f].properties.colorscheme, f end if lastscheme then local sc = lastscheme[n.char] if sc then done = true - fcs(n,"colorscheme:"..a..":"..sc) -- slow + setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow end end end @@ -292,44 +301,43 @@ end -- installation (collected to keep the overview) -- also for type 1 -fonts.otf.tables.features['goodies'] = 'Goodies on top of built in features' -fonts.otf.tables.features['featureset'] = 'Goodie Feature Set' -fonts.otf.tables.features['colorscheme'] = 'Goodie Color Scheme' -fonts.otf.tables.features['postprocessor'] = 'Goodie Postprocessor' - -fonts.otf.features.register('goodies') -fonts.otf.features.register('featureset') -fonts.otf.features.register('colorscheme') -fonts.otf.features.register('postprocessor') - -table.insert(fonts.triggers, 1, "goodies") -table.insert(fonts.triggers, 2, "featureset") -- insert after -table.insert(fonts.triggers, "colorscheme") -table.insert(fonts.triggers, "postprocessor") - -local base_initializers = fonts.initializers.base.otf -local node_initializers = fonts.initializers.node.otf - -base_initializers.goodies = setgoodies -node_initializers.goodies = setgoodies - -base_initializers.featureset = setfeatureset -node_initializers.featureset = setfeatureset - -base_initializers.colorscheme = setcolorscheme -node_initializers.colorscheme = setcolorscheme - -base_initializers.postprocessor = setpostprocessor -node_initializers.postprocessor = setpostprocessor +registerotffeature { + name = "goodies", + description = "goodies on top of built in features", + initializers = { + position = 1, + base = setgoodies, + node = setgoodies, + } +} -local base_initializers = fonts.initializers.base.afm -local node_initializers = fonts.initializers.node.afm +registerotffeature { + name = "featureset", + description = "goodie feature set", + initializers = { + position = 2, + base = setfeatureset, + node = setfeatureset, + } +} -base_initializers.goodies = setgoodies -node_initializers.goodies = setgoodies +registerotffeature { + name = "colorscheme", + description = "goodie color scheme", + initializers = { + base = setcolorscheme, + node = setcolorscheme, + } +} -base_initializers.postprocessor = setpostprocessor -node_initializers.postprocessor = setpostprocessor +registerotffeature { + name = "postprocessor", + description = "goodie postprocessor", + initializers = { + base = setpostprocessor, + node = setpostprocessor, + } +} -- experiment, we have to load the definitions immediately as they precede -- the definition so they need to be initialized in the typescript @@ -337,23 +345,23 @@ node_initializers.postprocessor = setpostprocessor local function initialize(goodies) local mathgoodies = goodies.mathematics if mathgoodies then - local virtuals = mathgoodies.virtuals - local mapfiles = mathgoodies.mapfiles - local maplines = mathgoodies.maplines - local variables = mathgoodies.variables + local virtuals = mathgoodies.virtuals + local mapfiles = mathgoodies.mapfiles + local maplines = mathgoodies.maplines if virtuals then for name, specification in next, virtuals do - mathematics.makefont(name,specification,variables) + -- beware, they are all constructed + mathematics.makefont(name,specification,goodies) end end if mapfiles then for i=1,#mapfiles do - fonts.map.loadfile(mapfiles[i]) -- todo: backend function + fonts.mappings.loadfile(mapfiles[i]) -- todo: backend function end end if maplines then for i=1,#maplines do - fonts.map.loadline(maplines[i]) -- todo: backend function + fonts.mappings.loadline(maplines[i]) -- todo: backend function end end end @@ -382,7 +390,7 @@ local function initialize(goodies) end end -fonts.goodies.register("files", initialize) +fontgoodies.register("files", initialize) -- some day we will have a define command and then we can also do some -- proper tracing @@ -408,7 +416,7 @@ local function initialize(goodies) end end -fonts.goodies.register("typefaces", initialize) +fontgoodies.register("typefaces", initialize) local function initialize(goodies) local typefaces = goodies.typefaces @@ -420,12 +428,12 @@ local function initialize(goodies) end end -fonts.goodies.register("typefaces", initialize) +fontgoodies.register("typefaces", initialize) local compositions = { } -function fonts.goodies.getcompositions(tfmdata) - return compositions[file.nameonly(tfmdata.filename or "")] +function fontgoodies.getcompositions(tfmdata) + return compositions[file.nameonly(tfmdata.properties.filename or "")] end local function initialize(goodies) @@ -437,7 +445,7 @@ local function initialize(goodies) end end -fonts.goodies.register("compositions", initialize) +fontgoodies.register("compositions", initialize) -- 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 diff --git a/tex/context/base/font-ini.lua b/tex/context/base/font-ini.lua index df534c6ad..8eeba0ce7 100644 --- a/tex/context/base/font-ini.lua +++ b/tex/context/base/font-ini.lua @@ -6,16 +6,14 @@ if not modules then modules = { } end modules ['font-ini'] = { license = "see context related readme files" } --- The font code will be upgraded and reorganized so that we have a --- leaner generic code base and can do more tuning for context. +-- basemethods -> can also be in list +-- presetcontext -> defaults +-- hashfeatures -> ctx version --[[ldx--

Not much is happening here.

--ldx]]-- -local utf = unicode.utf8 -local format, serialize = string.format, table.serialize -local write_nl = texio.write_nl local lower = string.lower local allocate, mark = utilities.storage.allocate, utilities.storage.mark @@ -23,96 +21,18 @@ local report_defining = logs.reporter("fonts","defining") fontloader.totable = fontloader.to_table --- vtf comes first --- fix comes last +fonts = fonts or { } -- already defined in context +local fonts = fonts -fonts = fonts or { } +-- some of these might move to where they are used first: --- beware, some already defined +fonts.hashes = { identifiers = allocate() } +fonts.analyzers = { } -- not needed here +fonts.readers = { } +fonts.tables = { } +fonts.definers = { methods = { } } +fonts.specifiers = fonts.specifiers or { } -- in format ! +fonts.loggers = { register = function() end } +fonts.helpers = { } -fonts.identifiers = mark(fonts.identifiers or { }) -- fontdata ------.characters = mark(fonts.characters or { }) -- chardata ------.csnames = mark(fonts.csnames or { }) -- namedata ------.quads = mark(fonts.quads or { }) -- quaddata - ---~ fonts.identifiers[0] = { -- nullfont ---~ characters = { }, ---~ descriptions = { }, ---~ name = "nullfont", ---~ } - -fonts.tfm = fonts.tfm or { } -fonts.vf = fonts.vf or { } -fonts.afm = fonts.afm or { } -fonts.pfb = fonts.pfb or { } -fonts.otf = fonts.otf or { } - -fonts.privateoffset = 0xF0000 -- 0x10FFFF -fonts.verbose = false -- more verbose cache tables (will move to context namespace) - -fonts.methods = fonts.methods or { - base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, - node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, -} - -fonts.initializers = fonts.initializers or { - base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, - node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } } -} - -fonts.triggers = fonts.triggers or { - 'mode', - 'language', - 'script', - 'strategy', -} - -fonts.processors = fonts.processors or { -} - -fonts.analyzers = fonts.analyzers or { - useunicodemarks = false, -} - -fonts.manipulators = fonts.manipulators or { -} - -fonts.tracers = fonts.tracers or { -} - -fonts.typefaces = fonts.typefaces or { -} - -fonts.definers = fonts.definers or { } -fonts.definers.specifiers = fonts.definers.specifiers or { } -fonts.definers.specifiers.synonyms = fonts.definers.specifiers.synonyms or { } - --- tracing - -if not fonts.colors then - - fonts.colors = allocate { - set = function() end, - reset = function() end, - } - -end - --- format identification - -fonts.formats = allocate() - -function fonts.fontformat(filename,default) - local extname = lower(file.extname(filename)) - local format = fonts.formats[extname] - if format then - return format - else - report_defining("unable to determine font format for '%s'",filename) - return default - end -end - --- readers - -fonts.tfm.readers = fonts.tfm.readers or { } +fonts.tracers = { } -- for the moment till we have move to moduledata diff --git a/tex/context/base/font-ini.mkiv b/tex/context/base/font-ini.mkiv index 781f79ad4..69a00450b 100644 --- a/tex/context/base/font-ini.mkiv +++ b/tex/context/base/font-ini.mkiv @@ -58,38 +58,50 @@ \writestatus{loading}{ConTeXt Font Macros / Initialization} \registerctxluafile{font-ini}{1.001} -\registerctxluafile{font-clr}{1.001} -\registerctxluafile{node-fnt}{1.001} % here +\registerctxluafile{font-log}{1.001} +\registerctxluafile{font-con}{1.001} \registerctxluafile{font-enc}{1.001} %registerctxluafile{font-agl}{1.001} % loaded when needed, saves 100K in format +\registerctxluafile{font-cid}{1.001} % cid maps \registerctxluafile{font-map}{1.001} \registerctxluafile{font-syn}{1.001} -\registerctxluafile{font-log}{1.001} + \registerctxluafile{font-tfm}{1.001} -\registerctxluafile{font-enh}{1.001} + \registerctxluafile{font-afm}{1.001} -\registerctxluafile{font-lua}{1.001} -\registerctxluafile{font-cid}{1.001} % cid maps -\registerctxluafile{font-ott}{1.001} % otf tables -\registerctxluafile{font-otf}{1.001} % otf main -\registerctxluafile{font-otd}{1.001} % otf dynamics + \registerctxluafile{font-oti}{1.001} % otf initialization +\registerctxluafile{font-ott}{1.001} % otf tables (first) +\registerctxluafile{font-otf}{1.001} % otf main \registerctxluafile{font-otb}{1.001} % otf main base +\registerctxluafile{node-inj}{1.001} % we might split it off \registerctxluafile{font-otn}{1.001} % otf main node +\registerctxluafile{font-otd}{1.001} % otf dynamics (does an overload) \registerctxluafile{font-ota}{1.001} % otf analyzers (needs dynamics) \registerctxluafile{font-otp}{1.001} % otf pack \registerctxluafile{font-otc}{1.001} % otf context \registerctxluafile{font-oth}{1.001} % otf helpers + +\registerctxluafile{font-pat}{1.001} % patchers + +\registerctxluafile{node-fnt}{1.001} % here + +\registerctxluafile{font-lua}{1.001} + \registerctxluafile{font-vf} {1.001} +\registerctxluafile{font-enh}{1.001} + +\registerctxluafile{font-gds}{1.001} % currently only otf + \registerctxluafile{font-def}{1.001} \registerctxluafile{font-ctx}{1.001} % after def as it overloads -\registerctxluafile{font-xtx}{1.001} -\registerctxluafile{font-gds}{1.001} -\registerctxluafile{font-fbk}{1.001} + \registerctxluafile{font-ext}{1.001} -\registerctxluafile{font-pat}{1.001} +\registerctxluafile{font-fbk}{1.001} \registerctxluafile{font-chk}{1.001} +\registerctxluafile{font-aux}{1.001} + \unprotect % \def\fontrange#1% @@ -169,9 +181,11 @@ \let\thedefinedfont\relax \def\dodefinedfont[#1]% - {\iffirstargument\definefont[thedefinedfont][#1]\fi % we can speed this one up + {\setfalse\inheritfromfontclass + \iffirstargument\definefont[thedefinedfont][#1]\fi % we can speed this one up \thedefinedfont - \the\everydefinedfont} + \the\everydefinedfont + \settrue\inheritfromfontclass} \unexpanded\def\definedfont {\dosingleempty\dodefinedfont} @@ -687,6 +701,8 @@ \let\relativefontid\empty +\newconditional\inheritfromfontclass % used in \definefont and \definedfont + \unexpanded\def\lowleveldefinefont#1#2% #2 = cs {% we can now set more at the lua end \ctxlua{fonts.definers.stage_one("\luaescapestring{#1}")}% the escapestring catches at \somedimen @@ -726,14 +742,14 @@ "#2", % cs, trailing % is gone "\somefontfile", \number\scaledfontsize, - "\@@fontclassfeatures", + "\ifconditional\inheritfromfontclass\@@fontclassfeatures\fi", "\@@fontfeatures", - "\@@fontclassfallbacks", + "\ifconditional\inheritfromfontclass\@@fontclassfallbacks\fi", "\@@fontfallbacks", 0\currentmathsize, \number\dimexpr\textface\relax, "\relativefontid", % experiment - "\@@fontclassgoodies", % experiment (not yet used) + "\ifconditional\inheritfromfontclass\@@fontclassgoodies\fi", % experiment (not yet used) "\@@fontgoodies" % experiment )}% % \edef\somefontspec{at \somefontsize}% we need the resolved designsize (for fallbacks) @@ -1167,7 +1183,8 @@ \def\newfontidentifier{*\fontclass\lastfontidentifier\fontstyle\fontsize*} \def\dododefinefont#1#2% - {\edef\lastfontidentifier{#1}% + {\setfalse\inheritfromfontclass + \edef\lastfontidentifier{#1}% \let\localrelativefontsize\defaultrelativefontsize \let\localabsolutefontsize\fontbody \lowleveldefinefont{#2}\rawfontidentifier @@ -1175,7 +1192,8 @@ \autofontsizefalse \setfontcharacteristics \the\everyfontswitch - \let\rawfontidentifier\oldrawfontidentifier} + \let\rawfontidentifier\oldrawfontidentifier + \settrue\inheritfromfontclass} \def\xxdododefinefont#1#2#3#4% \autofontsizetrue is set by calling routine {\edef\lastfontidentifier{#3}% @@ -1969,10 +1987,10 @@ % \s!pt}} \def\normalizebodyfontsize#1\to#2% - {\edef#2{\ctxlua{fonts.nbfs(\number\dimexpr#1\relax,\number\fontdigits)}}} + {\edef#2{\ctxcommand{nbfs(\number\dimexpr#1\relax,\number\fontdigits)}}} \def\thenormalizedbodyfontsize#1% - {\ctxlua{fonts.nbfs(\number\dimexpr#1\relax,\number\fontdigits)}} + {\ctxcommand{nbfs(\number\dimexpr#1\relax,\number\fontdigits)}} \edef\normalizedglobalbodyfontsize{\thenormalizedbodyfontsize\bodyfontsize} \edef\normalizedlocalbodyfontsize {\thenormalizedbodyfontsize\bodyfontsize} @@ -2825,10 +2843,6 @@ % \installfontfeature[otf][tlig] % \installfontfeature[otf][trep] -%D tricky but ok: - -\appendtoks\ctxlua{fonts.tfm.cleanup()}\to\everyshipout - %D Todo: % \def\os{\groupedcommand{\setfontfeature{oldstyle}}{}} @@ -2840,54 +2854,79 @@ \def\dodefinefontfeature[#1][#2][#3]% {\global\expandafter\chardef\csname\??fq=#1\endcsname % beware () needed as we get two values returned - \ctxlua{tex.write((fonts.definers.specifiers.presetcontext("#1","#2","#3")))}\relax} + \ctxsprint{((fonts.specifiers.presetcontext("#1","#2","#3")))}\relax} \definefontfeature [default] - [%mode=node, - liga=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + [mode=auto, % was nothing + liga=yes, + kern=yes, + tlig=yes, + trep=yes] % texligatures=yes,texquotes=yes \definefontfeature [smallcaps] - [%mode=node,liga=yes, - smcp=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + [mode=auto, % was nothing + smcp=yes, + kern=yes, + tlig=yes, + trep=yes] % texligatures=yes,texquotes=yes \definefontfeature [oldstyle] - [%mode=node, - onum=yes,liga=yes,kern=yes,tlig=yes,trep=yes] % texligatures=yes,texquotes=yes + [mode=auto, % was nothing + onum=yes, + liga=yes, + kern=yes, + tlig=yes, + trep=yes] % texligatures=yes,texquotes=yes \definefontfeature % == default unless redefined [ligatures] - [%mode=node, - liga=yes,kern=yes,tlig=yes,trep=yes] + [mode=auto, % was nothing + liga=yes, + kern=yes, + tlig=yes, + trep=yes] \definefontfeature % can be used for type1 fonts [complete] - [liga=yes,kern=yes,compose=yes,tlig=yes,trep=yes] + [mode=auto, % was nothing + liga=yes, + kern=yes, + compose=yes, + tlig=yes, + trep=yes] \definefontfeature - [arabic] % this will become obsolete + [none] + [mode=none, + features=no] + +\definefontfeature % might move + [arabic] [mode=node,language=dflt,script=arab,ccmp=yes, init=yes,medi=yes,fina=yes,isol=yes, liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes, mark=yes,mkmk=yes,kern=yes,curs=yes] -\definefontfeature +\definefontfeature % might move [simplearabic] [mode=node,language=dflt,script=arab, init=yes,medi=yes,fina=yes,calt=yes, rlig=yes,curs=yes,mark=yes,mkmk=yes] -\definefontfeature - [none] - [mode=none,features=no] +% math: \definefontfeature [virtualmath] - [mode=base,liga=yes,kern=yes,tlig=yes,trep=yes,language=dflt,script=math] - -% for the moment here, this will change but we need it for mk.tex + [mode=base, + liga=yes, + kern=yes, + tlig=yes, + trep=yes, + language=dflt, + script=math] \definefontfeature[math-text] [virtualmath][mathalternates=yes,ssty=no] \definefontfeature[math-script] [virtualmath][mathalternates=yes,ssty=1,mathsize=yes] @@ -2903,10 +2942,7 @@ %D Also new, handy for manuals: -\unexpanded\def\fontchar#1{\ctxlua{fonts.char("#1")}} - -\let\otfchar\fontchar % will disappear, for compatibility only -\let\afmchar\fontchar % will disappear, for compatibility only +\unexpanded\def\fontchar#1{\ctxcommand{fontchar("#1")}} %D: We cannot yet inherit because no colors are predefined. @@ -2942,7 +2978,7 @@ {\dodoubleargument\dofontfeatureslist} \def\dofontfeatureslist[#1][#2]% todo: arg voor type - {\ctxsprint{fonts.definers.specifiers.contexttostring("#1","otf","\luaescapestring{#2}","yes","no",true,{"number"})}} + {\ctxsprint{fonts.specifiers.contexttostring("#1","otf","\luaescapestring{#2}","yes","no",true,{"number"})}} \attribute\zerocount\zerocount % first in list, so fast match @@ -2963,14 +2999,24 @@ % % \typebuffer \getbuffer -\def\featureattribute#1{\ctxlua{tex.sprint(fonts.definers.specifiers.contextnumber("#1"))}} -\def\setfontfeature #1{\edef\currentfeature{#1}\attribute\zerocount\featureattribute{#1}\relax} -\def\resetfontfeature#1{\let\currentfeature\empty\attribute\zerocount\zerocount} % initial value +% \unexpanded\def\featureattribute#1{\ctxsprint{fonts.specifiers.contextnumber("#1"))}} +% \unexpanded\def\setfontfeature #1{\edef\currentfeature{#1}\attribute\zerocount\featureattribute{#1}\relax} +% \unexpanded\def\resetfontfeature#1{\let\currentfeature\empty\attribute\zerocount\zerocount} % initial value + +% \def\addfontfeaturetoset #1{\ctxlua{fonts.withset("#1", 1)}} % merge +% \def\subtractfontfeaturefromset #1{\ctxlua{fonts.withset("#1",-1)}} % merge +% \def\addfontfeaturetofont #1{\ctxlua{fonts.withfnt("#1", 2)}} % overload +% \def\subtractfontfeaturefromfont#1{\ctxlua{fonts.withfnt("#1",-2)}} % overload + +\unexpanded\def\featureattribute#1{\ctxcommand{featureattribute("#1")}} +\unexpanded\def\setfontfeature #1{\ctxcommand{setfontfeature("#1")}\edef\currentfeature{#1}} +\unexpanded\def\resetfontfeature#1{\ctxcommand{resetfontfeature()}\let\currentfeature\empty} % initial value +\unexpanded\def\resetfontfeature#1{\attribute\zerocount\zerocount\let\currentfeature\empty} % initial value -\def\addfontfeaturetoset #1{\ctxlua{fonts.withset("#1", 1)}} % merge -\def\subtractfontfeaturefromset #1{\ctxlua{fonts.withset("#1",-1)}} % merge -\def\addfontfeaturetofont #1{\ctxlua{fonts.withfnt("#1", 2)}} % overload -\def\subtractfontfeaturefromfont#1{\ctxlua{fonts.withfnt("#1",-2)}} % overload +\unexpanded\def\addfontfeaturetoset #1{\ctxcommand{addfs("#1")}} % merge +\unexpanded\def\subtractfontfeaturefromset #1{\ctxcommand{subfs("#1")}} % merge +\unexpanded\def\addfontfeaturetofont #1{\ctxcommand{addff("#1")}} % overload +\unexpanded\def\subtractfontfeaturefromfont#1{\ctxcommand{subff("#1")}} % overload \let\setff\setfontfeature \let\addfs\addfontfeaturetoset @@ -4303,8 +4349,8 @@ %D goodies: -\def\showchardata#1{\ctxlua{fonts.showchardata("#1")}} -\def\showfontdata {\ctxlua{fonts.showfontparameters()}} +\unexpanded\def\showchardata#1{\ctxcommand{showchardata("#1")}} +\unexpanded\def\showfontdata {\ctxcommand{showfontparameters()}} %D some low level helpers %D @@ -4325,11 +4371,11 @@ % we can also move the lookups to the fonts.namespace (of commands) -\def\dolookupfontbyspec #1{\ctxlua{fonts.names.lookup("#1")}} -\def\dolookupnoffound {\ctxlua{tex.write(fonts.names.noflookups())}} -\def\dolookupgetkeyofindex#1#2{\ctxlua{tex.write(fonts.names.getlookupkey("#1",#2))}} -\def\dolookupgetkey #1{\ctxlua{tex.write(fonts.names.getlookupkey("#1"))}} -\def\cleanfontname #1{\ctxlua{fonts.cleanname("#1")}} +\def\dolookupfontbyspec #1{\ctxcommand{fontlookupinitialize("#1")}} +\def\dolookupnoffound {\ctxcommand{fontlookupnoffound()}} +\def\dolookupgetkeyofindex#1#2{\ctxcommand{fontlookupgetkeyofindex("#1",#2))}} +\def\dolookupgetkey #1{\ctxcommand{fontlookupgetkey("#1")}} +\def\cleanfontname #1{\ctxcommand{cleanfontname("#1")}} % \doifelsecurrentfonthasfeature{smcp}{YES}{NO} % \doifelsecurrentfonthasfeature{crap}{YES}{NO} diff --git a/tex/context/base/font-log.lua b/tex/context/base/font-log.lua index 0dbde8e39..5f18c52cb 100644 --- a/tex/context/base/font-log.lua +++ b/tex/context/base/font-log.lua @@ -8,13 +8,16 @@ if not modules then modules = { } end modules ['font-log'] = { 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 trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) local report_defining = logs.reporter("fonts","defining") -local fonts = fonts -fonts.logger = fonts.logger or { } -local logger = fonts.logger +local basename = file.basename + +local fonts = fonts +local loggers = { } +fonts.loggers = loggers +local usedfonts = utilities.storage.allocate() +----- loadedfonts = utilities.storage.allocate() --[[ldx--

The following functions are used for reporting about the fonts @@ -23,41 +26,55 @@ we now have several readers it may be handy to know what reader is used for which font.

--ldx]]-- -function logger.save(tfmtable,source,specification) -- save file name in spec here ! ! ! ! ! ! - if tfmtable and specification and specification.specification then - local name = lower(specification.name) - if trace_defining and not fonts.used[name] then - report_defining("registering %s as %s (used: %s)",file.basename(specification.name),source,file.basename(specification.filename)) +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 - specification.source = source - fonts.loaded[lower(specification.specification)] = specification - -- fonts.used[name] = source - fonts.used[lower(specification.filename or specification.name)] = source + reporter("char U+%04X in font '%s' with id %s: %s",char,tfmdata.properties.fullname,font,message) + category[char] = true end end -function logger.report(complete) - local t, n = { }, 0 - for name, used in table.sortedhash(fonts.used) do - n = n + 1 - if complete then - t[n] = used .. "->" .. file.basename(name) - else - t[n] = file.basename(name) +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 fonts.used[name] then + report_defining("registering %s as %s (used: %s)",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 - return t end -function logger.format(name) - return fonts.used[name] or "unknown" +function loggers.format(name) -- should be avoided + return usedfonts[name] or "unknown" end statistics.register("loaded fonts", function() - if next(fonts.used) then - local t = logger.report() - return (#t > 0 and format("%s files: %s",#t,concat(t," "))) or "none" - else - return nil + if next(usedfonts) then + local t, n = { }, 0 + for name, used in table.sortedhash(usedfonts) do + n = n + 1 + if complete then + t[n] = used .. "->" .. basename(name) + else + t[n] = basename(name) + 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 a6fcc1642..48ce3c2f5 100644 --- a/tex/context/base/font-lua.lua +++ b/tex/context/base/font-lua.lua @@ -8,11 +8,13 @@ if not modules then modules = { } end modules ['font-lua'] = { local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) -local report_lua = logs.reporter("fonts","lua loading") +local report_lua = logs.reporter("fonts","lua loading") -fonts.formats.lua = "lua" +local fonts = fonts +local readers = fonts.readers +fonts.formats.lua = "lua" -local readers = fonts.tfm.readers +-- we could add support for features here local function check_lua(specification,fullname) -- standard tex file lookup @@ -24,22 +26,21 @@ local function check_lua(specification,fullname) 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 '%s'",original) end - local fullname, tfmtable = specification.filename or "", nil + local fullname = specification.filename or "" if fullname == "" then local forced = specification.forced or "" if forced ~= "" then - tfmtable = check_lua(specification,specification.name .. "." .. forced) - end - if not tfmtable then - tfmtable = check_lua(specification,specification.name) + fullname = specification.name .. "." .. forced + else + fullname = specification.name end - else - tfmtable = check_lua(specification,fullname) end - return tfmtable + return check_lua(specification,fullname) end diff --git a/tex/context/base/font-map.lua b/tex/context/base/font-map.lua index 26b22b678..0733f245c 100644 --- a/tex/context/base/font-map.lua +++ b/tex/context/base/font-map.lua @@ -6,15 +6,18 @@ if not modules then modules = { } end modules ['font-map'] = { license = "see context related readme files" } -local utf = unicode.utf8 local match, format, find, concat, gsub, lower = string.match, string.format, string.find, table.concat, string.gsub, string.lower -local lpegmatch = lpeg.match +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 trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) +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_otf = logs.reporter("fonts","otf loading") +local report_fonts = logs.reporter("fonts","loading") -- not otf only + +local fonts = fonts +local mappings = { } +fonts.mappings = mappings --[[ldx--

Eventually this code will disappear because map files are kind @@ -22,23 +25,18 @@ of obsolete. Some code may move to runtime or auxiliary modules.

The name to unciode related code will stay of course.

--ldx]]-- -local fonts = fonts -fonts.map = fonts.map or { } - 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_unimapping then - report_otf("enhance: loading %s ",lumfile) + if trace_loading or trace_mapping then + report_fonts("enhance: loading %s ",lumfile) end lumunic = dofile(lumfile) return lumunic, lumfile end end -local P, R, S, C, Ct, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc - local hex = R("AF","09") local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end local hexsix = (hex^1) / function(s) return tonumber(s,16) end @@ -65,8 +63,8 @@ local function makenameparser(str) end end ---~ local parser = fonts.map.makenameparser("Japan1") ---~ local parser = fonts.map.makenameparser() +--~ local parser = mappings.makenameparser("Japan1") +--~ local parser = mappings.makenameparser() --~ local function test(str) --~ local b, a = lpegmatch(parser,str) --~ print((a and table.serialize(b)) or b) @@ -107,7 +105,7 @@ end --~ --~ local cache = { } --~ ---~ function fonts.map.tounicode16(unicode) +--~ function mappings.tounicode16(unicode) --~ local s = cache[unicode] --~ if not s then --~ if unicode < 0x10000 then @@ -120,10 +118,10 @@ end --~ return s --~ end -fonts.map.loadlumtable = loadlumtable -fonts.map.makenameparser = makenameparser -fonts.map.tounicode16 = tounicode16 -fonts.map.tounicode16sequence = tounicode16sequence +mappings.loadlumtable = loadlumtable +mappings.makenameparser = makenameparser +mappings.tounicode16 = tounicode16 +mappings.tounicode16sequence = tounicode16sequence local separator = S("_.") local other = C((1 - separator)^1) @@ -135,8 +133,11 @@ local ligsplitter = Ct(other * (separator * other)^0) --~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more"))) --~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that"))) -fonts.map.addtounicode = function(data,filename) - local unicodes = data.luatex and data.luatex.unicodes +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 @@ -146,28 +147,35 @@ fonts.map.addtounicode = function(data,filename) unicodes['zwj'] = unicodes['zwj'] or 0x200D unicodes['zwnj'] = unicodes['zwnj'] or 0x200C -- the tounicode mapping is sparse and only needed for alternatives - local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.privateoffset, format("%04X",utfbyte("?")) - data.luatex.tounicode, data.luatex.originals = tounicode, originals + 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 - local cidinfo, cidnames, cidcodes = data.cidinfo - local usedmap = cidinfo and cidinfo.usedname - usedmap = usedmap and lower(usedmap) - usedmap = usedmap and fonts.cid.map[usedmap] + -- + cidinfo = properties.cidinfo + usedmap = cidinfo and fonts.cid.getmap(cidinfo) + -- if usedmap then - oparser = usedmap and makenameparser(cidinfo.ordering) + oparser = usedmap and makenameparser(cidinfo.ordering) cidnames = usedmap.names cidcodes = usedmap.unicodes end uparser = makenameparser() - local unicodevector = fonts.enc.agl.unicodes -- loaded runtime in context - for index, glyph in next, data.glyphs do - local name, unic = glyph.name, glyph.unicode or -1 -- play safe + 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] + local unicode = lumunic and lumunic[name] or unicodevector[name] if unicode then originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 end @@ -258,17 +266,19 @@ fonts.map.addtounicode = function(data,filename) end end end - if trace_unimapping then - for index, glyph in table.sortedhash(data.glyphs) do - local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe + 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_otf("internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) + report_fonts("internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) else - report_otf("internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) + report_fonts("internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) end end end if trace_loading and (ns > 0 or nl > 0) then - report_otf("enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) + report_fonts("enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) end end diff --git a/tex/context/base/font-mis.lua b/tex/context/base/font-mis.lua index 954135be1..979288913 100644 --- a/tex/context/base/font-mis.lua +++ b/tex/context/base/font-mis.lua @@ -1,6 +1,6 @@ if not modules then modules = { } end modules ['font-mis'] = { version = 1.001, - comment = "companion to luatex-fonts.tex", + comment = "companion to mtx-fonts", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" @@ -9,12 +9,23 @@ if not modules then modules = { } end modules ['font-mis'] = { local next = next local lower, strip = string.lower, string.strip -fonts.otf = fonts.otf or { } +-- also used in other scripts so we need to check some tables: -fonts.otf.version = fonts.otf.version or 2.710 -fonts.otf.cache = containers.define("fonts", "otf", fonts.otf.version, true) +fonts = fonts or { } -function fonts.otf.loadcached(filename,format,sub) +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.710 +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 @@ -23,9 +34,9 @@ function fonts.otf.loadcached(filename,format,sub) hash = hash .. "-" .. sub end hash = containers.cleanname(hash) - local data = containers.read(fonts.otf.cache, hash) + local data = containers.read(otf.cache, hash) if data and not data.verbose then - fonts.otf.enhancers.unpack(data) + otf.enhancers.unpack(data) return data else return nil @@ -34,14 +45,14 @@ end local featuregroups = { "gsub", "gpos" } -function fonts.get_features(name,t,script,language) +function fonts.helpers.getfeatures(name,t,script,language) -- maybe per font type local t = lower(t or (name and file.extname(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 = fonts.otf.loadcached(filename) - if data and data.luatex and data.luatex.features then - return data.luatex.features + 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 diff --git a/tex/context/base/font-ota.lua b/tex/context/base/font-ota.lua index f972d289f..8ca9d8e6a 100644 --- a/tex/context/base/font-ota.lua +++ b/tex/context/base/font-ota.lua @@ -13,50 +13,110 @@ local type, tostring, match, format, concat = type, tostring, string.match, stri if not trackers then trackers = { register = function() end } end local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end) -local trace_cjk = false trackers.register("cjk.injections", function(v) trace_cjk = v end) - -trackers.register("cjk.analyzing","otf.analyzing") local fonts, nodes = fonts, nodes local node = node -local otf = fonts.otf -local tfm = fonts.tfm +local otf = fonts.handlers.otf + +local analyzers = fonts.analyzers +local initializers = { } +local methods = { } + +analyzers.initializers = initializers +analyzers.methods = methods +analyzers.useunicodemarks = false -fonts.analyzers = fonts.analyzers or { } -local analyzers = fonts.analyzers +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph -analyzers.initializers = analyzers.initializers or { node = { otf = { } } } -analyzers.methods = analyzers.methods or { node = { otf = { } } } +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local traverse_id = node.traverse_id +local traverse_node_list = node.traverse -local initializers = analyzers.initializers -local methods = analyzers.methods +local fontdata = fonts.hashes.identifiers +local state = attributes.private('state') +local categories = characters and characters.categories or { } -- sorry, only in context -local nodecodes = nodes.nodecodes -local glyph_code = nodecodes.glyph +local tracers = nodes.tracers +local colortracers = tracers and tracers.colors +local setnodecolor = colortracers and colortracers.set or function() end +local resetnodecolor = colortracers and colortracers.reset or function() end -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute -local traverse_id = node.traverse_id -local traverse_node_list = node.traverse +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -local fontdata = fonts.identifiers -local state = attributes.private('state') -local categories = characters and characters.categories or { } -- sorry, only in context +--[[ldx-- +

Analyzers run per script and/or language and are needed in order to +process features right.

+--ldx]]-- -local fontscolors = fonts.colors -local fcs = (fontscolors and fontscolors.set) or function() end -local fcr = (fontscolors and fontscolors.reset) or function() end +-- todo: analyzers per script/lang, cross font, so we need an font id hash -> script +-- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace +-- an example analyzer (should move to font-ota.lua) + +local state = attributes.private('state') + +function analyzers.setstate(head,font) + local useunicodemarks = analyzers.useunicodemarks + local tfmdata = fontdata[font] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean + while current do + local id = current.id + if id == glyph_code and current.font == font then + local char = current.char + local d = descriptions[char] + if d then + if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then + done = true + set_attribute(current,state,5) -- mark + elseif n == 0 then + first, last, n = current, current, 1 + set_attribute(current,state,1) -- init + else + last, n = current, n+1 + set_attribute(current,state,2) -- medi + end + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + elseif id == disc_code then + -- always in the middle + set_attribute(current,state,2) -- midi + last = current + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + current = current.next + end + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + return head, done +end -- in the future we will use language/script attributes instead of the -- font related value, but then we also need dynamic features which is -- somewhat slower; and .. we need a chain of them -local scriptandlanguage = otf.scriptandlanguage - -function fonts.initializers.node.otf.analyze(tfmdata,value,attr) - local script, language = otf.scriptandlanguage(tfmdata,attr) +local function analyzeinitializer(tfmdata,value) -- attr + local script, language = otf.scriptandlanguage(tfmdata) -- attr local action = initializers[script] if action then if type(action) == "function" then @@ -68,10 +128,9 @@ function fonts.initializers.node.otf.analyze(tfmdata,value,attr) end end end - return nil end -function fonts.methods.node.otf.analyze(head,font,attr) +local function analyzeprocessor(head,font,attr) local tfmdata = fontdata[font] local script, language = otf.scriptandlanguage(tfmdata,attr) local action = methods[script] @@ -88,12 +147,22 @@ function fonts.methods.node.otf.analyze(head,font,attr) return head, false end -otf.features.register("analyze",true) -- we always analyze -table.insert(fonts.triggers,"analyze") -- we need a proper function for doing this +registerotffeature { + name = "analyze", + description = "analysis of (for instance) character classes", + default = true, + initializers = { + node = analyzeinitializer, + }, + processors = { + position = 1, + node = analyzeprocessor, + } +} -- latin -analyzers.methods.latn = analyzers.aux.setstate +methods.latn = analyzers.setstate -- this info eventually will go into char-def @@ -175,10 +244,10 @@ local function warning(current,what) end end -function analyzers.methods.nocolor(head,font,attr) +function methods.nocolor(head,font,attr) for n in traverse_id(glyph_code,head) do if not font or n.font == font then - fcr(n) + resetnodecolor(n) end end return head, true @@ -190,22 +259,22 @@ local function finish(first,last) local fc = first.char if isol_fina_medi_init[fc] or isol_fina[fc] then set_attribute(first,state,4) -- isol - if trace_analyzing then fcs(first,"font:isol") end + if trace_analyzing then setnodecolor(first,"font:isol") end else warning(first,"isol") set_attribute(first,state,0) -- error - if trace_analyzing then fcr(first) end + if trace_analyzing then resetnodecolor(first) end end else local lc = last.char if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? -- if laststate == 1 or laststate == 2 or laststate == 4 then set_attribute(last,state,3) -- fina - if trace_analyzing then fcs(last,"font:fina") end + if trace_analyzing then setnodecolor(last,"font:fina") end else warning(last,"fina") set_attribute(last,state,0) -- error - if trace_analyzing then fcr(last) end + if trace_analyzing then resetnodecolor(last) end end end first, last = nil, nil @@ -214,21 +283,21 @@ local function finish(first,last) local fc = first.char if isol_fina_medi_init[fc] or isol_fina[fc] then set_attribute(first,state,4) -- isol - if trace_analyzing then fcs(first,"font:isol") end + if trace_analyzing then setnodecolor(first,"font:isol") end else warning(first,"isol") set_attribute(first,state,0) -- error - if trace_analyzing then fcr(first) end + if trace_analyzing then resetnodecolor(first) end end first = nil end return first, last end -function analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace +function methods.arab(head,font,attr) -- maybe make a special version with no trace local useunicodemarks = analyzers.useunicodemarks local tfmdata = fontdata[font] - local marks = tfmdata.marks + local marks = tfmdata.resources.marks local first, last, current, done = nil, nil, head, false while current do if current.id == glyph_code and current.subtype<256 and current.font == font and not has_attribute(current,state) then @@ -236,20 +305,20 @@ function analyzers.methods.arab(head,font,attr) -- maybe make a special version local char = current.char if marks[char] or (useunicodemarks and categories[char] == "mn") then set_attribute(current,state,5) -- mark - if trace_analyzing then fcs(current,"font:mark") end + if trace_analyzing then setnodecolor(current,"font:mark") end elseif isol[char] then -- can be zwj or zwnj too first, last = finish(first,last) set_attribute(current,state,4) -- isol - if trace_analyzing then fcs(current,"font:isol") end + if trace_analyzing then setnodecolor(current,"font:isol") end first, last = nil, nil elseif not first then if isol_fina_medi_init[char] then set_attribute(current,state,1) -- init - if trace_analyzing then fcs(current,"font:init") end + if trace_analyzing then setnodecolor(current,"font:init") end first, last = first or current, current elseif isol_fina[char] then set_attribute(current,state,4) -- isol - if trace_analyzing then fcs(current,"font:isol") end + if trace_analyzing then setnodecolor(current,"font:isol") end first, last = nil, nil else -- no arab first, last = finish(first,last) @@ -257,18 +326,18 @@ function analyzers.methods.arab(head,font,attr) -- maybe make a special version elseif isol_fina_medi_init[char] then first, last = first or current, current set_attribute(current,state,2) -- medi - if trace_analyzing then fcs(current,"font:medi") end + if trace_analyzing then setnodecolor(current,"font:medi") end elseif isol_fina[char] then if not has_attribute(last,state,1) then -- tricky, we need to check what last may be ! set_attribute(last,state,2) -- medi - if trace_analyzing then fcs(last,"font:medi") end + if trace_analyzing then setnodecolor(last,"font:medi") end end set_attribute(current,state,3) -- fina - if trace_analyzing then fcs(current,"font:fina") end + if trace_analyzing then setnodecolor(current,"font:fina") end first, last = nil, nil elseif char >= 0x0600 and char <= 0x06FF then - if trace_analyzing then fcs(current,"font:rest") end + if trace_analyzing then setnodecolor(current,"font:rest") end first, last = finish(first,last) else --no first, last = finish(first,last) diff --git a/tex/context/base/font-otb.lua b/tex/context/base/font-otb.lua index 8ee39b8c5..d4019b31d 100644 --- a/tex/context/base/font-otb.lua +++ b/tex/context/base/font-otb.lua @@ -5,33 +5,31 @@ if not modules then modules = { } end modules ['font-otb'] = { 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_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 fonts = fonts -local otf = fonts.otf -local tfm = fonts.tfm +local report_prepare = logs.reporter("fonts","otf prepare") -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_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 fonts = fonts +local otf = fonts.handlers.otf -local report_prepare = logs.reporter("fonts","otf prepare") +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register local wildcard = "*" local default = "dflt" -local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway - -local pcache, fcache = { }, { } -- could be weak - local function gref(descriptions,n) if type(n) == "number" then local name = descriptions[n].name @@ -42,22 +40,10 @@ local function gref(descriptions,n) end elseif n then local num, nam = { }, { } - for i=1,#n do + for i=2,#n do -- first is likely a key local ni = n[i] - -- ! ! ! could be a helper ! ! ! - if type(ni) == "table" then - local nnum, nnam = { }, { } - for j=1,#ni do - local nj = ni[j] - nnum[j] = format("U+%04X",nj) - nnam[j] = descriptions[nj].name or "?" - end - num[i] = concat(nnum,"|") - nam[i] = concat(nnam,"|") - else - num[i] = format("U+%04X",ni) - nam[i] = descriptions[ni].name or "?" - end + num[i] = format("U+%04X",ni) + nam[i] = descriptions[ni].name or "?" end return format("%s (%s)",concat(num," "), concat(nam," ")) else @@ -65,324 +51,531 @@ local function gref(descriptions,n) end end -local function cref(kind,lookupname) +local function cref(feature,lookupname) if lookupname then - return format("feature %s, lookup %s",kind,lookupname) + return format("feature %s, lookup %s",feature,lookupname) else - return format("feature %s",kind) + return format("feature %s",feature) end end -local function resolve_ligatures(tfmdata,ligatures,kind) - kind = kind or "unknown" - local unicodes = tfmdata.unicodes - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local changed = tfmdata.changed - local done = { } - while true do - local ok = false - for k,v in next, ligatures do - local lig = v[1] - if not done[lig] then - local ligs = lpegmatch(split_at_space,lig) - if #ligs == 2 then - local uc = v[2] - local c, f, s = characters[uc], ligs[1], ligs[2] - local uft, ust = unicodes[f] or 0, unicodes[s] or 0 - if not uft or not ust then - report_prepare("%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) - -- some kind of error - else - if type(uft) == "number" then uft = { uft } end - if type(ust) == "number" then ust = { ust } end - for ufi=1,#uft do - local uf = uft[ufi] - for usi=1,#ust do - local us = ust[usi] - if changed[uf] or changed[us] then - if trace_baseinit and trace_ligatures then - report_prepare("%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) - end - else - local first, second = characters[uf], us - if first and second then - local t = first.ligatures - if not t then - t = { } - first.ligatures = t - end - if type(uc) == "number" then - t[second] = { type = 0, char = uc } - else - t[second] = { type = 0, char = uc[1] } -- can this still happen? - end - if trace_baseinit and trace_ligatures then - report_prepare("%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) - end - end +local basemethods = { } +local basemethod = "" + +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: '%s', featureset '%s'",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 + print("BUILDING",concat(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 + print(" DEFINING",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 + print("CODES",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 - ok, done[lig] = true, descriptions[uc].name + if okay then + ligatures[i] = false + done = done + 1 + end end end + alldone = done == 0 end - if ok then - -- done has "a b c" = "a_b_c" and ligatures the already set ligatures: "a b" = 123 - -- and here we add extras (f i i = fi + i and alike) - -- - -- we could use a hash for fnc and pattern - -- - -- this might be interfering ! - for d,n in next, done do - local pattern = pcache[d] if not pattern then pattern = "^(" .. d .. ") " pcache[d] = pattern end - local fnc = fcache[n] if not fnc then fnc = function() return n .. " " end fcache[n] = fnc end - for k,v in next, ligatures do - v[1] = gsub(v[1],pattern,fnc) - end + if trace then + for k, v in next, characters do + if v.ligatures then table.print(v,k) end end - else - break end + tfmdata.resources.private = private end end -local splitter = lpeg.splitat(" ") - -local function prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features - if value then - local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local ligatures = { } - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local changed = tfmdata.changed - -- - local actions = { - substitution = function(p,lookup,k,glyph,unicode) - local pv = p[2] -- p.variant - if pv then - local upv = unicodes[pv] - if upv then - if type(upv) == "table" then -- zero change that table - upv = upv[1] - end - if characters[upv] then - if trace_baseinit and trace_singles then - report_prepare("%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) - end - changed[k] = upv - end - end - end - end, - alternate = function(p,lookup,k,glyph,unicode) - local pc = p[2] -- p.components - if pc then - -- a bit optimized ugliness - if value == 1 then - pc = lpegmatch(splitter,pc) - elseif value == 2 then - local a, b = lpegmatch(splitter,pc) - pc = b or a - else - pc = { lpegmatch(splitter,pc) } - pc = pc[value] or pc[#pc] - end - if pc then - local upc = unicodes[pc] - if upc then - if type(upc) == "table" then -- zero change that table - upc = upc[1] - end - if characters[upc] then - if trace_baseinit and trace_alternatives then - report_prepare("%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) - end - changed[k] = upc - end - end - end - end - end, - ligature = function(p,lookup,k,glyph,unicode) - local pc = p[2] - if pc then - if trace_baseinit and trace_ligatures then - local upc = { lpegmatch(splitter,pc) } - for i=1,#upc do upc[i] = unicodes[upc[i]] end - -- we assume that it's no table - report_prepare("%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) - end - ligatures[#ligatures+1] = { pc, k } - end - end, - } - -- - for k,c in next, characters do - local glyph = descriptions[k] - local lookups = glyph.slookups - if lookups then - for l=1,#lookuplist do - local lookup = lookuplist[l] - local p = lookups[lookup] - if p then - local a = actions[p[1]] - if a then - a(p,lookup,k,glyph,unicode) - 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 actions = { + substitution = function(lookupdata,lookupname,description,unicode) + if trace_baseinit and trace_singles then + report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), + gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = lookupdata + end, + alternate = function(lookupdata,lookupname,description,unicode) + local replacement = lookupdata[value] or lookupdata[#lookupdata] + if trace_baseinit and trace_alternatives then + report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = replacement + end, + ligature = function(lookupdata,lookupname,description,unicode) + if trace_baseinit and trace_alternatives then + report_prepare("%s: base ligature %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,lookupdata),gref(descriptions,unicode)) + 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 - local lookups = glyph.mlookups - if lookups then - for l=1,#lookuplist do - local lookup = lookuplist[l] - local ps = lookups[lookup] - if ps then - for i=1,#ps do - local p = ps[i] - local a = actions[p[1]] - if a then - a(p,lookup,k,glyph,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 - resolve_ligatures(tfmdata,ligatures,kind) end - else - tfmdata.ligatures = tfmdata.ligatures or { } -- left over from what ? end + + finalize_ligatures(tfmdata,ligatures) end -local function preparebasekerns(tfmdata,kind,value) -- todo what kind of kerns, currently all - if value then - local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local sharedkerns = { } - for u, chr in next, characters do - local d = descriptions[u] - if d then - local dk = d.kerns -- shared - if dk then - local s = sharedkerns[dk] - if s == false then - -- skip - elseif s then - chr.kerns = s - else - local t, done = chr.kerns or { }, false - for l=1,#lookuplist do - local lookup = lookuplist[l] - local kerns = dk[lookup] - if kerns then - for k, v in next, kerns do - if v ~= 0 and not t[k] then -- maybe no 0 test here - t[k], done = v, true - if trace_baseinit and trace_kerns then - report_prepare("%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) - end - end - 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_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,otherunicode),value) + end + elseif not newkerns[otherunicode] then -- first wins + newkerns[otherunicode] = value + done = true + if traceindeed then + report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,otherunicode),value) end - end - if done then - sharedkerns[dk] = t - chr.kerns = t -- no empty assignments - else - sharedkerns[dk] = false 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 --- In principle we could register each feature individually which was --- what we did in earlier versions. However, after the rewrite it --- made more sense to collect them in an overall features initializer --- just as with the node variant. There it was needed because we need --- to do complete mixed runs and not run featurewise (as we did before). - -local supported_gsub = { - 'liga', 'dlig', 'rlig', 'hlig', - 'pnum', 'onum', 'tnum', 'lnum', - 'zero', - 'smcp', 'cpsp', 'c2sc', 'ornm', 'aalt', - 'hwid', 'fwid', - 'ssty', 'rtlm', -- math --- 'tlig', 'trep', +basemethods.independent = { + preparesubstitutions = preparesubstitutions, + preparepositionings = preparepositionings, } -local supported_gpos = { - 'kern' -} +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 %s: 0x%04X (%s), preceding 0x%04X (%s)",lookupname,v,utfchar(v),preceding,utfchar(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 = { } + + 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_baseinit and trace_singles then + report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), + gref(descriptions,unicode),gref(descriptions,data)) + end + changed[unicode] = data + elseif lookuptype == "alternate" then + local replacement = data[value] or data[#data] + if trace_baseinit and trace_alternatives then + report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = replacement + elseif lookuptype == "ligature" then + ligatures[#ligatures+1] = { unicode, data, lookupname } + 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 + + if done then + for lookupname, list in next, done do + report_prepare("%s: base ligatures %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,done)) + end + end + + end -function otf.features.registerbasesubstitution(tag) - supported_gsub[#supported_gsub+1] = tag end -function otf.features.registerbasekern(tag) - supported_gsub[#supported_gpos+1] = tag + +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_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,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 -local basehash, basehashes = { }, 1 +basemethods.shared = { + initializehashes = initializehashes, + preparesubstitutions = preparesubstitutions, + preparepositionings = preparepositionings, +} + +basemethod = "independent" -function fonts.initializers.base.otf.features(tfmdata,value) +local function featuresinitializer(tfmdata,value) if true then -- value then - -- not shared local t = trace_preparing and os.clock() local features = tfmdata.shared.features if features then - local h = { } - for f=1,#supported_gsub do - local feature = supported_gsub[f] - local value = features[feature] - prepare_base_substitutions(tfmdata,feature,value) - if value then - h[#h+1] = feature .. "=" .. tostring(value) + 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 - for f=1,#supported_gpos do - local feature = supported_gpos[f] - local value = features[feature] - preparebasekerns(tfmdata,feature,features[feature]) - if value then - h[#h+1] = feature .. "=" .. tostring(value) + if basepositions then + for feature, data in next, basepositions 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 - local hash = concat(h," ") - local base = basehash[hash] - if not base then - basehashes = basehashes + 1 - base = basehashes - basehash[hash] = base - 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. - tfmdata.fullname = tfmdata.fullname .. "-" .. base -- tfmdata.psname is the original - --~ report_prepare("fullname base hash: '%s', featureset '%s'",tfmdata.fullname,hash) + registerbasehash(tfmdata) end if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.properties.fullname or "?") end end end + +registerotffeature { + name = "features", + description = "features", + default = true, + initializers = { + position = 1, + base = featuresinitializer, + } +} + +-- independent : collect lookups independently (takes more runtime ... neglectable) +-- shared : shares lookups with node mode (takes more memory ... 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 93b942f9a..50433e3cc 100644 --- a/tex/context/base/font-otc.lua +++ b/tex/context/base/font-otc.lua @@ -8,21 +8,17 @@ if not modules then modules = { } end modules ['font-otc'] = { 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 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.otf - -local report_otf = logs.reporter("fonts","otf loading") - --- instead of "script = "DFLT", langs = { 'dflt' }" we now use wildcards (we used to --- have always); some day we can write a "force always when true" trick for other --- features as well --- --- we could have a tnum variant as well +local fonts = fonts +local otf = fonts.handlers.otf +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -- In the userdata interface we can not longer tweak the loaded font as -- conveniently as before. For instance, instead of pushing extra data in @@ -115,76 +111,81 @@ local extra_features = { -- maybe just 1..n so that we prescribe order } local function enhancedata(data,filename,raw) - local luatex = data.luatex - local lookups = luatex.lookups - local sequences = luatex.sequences - local glyphs = data.glyphs - local indices = luatex.indices - local gsubfeatures = luatex.features.gsub - for kind, specifications in next, extra_features do - if gsub and gsub[kind] then + local descriptions = data.descriptions + local resources = data.resources + local lookups = resources.lookups + local gsubfeatures = resources.features.gsub + local sequences = resources.sequences + local fontfeatures = resources.features + local unicodes = resources.unicodes + local lookuptypes = resources.lookuptypes + local splitter = lpeg.splitter(" ",unicodes) + for feature, specifications in next, extra_features do + if gsub and gsub[feature] then -- already present else local done = 0 for s=1,#specifications do - local added = false local specification = specifications[s] - local features, subtables = specification.features, specification.subtables - local name, type, flags = specification.name, specification.type, specification.flags - local full = subtables[1] - local list = extra_lists[kind][s] - if type == "gsub_ligature" then - -- inefficient loop - for unicode, index in next, indices do - local glyph = glyphs[index] - local ligature = list[glyph.name] - if ligature then - if glyph.slookups then - glyph.slookups [full] = { "ligature", ligature, glyph.name } + local askedfeatures = specification.features + local subtables = specification.subtables + local featurename = specification.name + local featuretype = specification.type + local featureflags = specification.flags + local full = subtables[1] + local list = extra_lists[feature][s] + local added = false + if featuretype == "gsub_ligature" then + lookuptypes[full] = "ligature" + for name, ligature in next, list do + local unicode = unicodes[name] + local description = descriptions[unicode] + if description then + local slookups = description.slookups + if slookups then + slookups[full] = { lpegmatch(splitter,ligature) } else - glyph.slookups = { [full] = { "ligature", ligature, glyph.name } } + description.slookups = { [full] = { lpegmatch(splitter,ligature) } } end - done, added = done+1, true + done, added = done + 1, true end end - elseif type == "gsub_single" then - -- inefficient loop - for unicode, index in next, indices do - local glyph = glyphs[index] - local r = list[unicode] - if r then - local replacement = indices[r] - if replacement and glyphs[replacement] then - if glyph.slookups then - glyph.slookups [full] = { "substitution", glyphs[replacement].name } - else - glyph.slookups = { [full] = { "substitution", glyphs[replacement].name } } - end - done, added = done+1, true + elseif featuretype == "gsub_single" then + lookuptypes[full] = "substitution" + for name, replacement in next, list do + local unicode = unicodes[name] + local description = descriptions[unicode] + if description then + local slookups = description.slookups + if slookups then + slookups[full] = unicodes[replacement] + else + description.slookups = { [full] = unicodes[replacement] } end + done, added = done + 1, true end end end if added then sequences[#sequences+1] = { chain = 0, - features = { [kind] = features }, - flags = flags, - name = name, + features = { [feature] = askedfeatures }, + flags = featureflags, + name = featurename, subtables = subtables, - type = type, + type = featuretype, } -- register in metadata (merge as there can be a few) if not gsubfeatures then - gsubfeatures = { } - luatex.features.gsub = gsubfeatures + gsubfeatures = { } + fontfeatures.gsub = gsubfeatures end - local k = gsubfeatures[kind] + local k = gsubfeatures[feature] if not k then k = { } - gsubfeatures[kind] = k + gsubfeatures[feature] = k end - for script, languages in next, features do + for script, languages in next, askedfeatures do local kk = k[script] if not kk then kk = { } @@ -196,10 +197,8 @@ local function enhancedata(data,filename,raw) end end end - if done > 0 then - if trace_loading then - report_otf("enhance: registering %s feature (%s glyphs affected)",kind,done) - end + if done > 0 and trace_loading then + report_otf("enhance: registering %s feature (%s glyphs affected)",feature,done) end end end @@ -207,30 +206,18 @@ end otf.enhancers.register("check extra features",enhancedata) -local features = otf.tables.features - -features['tlig'] = 'TeX Ligatures' -features['trep'] = 'TeX Replacements' -features['anum'] = 'Arabic Digits' - -local registerbasesubstitution = otf.features.registerbasesubstitution - -registerbasesubstitution('tlig') -registerbasesubstitution('trep') -registerbasesubstitution('anum') - --- the functionality is defined elsewhere - -local initializers = fonts.initializers -local common_initializers = initializers.common -local base_initializers = initializers.base.otf -local node_initializers = initializers.node.otf +registerotffeature { + name = 'tlig', + description = 'tex ligatures', +} -base_initializers.equaldigits = common_initializers.equaldigits -node_initializers.equaldigits = common_initializers.equaldigits +registerotffeature { + name = 'trep', + description = 'tex replacements', +} -base_initializers.lineheight = common_initializers.lineheight -node_initializers.lineheight = common_initializers.lineheight +registerotffeature { + name = 'anum', + description = 'arabic digits', +} -base_initializers.compose = common_initializers.compose -node_initializers.compose = common_initializers.compose diff --git a/tex/context/base/font-otd.lua b/tex/context/base/font-otd.lua index 475481643..59c050e10 100644 --- a/tex/context/base/font-otd.lua +++ b/tex/context/base/font-otd.lua @@ -6,32 +6,49 @@ if not modules then modules = { } end modules ['font-otd'] = { license = "see context related readme files" } -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local match = string.match -local report_otf = logs.reporter("fonts","otf loading") +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 fonts = fonts -local otf = fonts.otf -local fontdata = fonts.identifiers +local report_otf = logs.reporter("fonts","otf loading") +local report_process = logs.reporter("fonts","otf process") -otf.features = otf.features or { } -otf.features.default = otf.features.default or { } +local fonts = fonts +local otf = fonts.handlers.otf +local fontdata = fonts.hashes.identifiers +local definers = fonts.definers +local constructors = fonts.constructors +local specifiers = fonts.specifiers -local definers = fonts.definers -local contextsetups = definers.specifiers.contextsetups -local contextnumbers = definers.specifiers.contextnumbers +local contextsetups = specifiers.contextsetups +local contextnumbers = specifiers.contextnumbers +local contextmerged = specifiers.contextmerged --- todo: dynamics namespace +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -local a_to_script = { } -local a_to_language = { } +local fontdynamics = { } +fonts.hashes.dynamics = fontdynamics -function otf.setdynamics(font,dynamics,attribute) +local a_to_script = { } +local a_to_language = { } + +setmetatable(fontdynamics, { __index = + function(t,font) + local d = fontdata[font].shared.dynamics or false + t[font] = d + return d + end +}) + +function otf.setdynamics(font,attribute) local features = contextsetups[contextnumbers[attribute]] -- can be moved to caller if features then + local dynamics = fontdynamics[font] local script = features.script or 'dflt' local language = features.language or 'dflt' - local ds = dynamics[script] + local ds = dynamics[script] -- can be metatable magic (less testing) if not ds then ds = { } dynamics[script] = ds @@ -42,50 +59,160 @@ function otf.setdynamics(font,dynamics,attribute) ds[language] = dsl end local dsla = dsl[attribute] - if dsla then - -- if trace_dynamics then - -- report_otf("using dynamics %s: attribute %s, script %s, language %s",contextnumbers[attribute],attribute,script,language) - -- end - return dsla - else + if not dsla then local tfmdata = fontdata[font] a_to_script [attribute] = script a_to_language[attribute] = language -- we need to save some values - local saved = { - script = tfmdata.script, - language = tfmdata.language, - mode = tfmdata.mode, - features = tfmdata.shared.features - } - tfmdata.mode = "node" - tfmdata.dynamics = true -- handy for tracing - tfmdata.language = language - tfmdata.script = script - tfmdata.shared.features = { } + 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 = definers.check(features,otf.features.default) + local set = constructors.checkedfeatures("otf",features) dsla = otf.setfeatures(tfmdata,set) if trace_dynamics then report_otf("setting dynamics %s: attribute %s, script %s, language %s, set: %s",contextnumbers[attribute],attribute,script,language,table.sequenced(set)) end -- we need to restore some values - tfmdata.script = saved.script - tfmdata.language = saved.language - tfmdata.mode = saved.mode - tfmdata.shared.features = saved.features + 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 - return dsla + elseif trace_dynamics then + -- report_otf("using dynamics %s: attribute %s, script %s, language %s",contextnumbers[attribute],attribute,script,language) end + return dsla end - return nil -- { } end function otf.scriptandlanguage(tfmdata,attr) + local properties = tfmdata.properties if attr and attr > 0 then - return a_to_script[attr] or tfmdata.script, a_to_language[attr] or tfmdata.language + return a_to_script[attr] or properties.script or "dflt", a_to_language[attr] or properties.language or "dflt" else - return tfmdata.script, tfmdata.language + return properties.script or "dflt", properties.language or "dflt" end end + +-- we reimplement the dataset resolver + +local special_attributes = { + init = 1, + medi = 2, + fina = 3, + isol = 4 +} + +local resolved = { } -- we only resolve a font,script,language,attribute pair once +local wildcard = "*" +local default = "dflt" + +local function initialize(sequence,script,language,s_enabled,a_enabled,attr,dynamic) + local features = sequence.features + if features then + for kind, scripts in next, features do + local s_e = s_enabled and s_enabled[kind] + local a_e = a_enabled and a_enabled[kind] + local e_e = s_e or a_e + if e_e then + local languages = scripts[script] or scripts[wildcard] + if languages then + local valid, what = 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 = true + what = language + -- elseif languages[default] then + -- valid = true + -- what = default + elseif languages[wildcard] then + valid = true + what = wildcard + end + if valid then + local attribute = special_attributes[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, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", + (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) + end + return { valid, attribute, sequence.chain or 0, kind } + end + end + end + end + return false -- { valid, attribute, chain, "generic" } -- false anyway, could be flag instead of table + else + return false -- { false, false, chain } -- indirect lookup, part of chain (todo: make this a separate table) + end +end + +function otf.dataset(tfmdata,sequences,font,attr) + + local shared = tfmdata.shared + local properties = tfmdata.properties + + local script, language, s_enabled, a_enabled, dynamic + + if attr and attr ~= 0 then + local features = contextsetups[contextnumbers[attr]] -- could be a direct list + language = features.language or "dflt" + script = features.script or "dflt" + a_enabled = features + dynamic = contextmerged[attr] or 0 + if dynamic == 2 or dynamic == -2 then + -- font based + s_enabled = shared.features + end + else + language = properties.language or "dflt" + script = properties.script or "dflt" + s_enabled = 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 = { } + rl[attr] = ra + setmetatable(ra, { __index = function(t,k) + local v = initialize(sequences[k],script,language,s_enabled,a_enabled,attr,dynamic) + t[k] = v + return v + end}) + end + + return ra + +end diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index c974d89c8..d5de04f90 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -10,6 +10,7 @@ if not modules then modules = { } end modules ['font-otf'] = { -- anchor_classes vs kernclasses -- modification/creationtime in subfont is runtime dus zinloos -- to_table -> totable +-- ascent descent local utf = unicode.utf8 @@ -19,93 +20,71 @@ local type, next, tonumber, tostring = type, next, tonumber, tostring local abs = math.abs local getn = table.getn local lpegmatch = lpeg.match -local reversed, concat = table.reversed, table.concat +local reversed, concat, remove = table.reversed, table.concat, table.remove local ioflush = io.flush - -local allocate = utilities.storage.allocate - -local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) -local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) -local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) -local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) - -local report_otf = logs.reporter("fonts","otf loading") - -local starttiming, stoptiming, elapsedtime = statistics.starttiming, statistics.stoptiming, statistics.elapsedtime - -local findbinfile = resolvers.findbinfile - -local fonts = fonts - -fonts.otf = fonts.otf or { } -local otf = fonts.otf -local tfm = fonts.tfm - -local fontdata = fonts.identifiers -local chardata = characters and characters.data -- not used - --- todo: probably first time so local first - -otf.features = otf.features or { } -local features = otf.features -features.list = features.list or { } -local featurelist = features.list -features.default = features.default or { } -local defaultfeatures = features.default - -otf.enhancers = allocate() -local enhancers = otf.enhancers -enhancers.patches = { } -local patches = enhancers.patches - -local definers = fonts.definers -local readers = fonts.tfm.readers - -otf.glists = { "gsub", "gpos" } - -otf.version = 2.710 -- beware: also sync font-mis.lua -otf.cache = containers.define("fonts", "otf", otf.version, true) - -local loadmethod = "table" -- table, mixed, sparse -local forceload = false -local cleanup = 0 -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 wildcard = "*" -local default = "dflt" - -local fontloaderfields = fontloader.fields -local mainfields = nil -local glyphfields = nil -- not used yet - -directives.register("fonts.otf.loader.method", function(v) - if v == "sparse" and fontloaderfields then - loadmethod = "sparse" - elseif v == "mixed" then - loadmethod = "mixed" - elseif v == "table" then - loadmethod = "table" - else - loadmethod = "table" - report_otf("no loader method '%s', using '%s' instead",v,loadmethod) - end -end) - -directives.register("fonts.otf.loader.cleanup",function(v) - cleanup = tonumber(v) or (v and 1) or 0 -end) - -directives.register("fonts.otf.loader.force", function(v) forceload = v end) -directives.register("fonts.otf.loader.usemetatables", function(v) usemetatables = v end) -directives.register("fonts.otf.loader.pack", function(v) packdata = v end) -directives.register("fonts.otf.loader.syncspace", function(v) syncspace = v end) -directives.register("fonts.otf.loader.forcenotdef", function(v) forcenotdef = v end) +local fastcopy = table.fastcopy + +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.710 -- 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 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 @@ -116,19 +95,19 @@ local function load_featurefile(raw,featurefile) end end -local function showfeatureorder(otfdata,filename) - local sequences = otfdata.luatex.sequences +local function showfeatureorder(rawdata,filename) + local sequences = rawdata.resources.sequences if sequences and #sequences > 0 then if trace_loading then report_otf("font %s 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 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 + local features = sequence.features if trace_loading then report_otf("%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) end @@ -160,25 +139,6 @@ end

We start with a lot of tables and related functions.

--ldx]]-- -local global_fields = table.tohash { - "metadata", - "lookups", - "glyphs", - "subfonts", - "luatex", - "pfminfo", - "cidinfo", - "tables", - "names", - "unicodes", - "names", - -- "math", - "anchor_classes", - "kern_classes", - "gpos", - "gsub" -} - local valid_fields = table.tohash { -- "anchor_classes", "ascent", @@ -194,32 +154,32 @@ local valid_fields = table.tohash { "extrema_bound", "familyname", "fontname", + "fontname", "fontstyle_id", "fontstyle_name", "fullname", -- "glyphs", "hasvmetrics", - "head_optimized_for_cleartype", + -- "head_optimized_for_cleartype", "horiz_base", "issans", "isserif", "italicangle", -- "kerns", -- "lookups", - -- "luatex", "macstyle", -- "modificationtime", "onlybitmaps", "origname", "os2_version", - -- "pfminfo", + "pfminfo", -- "private", "serifcheck", "sfd_version", -- "size", "strokedfont", "strokewidth", - "subfonts", + -- "subfonts", "table_version", -- "tables", -- "ttf_tab_saved", @@ -231,7 +191,6 @@ local valid_fields = table.tohash { "use_typo_metrics", "uwidth", -- "validation_state", - "verbose", "version", "vert_base", "weight", @@ -258,43 +217,47 @@ local ordered_enhancers = { "reorganize glyph lookups", "reorganize glyph anchors", + "merge kern classes", + "reorganize features", "reorganize subtables", "check glyphs", "check metadata", - "check math parameters", "check extra features", -- after metadata + + "cleanup tables", } --[[ldx--

Here we go.

--ldx]]-- -local actions = { } - -patches.before = allocate() -patches.after = allocate() +local actions = allocate() +local before = allocate() +local after = allocate() -local before = patches.before -local after = patches.after +patches.before = before +patches.after = after -local function enhance(name,data,filename,raw,verbose) +local function enhance(name,data,filename,raw) local enhancer = actions[name] if enhancer then - if verbose then + if trace_loading then report_otf("enhance: %s (%s)",name,filename) ioflush() end enhancer(data,filename,raw) - else - report_otf("enhance: %s is undefined",name) + elseif trace_loading then + -- report_otf("enhance: %s is undefined",name) end end -function enhancers.apply(data,filename,raw,verbose) +function enhancers.apply(data,filename,raw) local basename = file.basename(lower(filename)) - report_otf("start enhancing: %s",filename) + if trace_loading then + report_otf("start enhancing: %s",filename) + end ioflush() -- we want instant messages for e=1,#ordered_enhancers do local enhancer = ordered_enhancers[e] @@ -306,7 +269,7 @@ function enhancers.apply(data,filename,raw,verbose) end end end - enhance(enhancer,data,filename,raw,verbose) + enhance(enhancer,data,filename,raw) local a = after[enhancer] if a then for pattern, action in next, a do @@ -317,7 +280,9 @@ function enhancers.apply(data,filename,raw,verbose) end ioflush() -- we want instant messages end - report_otf("stop enhancing") + if trace_loading then + report_otf("stop enhancing") + end ioflush() -- we want instant messages end @@ -345,11 +310,14 @@ end function otf.load(filename,format,sub,featurefile) local name = file.basename(file.removesuffix(filename)) local attr = lfs.attributes(filename) - local size, time = attr and attr.size or 0, attr and attr.modification or 0 + 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 + if sub == "" then + sub = false + end local hash = name if sub then hash = hash .. "-" .. sub @@ -366,8 +334,8 @@ function otf.load(filename,format,sub,featurefile) local attr = lfs.attributes(name) featurefiles[#featurefiles+1] = { name = name, - size = attr.size or 0, - time = attr.modification or 0, + size = size, + time = time, } end end @@ -376,7 +344,7 @@ function otf.load(filename,format,sub,featurefile) end end local data = containers.read(otf.cache,hash) - local reload = not data or data.verbose ~= fonts.verbose or data.size ~= size or data.time ~= time + local reload = not data or data.size ~= size or data.time ~= time if forceload then report_otf("loading: forced reload due to hard coded flag") reload = true @@ -404,7 +372,7 @@ function otf.load(filename,format,sub,featurefile) end if reload then report_otf("loading: %s (hash: %s)",filename,hash) - local fontdata, messages, rawdata + local fontdata, messages if sub then fontdata, messages = fontloader.open(filename,sub) else @@ -430,51 +398,66 @@ function otf.load(filename,format,sub,featurefile) load_featurefile(fontdata,featurefiles[i].name) end end - report_otf("loading method: %s",loadmethod) - if loadmethod == "sparse" then - rawdata = fontdata - else - rawdata = fontloader.to_table(fontdata) - fontloader.close(fontdata) - end - if rawdata then - data = { } - starttiming(data) - local verboseindeed = verbose ~= nil and verbose or trace_loading - report_otf("file size: %s", size) - enhancers.apply(data,filename,rawdata,verboseindeed) - if packdata and not fonts.verbose then - enhance("pack",data,filename,nil,verboseindeed) - end - data.size = size - data.time = time - data.format = format - if featurefiles then - data.featuredata = featurefiles - end - data.verbose = fonts.verbose - report_otf("saving in cache: %s",filename) - data = containers.write(otf.cache, hash, data) + 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 = { + -- unicodes to names + }, + 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) + if packdata then if cleanup > 0 then collectgarbage("collect") end - stoptiming(data) - if elapsedtime then -- not in generic - report_otf("preprocessing and caching took %s seconds",elapsedtime(data)) - end - data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one - if cleanup > 1 then - collectgarbage("collect") - end - else - data = nil - report_otf("loading failed (table conversion error)") + enhance("pack",data,filename,nil) end - if loadmethod == "sparse" then - fontloader.close(fontdata) - if cleanup > 2 then - -- collectgarbage("collect") - end + report_otf("saving in cache: %s",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 took %s seconds",elapsedtime(data)) + 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 @@ -510,35 +493,42 @@ local mt = { end } +actions["prepare tables"] = function(data,filename,raw) + data.properties.italic_correction = 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 luatex = data.luatex - local defaultwidth = luatex.defaultwidth or 0 - local defaultheight = luatex.defaultheight or 0 - local defaultdepth = luatex.defaultdepth or 0 + 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 if usemetatables then - for _, d in next, data.glyphs do + for _, d in next, descriptions do local wd = d.width if not wd then d.width = defaultwidth - elseif wd ~= 0 and d.class == "mark" then - d.width = -wd + elseif trace_markwidth and wd ~= 0 and d.class == "mark" then + report_otf("mark with width %s (%s) in %s",wd,d.name or "",file.basename(filename)) + -- d.width = -wd end setmetatable(d,mt) end else - for _, d in next, data.glyphs do + for _, d in next, descriptions do local bb, wd = d.boundingbox, d.width if not wd then d.width = defaultwidth - elseif wd ~= 0 and d.class == "mark" then - d.width = -wd - end - if forcenotdef and not d.name then - d.name = ".notdef" + elseif trace_markwidth and wd ~= 0 and d.class == "mark" then + report_otf("mark with width %s (%s) in %s",wd,d.name or "",file.basename(filename)) + -- 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 @@ -557,16 +547,6 @@ actions["add dimensions"] = function(data,filename) end end -actions["prepare tables"] = function(data,filename,raw) - local luatex = { - filename = resolvers.unresolve(filename), -- no shortcut - version = otf.version, - creator = "context mkiv", - } - data.luatex = luatex - data.metadata = { } -end - local function somecopy(old) -- fast one if old then local new = { } @@ -603,192 +583,220 @@ end -- table cronstruction can save some mem actions["prepare glyphs"] = function(data,filename,raw) - -- we can also move the names to data.luatex.names which might - -- save us some more memory (at the cost of harder tracing) - local rawglyphs = raw.glyphs - local glyphs, udglyphs - if loadmethod == "sparse" then - glyphs, udglyphs = { }, { } - elseif loadmethod == "mixed" then - glyphs, udglyphs = { }, rawglyphs - else - glyphs, udglyphs = rawglyphs, rawglyphs - end - data.glyphs, data.udglyphs = glyphs, udglyphs - local subfonts = raw.subfonts - if subfonts then - if data.glyphs and next(data.glyphs) then - report_otf("replacing existing glyph table due to subfonts") - end - local cidinfo = raw.cidinfo - if cidinfo.registry then - local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) + 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 + + if rawsubfonts then + + metadata.subfonts = { } + properties.cidinfo = rawcidinfo + + if rawcidinfo.registry then + local cidmap = fonts.cid.getmap(rawcidinfo) if cidmap then - cidinfo.usedname = cidmap.usedname - local uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, 0, 0 - local unicodes, names = cidmap.unicodes, cidmap.names - for cidindex=1,#subfonts do - local subfont = subfonts[cidindex] - if loadmethod == "sparse" then - local rawglyphs = subfont.glyphs - for index=0,subfont.glyphmax - 1 do - local g = rawglyphs[index] - if g then - local unicode, name = unicodes[index], names[index] - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - elseif name then - nofnames = nofnames + 1 - end - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name or name or "unknown", -- uniXXXX - cidindex = cidindex, - unicode = unicode, - } + 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 + metadata.subfonts[cidindex] = somecopy(subfont) + for index=0,subfont.glyphmax - 1 do + 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 - end - -- If we had more userdata, we would need more of this - -- and it would start working against us in terms of - -- convenience and speed. - subfont = somecopy(subfont) - subfont.glyphs = nil - subfont[cidindex] = subfont - elseif loadmethod == "mixed" then - for index, g in next, subfont.glyphs do - local unicode, name = unicodes[index], names[index] - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - elseif name then + 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("enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",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 - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name or name or "unknown", -- uniXXXX + indices[unicode] = index -- each index in unique (at least now) + + local description = { + -- width = glyph.width, + boundingbox = glyph.boundingbox, + name = glyph.name or name or "unknown", -- uniXXXX cidindex = cidindex, - unicode = unicode, + index = index, + glyph = glyph, } + + descriptions[unicode] = description end - subfont.glyphs = nil - else - for index, g in next, subfont.glyphs do - local unicode, name = unicodes[index], names[index] - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - g.unicode = unicode - elseif name then - nofnames = nofnames + 1 - end - g.cidindex = cidindex - glyphs[index] = g - end - subfont.glyphs = nil end end if trace_loading then report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) end - data.map = data.map or { } - data.map.map = uni_to_int - data.map.backmap = int_to_uni elseif trace_loading then report_otf("unable to remap cid font, missing cid file for %s",filename) end - data.subfonts = subfonts elseif trace_loading then report_otf("font %s has no glyphs",filename) end + else - if loadmethod == "sparse" then - -- we get fields from the userdata glyph table and create - -- a minimal entry first - for index=0,raw.glyphmax - 1 do - local g = rawglyphs[index] - if g then - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name, - unicode = g.unicode, - } + + for index=0,raw.glyphmax-1 do + 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("enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) + end + private = private + 1 + else + unicodes[name] = unicode end - end - elseif loadmethod == "mixed" then - -- we get fields from the totable glyph table and copy to the - -- final glyph table so first we create a minimal entry - for index, g in next, rawglyphs do - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name, - unicode = g.unicode, + indices[unicode] = index + if not name then + name = format("u%06X",unicode) + end + + descriptions[unicode] = { + -- width = glyph.width, + boundingbox = glyph.boundingbox, + name = name, + index = index, + glyph = glyph, } end - else - -- we use the totable glyph table directly and manipulate the - -- entries in this (also final) table end - data.map = raw.map + end - data.cidinfo = raw.cidinfo -- hack + + resources.private = private + end --- watch copy of cidinfo: we can best make some more copies to data +-- the next one is still messy but will get better when we have +-- flattened map/enc tables in the font loader + +actions["prepare unicodes"] = 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 + + -- begin of messy (not needed whwn 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("using embedded unicode map '%s'",encname) + end + local multiples, nofmultiples = { }, 0 + for unicode, index in next, unicodetoindex do + if unicode <= criterium and not descriptions[unicode] then + local parent = indices[index] + local description = descriptions[parent] + if description then + local c = fastcopy(description) + c.comment = format("copy of 0x%04X", parent) + descriptions[unicode] = c + local name = c.name + if not unicodes[name] then + unicodes[name] = unicode + end + nofmultiples = nofmultiples + 1 + multiples[nofmultiples] = name -- we can save duplicates if needed + else + -- make it a notdef + report_otf("weird unicode 0x%04X at index 0x%04X",unicode,index) + end + end + end + if trace_loading then + if nofmultiples > 0 then + report_otf("%s glyphs are reused: %s",nofmultiples,concat(multiples," ")) + else + report_otf("no glyphs are reused") + end + end + elseif properties.cidinfo then + report_otf("warning: no unicode map, used cidmap '%s'",properties.cidinfo.usedname or "?") + else + report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") + end + + if mapdata then + mapdata.map = { } -- clear some memory + 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 glyphs = data.glyphs - -- collect info - local has_italic, widths, marks = false, { }, { } - for index, glyph in next, glyphs do - local italic = glyph.italic_correction + local descriptions = data.descriptions + local resources = data.resources + local metadata = data.metadata + local properties = data.properties + local italic_correction = false + local widths = { } + local marks = { } + for unicode, description in next, descriptions do + local glyph = description.glyph if not italic then -- skip elseif italic == 0 then - glyph.italic_correction = nil - glyph.italic = nil + -- skip else - glyph.italic_correction = nil - glyph.italic = italic - has_italic = true + description.italic = italic + italic_correction = true end local width = glyph.width widths[width] = (widths[width] or 0) + 1 local class = glyph.class - local unicode = glyph.unicode - if class == "mark" then - marks[unicode] = true - -- elseif chardata[unicode].category == "mn" then - -- marks[unicode] = true - -- glyph.class = "mark" + if class then + if class == "mark" then + marks[unicode] = true + end + description.class = class end - local a = glyph.altuni if a then glyph.altuni = nil end - local d = glyph.dependents if d then glyph.dependents = nil end - local v = glyph.vwidth if v then glyph.vwidth = nil end end -- flag italic - data.metadata.has_italic = has_italic + properties.italic_correction = italic_correction -- flag marks - data.luatex.marks = marks + resources.marks = marks -- share most common width for cjk fonts local wd, most = 0, 1 for k,v in next, widths do @@ -800,43 +808,41 @@ actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this if trace_loading then report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) end - for index, glyph in next, glyphs do - if glyph.width == wd then - glyph.width = nil + for unicode, description in next, descriptions do + if description.width == wd then + -- description.width = nil + else + description.width = description.glyph.width end end - data.luatex.defaultwidth = wd + 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 luatex = data.luatex - local unicodes = luatex.unicodes - local reverse = { } - luatex.markclasses = reverse + 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 - local us = unicodes[s] - if type(us) == "table" then - for u=1,#us do - t[us[u]] = true - end - else - t[us] = true - end + t[unicodes[s]] = true end - reverse[name] = t + markclasses[name] = t end - data.mark_classes = nil -- when using table end end actions["reorganize features"] = function(data,filename,raw) -- combine with other local features = { } - data.luatex.features = features + data.resources.features = features for k, what in next, otf.glists do local dw = raw[what] if dw then @@ -849,7 +855,11 @@ actions["reorganize features"] = function(data,filename,raw) -- combine with oth 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 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] @@ -868,25 +878,34 @@ actions["reorganize features"] = function(data,filename,raw) -- combine with oth end actions["reorganize anchor classes"] = function(data,filename,raw) - local classes = raw.anchor_classes -- anchor classes not in final table - local luatex = data.luatex - local anchor_to_lookup, lookup_to_anchor = { }, { } - luatex.anchor_to_lookup, luatex.lookup_to_anchor = anchor_to_lookup, lookup_to_anchor + 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 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 + 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 not l then l = { } lookup_to_anchor[lookup] = l end - l[anchor] = true + if l then + l[anchor] = true + else + l = { [anchor] = true } + lookup_to_anchor[lookup] = l + end a[lookup] = true end end @@ -894,13 +913,16 @@ actions["reorganize anchor classes"] = function(data,filename,raw) end actions["prepare tounicode"] = function(data,filename,raw) - fonts.map.addtounicode(data,filename) + fonts.mappings.addtounicode(data,filename) end actions["reorganize subtables"] = function(data,filename,raw) - local luatex = data.luatex - local sequences, lookups = { }, { } - luatex.sequences, luatex.lookups = sequences, lookups + 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 @@ -915,9 +937,7 @@ actions["reorganize subtables"] = function(data,filename,raw) if subtables then local t = { } for s=1,#subtables do - local subtable = subtables[s] - local name = subtable.name - t[#t+1] = name + t[s] = subtables[s].name end subtables = t end @@ -931,7 +951,7 @@ actions["reorganize subtables"] = function(data,filename,raw) } markclass = flags.mark_class if markclass then - markclass = luatex.markclasses[markclass] + markclass = resources.markclasses[markclass] end flags = t end @@ -980,138 +1000,165 @@ actions["reorganize subtables"] = function(data,filename,raw) end end --- the next one is still messy but will get better when we have --- flattened map/enc tables in the font loader +-- test this: +-- +-- for _, what in next, otf.glists do +-- raw[what] = nil +-- end -actions["prepare unicodes"] = function(data,filename,raw) - local luatex = data.luatex - local indices, unicodes, multiples, internals= { }, { }, { }, { } - local mapdata = data.map or raw.map -- map already moved - local mapmap - if not mapdata then - report_otf("no mapdata in '%s'",filename) - mapmap = { } - mapdata = { map = mapmap } - data.map = mapdata - elseif not mapdata.map then - report_otf("no map in mapdata of '%s'",filename) - mapmap = { } - mapdata.map = mapmap - else - mapmap = mapdata.map - end - local encname = lower(data.enc_name or raw.enc_name or mapdata.enc_name or "") - local criterium = fonts.privateoffset - local private = criterium - local glyphs = data.glyphs - -- todo: nofmultiples - for index, glyph in next, glyphs do - if index > 0 then - local name = glyph.name -- really needed ? - if name then - local unicode = glyph.unicode - if not unicode or unicode == -1 or unicode >= criterium then - glyph.unicode = private - indices[private] = index - unicodes[name] = private - internals[index] = true - if trace_private then - report_otf("enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) - end - private = private + 1 - else - indices[unicode] = index - unicodes[name] = unicode - end - -- maybe deal with altuni here in the future but first we need - -- to encounter a proper font that sets them; we have to wait till - -- a next luatex binary as currently the unicode numbers can be out - -- of bounds - if false then - local altuni = glyph.altuni - if altuni then - local un = { unicodes[name] } - for i=1,#altuni do - local unicode = altuni[i].unicode - multiples[#multiples+1] = name - un[i+1] = unicode - indices[unicode] = index -- maybe check for duplicates - end - unicodes[name] = un - end - end - else - -- message that something is wrong - end - end - end - -- beware: the indices table is used to initialize the tfm table - if find(encname,"unicode") then -- unicodebmp, unicodefull, ... - if trace_loading then - report_otf("using embedded unicode map '%s'",encname) - end - -- ok -- we can also consider using the altuni - for unicode, index in next, mapmap do - if not internals[index] then - local name = glyphs[index].name - if name then - local un = unicodes[name] - if not un then - unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then -- tonumber(un) - if un ~= unicode then - multiples[#multiples+1] = name - unicodes[name] = { un, unicode } - indices[unicode] = index - end - else - local ok = false - for u=1,#un do - if un[u] == unicode then - ok = true - break - end - end - if not ok then - multiples[#multiples+1] = name - un[#un+1] = unicode - indices[unicode] = index - end - end - end - end - end - else - report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") +actions["prepare lookups"] = function(data,filename,raw) + local lookups = raw.lookups + if lookups then + data.lookups = lookups end - if trace_loading then - if #multiples > 0 then - report_otf("%s glyphs are reused: %s",#multiples, concat(multiples," ")) - else - report_otf("no glyphs are reused") +end + +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 - luatex.indices = indices - luatex.unicodes = unicodes - luatex.private = private + return result end -actions["prepare lookups"] = function(data,filename,raw) - local lookups = raw.lookups - if lookups then - data.lookups = lookups +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) + cache[cover] = uncovered + end + return uncovered end end actions["reorganize lookups"] = function(data,filename,raw) -- we prefer the before lookups in a normal order if data.lookups then - for _, v in next, data.lookups do - if v.rules then - for _, vv in next, v.rules do - local c = vv.coverage - if c and c.before then - c.before = reversed(c.before) + local splitter = data.helpers.tounicodetable + local cache = { } + 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,cache,reversed(before_class)) + end + local current_class = lookup.current_class + if current_class then + current_class = t_uncover(splitter,cache,current_class) + end + local after_class = lookup.after_class + if after_class then + after_class = t_uncover(splitter,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 = before + end + local current = class.current + local lookups = rule.lookups + if current then + for i=1,#current do + current[i] = current_class[current[i]] or { } + if lookups and not lookups[i] then + lookups[i] = false + end + end + rule.current = current + end + local after = class.after + if after then + for i=1,#after do + after[i] = after_class[after[i]] or { } + end + rule.after = after + 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 + rule.before = t_uncover(splitter,cache,reversed(before)) + end + local current = coverage.current + if current then + rule.current = t_uncover(splitter,cache,current) + end + local after = coverage.after + if after then + rule.after = t_uncover(splitter,cache,after) + end + rule.coverage = nil + end + end + elseif format == "reversecoverage" then + for i=1,#rules do + local rule = rules[i] + local reversecoverage = rule.reversecoverage + if reversecoverage then + local before = reversecoverage.before + if before then + rule.before = t_uncover(splitter,cache,reversed(before)) + end + local current = reversecoverage.current + if current then + rule.current = t_uncover(splitter,cache,current) + end + local after = reversecoverage.after + if after then + rule.after = t_uncover(splitter,cache,after) + end + local replacements = reversecoverage.replacements + if replacements then + rule.replacements = s_uncover(splitter,cache,replacements) + end + rule.reversecoverage = nil + end + end + elseif format == "glyphs" then + for i=1,#rules do + local rule = rules[i] + local glyphs = rule.glyphs + if glyphs then + local fore = glyphs.fore + if fore then + rule.fore = s_uncover(splitter,cache,fore) + end + local back = glyphs.back + if back then + rule.back = s_uncover(splitter,cache,back) + end + local names = glyphs.names + if names then + rule.names = s_uncover(splitter,cache,names) + end + rule.glyphs = nil + end end end end @@ -1119,144 +1166,152 @@ actions["reorganize lookups"] = function(data,filename,raw) end end +-- to be checked italic_correction + actions["analyze math"] = function(data,filename,raw) if raw.math then -data.metadata.math = raw.math - -- we move the math stuff into a math subtable because we then can - -- test faster in the tfm copy - local glyphs, udglyphs = data.glyphs, data.udglyphs - local unicodes = data.luatex.unicodes - for index, udglyph in next, udglyphs do - local mk = udglyph.mathkern - local hv = udglyph.horiz_variants - local vv = udglyph.vert_variants - if mk or hv or vv then - local glyph = glyphs[index] + 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 = { } - glyph.math = math - if mk then - for k, v in next, mk do + 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 - mk[k] = nil + 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 = mk + math.kerns = mathkerns end - if hv then - math.horiz_variants = hv.variants - local p = hv.parts - if p and #p > 0 then - for i=1,#p do - local pi = p[i] + if horiz_variants then + local variants = horiz_variants.variants + if variants then -- use splitter + local glyphs = lpegmatch(splitter,variants) + for i=1,#glyphs do + if glyphs[i] == u then + remove(glyphs,i) + break + end + end + math.horiz_variants = glyphs + end + local parts = horiz_variants.parts + if parts and #parts > 0 then + for i=1,#parts do + local pi = parts[i] pi.glyph = unicodes[pi.component] or 0 + pi.component = nil end - math.horiz_parts = p + math.horiz_parts = parts end - local ic = hv.italic_correction - if ic and ic ~= 0 then - math.horiz_italic_correction = ic + local italic_correction = horiz_variants.italic_correction + if italic_correction and italic_correction ~= 0 then + math.horiz_italic_correction = italic_correction end end - if vv then - local uc = unicodes[index] - math.vert_variants = vv.variants - local p = vv.parts - if p and #p > 0 then - for i=1,#p do - local pi = p[i] + if vert_variants then + local variants = vert_variants.variants + if variants then + local glyphs = lpegmatch(splitter,variants) + for i=1,#glyphs do + if glyphs[i] == u then + remove(glyphs,i) + break + end + end + math.vert_variants = glyphs + end + local p = vert_variants.parts + if parts and #parts > 0 then + for i=1,#parts do + local pi = parts[i] pi.glyph = unicodes[pi.component] or 0 + pi.component = nil end - math.vert_parts = p + math.vert_parts = parts end - local ic = vv.italic_correction - if ic and ic ~= 0 then - math.vert_italic_correction = ic + local italic_correction = vert_variants.italic_correction + if italic_correction and italic_correction ~= 0 then + math.vert_italic_correction = italic_correction end end - local ic = glyph.italic_correction - if ic then - if ic ~= 0 then - math.italic_correction = ic - 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 luatex = data.luatex - local udglyphs, glyphs, mapmap, unicodes = data.udglyphs, data.glyphs, luatex.indices, luatex.unicodes - local mkdone = false - local function do_it(lookup,first_unicode,extrakerns) -- can be moved inline but seldom used - local glyph = glyphs[mapmap[first_unicode]] - if glyph then - local kerns = glyph.kerns - if not kerns then - kerns = { } -- unicode indexed ! - glyph.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+%04X", first_unicode) - end - end - for index, udglyph in next, data.udglyphs do - local kerns = udglyph.kerns + 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 glyph = glyphs[index] local newkerns = { } - for k,v in next, kerns do - local vc, vo, vl = v.char, v.off, v.lookup - if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones - local uvc = unicodes[vc] - if not uvc then - if trace_loading then - report_otf("problems with unicode %s of kern %s at glyph %s",vc,k,index) - end - else - if type(vl) ~= "table" then - vl = { vl } - end - for l=1,#vl do - local vll = vl[l] - local mkl = newkerns[vll] - if not mkl then - mkl = { } - newkerns[vll] = mkl - end - if type(uvc) == "table" then - for u=1,#uvc do - mkl[uvc[u]] = vo + 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 - mkl[uvc] = vo + newkerns[lookup] = { [unicode] = offset } end end + elseif trace_loading then + report_otf("problems with unicode %s of kern %s of glyph 0x%04X",name,k,unicode) end end end - glyph.kerns = newkerns -- udglyph.kerns = nil when in mixed mode - mkdone = true + description.kerns = newkerns end end - if trace_loading and mkdone then - report_otf("replacing 'kerns' tables by a new 'kerns' tables") - end - local dgpos = raw.gpos - if dgpos then - local separator = lpeg.P(" ") - local other = ((1 - separator)^0) / unicodes - local splitter = lpeg.Ct(other * (separator * other)^0) - for gp=1,#dgpos do - local gpos = dgpos[gp] +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 @@ -1266,56 +1321,71 @@ actions["reorganize glyph kerns"] = function(data,filename,raw) local split = { } -- saves time for k=1,#kernclass do local kcl = kernclass[k] - local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular + 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 - local maxfirsts, maxseconds = getn(firsts), getn(seconds) - -- here we could convert split into a list of unicodes which is a bit - -- faster but as this is only done when caching it does not save us much - for _, s in next, firsts do + -- 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 - for _, s in next, seconds do + 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 + for fk=1,#firsts do -- maxfirsts ? local fv = firsts[fk] local splt = split[fv] if splt then - local kerns, baseoffset = { }, (fk-1) * maxseconds - for sk=2,maxseconds do - local sv = seconds[sk] + local extrakerns = { } + local baseoffset = (fk-1) * maxseconds + -- for sk=2,maxseconds do + -- local sv = seconds[sk] + for sk, sv in next, seconds do local splt = split[sv] - if splt then + if splt then -- redundant test local offset = offsets[baseoffset + sk] if offset then for i=1,#splt do - local second_unicode = splt[i] - if tonumber(second_unicode) then - kerns[second_unicode] = offset - else for s=1,#second_unicode do - kerns[second_unicode[s]] = offset - end end + extrakerns[splt[i]] = offset end end end end for i=1,#splt do local first_unicode = splt[i] - if tonumber(first_unicode) then - do_it(lookup,first_unicode,kerns) - else for f=1,#first_unicode do - do_it(lookup,first_unicode[f],kerns) - end end + 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+%04X", first_unicode) + end end end end end end - subtable.comment = "The kernclass table is merged into kerns in the indexed glyph tables." subtable.kernclass = { } end end @@ -1325,112 +1395,37 @@ actions["reorganize glyph kerns"] = function(data,filename,raw) end actions["check glyphs"] = function(data,filename,raw) - local verbose = fonts.verbose - local int_to_uni = data.luatex.unicodes - for k, v in next, data.glyphs do - if verbose then - local code = int_to_uni[k] - -- looks like this is done twice ... bug? - if code then - local vu = v.unicode - if not vu then - v.unicode = code - elseif type(vu) == "table" then - if vu[#vu] == code then - -- weird - else - vu[#vu+1] = code - end - elseif vu ~= code then - v.unicode = { vu, code } - end - end - else - v.unicode = nil - v.index = nil - end - -- only needed on non sparse/mixed mode - if v.math then - if v.mathkern then v.mathkern = nil end - if v.horiz_variant then v.horiz_variant = nil end - if v.vert_variants then v.vert_variants = nil end - end - -- + for unicode, description in next, data.descriptions do + description.glyph = nil end - data.luatex.comment = "Glyph tables have their original index. When present, kern tables are indexed by unicode." end +-- future versions will remove _ + actions["check metadata"] = function(data,filename,raw) local metadata = data.metadata - metadata.method = loadmethod - if loadmethod == "sparse" then - for _, k in next, mainfields do - if valid_fields[k] then - local v = raw[k] - if global_fields[k] then - if not data[k] then - data[k] = v - end - else - if not metadata[k] then - metadata[k] = v - end - end + 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 - else - for k, v in next, raw do - if valid_fields[k] then - if global_fields[k] then - if not data[k] then - data[v] = v - end - else - if not metadata[k] then - metadata[k] = v - end - end - end - end - end - local pfminfo = raw.pfminfo - if pfminfo then - data.pfminfo = pfminfo - metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced") - metadata.charwidth = pfminfo and pfminfo.avgwidth 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 - metadata.xuid = nil - data.udglyphs = nil - data.map = nil end -local private_mathparameters = { - "FractionDelimiterSize", - "FractionDelimiterDisplayStyleSize", -} - -actions["check math parameters"] = function(data,filename,raw) - local mathdata = data.metadata.math - if mathdata then - for m=1,#private_mathparameters do - local pmp = private_mathparameters[m] - if not mathdata[pmp] then - if trace_loading then - report_otf("setting math parameter '%s' to 0", pmp) - end - mathdata[pmp] = 0 - 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 @@ -1438,272 +1433,239 @@ end -- 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 glyphs = data.glyphs - for index, udglyph in next, data.udglyphs do - local lookups = udglyph.lookups + 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 - local glyph = glyphs[index] - local l = { } - for kk, vv in next, lookups do - local aa = { } - l[kk] = aa - for kkk=1,#vv do - local vvv = vv[kkk] - local s = vvv.specification - local t = vvv.type - -- #aa+1 - if t == "ligature" then - aa[kkk] = { "ligature", s.components, s.char } - elseif t == "alternate" then - aa[kkk] = { "alternate", s.components } - elseif t == "substitution" then - aa[kkk] = { "substitution", s.variant } - elseif t == "multiple" then - aa[kkk] = { "multiple", s.components } - elseif t == "position" then - aa[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } - elseif t == "pair" then - -- maybe flatten this one - local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" + 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: %s => %s and %s",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 - aa[kkk] = { "pair", 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 } } + 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 - aa[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } + lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } end else if two then - aa[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } + lookuplist[l] = { paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } else - aa[kkk] = { "pair", paired } + lookuplist[l] = { paired } end end end end end - -- we could combine this local slookups, mlookups - for kk, vv in next, l do - if #vv == 1 then - if not slookups then - slookups = { } - glyph.slookups = slookups + for tag, lookuplist in next, lookups do + if #lookuplist == 1 then + if slookups then + slookups[tag] = lookuplist[1] + else + slookups = { [tag] = lookuplist[1] } end - slookups[kk] = vv[1] else - if not mlookups then - mlookups = { } - glyph.mlookups = mlookups + if mlookups then + mlookups[tag] = lookuplist + else + mlookups = { [tag] = lookuplist } end - mlookups[kk] = vv end end - glyph.lookups = nil -- when using table + 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) - local glyphs = data.glyphs - for index, udglyph in next, data.udglyphs do - local anchors = udglyph.anchors +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 - local glyph = glyphs[index] - local a = { } - glyph.anchors = a - for kk, vv in next, anchors do - local aa = { } - a[kk] = aa - for kkk, vvv in next, vv do - if vvv.x or vvv.y then - aa[kkk] = { vvv.x , vvv.y } - else - local aaa = { } - aa[kkk] = aaa - for kkkk=1,#vvv do - local vvvv = vvv[kkkk] - aaa[kkkk] = { vvvv.x, vvvv.y } + 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 ---~ actions["check extra features"] = function(data,filename,raw) ---~ -- later, ctx only ---~ end - --- -- -- -- -- -- --- -- -- -- -- -- - -function features.register(name,default,description) - featurelist[#featurelist+1] = name - defaultfeatures[name] = default - if description and description ~= "" then - fonts.otf.tables.features[name] = description - end -end - --- for context this will become a task handler - -local lists = { -- why local - fonts.triggers, - fonts.processors, - fonts.manipulators, -} +-- modes: node, base, none function otf.setfeatures(tfmdata,features) - local processes = { } - if features and next(features) then - local mode = tfmdata.mode or features.mode or "base" - local initializers = fonts.initializers - local fi = initializers[mode] - if fi then - local fiotf = fi.otf - if fiotf then - local done = { } - for l=1,#lists do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - local value = features[f] - if value and fiotf[f] then -- brr - if not done[f] then -- so, we can move some to triggers - if trace_features then - report_otf("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown') - end - fiotf[f](tfmdata,value) -- can set mode (no need to pass otf) - mode = tfmdata.mode or features.mode or "base" - local im = initializers[mode] - if im then - fiotf = initializers[mode].otf - end - done[f] = true - end - end - end - end - end - end - end -tfmdata.mode = mode - local fm = fonts.methods[mode] -- todo: zonder node/mode otf/... - if fm then - local fmotf = fm.otf - if fmotf then - for l=1,#lists do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - if fmotf[f] then -- brr - if trace_features then - report_otf("installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown') - end - processes[#processes+1] = fmotf[f] - end - end - end - end - end - else - -- message - end + 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 - return processes, features 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 +-- 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) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many) +local function copytotfm(data,cache_id) if data then - local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } - local luatex = data.luatex - local unicodes = luatex.unicodes -- names to unicodes - local indices = luatex.indices - local mode = data.mode or "base" - local characters, parameters, mathparameters, descriptions = { }, { }, { }, { } - local designsize = metadata.designsize or metadata.design_size or 100 + local metadata = data.metadata + local resources = data.resources + local properties = table.derive(data.properties) + local descriptions = table.derive(data.descriptions) + local goodies = table.derive(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 - local spaceunits, spacer = 500, "space" - -- indices maps from unicodes to indices - -- this wil stay as we can manipulate indices - -- beforehand - for u, i in next, indices do - characters[u] = { } -- we need this because for instance we add protruding info and loop over characters - descriptions[u] = glyphs[i] - end - -- math - if metadata.math then - -- parameters - for name, value in next, metadata.math do + if mathspecs then + for name, value in next, mathspecs do mathparameters[name] = value end - -- we could use a subset - for u, char in next, characters do - local d = descriptions[u] + 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 - -- we have them shared because that packs nicer - -- we could prepare the variants and keep 'm in descriptions if m then - local variants, parts, c = m.horiz_variants, m.horiz_parts, char + -- watch out: luatex uses horiz_variants for the parts + local variants, parts = m.horiz_variants, m.horiz_parts if variants then - for n in gmatch(variants,"[^ ]+") do - local un = unicodes[n] - if un and u ~= un then - c.next = un - c = characters[un] - end - end + local c = character + for i=1,#variants do + local un = variants[i] + c.next = un + c = characters[un] + end -- c is now last in chain c.horiz_variants = parts elseif parts then - c.horiz_variants = parts + character.horiz_variants = parts end - local variants, parts, c = m.vert_variants, m.vert_parts, char + local variants, parts = m.vert_variants, m.vert_parts if variants then - for n in gmatch(variants,"[^ ]+") do - local un = unicodes[n] - if un and u ~= un then - c.next = un - c = characters[un] - end + local c = character + for i=1,#variants do + local un = variants[i] + c.next = un + c = characters[un] end -- c is now last in chain c.vert_variants = parts elseif parts then - c.vert_variants = parts + character.vert_variants = parts end local italic_correction = m.vert_italic_correction if italic_correction then - c.vert_italic_correction = italic_correction + 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 - char.mathkerns = kerns + character.mathkerns = kerns end end end end -- end math - local space, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash'] - if metadata.isfixedpitch then + 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 metadata.charwidth then - spaceunits, spacer = metadata.charwidth, "charwidth" + if not spaceunits and charwidth then + spaceunits, spacer = charwidth, "charwidth" end else if descriptions[space] then @@ -1712,20 +1674,17 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th if not spaceunits and descriptions[emdash] then spaceunits, spacer = descriptions[emdash].width/2, "emdash/2" end - if not spaceunits and metadata.charwidth then - spaceunits, spacer = metadata.charwidth, "charwidth" + 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 = fonts.tfm.checkedfilename(luatex) + local filename = constructors.checkedfilename(resources) local fontname = metadata.fontname local fullname = metadata.fullname or fontname - local cidinfo = data.cidinfo -- or { } local units = metadata.units_per_em or 1000 -- - cidinfo.registry = cidinfo and cidinfo.registry or "" -- weird here, fix upstream - -- parameters.slant = 0 parameters.space = spaceunits -- 3.333 (cmr10) parameters.space_stretch = units/2 -- 500 -- 1.666 (cmr10) @@ -1735,11 +1694,12 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th if spaceunits < 2*units/5 then -- todo: warning end - local italicangle = metadata.italicangle - if italicangle then -- maybe also in afm _ - parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) + 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 metadata.isfixedpitch then + if monospaced then parameters.space_stretch = 0 parameters.space_shrink = 0 elseif syncspace then -- @@ -1747,8 +1707,8 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th parameters.space_shrink = spaceunits/3 end parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10) - if pfminfo.os2_xheight and pfminfo.os2_xheight > 0 then - parameters.x_height = pfminfo.os2_xheight + if charxheight then + parameters.x_height = charxheight else local x = 0x78 -- unicodes['x'] if x then @@ -1759,150 +1719,109 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th end end -- - local fileformat = data.format or fonts.fontformat(filename,"opentype") - if units > 1000 then - fileformat = "truetype" - 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, - parameters = parameters, - mathparameters = mathparameters, - descriptions = descriptions, - indices = indices, - unicodes = unicodes, - type = "real", - direction = 0, - boundarychar_label = 0, - boundarychar = 65536, - designsize = (designsize/10)*65536, - encodingbytes = 2, - mode = mode, - filename = filename, - fontname = fontname, - fullname = fullname, - psname = fontname or fullname, - name = filename or fullname, - units = units, - format = fileformat, - cidinfo = cidinfo, - ascender = abs(metadata.ascent or 0), - descender = abs(metadata.descent or 0), - spacer = spacer, - italicangle = italicangle, + characters = characters, + descriptions = descriptions, + parameters = parameters, + mathparameters = mathparameters, + resources = resources, + properties = properties, + goodies = goodies, } - else - return nil end end local function otftotfm(specification) - local name = specification.name - local sub = specification.sub - local filename = specification.filename - local format = specification.format - local features = specification.features.normal local cache_id = specification.hash - local tfmdata = containers.read(tfm.cache,cache_id) ---~ print(cache_id) + local tfmdata = containers.read(constructors.cache,cache_id) if not tfmdata then - local otfdata = otf.load(filename,format,sub,features and features.featurefile) - if otfdata and next(otfdata) then - otfdata.shared = otfdata.shared or { - featuredata = { }, - anchorhash = { }, - initialized = false, - } - tfmdata = copytotfm(otfdata,cache_id) + 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 - tfmdata.unique = tfmdata.unique or { } - tfmdata.shared = tfmdata.shared or { } -- combine - local shared = tfmdata.shared - shared.otfdata = otfdata - shared.features = features -- default - shared.dynamics = { } - shared.processes = { } - shared.setdynamics = otf.setdynamics -- fast access and makes other modules independent - -- this will be done later anyway, but it's convenient to have - -- them already for fast access - tfmdata.luatex = otfdata.luatex - tfmdata.indices = otfdata.luatex.indices - tfmdata.unicodes = otfdata.luatex.unicodes - tfmdata.marks = otfdata.luatex.marks - tfmdata.originals = otfdata.luatex.originals - tfmdata.changed = { } - tfmdata.has_italic = otfdata.metadata.has_italic - if not tfmdata.language then tfmdata.language = 'dflt' end - if not tfmdata.script then tfmdata.script = 'dflt' end - -- at this moment no characters are assinged yet, only empty slots - shared.processes, shared.features = otf.setfeatures(tfmdata,definers.check(features,defaultfeatures)) + -- 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(tfm.cache,cache_id,tfmdata) + containers.write(constructors.cache,cache_id,tfmdata) end return tfmdata end -features.register('mathsize') - -local function read_from_otf(specification) -- wrong namespace - local tfmtable = otftotfm(specification) - if tfmtable then - local otfdata = tfmtable.shared.otfdata - tfmtable.name = specification.name - tfmtable.sub = specification.sub - local s = specification.size - local m = otfdata.metadata.math - if m then - -- this will move to a function - local f = specification.features - if f then - local f = f.normal - if f and f.mathsize then - local mathsize = specification.mathsize or 0 - if mathsize == 2 then - local p = m.ScriptPercentScaleDown - if p then - local ps = p * specification.textsize / 100 - if trace_math then - report_otf("asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s = ps - end - elseif mathsize == 3 then - local p = m.ScriptScriptPercentScaleDown - if p then - local ps = p * specification.textsize / 100 - if trace_math then - report_otf("asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s = ps - end - end - end - end - end - tfmtable = tfm.scale(tfmtable,s,specification.relativeid) - if tfm.fontnamemode == "specification" then - -- not to be used in context ! - local specname = specification.specification - if specname then - tfmtable.name = specname - if trace_defining then - report_otf("overloaded fontname: '%s'",specname) - end - end - end - fonts.logger.save(tfmtable,file.extname(specification.filename),specification) +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) + constructors.applymanipulators("otf",tfmdata,specification.features.normal,trace_features,report_otf) + constructors.setname(tfmdata,specification) -- only otf? + fonts.loggers.register(tfmdata,file.extname(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 ---~ print(tfmtable.fullname) - return tfmtable end +registerotffeature { + name = "mathsize", + description = "apply mathsize as specified in the font", + initializers = { + base = checkmathsize, + node = checkmathsize, + } +} + -- helpers -function otf.collectlookups(otfdata,kind,script,language) - -- maybe store this in the font - local sequences = otfdata.luatex.sequences +function otf.collectlookups(rawdata,kind,script,language) + local sequences = rawdata.resources.sequences if sequences then local featuremap, featurelist = { }, { } for s=1,#sequences do @@ -1933,42 +1852,23 @@ end -- readers -fonts.formats.dfont = "truetype" -fonts.formats.ttc = "truetype" -fonts.formats.ttf = "truetype" -fonts.formats.otf = "opentype" - local function check_otf(forced,specification,suffix,what) local name = specification.name if forced then name = file.addsuffix(name,suffix,true) end - local fullname, tfmtable = findbinfile(name,suffix) or "", nil -- one shot - -- if false then -- can be enabled again when needed - -- if fullname == "" then - -- local fb = fonts.names.old_to_new[name] - -- if fb then - -- fullname = findbinfile(fb,suffix) or "" - -- end - -- end - -- if fullname == "" then - -- local fb = fonts.names.new_to_old[name] - -- if fb then - -- fullname = findbinfile(fb,suffix) or "" - -- end - -- end - -- end + local fullname, tfmdata = findbinfile(name,suffix) or "", nil -- one shot if fullname == "" then fullname = fonts.names.getfilename(name,suffix) end if fullname ~= "" then specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then - tfmtable = read_from_otf(specification) -- we need to do it for all matches / todo + tfmdata = read_from_otf(specification) -- we need to do it for all matches / todo end - return tfmtable + return tfmdata end -function readers.opentype(specification,suffix,what) +local function opentypereader(specification,suffix,what) local forced = specification.forced or "" if forced == "otf" then return check_otf(true,specification,forced,"opentype") @@ -1979,7 +1879,23 @@ function readers.opentype(specification,suffix,what) end end -function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end -function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end -function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end -- !! -function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") 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 d1a68d809..59dca31d9 100644 --- a/tex/context/base/font-oth.lua +++ b/tex/context/base/font-oth.lua @@ -6,34 +6,40 @@ if not modules then modules = { } end modules ['font-oth'] = { license = "see context related readme files" } -local lpegmatch = lpeg.match -local splitter = lpeg.Ct(lpeg.splitat(" ")) +local fonts = fonts +local otf = fonts.handlers.otf -local collectlookups = fonts.otf.collectlookups +-- todo: use nodemode data is available --- For the moment there is no need to cache this but this might --- happen when I get the feeling that there is a performance --- penalty involved. - -function fonts.otf.getalternate(tfmdata,k,kind,value) +function otf.getalternate(tfmdata,k,kind,value) -- just initialize nodemode and use that (larger mem print) if value then - local shared = tfmdata.shared - local otfdata = shared and shared.otfdata - if otfdata then - local validlookups, lookuplist = collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local lookups = tfmdata.descriptions[k].slookups -- we assume only slookups (we can always extend) - if lookups then - local unicodes = tfmdata.unicodes -- names to unicodes - local choice = tonumber(value) - for l=1,#lookuplist do - local lookup = lookuplist[l] - local p = lookups[lookup] - if p then - local pc = p[2] -- p.components - if pc then - pc = lpegmatch(splitter,pc) - return unicodes[pc[choice] or pc[#pc]] + 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 diff --git a/tex/context/base/font-oti.lua b/tex/context/base/font-oti.lua index e531ba8b2..d6853db31 100644 --- a/tex/context/base/font-oti.lua +++ b/tex/context/base/font-oti.lua @@ -8,51 +8,85 @@ if not modules then modules = { } end modules ['font-oti'] = { local lower = string.lower -local fonts = fonts +local allocate = utilities.storage.allocate -local otf = fonts.otf -local initializers = fonts.initializers +local fonts = fonts +local otf = { } +fonts.handlers.otf = otf -local languages = otf.tables.languages -local scripts = otf.tables.scripts +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -local function set_language(tfmdata,value) +registerotffeature { + name = "features", + description = "initialization of feature handler", + default = true, +} + +-- these are later hooked into node and base initializaters + +local otftables = otf.tables -- not always defined + +local function setmode(tfmdata,value) if value then - value = lower(value) - if languages[value] then - tfmdata.language = value - end + tfmdata.properties.mode = lower(value) end end -local function set_script(tfmdata,value) +local function setlanguage(tfmdata,value) if value then - value = lower(value) - if scripts[value] then - tfmdata.script = value + 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 set_mode(tfmdata,value) +local function setscript(tfmdata,value) if value then - tfmdata.mode = lower(value) + 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 -local base_initializers = initializers.base.otf -local node_initializers = initializers.node.otf - -base_initializers.language = set_language -base_initializers.script = set_script -base_initializers.mode = set_mode -base_initializers.method = set_mode +registerotffeature { + name = "mode", + description = "mode", + initializers = { + base = setmode, + node = setmode, + } +} -node_initializers.language = set_language -node_initializers.script = set_script -node_initializers.mode = set_mode -node_initializers.method = set_mode +registerotffeature { + name = "language", + description = "language", + initializers = { + base = setlanguage, + node = setlanguage, + } +} -otf.features.register("features",true) -- we always do features -table.insert(fonts.processors,"features") -- we need a proper function for doing this +registerotffeature { + name = "script", + description = "script", + initializers = { + base = setscript, + node = setscript, + } +} diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index 6c5ba12c8..38d01dec0 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -10,14 +10,6 @@ if not modules then modules = { } end modules ['font-otn'] = { -- much functionality could only be implemented thanks to the husayni font -- of Idris Samawi Hamid to who we dedicate this module. --- I'm in the process of cleaning up the code (which happens in another --- file) so don't rely on things staying the same. - --- some day when we can jit this, we can use more functions - --- we can use more lpegs when lpeg is extended with function args and so --- resolving to unicode does not gain much - -- in retrospect it always looks easy but believe it or not, it took a lot -- of work to get proper open type support done: buggy fonts, fuzzy specs, -- special made testfonts, many skype sessions between taco, idris and me, @@ -32,10 +24,7 @@ if not modules then modules = { } end modules ['font-otn'] = { -- alternative loop quitters -- check cursive and r2l -- find out where ignore-mark-classes went --- remove unused tables --- slide tail (always glue at the end so only needed once -- default features (per language, script) --- cleanup kern(class) code, remove double info -- handle positions (we need example fonts) -- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) @@ -111,6 +100,8 @@ results in different tables.

-- gpos_context ok -- -- gpos_contextchain ok -- -- +-- todo: contextpos and contextsub and class stuff +-- -- actions: -- -- handler : actions triggered by lookup @@ -120,16 +111,20 @@ results in different tables.

-- remark: the 'not implemented yet' variants will be done when we have fonts that use them -- remark: we need to check what to do with discretionaries +-- We used to have independent hashes for lookups but as the tags are unique +-- we now use only one hash. If needed we can have multiple again but in that +-- case I will probably prefix (i.e. rename) the lookups in the cached font file. + local concat, insert, remove = table.concat, table.insert, table.remove 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 random = math.random -local logs, trackers, fonts, nodes, attributes = logs, trackers, fonts, nodes, attributes +local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes -local otf = fonts.otf -local tfm = fonts.tfm +local fonts = fonts +local otf = fonts.handlers.otf local trace_lookups = false trackers.register("otf.lookups", function(v) trace_lookups = v end) local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end) @@ -164,93 +159,82 @@ trackers.register("otf.injections","nodes.injections") trackers.register("*otf.sample","otf.steps,otf.actions,otf.analyzing") -local insert_node_after = node.insert_after -local delete_node = nodes.delete -local copy_node = node.copy -local find_node_tail = node.tail or node.slide -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute +local insert_node_after = node.insert_after +local delete_node = nodes.delete +local copy_node = node.copy +local find_node_tail = node.tail or node.slide +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute + +local zwnj = 0x200C +local zwj = 0x200D +local wildcard = "*" +local default = "dflt" -local zwnj = 0x200C -local zwj = 0x200D -local wildcard = "*" -local default = "dflt" +local nodecodes = nodes.nodecodes +local whatcodes = nodes.whatcodes +local glyphcodes = nodes.glyphcodes -local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue +local disc_code = nodecodes.disc +local whatsit_code = nodecodes.whatsit -local nodecodes = nodes.nodecodes -local whatcodes = nodes.whatcodes -local glyphcodes = nodes.glyphcodes +local dir_code = whatcodes.dir +local localpar_code = whatcodes.localpar -local glyph_code = nodecodes.glyph -local glue_code = nodecodes.glue -local disc_code = nodecodes.disc -local whatsit_code = nodecodes.whatsit +local ligature_code = glyphcodes.ligature -local dir_code = whatcodes.dir -local localpar_code = whatcodes.localpar +local privateattribute = attributes.private -local ligature_code = glyphcodes.ligature +local state = privateattribute('state') +local markbase = privateattribute('markbase') +local markmark = privateattribute('markmark') +local markdone = privateattribute('markdone') +local cursbase = privateattribute('cursbase') +local curscurs = privateattribute('curscurs') +local cursdone = privateattribute('cursdone') +local kernpair = privateattribute('kernpair') -local state = attributes.private('state') -local markbase = attributes.private('markbase') -local markmark = attributes.private('markmark') -local markdone = attributes.private('markdone') -local cursbase = attributes.private('cursbase') -local curscurs = attributes.private('curscurs') -local cursdone = attributes.private('cursdone') -local kernpair = attributes.private('kernpair') +local injections = nodes.injections +local setmark = injections.setmark +local setcursive = injections.setcursive +local setkern = injections.setkern +local setpair = injections.setpair -local injections = nodes.injections -local setmark = injections.setmark -local setcursive = injections.setcursive -local setkern = injections.setkern -local setpair = injections.setpair +local markonce = true +local cursonce = true +local kernonce = true -local markonce = true -local cursonce = true -local kernonce = true +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers -local fontdata = fonts.identifiers +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -otf.features.process = { } +local onetimemessage = fonts.loggers.onetimemessage -- we share some vars here, after all, we have no nested lookups and -- less code -local tfmdata = false -local otfdata = false -local characters = false -local descriptions = false -local marks = false -local indices = false -local unicodes = false -local currentfont = false -local lookuptable = false -local anchorlookups = false -local handlers = { } -local rlmode = 0 -local featurevalue = false - --- we cheat a bit and assume that a font,attr combination are kind of ranged - -local specifiers = fonts.definers.specifiers -local contextsetups = specifiers.contextsetups -local contextnumbers = specifiers.contextnumbers -local contextmerged = specifiers.contextmerged +local tfmdata = false +local characters = false +local descriptions = false +local resources = false +local marks = false +local currentfont = false +local lookuptable = false +local anchorlookups = false +local lookuptypes = false +local handlers = { } +local rlmode = 0 +local featurevalue = false -- we cannot optimize with "start = first_glyph(head)" because then we don't -- know which rlmode we're in which messes up cursive handling later on -- -- head is always a whatsit so we can safely assume that head is not changed -local special_attributes = { - init = 1, - medi = 2, - fina = 3, - isol = 4 -} - -- we use this for special testing and documentation local checkstep = (nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end @@ -263,6 +247,7 @@ local function logprocess(...) end report_direct(...) end + local function logwarning(...) report_direct(...) end @@ -282,9 +267,11 @@ local function gref(n) local num, nam = { }, { } for i=1,#n do local ni = n[i] - local di = descriptions[ni] - num[i] = format("U+%04X",ni) - nam[i] = di and di.name or "?" + if tonumber(di) then -- later we will start at 2 + local di = descriptions[ni] + num[i] = format("U+%04X",ni) + nam[i] = di and di.name or "?" + end end return format("%s (%s)",concat(num," "), concat(nam," ")) end @@ -327,84 +314,72 @@ local function markstoligature(kind,lookupname,start,stop,char) end local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- brr head - if start ~= stop then ---~ if discfound then ---~ local lignode = copy_node(start) ---~ lignode.font = start.font ---~ lignode.char = char ---~ lignode.subtype = ligature_code ---~ start = node.do_ligature_n(start, stop, lignode) ---~ if start.id == disc_code then ---~ local prev = start.prev ---~ start = start.next ---~ end - if discfound then - -- print("start->stop",nodes.tosequence(start,stop)) - local lignode = copy_node(start) - lignode.font, lignode.char, lignode.subtype = start.font, char, ligature_code - local next, prev = stop.next, start.prev - stop.next = nil - lignode = node.do_ligature_n(start, stop, lignode) - prev.next = lignode - if next then - next.prev = lignode - end - lignode.next, lignode.prev = next, prev - start = lignode - -- print("start->end",nodes.tosequence(start)) - else -- start is the ligature - local deletemarks = markflag ~= "mark" - local n = copy_node(start) - local current - current, start = insert_node_after(start,start,n) - local snext = stop.next - current.next = snext - if snext then - snext.prev = current - end - start.prev, stop.next = nil, nil - current.char, current.subtype, current.components = char, ligature_code, start - local head = current - if deletemarks then - if trace_marks then - while start do - if marks[start.char] then - logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char)) - end - start = start.next - end - end - else - local i = 0 + if start == stop then + start.char = char + elseif discfound then + -- print("start->stop",nodes.tosequence(start,stop)) + local lignode = copy_node(start) + lignode.font, lignode.char, lignode.subtype = start.font, char, ligature_code + local next, prev = stop.next, start.prev + stop.next = nil + lignode = node.do_ligature_n(start, stop, lignode) + prev.next = lignode + if next then + next.prev = lignode + end + lignode.next, lignode.prev = next, prev + start = lignode + -- print("start->end",nodes.tosequence(start)) + else -- start is the ligature + local deletemarks = markflag ~= "mark" + local n = copy_node(start) + local current + current, start = insert_node_after(start,start,n) + local snext = stop.next + current.next = snext + if snext then + snext.prev = current + end + start.prev, stop.next = nil, nil + current.char, current.subtype, current.components = char, ligature_code, start + local head = current + if deletemarks then + if trace_marks then while start do if marks[start.char] then - set_attribute(start,markdone,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - head, current = insert_node_after(head,current,copy_node(start)) - else - i = i + 1 + logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char)) end start = start.next end - start = current.next - while start and start.id == glyph_code do - if marks[start.char] then - set_attribute(start,markdone,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - else - break + end + else + local i = 0 + while start do + if marks[start.char] then + set_attribute(start,markdone,i) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) end - start = start.next + head, current = insert_node_after(head,current,copy_node(start)) + else + i = i + 1 end + start = start.next + end + start = current.next + while start and start.id == glyph_code do + if marks[start.char] then + set_attribute(start,markdone,i) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) + end + else + break + end + start = start.next end - return head end - else - start.char = char + return head end return start end @@ -447,6 +422,30 @@ local function alternative_glyph(start,alternatives,kind,chainname,chainlookupna return choice, value end +local function multiple_glyphs(start,multiple) + local nofmultiples = #multiple + if nofmultiples > 0 then + start.char = multiple[1] + if nofmultiples > 1 then + local sn = start.next + for k=2,nofmultiples do -- todo: use insert_node + local n = copy_node(start) + n.char = multiple[k] + n.next = sn + n.prev = start + if sn then + sn.prev = n + end + start.next = n + start = n + end + end + return start, true + else + return start, false + end +end + function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence) local choice, index = alternative_glyph(start,alternative,kind,lookupname) if trace_alternatives then @@ -460,22 +459,7 @@ function handlers.gsub_multiple(start,kind,lookupname,multiple) if trace_multiples then logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) end - start.char = multiple[1] - if #multiple > 1 then - for k=2,#multiple do - local n = copy_node(start) - n.char = multiple[k] - local sn = start.next - n.next = sn - n.prev = start - if sn then - sn.prev = n - end - start.next = n - start = n - end - end - return start, true + return multiple_glyphs(start,multiple) end function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or maybe pass lookup ref @@ -484,17 +468,12 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma if marks[startchar] then while s do local id = s.id - if id == glyph_code and s.subtype<256 then - if s.font == currentfont then - local char = s.char - local lg = ligature[1][char] - if not lg then - break - else - stop = s - ligature = lg - s = s.next - end + if id == glyph_code and s.subtype<256 and s.font == currentfont then + local lg = ligature[s.char] + if lg then + stop = s + ligature = lg + s = s.next else break end @@ -502,13 +481,14 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma break end end - if stop and ligature[2] then + if stop then + local lig = ligature.ligature if trace_ligatures then local stopchar = stop.char - start = markstoligature(kind,lookupname,start,stop,ligature[2]) + start = markstoligature(kind,lookupname,start,stop,lig) logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else - start = markstoligature(kind,lookupname,start,stop,ligature[2]) + start = markstoligature(kind,lookupname,start,stop,lig) end return start, true end @@ -522,13 +502,13 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma if skipmark and marks[char] then s = s.next else - local lg = ligature[1][char] - if not lg then - break - else + local lg = ligature[char] + if lg then stop = s ligature = lg s = s.next + else + break end end else @@ -541,13 +521,14 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma break end end - if stop and ligature[2] then + if stop then + local lig = ligature.ligature if trace_ligatures then local stopchar = stop.char - start = toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) + start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else - start = toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) + start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) end return start, true end @@ -594,7 +575,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -609,7 +590,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.registermessage(currentfont,basechar,"no base anchors") + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -662,7 +643,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) if ma then ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,index) if trace_marks then logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) @@ -679,7 +660,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.registermessage(currentfont,basechar,"no base anchors") + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -709,7 +690,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -725,7 +706,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.registermessage(currentfont,basechar,"no base anchors") + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end elseif trace_bugs then logwarning("%s: prev node is no mark",pref(kind,lookupname)) @@ -767,7 +748,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to if al[anchor] then local exit = exitanchors[anchor] if exit then - local dx, dy, bound = setcursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end @@ -780,7 +761,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.registermessage(currentfont,startchar,"no entry anchors") + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) end break end @@ -797,7 +778,8 @@ end function handlers.gpos_single(start,kind,lookupname,kerns,sequence) local startchar = start.char - local dx, dy, w, h = setpair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + local kerns = kerns[start.char] + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) end @@ -812,7 +794,8 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) return start, false else local prev, done = start, false - local factor = tfmdata.factor + local factor = tfmdata.parameters.factor + local lookuptype = lookuptypes[lookupname] while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do local nextchar = snext.char local krn = kerns[nextchar] @@ -824,8 +807,8 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) if not krn then -- skip elseif type(krn) == "table" then - if krn[1] == "pair" then - local a, b = krn[3], krn[4] + if lookuptype == "pair" then -- probably not needed + local a, b = krn[2], krn[3] if a and #a > 0 then local startchar = start.char local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) @@ -840,18 +823,18 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) end end - else + else -- wrong ... position has different entries report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) - local a, b = krn[3], krn[7] - if a and a ~= 0 then - local k = setkern(snext,factor,rlmode,a) - if trace_kerns then - logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) - end - end - if b and b ~= 0 then - logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) - end + -- local a, b = krn[2], krn[6] + -- if a and a ~= 0 then + -- local k = setkern(snext,factor,rlmode,a) + -- if trace_kerns then + -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + -- end + -- end + -- if b and b ~= 0 then + -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) + -- end end done = true elseif krn ~= 0 then @@ -885,37 +868,31 @@ end local logwarning = report_subchain --- ['coverage']={ --- ['after']={ "r" }, --- ['before']={ "q" }, --- ['current']={ "a", "b", "c" }, --- }, --- ['lookups']={ "ls_l_1", "ls_l_1", "ls_l_1" }, - -function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n) +function chainmores.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) return start, false end -- handled later: -- --- function chainmores.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) --- return chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- function chainmores.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) -- end -function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) return start, false end -function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) + +function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) return start, false end -- handled later: -- --- function chainmores.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) --- return chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- function chainmores.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) -- end local function logprocess(...) @@ -930,7 +907,7 @@ local logwarning = report_chain -- We could share functions but that would lead to extra function calls with many -- arguments, redundant tests and confusing messages. -function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname) +function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) return start, false end @@ -939,7 +916,7 @@ end -- in a bit weird way. There is no lookup and the replacement comes from the lookup -- itself. It is meant mostly for dealing with Urdu. -function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements) +function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,lookuphash,replacements) local char = start.char local replacement = replacements[char] if replacement then @@ -985,18 +962,21 @@ end match.

--ldx]]-- -function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) +function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -- todo: marks ? if not chainindex then - delete_till_stop(start,stop) -- ,currentlookup.flags[1]) + delete_till_stop(start,stop) -- ,currentlookup.flags[1] end local current = start local subtables = currentlookup.subtables +if #subtables > 1 then + log_warning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) +end while current do if current.id == glyph_code then local currentchar = current.char local lookupname = subtables[1] - local replacement = cache.gsub_single[lookupname] + local replacement = lookuphash[lookupname] if not replacement then if trace_bugs then logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) @@ -1031,12 +1011,12 @@ chainmores.gsub_single = chainprocs.gsub_single the match.

--ldx]]-- -function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) delete_till_stop(start,stop) local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local replacements = cache.gsub_multiple[lookupname] + local replacements = lookuphash[lookupname] if not replacements then if trace_bugs then logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) @@ -1051,21 +1031,7 @@ function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache if trace_multiples then logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) end - local sn = start.next - for k=1,#replacements do - if k == 1 then - start.char = replacements[k] - else - local n = copy_node(start) -- maybe delete the components and such - n.char = replacements[k] - n.next, n.prev = sn, start - if sn then - sn.prev = n - end - start.next, start = n, n - end - end - return start, true + return multiple_glyphs(start,replacements) end end return start, false @@ -1075,7 +1041,7 @@ end

Here we replace start by new glyph. First we delete the rest of the match.

--ldx]]-- -function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -- todo: marks ? delete_till_stop(start,stop) local current = start @@ -1084,7 +1050,7 @@ function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cach if current.id == glyph_code then local currentchar = current.char local lookupname = subtables[1] - local alternatives = cache.gsub_alternate[lookupname] + local alternatives = lookuphash[lookupname] if not alternatives then if trace_bugs then logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname)) @@ -1119,11 +1085,11 @@ this function (move code inline and handle the marks by a separate function). We assume rather stupid ligatures (no complex disc nodes).

--ldx]]-- -function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) +function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local ligatures = cache.gsub_ligature[lookupname] + local ligatures = lookuphash[lookupname] if not ligatures then if trace_bugs then logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) @@ -1146,21 +1112,21 @@ function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache if marks[schar] then -- marks s = s.next else - local lg = ligatures[1][schar] - if not lg then - break - else + local lg = ligatures[schar] + if lg then ligatures, last, nofreplacements = lg, s, nofreplacements + 1 if s == stop then break else s = s.next end + else + break end end end end - local l2 = ligatures[2] + local l2 = ligatures.ligature if l2 then if chainindex then stop = last @@ -1188,12 +1154,12 @@ end chainmores.gsub_ligature = chainprocs.gsub_ligature -function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then local subtables = currentlookup.subtables local lookupname = subtables[1] - local markanchors = cache.gpos_mark2base[lookupname] + local markanchors = lookuphash[lookupname] if markanchors then markanchors = markanchors[markchar] end @@ -1226,7 +1192,7 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cach if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1252,12 +1218,12 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cach return start, false end -function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then local subtables = currentlookup.subtables local lookupname = subtables[1] - local markanchors = cache.gpos_mark2ligature[lookupname] + local markanchors = lookuphash[lookupname] if markanchors then markanchors = markanchors[markchar] end @@ -1299,7 +1265,7 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, if ma then ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,index) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) @@ -1326,7 +1292,7 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, return start, false end -function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then --~ local alreadydone = markonce and has_attribute(start,markmark) @@ -1334,7 +1300,7 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cach -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark local subtables = currentlookup.subtables local lookupname = subtables[1] - local markanchors = cache.gpos_mark2mark[lookupname] + local markanchors = lookuphash[lookupname] if markanchors then markanchors = markanchors[markchar] end @@ -1351,7 +1317,7 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cach if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -1382,13 +1348,13 @@ end -- ! ! ! untested ! ! ! -function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local alreadydone = cursonce and has_attribute(start,cursbase) if not alreadydone then local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local exitanchors = cache.gpos_cursive[lookupname] + local exitanchors = lookuphash[lookupname] if exitanchors then exitanchors = exitanchors[startchar] end @@ -1417,7 +1383,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, if al[anchor] then local exit = exitanchors[anchor] if exit then - local dx, dy, bound = setcursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end @@ -1430,7 +1396,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.registermessage(currentfont,startchar,"no entry anchors") + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) end break end @@ -1447,16 +1413,16 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, return start, false end -function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) - -- untested +function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + -- untested .. needs checking for the new model local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local kerns = cache.gpos_single[lookupname] + local kerns = lookuphash[lookupname] if kerns then kerns = kerns[startchar] if kerns then - local dx, dy, w, h = setpair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) end @@ -1467,19 +1433,20 @@ end -- when machines become faster i will make a shared function -function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) +function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -- logwarning("%s: gpos_pair not yet supported",cref(kind,chainname,chainlookupname)) local snext = start.next if snext then local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local kerns = cache.gpos_pair[lookupname] + local kerns = lookuphash[lookupname] if kerns then kerns = kerns[startchar] if kerns then + local lookuptype = lookuptypes[lookupname] local prev, done = start, false - local factor = tfmdata.factor + local factor = tfmdata.parameters.factor while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do local nextchar = snext.char local krn = kerns[nextchar] @@ -1490,8 +1457,8 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,cur if not krn then -- skip elseif type(krn) == "table" then - if krn[1] == "pair" then - local a, b = krn[3], krn[4] + if lookuptype == "pair" then + local a, b = krn[2], krn[3] if a and #a > 0 then local startchar = start.char local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) @@ -1508,7 +1475,7 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,cur end else report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) - local a, b = krn[3], krn[7] + local a, b = krn[2], krn[6] if a and a ~= 0 then local k = setkern(snext,factor,rlmode,a) if trace_kerns then @@ -1553,7 +1520,7 @@ local function show_skip(kind,chainname,char,ck,class) end end -local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) +local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,lookuphash) -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] local flags, done = sequence.flags, false local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] @@ -1754,35 +1721,11 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local chainlookup = lookuptable[chainlookupname] local cp = chainprocs[chainlookup.type] if cp then - start, done = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,nil,sequence) + start, done = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) else logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) end else - -- actually this needs a more complex treatment for which we will use chainmores ---~ local i = 1 ---~ repeat ---~ local chainlookupname = chainlookups[i] ---~ local chainlookup = lookuptable[chainlookupname] ---~ local cp = chainmores[chainlookup.type] ---~ if cp then ---~ local ok, n ---~ start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) ---~ -- messy since last can be changed ! ---~ if ok then ---~ done = true ---~ start = start.next ---~ if n then ---~ -- skip next one(s) if ligature ---~ i = i + n - 1 ---~ end ---~ end ---~ else ---~ logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) ---~ end ---~ i = i + 1 ---~ until i > nofchainlookups - local i = 1 repeat if skipped then @@ -1806,7 +1749,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local cp = chainmores[chainlookup.type] if cp then local ok, n - start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) + start, ok, n = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) -- messy since last can be changed ! if ok then done = true @@ -1826,7 +1769,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence else local replacements = ck[7] if replacements then - start, done = chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements) -- sequence + start, done = chainprocs.reversesub(start,last,kind,chainname,ck,lookuphash,replacements) -- sequence else done = true -- can be meant to be skipped if trace_contexts then @@ -1891,122 +1834,120 @@ local function report_missing_cache(typ,lookup) local t = f[typ] if not t then t = { } f[typ] = t end if not t[lookup] then t[lookup] = true - logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname) + logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.properties.fullname) end end local resolved = { } -- we only resolve a font,script,language pair once -- todo: pass all these 'locals' in a table --- --- dynamics will be isolated some day ... for the moment we catch attribute zero --- not being set -function fonts.methods.node.otf.features(head,font,attr) - if trace_steps then - checkstep(head) - end - tfmdata = fontdata[font] - local shared = tfmdata.shared - otfdata = shared.otfdata - local luatex = otfdata.luatex - descriptions = tfmdata.descriptions - characters = tfmdata.characters - indices = tfmdata.indices - unicodes = tfmdata.unicodes - marks = tfmdata.marks - anchorlookups = luatex.lookup_to_anchor - currentfont = font - rlmode = 0 - local featuredata = otfdata.shared.featuredata -- can be made local to closure - local sequences = luatex.sequences - lookuptable = luatex.lookups - local done = false - local script, language, s_enabled, a_enabled, dyn - local attribute_driven = attr and attr ~= 0 - if attribute_driven then - local features = contextsetups[contextnumbers[attr]] -- could be a direct list - dyn = contextmerged[attr] or 0 - language, script = features.language or "dflt", features.script or "dflt" - a_enabled = features -- shared.features -- can be made local to the resolver - if dyn == 2 or dyn == -2 then - -- font based - s_enabled = shared.features +local lookuphashes = { } + +setmetatable(lookuphashes, { __index = + function(t,font) + local lookuphash = fontdata[font].resources.lookuphash + if not lookuphash or not next(lookuphash) then + lookuphash = false end - else - language, script = tfmdata.language or "dflt", tfmdata.script or "dflt" - s_enabled = shared.features -- can be made local to the resolver - dyn = 0 + t[font] = lookuphash + return lookuphash end - -- we can save some runtime by caching feature tests - 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 ra = { } rl [attr] = ra end -- attr can be false - -- sequences always > 1 so no need for optimization - for s=1,#sequences do - local pardir, txtdir, success = 0, { }, false - local sequence = sequences[s] - local r = ra[s] -- cache - if r == nil then - -- - -- this bit will move to font-ctx and become a function - --- - local chain = sequence.chain or 0 - local features = sequence.features - if not features then - -- indirect lookup, part of chain (todo: make this a separate table) - r = false -- { false, false, chain } - else - local valid, attribute, kind, what = false, false - for k,v in next, features do - -- we can quit earlier but for the moment we want the tracing - local s_e = s_enabled and s_enabled[k] - local a_e = a_enabled and a_enabled[k] - if s_e or a_e then - local l = v[script] or v[wildcard] - if l then - -- not l[language] or l[default] or l[wildcard] because we want tracing - -- only first attribute match check, so we assume simple fina's - -- default can become a font feature itself - if l[language] then - valid, what = s_e or a_e, language - -- elseif l[default] then - -- valid, what = true, default - elseif l[wildcard] then - valid, what = s_e or a_e, wildcard - end - if valid then - kind, attribute = k, special_attributes[k] or false - if a_e and dyn < 0 then - valid = false - end - if trace_applied then - local typ, action = match(sequence.type,"(.*)_(.*)") - report_process( - "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", - (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) - end - break - end - end - end - end - if valid then - r = { valid, attribute, chain, kind } - else - r = false -- { valid, attribute, chain, "generic" } -- false anyway, could be flag instead of table +}) + +-- fonts.hashes.lookups = lookuphashes + +local special_attributes = { + init = 1, + medi = 2, + fina = 3, + isol = 4 +} + +local function initialize(sequence,script,language,enabled) + local features = sequence.features + if features then + for kind, scripts in next, features do + local valid = enabled[kind] + if valid then + local languages = scripts[script] or scripts[wildcard] + if languages and (languages[language] or languages[wildcard]) then + return { valid, special_attributes[kind] or false, sequence.chain or 0, kind } end end - ra[s] = r end - featurevalue = r and r[1] -- todo: pass to function instead of using a global + end + return false +end + +function otf.dataset(ftfmdata,sequences,font) -- generic variant, overloaded in context + local shared = tfmdata.shared + local properties = tfmdata.properties + local language = properties.language or "dflt" + local script = properties.script or "dflt" + local enabled = shared.features + local 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 + setmetatable(rl, { __index = function(t,k) + local v = enabled and initialize(sequences[k],script,language,enabled) + t[k] = v + return v + end}) + end + return rl +end + +local function featuresprocessor(head,font,attr) + + local lookuphash = lookuphashes[font] -- we can also check sequences here + + if not lookuphash then + return head, false + end + + if trace_steps then + checkstep(head) + end + + tfmdata = fontdata[font] + descriptions = tfmdata.descriptions + characters = tfmdata.characters + resources = tfmdata.resources + + marks = resources.marks + anchorlookups = resources.lookup_to_anchor + lookuptable = resources.lookups + lookuptypes = resources.lookuptypes + + currentfont = font + rlmode = 0 + + local sequences = resources.sequences + local done = false + local datasets = otf.dataset(tfmdata,sequences,font,attr) + + for s=1,#sequences do + local pardir, txtdir, success = 0, { }, false -- we could reuse txtdir and use a top pointer + local sequence = sequences[s] + local dataset = datasets[s] -- cache + featurevalue = dataset and dataset[1] -- todo: pass to function instead of using a global if featurevalue then - local attribute, chain, typ, subtables = r[2], r[3], sequence.type, sequence.subtables + local attribute, chain, typ, subtables = dataset[2], dataset[3], sequence.type, sequence.subtables if chain < 0 then -- this is a limited case, no special treatments like 'init' etc local handler = handlers[typ] - local thecache = featuredata[typ] or { } -- we need to get rid of this slide ! local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo while start do @@ -2022,11 +1963,11 @@ function fonts.methods.node.otf.features(head,font,attr) if a then for i=1,#subtables do local lookupname = subtables[i] - local lookupcache = thecache[lookupname] + local lookupcache = lookuphash[lookupname] if lookupcache then local lookupmatch = lookupcache[start.char] if lookupmatch then - start, success = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + start, success = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if success then break end @@ -2049,12 +1990,11 @@ function fonts.methods.node.otf.features(head,font,attr) else local handler = handlers[typ] local ns = #subtables - local thecache = featuredata[typ] or { } local start = head -- local ? rlmode = 0 -- to be checked ? if ns == 1 then local lookupname = subtables[1] - local lookupcache = thecache[lookupname] + local lookupcache = lookuphash[lookupname] if not lookupcache then report_missing_cache(typ,lookupname) else @@ -2073,7 +2013,7 @@ function fonts.methods.node.otf.features(head,font,attr) if lookupmatch then -- sequence kan weg local ok - start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) + start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) if ok then success = true end @@ -2155,13 +2095,13 @@ function fonts.methods.node.otf.features(head,font,attr) if a then for i=1,ns do local lookupname = subtables[i] - local lookupcache = thecache[lookupname] + local lookupcache = lookuphash[lookupname] if lookupcache then local lookupmatch = lookupcache[start.char] if lookupmatch then -- we could move all code inline but that makes things even more unreadable local ok - start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if ok then success = true break @@ -2246,305 +2186,156 @@ function fonts.methods.node.otf.features(head,font,attr) return head, done end -otf.features.prepare = { } - --- we used to share code in the following functions but that costs a lot of --- memory due to extensive calls to functions (easily hundreds of thousands per --- document) - -local function split(replacement,original,cache,unicodes) - -- we can cache this too, but not the same (although unicode is a unique enough hash) - local o, t, n, no = { }, { }, 0, 0 - for s in gmatch(original,"[^ ]+") do - local us = unicodes[s] - no = no + 1 - if type(us) == "number" then -- tonumber(us) - o[no] = us - else - o[no] = us[1] - end - end - for s in gmatch(replacement,"[^ ]+") do - n = n + 1 - local us = unicodes[s] - if type(us) == "number" then -- tonumber(us) - t[o[n]] = us - else - t[o[n]] = us[1] - end +local function generic(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if target then + target[unicode] = lookupdata + else + lookuphash[lookupname] = { [unicode] = lookupdata } end - return t end -local function uncover(covers,result,cache,unicodes) - -- lpeg hardly faster (.005 sec on mk) - local nofresults = #result - for n=1,#covers do - local c = covers[n] - local cc = cache[c] - nofresults = nofresults + 1 - if not cc then - local t = { } - for s in gmatch(c,"[^ ]+") do - local us = unicodes[s] - if type(us) == "number" then - t[us] = true - else - for i=1,#us do - t[us[i]] = true - end - end +local action = { + + substitution = generic, + multiple = generic, + alternate = generic, + position = generic, + + ligature = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + for i=1,#lookupdata do + local li = lookupdata[i] + local tu = target[li] + if not tu then + tu = { } + target[li] = tu end - cache[c] = t - result[nofresults] = t + target = tu + end + target.ligature = unicode + end, + + pair = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + local others = target[unicode] + local paired = lookupdata[1] + if others then + others[paired] = lookupdata else - result[nofresults] = cc + others = { [paired] = lookupdata } + target[unicode] = others end - end -end + end, + +} local function prepare_lookups(tfmdata) - local otfdata = tfmdata.shared.otfdata - local featuredata = otfdata.shared.featuredata - local anchor_to_lookup = otfdata.luatex.anchor_to_lookup - local lookup_to_anchor = otfdata.luatex.lookup_to_anchor - -- - local multiple = featuredata.gsub_multiple - local alternate = featuredata.gsub_alternate - local single = featuredata.gsub_single - local ligature = featuredata.gsub_ligature - local pair = featuredata.gpos_pair - local position = featuredata.gpos_single - local kerns = featuredata.gpos_pair - local mark = featuredata.gpos_mark2mark - local cursive = featuredata.gpos_cursive - -- - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local descriptions = tfmdata.descriptions - -- - -- we can change the otf table after loading but then we need to adapt base mode - -- as well (no big deal) - -- - local action = { - substitution = function(p,lookup,glyph,unicode) - local old, new = unicode, unicodes[p[2]] - if type(new) == "table" then - new = new[1] - end - local s = single[lookup] - if not s then s = { } single[lookup] = s end - s[old] = new - --~ if trace_lookups then - --~ report_prepare("lookup %s: substitution %s => %s",lookup,old,new) - --~ end - end, - multiple = function (p,lookup,glyph,unicode) - local old, new, nnew = unicode, { }, 0 - local m = multiple[lookup] - if not m then m = { } multiple[lookup] = m end - m[old] = new - for pc in gmatch(p[2],"[^ ]+") do - local upc = unicodes[pc] - nnew = nnew + 1 - if type(upc) == "number" then - new[nnew] = upc - else - new[nnew] = upc[1] - end - end - --~ if trace_lookups then - --~ report_prepare("lookup %s: multiple %s => %s",lookup,old,concat(new," ")) - --~ end - end, - alternate = function(p,lookup,glyph,unicode) - local old, new, nnew = unicode, { }, 0 - local a = alternate[lookup] - if not a then a = { } alternate[lookup] = a end - a[old] = new - for pc in gmatch(p[2],"[^ ]+") do - local upc = unicodes[pc] - nnew = nnew + 1 - if type(upc) == "number" then - new[nnew] = upc - else - new[nnew] = upc[1] - end - end - --~ if trace_lookups then - --~ report_prepare("lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) - --~ end - end, - ligature = function (p,lookup,glyph,unicode) - --~ if trace_lookups then - --~ report_prepare("lookup %s: ligature %s => %s",lookup,p[2],glyph.name) - --~ end - local first = true - local t = ligature[lookup] - if not t then t = { } ligature[lookup] = t end - for s in gmatch(p[2],"[^ ]+") do - if first then - local u = unicodes[s] - if not u then - report_prepare("lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) - break - elseif type(u) == "number" then - if not t[u] then - t[u] = { { } } - end - t = t[u] - else - local tt = t - local tu - for i=1,#u do - local u = u[i] - if i==1 then - if not t[u] then - t[u] = { { } } - end - tu = t[u] - t = tu - else - if not t[u] then - tt[u] = tu - end - end - end - end - first = false - else - s = unicodes[s] - local t1 = t[1] - if not t1[s] then - t1[s] = { { } } - end - t = t1[s] - end - end - t[2] = unicode - end, - position = function(p,lookup,glyph,unicode) - -- not used - local s = position[lookup] - if not s then s = { } position[lookup] = s end - s[unicode] = p[2] -- direct pointer to kern spec - end, - pair = function(p,lookup,glyph,unicode) - local s = pair[lookup] - if not s then s = { } pair[lookup] = s end - local others = s[unicode] - if not others then others = { } s[unicode] = others end - -- todo: fast check for space - local two = p[2] - local upc = unicodes[two] - if not upc then - for pc in gmatch(two,"[^ ]+") do - local upc = unicodes[pc] - if type(upc) == "number" then - others[upc] = p -- direct pointer to main table - else - for i=1,#upc do - others[upc[i]] = p -- direct pointer to main table - end - end - end - elseif type(upc) == "number" then - others[upc] = p -- direct pointer to main table - else - for i=1,#upc do - others[upc[i]] = p -- direct pointer to main table - end - end - --~ if trace_lookups then - --~ report_prepare("lookup %s: pair for U+%04X",lookup,unicode) - --~ end - end, - } - -- - for unicode, glyph in next, descriptions do - local lookups = glyph.slookups + + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local anchor_to_lookup = resources.anchor_to_lookup + local lookup_to_anchor = resources.lookup_to_anchor + local lookuptypes = resources.lookuptypes + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + + -- we cannot free the entries in the descriptions as sometimes we access + -- then directly (for instance anchors) ... selectively freeing does save + -- much memory as it's only a reference to a table and the slot in the + -- description hash is not freed anyway + + for unicode, character in next, characters do -- we cannot loop over descriptions ! + + local description = descriptions[unicode] + + local lookups = description.slookups if lookups then - for lookup, p in next, lookups do - action[p[1]](p,lookup,glyph,unicode) + for lookupname, lookupdata in next, lookups do + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) end end - local lookups = glyph.mlookups + + local lookups = description.mlookups if lookups then - for lookup, whatever in next, lookups do - for i=1,#whatever do -- normaly one - local p = whatever[i] - action[p[1]](p,lookup,glyph,unicode) + for lookupname, lookuplist in next, lookups do + local lookuptype = lookuptypes[lookupname] + for l=1,#lookuplist do + local lookupdata = lookuplist[l] + action[lookuptype](lookupdata,lookupname,unicode,lookuphash) end end end - local list = glyph.kerns + + local list = description.kerns if list then - for lookup, krn in next, list do - local k = kerns[lookup] - if not k then k = { } kerns[lookup] = k end - k[unicode] = krn -- ref to glyph, saves lookup - --~ if trace_lookups then - --~ report_prepare("lookup %s: kern for U+%04X",lookup,unicode) - --~ end + for lookup, krn in next, list do -- ref to glyph, saves lookup + local target = lookuphash[lookup] + if target then + target[unicode] = krn + else + lookuphash[lookup] = { [unicode] = krn } + end end end - local oanchor = glyph.anchors - if oanchor then - for typ, anchors in next, oanchor do -- types - if typ == "mark" then - for name, anchor in next, anchors do - local lookups = anchor_to_lookup[name] - if lookups then - for lookup, _ in next, lookups do - local f = mark[lookup] - if not f then f = { } mark[lookup] = f end - f[unicode] = anchors -- ref to glyph, saves lookup - --~ if trace_lookups then - --~ report_prepare("lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) - --~ end - end - end - end - elseif typ == "cexit" then -- or entry? + + local list = description.anchors + if list then + for typ, anchors in next, list do -- types + if typ == "mark" or typ == "cexit" then -- or entry? for name, anchor in next, anchors do local lookups = anchor_to_lookup[name] if lookups then for lookup, _ in next, lookups do - local f = cursive[lookup] - if not f then f = { } cursive[lookup] = f end - f[unicode] = anchors -- ref to glyph, saves lookup - --~ if trace_lookups then - --~ report_prepare("lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) - --~ end + local target = lookuphash[lookup] + if target then + target[unicode] = anchors + else + lookuphash[lookup] = { [unicode] = anchors } + end end end end end end end + + end + +end + +local function split(replacement,original) + local result = { } + for i=1,#replacement do + result[original[i]] = replacement[i] end + return result end --- local cache = { } -luatex = luatex or {} -- this has to change ... we need a better one +local function uncover(covers,result) -- will change (we can store this in the raw table) + local nofresults = #result + for n=1,#covers do + nofresults = nofresults + 1 + result[nofresults] = covers[n] + end +end local function prepare_contextchains(tfmdata) - local otfdata = tfmdata.shared.otfdata - local lookups = otfdata.lookups + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local lookups = rawdata.lookups if lookups then - local featuredata = otfdata.shared.featuredata - local contextchain = featuredata.gsub_contextchain -- shared with gpos - local reversecontextchain = featuredata.gsub_reversecontextchain -- shared with gpos - local characters = tfmdata.characters - local unicodes = tfmdata.unicodes - local indices = tfmdata.indices - local cache = luatex.covers - if not cache then - cache = { } - luatex.covers = cache - end - -- - for lookupname, lookupdata in next, otfdata.lookups do + for lookupname, lookupdata in next, rawdata.lookups do local lookuptype = lookupdata.type if not lookuptype then report_prepare("missing lookuptype for %s",lookupname) @@ -2552,39 +2343,37 @@ local function prepare_contextchains(tfmdata) local rules = lookupdata.rules if rules then local fmt = lookupdata.format - -- contextchain[lookupname][unicode] - if fmt == "coverage" then + -- lookuphash[lookupname][unicode] + if fmt == "coverage" then -- or fmt == "class" (converted into "coverage") if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then + -- todo: dejavu-serif has one (but i need to see what use it has) report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) else - local contexts = contextchain[lookupname] + local contexts = lookuphash[lookupname] if not contexts then contexts = { } - contextchain[lookupname] = contexts + lookuphash[lookupname] = contexts end local t, nt = { }, 0 for nofrules=1,#rules do -- does #rules>1 happen often? local rule = rules[nofrules] - local coverage = rule.coverage - if coverage and coverage.current then - local current, before, after, sequence = coverage.current, coverage.before, coverage.after, { } - if before then - uncover(before,sequence,cache,unicodes) - end - local start = #sequence + 1 - uncover(current,sequence,cache,unicodes) - local stop = #sequence - if after then - uncover(after,sequence,cache,unicodes) - end - if sequence[1] then - nt = nt + 1 - t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } - for unic, _ in next, sequence[start] do - local cu = contexts[unic] - if not cu then - contexts[unic] = t - end + local current, before, after, sequence = rule.current, rule.before, rule.after, { } + if before then + uncover(before,sequence) + end + local start = #sequence + 1 + uncover(current,sequence) + local stop = #sequence + if after then + uncover(after,sequence) + end + if sequence[1] then + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t end end end @@ -2594,79 +2383,69 @@ local function prepare_contextchains(tfmdata) if lookuptype ~= "reversesub" then report_prepare("unsupported reverse coverage %s for %s",lookuptype,lookupname) else - local contexts = reversecontextchain[lookupname] + local contexts = lookuphash[lookupname] if not contexts then contexts = { } - reversecontextchain[lookupname] = contexts + lookuphash[lookupname] = contexts end local t, nt = { }, 0 for nofrules=1,#rules do local rule = rules[nofrules] - local reversecoverage = rule.reversecoverage - if reversecoverage and reversecoverage.current then - local current, before, after, replacements, sequence = reversecoverage.current, reversecoverage.before, reversecoverage.after, reversecoverage.replacements, { } - if before then - uncover(before,sequence,cache,unicodes) - end - local start = #sequence + 1 - uncover(current,sequence,cache,unicodes) - local stop = #sequence - if after then - uncover(after,sequence,cache,unicodes) - end - if replacements then - replacements = split(replacements,current[1],cache,unicodes) - end - if sequence[1] then - -- this is different from normal coverage, we assume only replacements - nt = nt + 1 - t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } - for unic, _ in next, sequence[start] do - local cu = contexts[unic] - if not cu then - contexts[unic] = t - end + local current, before, after, replacements, sequence = rule.current, rule.before, rule.after, rule.replacements, { } + if before then + uncover(before,sequence) + end + local start = #sequence + 1 + uncover(current,sequence) + local stop = #sequence + if after then + uncover(after,sequence) + end + if replacements then + replacements = split(replacements,current[1]) + end + if sequence[1] then + -- this is different from normal coverage, we assume only replacements + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t end end end end end - elseif fmt == "glyphs" then + elseif fmt == "glyphs" then --maybe just make then before = { fore } and share with coverage if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) else - local contexts = contextchain[lookupname] + local contexts = lookuphash[lookupname] if not contexts then contexts = { } - contextchain[lookupname] = contexts + lookuphash[lookupname] = contexts end local t, nt = { }, 0 for nofrules=1,#rules do - -- nearly the same as coverage so we could as well rename it local rule = rules[nofrules] - local glyphs = rule.glyphs - if glyphs and glyphs.names then - local fore, back, names, sequence = glyphs.fore, glyphs.back, glyphs.names, { } - if fore and fore ~= "" then - fore = lpegmatch(split_at_space,fore) - uncover(fore,sequence,cache,unicodes) - end - local start = #sequence + 1 - names = lpegmatch(split_at_space,names) - uncover(names,sequence,cache,unicodes) - local stop = #sequence - if back and back ~= "" then - back = lpegmatch(split_at_space,back) - uncover(back,sequence,cache,unicodes) - end - if sequence[1] then - nt = nt + 1 - t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } - for unic, _ in next, sequence[start] do - local cu = contexts[unic] - if not cu then - contexts[unic] = t - end + local current, before, after, sequence = rule.names, rule.fore, rule.back, { } + if before then + uncover(before,sequence) + end + local start = #sequence + 1 + uncover(current,sequence) + local stop = #sequence + if after then + uncover(after,sequence) + end + if sequence[1] then + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t end end end @@ -2679,34 +2458,36 @@ local function prepare_contextchains(tfmdata) end end -function fonts.initializers.node.otf.features(tfmdata,value) +-- we can consider lookuphash == false (initialized but empty) vs lookuphash == table + +local function featuresinitializer(tfmdata,value) if true then -- value then - if not tfmdata.shared.otfdata.shared.initialized then - local t = trace_preparing and os.clock() - local otfdata = tfmdata.shared.otfdata - local featuredata = otfdata.shared.featuredata - -- caches - featuredata.gsub_multiple = { } - featuredata.gsub_alternate = { } - featuredata.gsub_single = { } - featuredata.gsub_ligature = { } - featuredata.gsub_contextchain = { } - featuredata.gsub_reversecontextchain = { } - featuredata.gpos_pair = { } - featuredata.gpos_single = { } - featuredata.gpos_mark2base = { } - featuredata.gpos_mark2ligature = featuredata.gpos_mark2base - featuredata.gpos_mark2mark = featuredata.gpos_mark2base - featuredata.gpos_cursive = { } - featuredata.gpos_contextchain = featuredata.gsub_contextchain - featuredata.gpos_reversecontextchain = featuredata.gsub_reversecontextchain - -- + -- beware we need to use the topmost properties table + local rawdata = tfmdata.shared.rawdata + local properties = rawdata.properties + if not properties.initialized then + local starttime = trace_preparing and os.clock() + local resources = rawdata.resources + resources.lookuphash = resources.lookuphash or { } prepare_contextchains(tfmdata) prepare_lookups(tfmdata) - otfdata.shared.initialized = true + properties.initialized = true if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + report_prepare("preparation time is %0.3f seconds for %s",os.clock()-starttime,tfmdata.properties.fullname or "?") end end end end + +registerotffeature { + name = "features", + description = "features", + default = true, + initializers = { + position = 1, + node = featuresinitializer, + }, + processors = { + node = featuresprocessor, + } +} diff --git a/tex/context/base/font-otp.lua b/tex/context/base/font-otp.lua index 221c127f2..55ddd539e 100644 --- a/tex/context/base/font-otp.lua +++ b/tex/context/base/font-otp.lua @@ -8,31 +8,39 @@ if not modules then modules = { } end modules ['font-otp'] = { -- todo: pack math (but not that much to share) -local next, type, tostring = next, type, tostring +local next, type = next, type local sort, concat = table.sort, table.concat local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +local report_otf = logs.reporter("fonts","otf loading") -local report_otf = logs.reporter("fonts","otf loading") +-- also used in other scripts so we need to check some tables: -fonts = fonts or { } -- this module is also used in mtxrun -local fonts = fonts -fonts.otf = fonts.otf or { } -- this module is also used in mtxrun -local otf = fonts.otf +fonts = fonts or { } +fonts.handlers = fonts.handlers or { } +local handlers = fonts.handlers +handlers.otf = handlers.otf or { } +local otf = handlers.otf +otf.enhancers = otf.enhancers or { } +local enhancers = otf.enhancers +otf.glists = otf.glists or { "gsub", "gpos" } +local glists = otf.glists -otf.enhancers = otf.enhancers or { } -local enhancers = otf.enhancers -otf.glists = otf.glists or { "gsub", "gpos" } -- these can be extended so no local here +local criterium = 1 +local threshold = 0 -local criterium, threshold, tabstr = 1, 0, table.serialize - -local function tabstr(t) -- hashed from core-uti / experiment - local s = { } +local function tabstr(t) + local s, n = { }, 0 for k, v in next, t do + n = n + 1 if type(v) == "table" then - s[#s+1] = k .. "={" .. tabstr(v) .. "}" + s[n] = k .. "={" .. tabstr(v) .. "}" + elseif v == true then + s[n] = k .. "=true" + elseif v then + s[n] = k .. "=" .. v else - s[#s+1] = k .. "=" .. tostring(v) + s[n] = k .. "=false" end end sort(s) @@ -43,12 +51,14 @@ local function packdata(data) if data then local h, t, c = { }, { }, { } local hh, tt, cc = { }, { }, { } - local function pack_1(v) + local nt, ntt = 0, 0 + local function pack_1(v,indexed) -- v == table - local tag = tabstr(v) + local tag = indexed and concat(v," ") or tabstr(v) local ht = h[tag] if not ht then - ht = #t+1 + nt = nt + 1 + ht = nt t[ht] = v h[tag] = ht c[ht] = 1 @@ -57,7 +67,7 @@ local function packdata(data) end return ht end - local function pack_2(v) + local function pack_2(v,indexed) -- v == number if c[v] <= criterium then return t[v] @@ -65,7 +75,8 @@ local function packdata(data) -- compact hash local hv = hh[v] if not hv then - hv = #tt+1 + ntt = ntt + 1 + hv = ntt tt[hv] = t[v] hh[v] = hv cc[hv] = c[v] @@ -74,12 +85,12 @@ local function packdata(data) end end local function success(stage,pass) - if #t == 0 then + if nt == 0 then if trace_loading then report_otf("pack quality: nothing to pack") end return false - elseif #t >= threshold then + elseif nt >= threshold then local one, two, rest = 0, 0, 0 if pass == 1 then for k,v in next, c do @@ -93,9 +104,9 @@ local function packdata(data) end else for k,v in next, cc do - if v >20 then + if v > 20 then rest = rest + 1 - elseif v >10 then + elseif v > 10 then two = two + 1 else one = one + 1 @@ -109,152 +120,151 @@ local function packdata(data) return true else if trace_loading then - report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)", stage, pass, #t, threshold) + report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)", stage, pass, nt, threshold) end return false end end + local resources = data.resources + local lookuptypes = resources.lookuptypes for pass=1,2 do local pack = (pass == 1 and pack_1) or pack_2 - for k, v in next, data.glyphs do - v.boundingbox = pack(v.boundingbox) - local l = v.slookups - if l then - for k,v in next, l do - l[k] = pack(v) - end - end - local l = v.mlookups - if l then - for k,v in next, l do - for kk=1,#v do - local vkk = v[kk] - local what = vkk[1] - if what == "pair" then - local t = vkk[3] if t then vkk[3] = pack(t) end - local t = vkk[4] if t then vkk[4] = pack(t) end - elseif what == "position" then - local t = vkk[2] if t then vkk[2] = pack(t) end + for unicode, description in next, data.descriptions do + local boundingbox = description.boundingbox + if boundingbox then + description.boundingbox = pack(boundingbox,true) + 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(t,true) end + local t = slookup[3] if t then slookup[3] = pack(t,true) end + elseif what ~= "substitution" then + slookups[tag] = pack(slookup) + 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(t,true) end + local t = lookup[3] if t then lookup[3] = pack(t,true) end + end + elseif what ~= "substitution" then + for i=1,#mlookup do + mlookup[i] = pack(mlookup[i]) -- true end - -- v[kk] = pack(vkk) end end end - local m = v.kerns - if m then - for k,v in next, m do - m[k] = pack(v) + local kerns = description.kerns + if kerns then + for tag, kern in next, kerns do + kerns[tag] = pack(kern) end end - local m = v.math - if m then - local mk = m.kerns - if mk then - for k,v in next, mk do - mk[k] = pack(v) + local math = description.math + if math then + local kerns = math.kerns + if kerns then + for tag, kern in next, kerns do + kerns[tag] = pack(kern) end end end - local a = v.anchors - if a then - for k,v in next, a do - if k == "baselig" then - for kk, vv in next, v do - for kkk=1,#vv do - vv[kkk] = pack(vv[kkk]) + 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(a[k]) end end else - for kk, vv in next, v do - v[kk] = pack(vv) + for k, v in next, anchor do + anchor[k] = pack(v) end end end end end - if data.lookups then - for k, v in next, data.lookups do - if v.rules then - for kk, vv in next, v.rules do - local l = vv.lookups - if l then - vv.lookups = pack(l) - end - local c = vv.coverage - if c then - local cc = c.before if cc then c.before = pack(cc) end - local cc = c.after if cc then c.after = pack(cc) end - local cc = c.current if cc then c.current = pack(cc) end - end - local c = vv.reversecoverage - if c then - local cc = c.before if cc then c.before = pack(cc) end - local cc = c.after if cc then c.after = pack(cc) end - local cc = c.current if cc then c.current = pack(cc) end - end - -- no need to pack vv.glyphs - local c = vv.glyphs - if c then - if c.fore == "" then c.fore = nil end - if c.back == "" then c.back = nil 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 for i=1,#r do r[i] = pack(r[i],true) end end + local r = rule.after if r then for i=1,#r do r[i] = pack(r[i],true) end end + local r = rule.current if r then for i=1,#r do r[i] = pack(r[i],true) end end + local r = rule.replacements if r then rule.replacements = pack(r, true) end + local r = rule.fore if r then rule.fore = pack(r, true) end + local r = rule.back if r then rule.back = pack(r, true) end + local r = rule.names if r then rule.names = pack(r, true) end + local r = rule.lookups if r then rule.lookups = pack(r) end end end end end - if data.luatex then - local la = data.luatex.anchor_to_lookup - if la then - for lookup, ldata in next, la do - la[lookup] = pack(ldata) - 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(lookup) end - local la = data.luatex.lookup_to_anchor - if la then - for lookup, ldata in next, la do - la[lookup] = pack(ldata) - 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(anchor) end - local ls = data.luatex.sequences - if ls then - for feature, fdata in next, ls do - local flags = fdata.flags - if flags then - fdata.flags = pack(flags) - end - local subtables = fdata.subtables - if subtables then - fdata.subtables = pack(subtables) - end - local features = fdata.features - if features then - for script, sdata in next, features do - features[script] = pack(sdata) - 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(flags) + end + local subtables = sequence.subtables + if subtables then + sequence.subtables = pack(subtables) + end + local features = sequence.features + if features then + for script, feature in next, features do + features[script] = pack(feature) end end end - local ls = data.luatex.lookups - if ls then - for lookup, fdata in next, ls do - local flags = fdata.flags - if flags then - fdata.flags = pack(flags) - end - local subtables = fdata.subtables - if subtables then - fdata.subtables = pack(subtables) - 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(flags) + end + local subtables = lookup.subtables + if subtables then + lookup.subtables = pack(subtables) end end - local lf = data.luatex.features - if lf then - for _, g in next, otf.glists do - local gl = lf[g] - if gl then - for feature, spec in next, gl do - gl[feature] = pack(spec) - 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(spec) end end end @@ -263,242 +273,404 @@ local function packdata(data) return end end - if #t > 0 then + if nt > 0 then for pass=1,2 do local pack = (pass == 1 and pack_1) or pack_2 - for k, v in next, data.glyphs do - local m = v.kerns - if m then - v.kerns = pack(m) - end - local m = v.math - if m then - local mk = m.kerns - if mk then - m.kerns = pack(mk) + for unicode, description in next, data.descriptions do + local kerns = description.kerns + if kerns then + description.kerns = pack(kerns) + end + local math = description.math + if math then + local kerns = math.kerns + if kerns then + math.kerns = pack(kerns) end end - local a = v.anchors - if a then - v.anchors = pack(a) + local anchors = description.anchors + if anchors then + description.anchors = pack(anchors) + end + local mlookups = description.mlookups + if mlookups then + for tag, mlookup in next, mlookups do + mlookups[tag] = pack(mlookup) + end end - local l = v.mlookups - if l then - for k,v in next, l do - for kk=1,#v do - v[kk] = pack(v[kk]) + 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(r) end + local r = rule.after if r then rule.after = pack(r) end + local r = rule.current if r then rule.current = pack(r) end end end end end - local ls = data.luatex.sequences - if ls then - for feature, fdata in next, ls do - fdata.features = pack(fdata.features) + local sequences = resources.sequences + if sequences then + for feature, sequence in next, sequences do + sequence.features = pack(sequence.features) end end if not success(2,pass) then ---~ return + -- return end end + + for pass=1,2 do + local pack = (pass == 1 and pack_1) or pack_2 + for unicode, description in next, data.descriptions do + local slookups = description.slookups + if slookups then + description.slookups = pack(slookups) + end + local mlookups = description.mlookups + if mlookups then + description.mlookups = pack(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 t = data.tables - if t then + local tables = data.tables + if tables then + local resources = data.resources + local lookuptypes = resources.lookuptypes local unpacked = { } - for k, v in next, data.glyphs do - local tv = t[v.boundingbox] if tv then v.boundingbox = tv end - local l = v.slookups - if l then - for k,v in next, l do - local tv = t[v] if tv then l[k] = tv end - end - end - local l = v.mlookups - if l then - for k,v in next, l do - for i=1,#v do - local vi = v[i] - local tv = t[vi] - if tv then - v[i] = tv - if unpacked[tv] then - vi = false - else - unpacked[tv], vi = true, tv + 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 - if vi then - local what = vi[1] + 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 - local tv = t[vi[3]] if tv then vi[3] = tv end - local tv = t[vi[4]] if tv then vi[4] = tv end - elseif what == "position" then - local tv = t[vi[2]] if tv then vi[2] = tv end + 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 m = v.kerns - if m then - local tm = t[m] + local kerns = description.kerns + if kerns then + local tm = tables[kerns] if tm then - v.kerns = tm - if unpacked[tm] then - m = false - else - unpacked[tm], m = true, tm - end + description.kerns = tm + kerns = unpacked[tm] end - if m then - for k,v in next, m do - local tv = t[v] if tv then m[k] = tv 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 m = v.math - if m then - local mk = m.kerns - if mk then - local tm = t[mk] + local math = description.math + if math then + local kerns = math.kerns + if kerns then + local tm = tables[kerns] if tm then - m.kerns = tm - if unpacked[tm] then - mk = false - else - unpacked[tm], mk = true, tm - end + math.kerns = tm + kerns = unpacked[tm] end - if mk then - for k,v in next, mk do - local tv = t[v] if tv then mk[k] = tv 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 a = v.anchors - if a then - local ta = t[a] + local anchors = description.anchors + if anchors then + local ta = tables[anchors] if ta then - v.anchors = ta - if not unpacked[ta] then - unpacked[ta], a = true, ta - else - a = false - end + description.anchors = ta + anchors = unpacked[ta] end - if a then - for k,v in next, a do - if k == "baselig" then - for kk, vv in next, v do - for kkk=1,#vv do - local tv = t[vv[kkk]] if tv then vv[kkk] = tv 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 kk, vv in next, v do - local tv = t[vv] if tv then v[kk] = tv end + for a, data in next, anchor do + local tv = tables[data] + if tv then + anchor[a] = tv + end end end end end end end - if data.lookups then - for k, v in next, data.lookups do - local r = v.rules - if r then - for kk, vv in next, r do - local l = vv.lookups - if l then - local tv = t[l] if tv then vv.lookups = tv 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 c = vv.coverage - if c then - local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end - cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end - cc = c.current if cc then local tv = t[cc] if tv then c.current = tv 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 c = vv.reversecoverage - if c then - local cc = c.before if cc then local tv = t[cc] if tv then c.before = tv end end - cc = c.after if cc then local tv = t[cc] if tv then c.after = tv end end - cc = c.current if cc then local tv = t[cc] if tv then c.current = tv 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 - -- no need to unpack vv.glyphs end end end end - local luatex = data.luatex - if luatex then - local la = luatex.anchor_to_lookup - if la then - for lookup, ldata in next, la do - local tv = t[ldata] if tv then la[lookup] = tv end - end - end - local la = luatex.lookup_to_anchor - if la then - for lookup, ldata in next, la do - local tv = t[ldata] if tv then la[lookup] = tv end - end - end - local ls = luatex.sequences - if ls then - for feature, fdata in next, ls do - local flags = fdata.flags - if flags then - local tv = t[flags] if tv then fdata.flags = tv 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 - local subtables = fdata.subtables - if subtables then - local tv = t[subtables] if tv then fdata.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 - local features = fdata.features if features then - local tv = t[features] - if tv then - fdata.features = tv - if not unpacked[tv] then - unpacked[tv], features = true, tv - else - features = false - end - end - if features then - for script, sdata in next, features do - local tv = t[sdata] if tv then features[script] = tv end + for script, data in next, features do + local tv = tables[data] + if tv then + features[script] = tv end end end end end - local ls = luatex.lookups - if ls then - for lookups, fdata in next, ls do - local flags = fdata.flags - if flags then - local tv = t[flags] if tv then fdata.flags = tv 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 - local subtables = fdata.subtables - if subtables then - local tv = t[subtables] if tv then fdata.subtables = tv end + end + local subtables = lookup.subtables + if subtables then + local tv = tables[subtables] + if tv then + lookup.subtables = tv end end end - local lf = luatex.features - if lf then - for _, g in next, otf.glists do - local gl = lf[g] - if gl then - for feature, spec in next, gl do - local tv = t[spec] if tv then gl[feature] = tv 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 @@ -514,6 +686,8 @@ 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 ec915b878..1dbf626ca 100644 --- a/tex/context/base/font-ott.lua +++ b/tex/context/base/font-ott.lua @@ -6,683 +6,728 @@ if not modules then modules = { } end modules ['font-otf'] = { license = "see context related readme files" } -local type, next, tonumber, tostring = type, next, tonumber, tostring -local gsub, lower, format = string.gsub, string.lower, string.format +local type, next, tonumber, tostring, rawget, setmetatable = type, next, tonumber, tostring, rawget, setmetatable +local gsub, lower, format, match = string.gsub, string.lower, string.format, string.match local is_boolean = string.is_boolean local allocate = utilities.storage.allocate -fonts = fonts or { } -- needed for font server local fonts = fonts -fonts.otf = fonts.otf or { } -local otf = fonts.otf +local otf = fonts.handlers.otf -otf.tables = otf.tables or { } -local tables = otf.tables +local tables = { } +otf.tables = tables -otf.meanings = otf.meanings or { } -local meanings = otf.meanings +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register local scripts = allocate { - ['dflt'] = 'Default', - - ['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', + ['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 { - ['dflt'] = 'Default', - - ['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' + ['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', + ['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', + ['trep'] = 'traditional tex replacements', + ['tlig'] = 'traditional tex ligatures', + + ['ss'] = 'stylistic set %s', } 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' + ['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 function swap(h) -- can be a tables.swap when we get a better name +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[v] = lower(gsub(k," ","")) + r[gsub(v,"[^a-z0-9]","")] = k -- is already lower end return r end -local verbosescripts = allocate(swap(scripts )) -local verboselanguages = allocate(swap(languages)) -local verbosefeatures = allocate(swap(features )) +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 -tables.scripts = scripts -tables.languages = languages -tables.features = features -tables.baselines = baselines +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 + return "dflt" +end + +setmetatable(verbosescripts, { __index = resolve }) +setmetatable(verboselanguages, { __index = resolve }) +setmetatable(verbosefeatures, { __index = resolve }) +setmetatable(verbosebaselines, { __index = resolve }) -tables.verbosescripts = verbosescripts -tables.verboselanguages = verboselanguages -tables.verbosefeatures = verbosefeatures +local function resolve(t,k) + if k then + k = lower(k) + local v = rawget(t,k) or rawget(t,gsub(k," ","")) + if v then + return v + end + end + return "dflt" +end -for k, v in next, verbosefeatures do - local stripped = gsub(k,"%-"," ") - verbosefeatures[stripped] = v - local stripped = gsub(k,"[^a-zA-Z0-9]","") - verbosefeatures[stripped] = v +local function assign(t,k,v) + -- forget about it end -for k, v in next, verbosefeatures do - verbosefeatures[lower(k)] = v + +setmetatable(scripts, { __index = resolve, __newindex = assign }) +setmetatable(languages, { __index = resolve, __newindex = assign }) +setmetatable(baselines, { __index = resolve, __newindex = assign }) + +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 format(v,tonumber(dd)) + end + end + end + return "dflt" end -local function resolve(tab,id) - if tab and id then - id = lower(id) - return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or '' - else - return "unknown" +local function assign(t,k,v) + if k then + v = lower(v) + rawset(t,k,v) + rawset(features,gsub(v,"[^a-z0-9]",""),k) end end -function meanings.script (id) return resolve(scripts, id) end -function meanings.language(id) return resolve(languages,id) end -function meanings.feature (id) return resolve(features, id) end -function meanings.baseline(id) return resolve(baselines,id) end +setmetatable(features, { __index = resolve, __newindex = assign }) local checkers = { rand = function(v) @@ -690,26 +735,24 @@ local checkers = { end } -meanings.checkers = checkers - -function meanings.normalize(features) +function otf.features.normalize(features) -- no longer 'lang' if features then local h = { } for k,v in next, features do k = lower(k) - if k == "language" or k == "lang" then - v = gsub(lower(v),"[^a-z0-9%-]","") - if not languages[v] then - h.language = verboselanguages[v] or "dflt" - else + if k == "language" then + v = gsub(lower(v),"[^a-z0-9]","") + if rawget(languages,v) then h.language = v + else + h.language = rawget(verboselanguages,v) or "dflt" end elseif k == "script" then - v = gsub(lower(v),"[^a-z0-9%-]","") - if not scripts[v] then - h.script = verbosescripts[v] or "dflt" - else + v = gsub(lower(v),"[^a-z0-9]","") + if rawget(scripts,v) then h.script = v + else + h.script = rawget(verbosescripts,v) or "dflt" end else if type(v) == "string" then @@ -720,7 +763,9 @@ function meanings.normalize(features) v = b end end - k = verbosefeatures[k] or k + 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 @@ -729,6 +774,8 @@ function meanings.normalize(features) end end +--~ table.print(otf.features.normalize({ language = "dutch", liga = "yes", ss99 = true, aalt = 3, abcd = "yes" } )) + -- When I feel the need ... --~ tables.aat = { diff --git a/tex/context/base/font-pat.lua b/tex/context/base/font-pat.lua index 8748f0ed1..b91502c74 100644 --- a/tex/context/base/font-pat.lua +++ b/tex/context/base/font-pat.lua @@ -12,7 +12,9 @@ local match, lower = string.match, string.lower -- so for them we get it from the name -- reporter moved to elsewhere -local patches = fonts.otf.enhancers.patches +local fonts = fonts +local otf = fonts.handlers.otf +local patches = otf.enhancers.patches local register = patches.register local report = patches.report diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua index 8f85b8582..1d444cdc7 100644 --- a/tex/context/base/font-syn.lua +++ b/tex/context/base/font-syn.lua @@ -33,11 +33,10 @@ using a table that has keys filtered from the font related files.

local texsprint = (tex and tex.sprint) or print -fonts = fonts or { } -- this module is also used in mtxrun -local fonts = fonts +fonts = fonts or { } -- also used elsewhere -fonts.names = fonts.names or { } -local names = fonts.names +local names = { } +fonts.names = names names.filters = names.filters or { } local filters = names.filters diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua index 5e841b24c..8f0a99d5a 100644 --- a/tex/context/base/font-tfm.lua +++ b/tex/context/base/font-tfm.lua @@ -6,808 +6,137 @@ if not modules then modules = { } end modules ['font-tfm'] = { license = "see context related readme files" } -local utf = unicode.utf8 +local next = next +local match = string.match -local next, format, match, lower, gsub = next, string.format, string.match, string.lower, string.gsub -local concat, sortedkeys, utfbyte, serialize = table.concat, table.sortedkeys, utf.byte, table.serialize +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 allocate = utilities.storage.allocate +local report_defining = logs.reporter("fonts","defining") +local report_tfm = logs.reporter("fonts","tfm loading") -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 fonts = fonts +local handlers = fonts.handlers +local readers = fonts.readers +local constructors = fonts.constructors +local encodings = fonts.encodings -local report_defining = logs.reporter("fonts","defining") +local tfm = { } +handlers.tfm = tfm --- tfmdata has also fast access to indices and unicodes --- to be checked: otf -> tfm -> tfmscaled --- --- watch out: no negative depths and negative eights permitted in regular fonts +constructors.resolvevirtualtoo = false -- wil be set in font-ctx.lua ---[[ldx-- -

Here we only implement a few helper functions.

---ldx]]-- - -local fonts = fonts -local tfm = fonts.tfm - -fonts.loaded = allocate() -fonts.dontembed = allocate() -fonts.triggers = fonts.triggers or { } -- brrr -fonts.initializers = fonts.initializers or { } -fonts.initializers.common = fonts.initializers.common or { } - -local set_attribute = node.set_attribute -local findbinfile = resolvers.findbinfile +local findbinfile = resolvers.findbinfile -local readers = fonts.tfm.readers -local fontdata = fonts.identifiers -local nodecodes = nodes.nodecodes +local tfmfeatures = fonts.constructors.newfeatures("tfm") +local registertfmfeature = tfmfeatures.register -local disc_code = nodecodes.disc -local glyph_code = nodecodes.glyph +fonts.formats.tfm = "type1" -- we need to have at least a value here --[[ldx--

The next function encapsulates the standard loader as supplied by .

--ldx]]-- -tfm.resolvevirtualtoo = true -- false -tfm.sharebasekerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too) -tfm.mathactions = { } -tfm.fontnamemode = "fullpath" - -tfm.enhance = tfm.enhance or function() end - -local function read_from_tfm(specification) - local fname, tfmdata = specification.filename or "", nil - if fname ~= "" then - if trace_defining then - report_defining("loading tfm file %s at size %s",fname,specification.size) - end - tfmdata = font.read_tfm(fname,specification.size) -- not cached, fast enough - if tfmdata then - tfmdata.descriptions = tfmdata.descriptions or { } - if tfm.resolvevirtualtoo then - fonts.logger.save(tfmdata,file.extname(fname),specification) -- strange, why here - fname = findbinfile(specification.name, 'ovf') - if fname and fname ~= "" then - local vfdata = font.read_vf(fname,specification.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 - tfmdata.type = 'virtual' - tfmdata.fonts = vfdata.fonts - end - end - end - tfm.enhance(tfmdata,specification) - end - elseif trace_defining then - report_defining("loading tfm with name %s fails",specification.name) - end - return tfmdata -end +-- 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 ---[[ldx-- -

We need to normalize the scale factor (in scaled points). This has to -do with the fact that uses a negative multiple of 1000 as -a signal for a font scaled based on the design size.

---ldx]]-- +-- ofm directive blocks local path search unless set; btw, in context we +-- don't support ofm files anyway as this format is obsolete -local factors = { - pt = 65536.0, - bp = 65781.8, -} - -function tfm.setfactor(f) - tfm.factor = factors[f or 'pt'] or factors.pt -end - -tfm.setfactor() - -function tfm.scaled(scaledpoints, designsize) -- handles designsize in sp as well - if scaledpoints < 0 then - if designsize then - if designsize > tfm.factor then -- or just 1000 / when? mp? - return (- scaledpoints/1000) * designsize -- sp's - else - return (- scaledpoints/1000) * designsize * tfm.factor - end - else - return (- scaledpoints/1000) * 10 * tfm.factor - end +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 scaledpoints + return { } -- will become false end end ---[[ldx-- -

Before a font is passed to we scale it. Here we also need -to scale virtual characters.

---ldx]]-- - ---~ function tfm.getvirtualid(tfmdata) ---~ -- since we don't know the id yet, we use 0 as signal ---~ local tf = tfmdata.fonts ---~ if not tf then ---~ tfmdata.type = "virtual" ---~ tfmdata.fonts = { { id = 0 } } ---~ return 1 ---~ else ---~ local ntf = #tf + 1 ---~ tf[ntf] = { id = 0 } ---~ return ntf ---~ end ---~ end - -function tfm.getvirtualid(tfmdata) - -- since we don't know the id yet, we use 0 as signal - local tf = tfmdata.fonts - if not tf then - tf = { } - tfmdata.type = "virtual" - tfmdata.fonts = tf - end - local ntf = #tf + 1 - tf[ntf] = { id = 0 } - return ntf -end - -function tfm.checkvirtualid(tfmdata, id) - if tfmdata and tfmdata.type == "virtual" then - if not tfmdata.fonts or #tfmdata.fonts == 0 then - tfmdata.type, tfmdata.fonts = "real", nil - else - local vfonts = tfmdata.fonts - 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 - ---[[ldx-- -

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.)

---ldx]]-- - -fonts.trace_scaling = false - --- the following hack costs a bit of runtime but safes memory --- --- basekerns are scaled and will be hashed by table id --- sharedkerns are unscaled and are be hashed by concatenated indexes - ---~ function tfm.check_base_kerns(tfmdata) ---~ if tfm.sharebasekerns then ---~ local sharedkerns = tfmdata.sharedkerns ---~ if sharedkerns then ---~ local basekerns = { } ---~ tfmdata.basekerns = basekerns ---~ return sharedkerns, basekerns ---~ end ---~ end ---~ return nil, nil ---~ end - ---~ function tfm.prepare_base_kerns(tfmdata) ---~ if tfm.sharebasekerns and not tfmdata.sharedkerns then ---~ local sharedkerns = { } ---~ tfmdata.sharedkerns = sharedkerns ---~ for u, chr in next, tfmdata.characters do ---~ local kerns = chr.kerns ---~ if kerns then ---~ local hash = concat(sortedkeys(kerns), " ") ---~ local base = sharedkerns[hash] ---~ if not base then ---~ sharedkerns[hash] = kerns ---~ else ---~ chr.kerns = base ---~ end ---~ end ---~ end ---~ end ---~ end - --- we can have cache scaled characters when we are in node mode and don't have --- protruding and expansion: hash == fullname @ size @ protruding @ expansion --- but in practice (except from mk) the otf hash will be enough already so it --- makes no sense to mess up the code now - -local charactercache = { } - --- The scaler is only used for otf and afm and virtual fonts. If --- a virtual font has italic correction make sure to set the --- has_italic flag. Some more flags will be added in the future. - ---[[ldx-- -

The reason why the scaler was originally split, is that for a while we experimented -with a helper function. However, in practice the calls are too slow to -make this profitable and the based variant was just faster. A days -wasted day but an experience richer.

---ldx]]-- - -tfm.autocleanup = true - -local lastfont = nil - --- 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 --- --- 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 - --- we don't need the glyph data as we can use the description .. but we will --- have to wait till we can access the internal tfm table efficiently in which --- case characters will become a metatable afterwards - -function tfm.cleanuptable(tfmdata) -- we need a cleanup callback, now we miss the last one - if tfm.autocleanup then -- ok, we can hook this into everyshipout or so ... todo - if tfmdata.type == 'virtual' or tfmdata.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 - else - -- for k, v in next, tfmdata.characters do - -- if v.kerns then v.kerns = nil end - -- end - end - end -end - -function tfm.cleanup(tfmdata) -- we need a cleanup callback, now we miss the last one -end - -function tfm.calculatescale(tfmtable, scaledpoints) - if scaledpoints < 0 then - scaledpoints = (- scaledpoints/1000) * tfmtable.designsize -- already in sp - end - local units = tfmtable.units or 1000 - local delta = scaledpoints/units -- brr, some open type fonts have 2048 - return scaledpoints, delta, units -end - -function tfm.scale(tfmtable, scaledpoints, relativeid) - -- tfm.prepare_base_kerns(tfmtable) -- optimalization - local t = { } -- the new table - local scaledpoints, delta, units = tfm.calculatescale(tfmtable, scaledpoints, relativeid) - -- is just a trigger for the backend - t.units_per_em = units or 1000 - -- - local hdelta, vdelta = delta, delta - -- unicoded unique descriptions shared cidinfo characters changed parameters indices - for k,v in next, tfmtable do - if type(v) == "table" then - -- print(k) - else - t[k] = v - end - end - local extend_factor = tfmtable.extend_factor or 0 - if extend_factor ~= 0 and extend_factor ~= 1 then - hdelta = hdelta * extend_factor - t.extend = extend_factor * 1000 - else - t.extend = 1000 - end - local slant_factor = tfmtable.slant_factor or 0 - if slant_factor ~= 0 then - t.slant = slant_factor * 1000 - else - t.slant = 0 - end - -- status - local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized - local hasmath = (tfmtable.mathparameters ~= nil and next(tfmtable.mathparameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil) - local nodemode = tfmtable.mode == "node" - local hasquality = tfmtable.auto_expand or tfmtable.auto_protrude - local hasitalic = tfmtable.has_italic - local descriptions = tfmtable.descriptions or { } - -- - if hasmath then - t.has_math = true -- this will move to elsewhere - end - -- - t.parameters = { } - t.characters = { } - t.MathConstants = { } - -- fast access - t.unscaled = tfmtable -- the original unscaled one (temp) - t.unicodes = tfmtable.unicodes - t.indices = tfmtable.indices - t.marks = tfmtable.marks - -- this will move to some subtable so that it is copied at once - t.goodies = tfmtable.goodies - t.colorscheme = tfmtable.colorscheme - t.postprocessors = tfmtable.postprocessors - -- - -- t.embedding = tfmtable.embedding - t.descriptions = descriptions - if tfmtable.fonts then - t.fonts = table.fastcopy(tfmtable.fonts) -- hm also at the end - end - local tp = t.parameters - local mp = t.mathparameters - local tfmp = tfmtable.parameters -- let's check for indexes - -- - tp.slant = (tfmp.slant or tfmp[1] or 0) - tp.space = (tfmp.space or tfmp[2] or 0)*hdelta - tp.space_stretch = (tfmp.space_stretch or tfmp[3] or 0)*hdelta - tp.space_shrink = (tfmp.space_shrink or tfmp[4] or 0)*hdelta - tp.x_height = (tfmp.x_height or tfmp[5] or 0)*vdelta - tp.quad = (tfmp.quad or tfmp[6] or 0)*hdelta - tp.extra_space = (tfmp.extra_space or tfmp[7] or 0)*hdelta - local protrusionfactor = (tp.quad ~= 0 and 1000/tp.quad) or 0 - local tc = t.characters - local characters = tfmtable.characters - local nameneeded = not tfmtable.shared.otfdata --hack - local changed = tfmtable.changed or { } -- for base mode - local ischanged = changed and next(changed) - local indices = tfmtable.indices - local luatex = tfmtable.luatex - local tounicode = luatex and luatex.tounicode - local defaultwidth = luatex and luatex.defaultwidth or 0 - local defaultheight = luatex and luatex.defaultheight or 0 - local defaultdepth = luatex and luatex.defaultdepth or 0 - -- experimental, sharing kerns (unscaled and scaled) saves memory - -- local sharedkerns, basekerns = tfm.check_base_kerns(tfmtable) - -- loop over descriptions (afm and otf have descriptions, tfm not) - -- there is no need (yet) to assign a value to chr.tonunicode - local scaledwidth = defaultwidth * hdelta - local scaledheight = defaultheight * vdelta - local scaleddepth = defaultdepth * vdelta - local stackmath = tfmtable.ignore_stack_math ~= true - local private = fonts.privateoffset - local sharedkerns = { } - for k,v in next, characters do - local chr, description, index - if ischanged then - -- basemode hack - local c = changed[k] - if c then - description = descriptions[c] or v - v = characters[c] or v - index = (indices and indices[c]) or c - else - description = descriptions[k] or v - index = (indices and indices[k]) or k - end - else - description = descriptions[k] or v - index = (indices and indices[k]) or k - 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 nameneeded then - chr = { - name = description.name, - index = index, - height = height, - depth = depth, - width = width, - } - else - chr = { - 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 nameneeded then - chr = { - name = description.name, - index = index, - height = height, - width = width, - } - else - chr = { - index = index, - height = height, - width = width, - } - end - end - -- if trace_scaling then - -- report_defining("t=%s, u=%s, i=%s, n=%s c=%s",k,chr.tounicode or "",index or 0,description.name or '-',description.class or '-') - -- end - if tounicode then - local tu = tounicode[index] -- nb: index! - if tu then - chr.tounicode = tu - end - end - if hasquality then - -- we could move these calculations elsewhere (saves calculations) - local ve = v.expansion_factor - if ve then - chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere - end - local vl = v.left_protruding - if vl then - chr.left_protruding = protrusionfactor*width*vl - end - local vr = v.right_protruding - if vr then - chr.right_protruding = protrusionfactor*width*vr - end - end - -- todo: hasitalic - if hasitalic then - local vi = description.italic or v.italic - if vi and vi ~= 0 then - chr.italic = vi*hdelta - end - end - -- to be tested - if hasmath then - -- todo, just operate on descriptions.math - local vn = v.next - if vn then - chr.next = vn - --~ if v.vert_variants or v.horiz_variants then - --~ report_defining("glyph 0x%05X has combination of next, vert_variants and horiz_variants",index) - --~ end - else - local vv = v.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 - --~ local ic = v.vert_italic_correction - --~ if ic then - --~ chr.italic = ic * hdelta - --~ print(format("0x%05X -> %s",k,chr.italic)) - --~ end - else - local hv = v.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 +local function read_from_tfm(specification) + local filename = specification.filename + local size = specification.size + if trace_defining then + report_defining("loading tfm file %s 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 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.parameters = parameters + tfmdata.shared = shared + -- + if constructors.resolvevirtualtoo then + fonts.loggers.register(tfmdata,file.extname(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 - end - end - local vt = description.top_accent - if vt then - chr.top_accent = vdelta*vt - end - if stackmath then - local mk = v.mathkerns - 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 + properties.virtualized = true + tfmdata.fonts = vfdata.fonts end end end - if not nodemode then - local vk = v.kerns - if vk then - --~ if sharedkerns then - --~ local base = basekerns[vk] -- hashed by table id, not content - --~ if not base then - --~ base = {} - --~ for k,v in next, vk do base[k] = v*hdelta end - --~ basekerns[vk] = base - --~ end - --~ chr.kerns = base - --~ else - --~ local tt = {} - --~ for k,v in next, vk do tt[k] = v*hdelta end - --~ chr.kerns = tt - --~ end - 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 = v.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 + -- + if next(features) then + constructors.applymanipulators("tfm",tfmdata,specification.features.normal,trace_features,report_tfm) end - if isvirtual then - local vc = v.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 + 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 - tc[k] = chr - end - -- t.encodingbytes, t.filename, t.fullname, t.name: elsewhere - t.size = scaledpoints - t.factor = delta - t.hfactor = hdelta - t.vfactor = vdelta - if t.fonts then - t.fonts = table.fastcopy(t.fonts) -- maybe we virtualize more afterwards - end - if hasmath then - -- mathematics.extras.copy(t) -- can be done elsewhere if needed - local ma = tfm.mathactions - for i=1,#ma do - ma[i](t,tfmtable,delta,hdelta,vdelta) -- what delta? - end - end - -- needed for \high cum suis - local tpx = tp.x_height - if hasmath then - if not tp[13] then tp[13] = .86*tpx end -- mathsupdisplay - if not tp[14] then tp[14] = .86*tpx end -- mathsupnormal - if not tp[15] then tp[15] = .86*tpx end -- mathsupcramped - if not tp[16] then tp[16] = .48*tpx end -- mathsubnormal - if not tp[17] then tp[17] = .48*tpx end -- mathsubcombined - if not tp[22] then tp[22] = 0 end -- mathaxisheight - if t.MathConstants then t.MathConstants.AccentBaseHeight = nil end -- safeguard - end - t.tounicode = 1 - t.cidinfo = tfmtable.cidinfo - -- we have t.name=metricfile and t.fullname=RealName and t.filename=diskfilename - -- when collapsing fonts, luatex looks as both t.name and t.fullname as ttc files - -- can have multiple subfonts - if hasmath then - if trace_defining then - report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") - end - else - if trace_defining then - report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") - end - t.nomath, t.MathConstants = true, nil + -- + return tfmdata end - if not t.psname then - -- name used in pdf file as well as for selecting subfont in ttc/dfont - t.psname = t.fontname or (t.fullname and fonts.names.cleanname(t.fullname)) - end - if trace_defining then - report_defining("used for accessing (sub)font: '%s'",t.psname or "nopsname") - report_defining("used for subsetting: '%s'",t.fontname or "nofontname") - end - -- this will move up (side effect of merging split call) - t.factor = delta - t.ascender = delta*(tfmtable.ascender or 0) - t.descender = delta*(tfmtable.descender or 0) - t.shared = tfmtable.shared or { } - t.unique = table.fastcopy(tfmtable.unique or {}) - tfm.cleanup(t) - -- print(t.fontname,table.serialize(t.MathConstants)) - return t -end - ---[[ldx-- -

Analyzers run per script and/or language and are needed in order to -process features right.

---ldx]]-- - -fonts.analyzers = fonts.analyzers or { } -local analyzers = fonts.analyzers - -analyzers.aux = analyzers.aux or { } -analyzers.methods = analyzers.methods or { } -analyzers.initializers = analyzers.initializers or { } - --- todo: analyzers per script/lang, cross font, so we need an font id hash -> script --- e.g. latin -> hyphenate, arab -> 1/2/3 analyze - --- an example analyzer (should move to font-ota.lua) - -local state = attributes.private('state') - -function analyzers.aux.setstate(head,font) - local useunicodemarks = analyzers.useunicodemarks - local tfmdata = fontdata[font] - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean - while current do - local id = current.id - if id == glyph_code and current.font == font then - local char = current.char - local d = descriptions[char] - if d then - if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then - done = true - set_attribute(current,state,5) -- mark - elseif n == 0 then - first, last, n = current, current, 1 - set_attribute(current,state,1) -- init - else - last, n = current, n+1 - set_attribute(current,state,2) -- medi - end - else -- finish - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - first, last, n = nil, nil, 0 - end - elseif id == disc_code then - -- always in the middle - set_attribute(current,state,2) -- midi - last = current - else -- finish - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - first, last, n = nil, nil, 0 - end - current = current.next - end - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina - end - return head, done -end - -function tfm.replacements(tfm,value) - -- tfm.characters[0x0022] = table.fastcopy(tfm.characters[0x201D]) - -- tfm.characters[0x0027] = table.fastcopy(tfm.characters[0x2019]) - -- tfm.characters[0x0060] = table.fastcopy(tfm.characters[0x2018]) - -- tfm.characters[0x0022] = tfm.characters[0x201D] - tfm.characters[0x0027] = tfm.characters[0x2019] - -- tfm.characters[0x0060] = tfm.characters[0x2018] -end - --- checking - -function tfm.checkedfilename(metadata,whatever) - local foundfilename = metadata.foundfilename - if not foundfilename then - local askedfilename = metadata.filename or "" - if askedfilename ~= "" then - askedfilename = resolvers.resolve(askedfilename) -- no shortcut - foundfilename = findbinfile(askedfilename,"") or "" - if foundfilename == "" then - report_defining("source file '%s' is not found",askedfilename) - foundfilename = findbinfile(file.basename(askedfilename),"") or "" - if foundfilename ~= "" then - report_defining("using source file '%s' (cache mismatch)",foundfilename) - end - end - elseif whatever then - report_defining("no source file for '%s'",whatever) - foundfilename = "" - end - metadata.foundfilename = foundfilename - -- report_defining("using source file '%s'",foundfilename) - end - return foundfilename end --- status info - -statistics.register("fonts load time", function() - return statistics.elapsedseconds(fonts) -end) - --- readers - -fonts.formats.tfm = "type1" -- we need to have at least a value here - -local function check_tfm(specification,fullname) - -- ofm directive blocks local path search unless set; btw, in context we - -- don't support ofm files anyway as this format is obsolete - local foundname = findbinfile(fullname, 'tfm') or "" -- just to be sure +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 "" -- bonus for usage outside context + foundname = findbinfile(fullname, 'ofm') or "" -- not needed in context end if foundname == "" then foundname = fonts.names.getfilename(fullname,"tfm") end if foundname ~= "" then - specification.filename, specification.format = foundname, "ofm" + specification.filename = foundname + specification.format = "ofm" return read_from_tfm(specification) + elseif trace_defining then + report_defining("loading tfm with name %s fails",specification.name) end end readers.check_tfm = check_tfm function readers.tfm(specification) - local fullname, tfmtable = specification.filename or "", nil + local fullname = specification.filename or "" if fullname == "" then local forced = specification.forced or "" if forced ~= "" then - tfmtable = check_tfm(specification,specification.name .. "." .. forced) - end - if not tfmtable then - tfmtable = check_tfm(specification,specification.name) + fullname = specification.name .. "." .. forced + else + fullname = specification.name end - else - tfmtable = check_tfm(specification,fullname) end - return tfmtable + return check_tfm(specification,fullname) end diff --git a/tex/context/base/font-vf.lua b/tex/context/base/font-vf.lua index 455646a22..8eab88b40 100644 --- a/tex/context/base/font-vf.lua +++ b/tex/context/base/font-vf.lua @@ -14,27 +14,79 @@ changes. This will change.

local next = next local fastcopy = table.fastcopy -local allocate = utilities.storage.allocate +local allocate = utilities.storage.allocate + +local fonts = fonts +local constructors = fonts.constructors +local vf = { } +fonts.handlers.vf = 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 %s",name) + end + return findbinfile(name,"ovf") + else + if trace_defining then + report_defining("vf for %s is already taken care of",name) + end + return nil -- "" + end + else + if trace_defining then + report_defining("locating vf for %s",name) + end + return findbinfile(name,"ovf") + end +end -local fonts = fonts -local vf = fonts.vf -local tfm = fonts.tfm +--[[ldx-- +

We overload the reader.

+--ldx]]-- + +callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more -fonts.definers = fonts.definers or { } -local definers = fonts.definers +-- specific code (will move to other module) -definers.methods = definers.methods or { } -local methods = definers.methods +local definers = fonts.definers +local methods = definers.methods -methods.variants = allocate() -local variants = methods.variants +local variants = allocate() +local combinations = { } +local combiner = { } +local whatever = allocate() -- can be used to store data +local predefined = allocate() -- can be used to store data +local helpers = allocate() -- can be used to store data -vf.combinations = vf.combinations or { } -vf.aux = vf.aux or { } -vf.aux.combine = vf.aux.combine or { } -local combine = vf.aux.combine +methods.variants = variants -- todo .. wrong namespace +vf.combinations = combinations +vf.combiner = combiner +vf.whatever = whatever +vf.helpers = helpers +vf.predefined = predefined -local chardata = characters.data +setmetatable(whatever, { __index = function(t,k) local v = { } t[k] = v return v end }) + +predefined.dummy = { "comment" } +predefined.push = { "push" } +predefined.pop = { "pop" } + +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 @@ -44,7 +96,7 @@ function methods.install(tag, rules) end local function combine_load(g,name) - return tfm.readanddefine(name or g.specification.name,g.specification.size) + return constructors.readanddefine(name or g.specification.name,g.specification.size) end local function combine_assign(g, name, from, to, start, force) @@ -66,26 +118,20 @@ local function combine_assign(g, name, from, to, start, force) end start = start + 1 end - if not g.parameters and #g.fonts > 0 then -- share this code ! - g.parameters = fastcopy(f.parameters) - g.italicangle = f.italicangle - g.ascender = f.ascender - g.descender = f.descender - g.factor = f.factor -- brrr - end + checkparameters(g,f) end end local function combine_process(g,list) if list then for _,v in next, list do - (combine.commands[v[1]] or nop)(g,v) + (combiner.commands[v[1]] or nop)(g,v) end end end local function combine_names(g,name,force) - local f, id = tfm.readanddefine(name,g.specification.size) + 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 @@ -98,13 +144,7 @@ local function combine_names(g,name,force) gd[i] = fd[i] end end - if not g.parameters and #g.fonts > 0 then -- share this code ! - g.parameters = fastcopy(f.parameters) - g.italicangle = f.italicangle - g.ascender = f.ascender - g.descender = f.descender - g.factor = f.factor -- brrr - end + checkparameters(g,f) end end @@ -124,111 +164,39 @@ local combine_feature = function(g,v) end end ---~ combine.load = combine_load ---~ combine.assign = combine_assign ---~ combine.process = combine_process ---~ combine.names = combine_names ---~ combine.feature = combine_feature +--~ combiner.load = combine_load +--~ combiner.assign = combine_assign +--~ combiner.process = combine_process +--~ combiner.names = combine_names +--~ combiner.feature = combine_feature -combine.commands = allocate { - ["initialize"] = function(g,v) combine_assign (g,g.name) end, - ["include-method"] = function(g,v) combine_process (g,vf.combinations[v[2]]) end, -- name +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, + ["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, - -- type = 'virtual', - virtualized = true, - fonts = { }, - characters = { }, - descriptions = { }, + properties = { + virtualized = true, + }, + fonts = { + }, + characters = { + }, + descriptions = { + }, specification = fastcopy(specification), } - combine_process(g,vf.combinations[tag]) + combine_process(g,combinations[tag]) return g end - --- simple example with features - -methods.install( - "ligatures", { - { "feature", "liga" } , - { "feature", "dlig" } , - { "initialize" } , - } -) - ---~ methods.install ( ---~ "ligatures-x", { ---~ { "feature", "liga" } , ---~ { "feature", "dlig" } , ---~ { "initialize" } , ---~ { "lineheight" } ---~ } ---~ ) - ---~ methods.install( ---~ "lmsymbol10", { ---~ { "fallback_names", "lmsy10.afm" } , ---~ { "fallback_names", "msam10.afm" } , ---~ { "fallback_names", "msbm10.afm" } ---~ } ---~ ) ---~ \font\TestFont=dummy@lmsymbol10 at 24pt - --- docu case - ---~ methods.install( ---~ "weird", { ---~ { "copy-range", "lmroman10-regular" } , ---~ { "copy-char", "lmroman10-regular", 65, 66 } , ---~ { "copy-range", "lmsans10-regular", 0x0100, 0x01FF } , ---~ { "copy-range", "lmtypewriter10-regular", 0x0200, 0xFF00 } , ---~ { "fallback-range", "lmtypewriter10-regular", 0x0000, 0x0200 } ---~ } ---~ ) - --- demo case -> move to module - --- todo: interface tables in back-ini - -variants["demo-1"] = function(specification) - local name = specification.name -- symbolic name - local size = specification.size -- given size - local f, id = tfm.readanddefine('lmroman10-regular',size) - if f and id then - local capscale, digscale = 0.85, 0.75 - -- f.name, f.type = name, 'virtual' - f.name, f.virtualized = name, true - f.fonts = { - { id = id }, - { name = 'lmsans10-regular' , size = size*capscale }, -- forced extra name - { name = 'lmtypewriter10-regular', size = size*digscale } -- forced extra name - } - local characters, descriptions = f.characters, f.descriptions - local vfspecials = backends.tables.vfspecials - local red, green, blue, black = vfspecials.red, vfspecials.green, vfspecials.blue, vfspecials.black - for u,v in next, characters do - local category = chardata[u].category - if category == 'lu' then - v.width = capscale*v.width - v.commands = { red, { 'slot', 2, u }, black } - elseif category == 'nd' then - v.width = digscale*v.width - v.commands = { blue, { 'slot', 3, u }, black } - else - v.commands = { green, { 'slot', 1, u }, black } - end - end - end - return f -end diff --git a/tex/context/base/font-xtx.lua b/tex/context/base/font-xtx.lua deleted file mode 100644 index 505e08583..000000000 --- a/tex/context/base/font-xtx.lua +++ /dev/null @@ -1,100 +0,0 @@ -if not modules then modules = { } end modules ['font-xtx'] = { - 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 texsprint, count = tex.sprint, tex.count -local format, concat, gmatch, match, find, lower = string.format, table.concat, string.gmatch, string.match, string.find, string.lower -local tostring, next = tostring, next -local lpegmatch = lpeg.match - -local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) - ---[[ldx-- -

Choosing a font by name and specififying its size is only part of the -game. In order to prevent complex commands, introduced -a method to pass feature information as part of the font name. At the -risk of introducing nasty parsing and compatinility problems, this -syntax was expanded over time.

- -

For the sake of users who have defined fonts using that syntax, we -will support it, but we will provide additional methods as well. -Normally users will not use this direct way, but use a more abstract -interface.

- -

The next one is the official one. However, in the plain -variant we need to support the crappy [] specification as -well and that does not work too well with the general design -of the specifier.

---ldx]]-- - -local fonts = fonts -local definers = fonts.definers -local specifiers = definers.specifiers -local normalize_meanings = fonts.otf.meanings.normalize - -local list = { } - -specifiers.colonizedpreference = "file" - -local function issome () list.lookup = specifiers.colonizedpreference end -local function isfile () list.lookup = 'file' end -local function isname () list.lookup = 'name' end -local function thename(s) list.name = s end -local function issub (v) list.sub = v end -local function iscrap (s) list.crap = string.lower(s) end -local function istrue (s) list[s] = 'yes' end -local function isfalse(s) list[s] = 'no' end -local function iskey (k,v) list[k] = v end - -local function istrue (s) list[s] = true end -local function isfalse(s) list[s] = false end - -local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C - -local spaces = P(" ")^0 -local namespec = (1-S("/:("))^0 -- was: (1-S("/: ("))^0 -local crapspec = spaces * P("/") * (((1-P(":"))^0)/iscrap) * spaces -local filename = (P("file:")/isfile * (namespec/thename)) + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]")) -local fontname = (P("name:")/isname * (namespec/thename)) + P(true)/issome * (namespec/thename) -local sometext = (R("az","AZ","09") + S("+-."))^1 -local truevalue = P("+") * spaces * (sometext/istrue) -local falsevalue = P("-") * spaces * (sometext/isfalse) -local keyvalue = (C(sometext) * spaces * P("=") * spaces * C(sometext))/iskey -local somevalue = sometext/istrue -local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim -local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces -local options = P(":") * spaces * (P(";")^0 * option)^0 -local pattern = (filename + fontname) * subvalue^0 * crapspec^0 * options^0 - -local function colonized(specification) -- xetex mode - list = { } - lpegmatch(pattern,specification.specification) - -- for k, v in next, list do - -- list[k] = is_boolean(v) - -- if type(list[a]) == "nil" then - -- list[k] = v - -- end - -- end - list.crap = nil -- style not supported, maybe some day - if list.name then - specification.name = list.name - list.name = nil - end - if list.lookup then - specification.lookup = list.lookup - list.lookup = nil - end - if list.sub then - specification.sub = list.sub - list.sub = nil - end - -- specification.features.normal = list - specification.features.normal = normalize_meanings(list) - return specification -end - -definers.registersplit(":",colonized,"cryptic") diff --git a/tex/context/base/grph-inc.lua b/tex/context/base/grph-inc.lua index 83b395f2e..bb29e6142 100644 --- a/tex/context/base/grph-inc.lua +++ b/tex/context/base/grph-inc.lua @@ -228,7 +228,6 @@ function figures.setpaths(locationset,pathlist) last_locationset = locationset end if h[iv["global"]] then - -- for s in gmatch(pathlist,",* *([^,]+)") do local list = settings_to_array(pathlist) for i=1,#list do local s = list[i] diff --git a/tex/context/base/java-imp-ans.mkiv b/tex/context/base/java-imp-ans.mkiv deleted file mode 100644 index cc63266e2..000000000 --- a/tex/context/base/java-imp-ans.mkiv +++ /dev/null @@ -1,33 +0,0 @@ -%D \module -%D [ file=java-ans, -%D version=1998.06.01, -%D title=\CONTEXT\ JavaScript Macros, -%D subtitle=Answer Analization, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -% event.target.display = display.hidden - -\startJSpreamble{Do_Check_Answer} used later - - function Do_Check_Answer(field,value) { - if (event.value.toLowerCase() == value.toLowerCase()) { - event.target.hidden = true ; - } - return("\040") // funny, "" does not work - } - -\stopJSpreamble - -% needs to be done better in mkiv: - -\startJScode{Check_Answer} uses {Do_Check_Answer} - event.value = Do_Check_Answer(JS_S_1, JS_S_2) ; -\stopJScode - -\endinput diff --git a/tex/context/base/java-imp-fld.mkiv b/tex/context/base/java-imp-fld.mkiv index c0c229aba..2186368bb 100644 --- a/tex/context/base/java-imp-fld.mkiv +++ b/tex/context/base/java-imp-fld.mkiv @@ -25,6 +25,9 @@ %D %D Probably a \UNICODE\ issue. Beware, in \MKIV\ we have a %D different escaping of \type {\\}. +%D +%D Watch out: cf. the latest pdf specification we've changed +%D On into Yes. \startluasetups javascript:pdfencoding local verbatim = context.verbatim @@ -161,7 +164,7 @@ function Do_Vide_Field(Name, Closable) { v.hidden = false ; if (Closable) { v.readonly = false ; - v.value = "On" ; + v.value = "Yes" ; } this.dirty = false ; } @@ -203,7 +206,7 @@ function Toggle_Hide(Name) { function Field_On(Name) { v = this.getField(Name) ; if (v) { - v.value = "On" ; + v.value = "Yes" ; this.dirty = false ; } } @@ -219,10 +222,10 @@ function Field_Off(Name) { function Toggle_Value(Name) { var v = this.getField(Name) ; if (v) { - if (v.value == "On") { + if (v.value == "Yes") { v.value = "Off" ; } else { - v.value = "On" ; + v.value = "Yes" ; } } this.dirty = false ; @@ -241,7 +244,7 @@ function Flip_Fields(Name) { v = this.getField(Names[i]) ; if (v) { v.hidden = !v.hidden ; - v.value = "On" ; + v.value = "Yes" ; } } } @@ -282,7 +285,7 @@ function Set_Fields(FieldSet) { if (!v) { break ; } else { - v.value = "On" ; + v.value = "Yes" ; } i++ ; } @@ -292,7 +295,7 @@ function Set_Field(FieldSet, FieldName) { Reset_Fields(FieldSet) ; v = this.getField(FieldSet+":"+FieldName) ; if (v) { - v.value = "On" ; + v.value = "Yes" ; } } @@ -309,7 +312,7 @@ function Walk_Field(FieldSet) { while (true) { v = this.getField(FieldSet+":"+i) ; if (v) { - if (v.value=="On") { + if (v.value=="Yes") { v.value = "Off" ; var ii = i ; ii++ ; @@ -318,7 +321,7 @@ function Walk_Field(FieldSet) { v = this.getField(FieldSet+":"+1) ; } if (v) { - v.value = "On" ; + v.value = "Yes" ; } break ; } @@ -351,7 +354,7 @@ function Do_Next_Auto_Walk_Field(FieldSet) { if (fieldset) { var v = this.getField(FieldSet + ":" + fieldset.number) ; if (v) { - if (v.value == "On") { + if (v.value == "Yes") { v.value = "Off" ; } } @@ -362,7 +365,7 @@ function Do_Next_Auto_Walk_Field(FieldSet) { v = this.getField(FieldSet + ":" + fieldset.number) ; } if (v) { - v.value = "On" ; + v.value = "Yes" ; } } } @@ -444,14 +447,14 @@ function Previous_Walk_Field(FieldSet) { if (fieldset.number>0) { var v = this.getField(FieldSet + ":" + fieldset.number) ; if (v) { - if (v.value == "On") { + if (v.value == "Yes") { v.value = "Off" ; } } fieldset.number-- ; v = this.getField(FieldSet + ":" + fieldset.number) ; if (v) { - v.value = "On" ; + v.value = "Yes" ; } } } @@ -468,14 +471,14 @@ function Next_Walk_Field(FieldSet) { if (v) { var v = this.getField(FieldSet + ":" + fieldset.number) ; if (v) { - if (v.value == "On") { + if (v.value == "Yes") { v.value = "Off" ; } } fieldset.number++ ; v = this.getField(FieldSet + ":" + fieldset.number) ; if (v) { - v.value = "On" ; + v.value = "Yes" ; } } } diff --git a/tex/context/base/java-ini.lua b/tex/context/base/java-ini.lua index 7872031a8..b3b066678 100644 --- a/tex/context/base/java-ini.lua +++ b/tex/context/base/java-ini.lua @@ -87,7 +87,7 @@ function javascripts.setpreamble(name,script) -- now later end end -function javascripts.addtopreamble(name,script) -- now later +function javascripts.addtopreamble(name,script) if name and name ~= "" then local p = preambled[name] if p then diff --git a/tex/context/base/l-dimen.lua b/tex/context/base/l-dimen.lua index 1c557bc49..f8a999cc9 100644 --- a/tex/context/base/l-dimen.lua +++ b/tex/context/base/l-dimen.lua @@ -324,25 +324,9 @@ is loaded, the relevant tables that hold the functions needed may not yet be available.

--ldx]]-- -function dimensions.texify() -- todo: % - local fti, fc = fonts and fonts.identifiers, font and font.current - if fti and fc then - dimenfactors["ex"] = function() return fti[fc()].ex_height end - dimenfactors["em"] = function() return fti[fc()].quad end - -- dimenfactors["%"] = function() return tex.dimen.hsize/100 end - else - dimenfactors["ex"] = 1/65536* 4 -- 4pt - dimenfactors["em"] = 1/65536*10 -- 10pt - -- dimenfactors["%"] = 1/65536* 4 -- 400pt/100 - end -end - ---[[ldx-- -

In order to set the defaults we call this function now. At some point -the macro package needs to make sure the function is called again.

---ldx]]-- - -dimensions.texify() + dimenfactors["ex"] = 4 * 1/65536 -- 4pt + dimenfactors["em"] = 10 * 1/65536 -- 10pt +-- dimenfactors["%"] = 4 * 1/65536 -- 400pt/100 --[[ldx--

The previous code is rather efficient (also thanks to ) but we diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua index b20bc8853..ce0cc67ef 100644 --- a/tex/context/base/l-lpeg.lua +++ b/tex/context/base/l-lpeg.lua @@ -537,3 +537,5 @@ end function lpeg.is_lpeg(p) return p and lpegtype(p) == "pattern" end + +--~ Cf(Ct("") * (Cg(C(...) * "=" * Cs(...)))^0, rawset) diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua index 0189c887c..727d80df7 100644 --- a/tex/context/base/l-table.lua +++ b/tex/context/base/l-table.lua @@ -93,24 +93,24 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed + 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 kind == 3 then + if category == 3 then -- no further check else local tkey = type(key) if tkey == "string" then - kind = (kind == 2 and 3) or 1 + category = (category == 2 and 3) or 1 elseif tkey == "number" then - kind = (kind == 1 and 3) or 2 + category = (category == 1 and 3) or 2 else - kind = 3 + category = 3 end end end - if kind == 0 or kind == 3 then + if category == 0 or category == 3 then sort(srt,compare) else sort(srt) @@ -276,6 +276,13 @@ end table.fastcopy = fastcopy table.copy = copy +function table.derive(parent) + local child = { } + if parent then + setmetatable(child,{ __index = parent }) + end + return child +end function table.tohash(t,value) local h = { } diff --git a/tex/context/base/lang-txt.lua b/tex/context/base/lang-txt.lua index 8b245ff12..3ba2d478c 100644 --- a/tex/context/base/lang-txt.lua +++ b/tex/context/base/lang-txt.lua @@ -27,7 +27,7 @@ if not modules then modules = { } end modules ['lang-txt'] = { -- fi Finish ... -- fr French Daniel Flipo, Arthur Reutenauer -- gr Greek Apostolos Syropoulos, Thomas Schmitz --- hr Croatian Željko Vrba, Richard Gabriel +-- hr Croatian Željko Vrba, Richard Gabriel, Vedran Miletić -- hu Hungarian ... -- it Italian Giuseppe Bilotta, Luigi Scarso -- ja Japanese Richard Gabriel @@ -71,30 +71,35 @@ data.labels={ arccos={ labels={ en="arccos", + hr="arc\\sixperemspace cos", pl="arc\\sixperemspace cos", }, }, arcctg={ labels={ en="arccot", + hr="arc\\sixperemspace ctg", pl="arc\\sixperemspace ctg", }, }, arcsin={ labels={ en="arcsin", + hr="arc\\sixperemspace sin", pl="arc\\sixperemspace sin", }, }, arctan={ labels={ en="arctan", + hr="arc\\sixperemspace tg", pl="arc\\sixperemspace tg", }, }, arctg={ labels={ en="arctan", + hr="arc\\sixperemspace tg", pl="arc\\sixperemspace tg", }, }, @@ -116,6 +121,7 @@ data.labels={ cot={ labels={ en="cot", + hr="ctg", pl="ctg", }, }, @@ -132,6 +138,7 @@ data.labels={ ctg={ labels={ en="cot", + hr="ctg", pl="ctg", }, }, @@ -158,6 +165,7 @@ data.labels={ gcd={ labels={ en="gcd", + hr="nzd", nl="ggd", }, }, @@ -184,6 +192,7 @@ data.labels={ lcm={ labels={ en="lcm", + hr="nzv", nl="kgv", }, }, @@ -265,6 +274,7 @@ data.labels={ tan={ labels={ en="tan", + hr="tg", pl="tg", }, }, @@ -276,6 +286,7 @@ data.labels={ tg={ labels={ en="tan", + hr="tg", pl="tg", }, }, @@ -293,7 +304,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="i", hu="", it="", la="", @@ -403,7 +414,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="tra", hu="", it="", la="", @@ -510,7 +521,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="kol", hu="", it="", la="", @@ -550,7 +561,7 @@ data.labels={ hu=",. fejezet:", it="", ja={"第","章"}, - kr={"제", "장"}, + kr={"제","장"}, la="", lt="", nb="", @@ -581,7 +592,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr=" (nastavak)", hu="", it="", la="", @@ -653,7 +664,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="pro", hu="", it="", la="", @@ -725,7 +736,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="velj", hu="", it="", la="", @@ -1013,7 +1024,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="sij", hu="", it="", la="", @@ -1086,7 +1097,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="srp", hu="", it="", la="", @@ -1158,7 +1169,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="lip", hu="", it="", la="", @@ -1303,7 +1314,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="ožu", hu="", it="", la="", @@ -1376,7 +1387,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="svi", hu="", it="", la="", @@ -1485,7 +1496,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="stu", hu="", it="", la="", @@ -1556,7 +1567,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="lis", hu="", it="", la="", @@ -1810,7 +1821,7 @@ data.labels={ fi="", fr="", gr="", - hr="", + hr="ruj", hu="", it="", la="", @@ -2365,7 +2376,7 @@ data.labels={ fi="Vertauskuva", fr="Logos", gr="Λογότυπα", - hr="Znakovi", + hr="Logotipi", hu="Fejlécek", it="Logotipi", ja="理性", diff --git a/tex/context/base/lpdf-ano.lua b/tex/context/base/lpdf-ano.lua index 60711044e..9f452c34d 100644 --- a/tex/context/base/lpdf-ano.lua +++ b/tex/context/base/lpdf-ano.lua @@ -70,6 +70,8 @@ local pdf_t = pdfconstant("T") local pdf_fit = pdfconstant("Fit") local pdf_named = pdfconstant("Named") +-- todo: 3dview + local pdf_border = pdfarray { 0, 0, 0 } local getinnermethod = references.getinnermethod @@ -130,19 +132,13 @@ local function link(url,filename,destination,page,actions) NewWindow = (actions.newwindow and true) or nil, } elseif destination and destination ~= "" then - local p = references.checkedpage(actions.n,page) return pdfdictionary { -- can be cached S = pdf_goto, D = destination, } - elseif page and page ~= "" then - local p = references.checkedpage(actions.n,page) + else + local p = tonumber(page) if p and p > 0 then - --~ return gotopagedestination(p) - --~ return pdfdictionary { -- can be cached - --~ S = pdf_goto, - --~ D = pagedestination(p), - --~ } return pdfdictionary { -- can be cached S = pdf_goto, D = pdfarray { @@ -151,7 +147,7 @@ local function link(url,filename,destination,page,actions) } } else - report_reference("invalid page reference: %s",page or "?") + report_reference("invalid page reference: %s",tostring(page)) end end return false @@ -302,9 +298,9 @@ local lln = latelua_node() if node.has_field(lln,'string') then directives.register("refences.sharelinks", function(v) if v then - backends.nodeinjections.reference, backends.codeinjections.finishreference = use_shared_annotations() + nodeinjections.reference, codeinjections.finishreference = use_shared_annotations() else - backends.nodeinjections.reference, backends.codeinjections.finishreference = use_normal_annotations() + nodeinjections.reference, codeinjections.finishreference = use_normal_annotations() end end) @@ -409,26 +405,32 @@ function specials.internal(var,actions) -- better resolve in strc-ref end end +-- realpage already resolved + specials.i = specials.internal local pages = references.pages -function specials.page(var,actions) -- better resolve in strc-ref +function specials.page(var,actions) local file = var.f ---~ table.print(var) if file then file = references.checkedfile(file) return link(nil,file,nil,var.operation,actions) else - local p = references.pages[var.operation] - if type(p) == "function" then -- double - p = p() + 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 = tonumber(references.realpageofpage(tonumber(o))) + end end return link(nil,nil,nil,p or var.operation,actions) end end -function specials.realpage(var,actions) -- better resolve in strc-ref +function specials.realpage(var,actions) local file = var.f if file then file = references.checkedfile(file) @@ -439,15 +441,16 @@ function specials.realpage(var,actions) -- better resolve in strc-ref end function specials.userpage(var,actions) - local p = references.realpageofpage(tonumber(var.operation or 0)) - if p then - local file = var.f - if file then - -- file = references.checkedfile(file) - -- return link(nil,file,nil,p,actions) - else - return link(nil,nil,nil,p,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 = tonumber(references.realpageofpage(var.operation)) end + return link(nil,nil,nil,p,actions) end end diff --git a/tex/context/base/lpdf-col.lua b/tex/context/base/lpdf-col.lua index a8e35dc74..dc40e555c 100644 --- a/tex/context/base/lpdf-col.lua +++ b/tex/context/base/lpdf-col.lua @@ -100,7 +100,28 @@ function lpdf.colorvalue(model,ca,default) end end -function lpdf.fdfcolor(model,ca,default) +--~ function lpdf.fdfcolor(model,ca,default) +--~ local cv = colorsvalue(ca) +--~ if cv then +--~ if model == 1 then +--~ model = cv[1] +--~ end +--~ model = forcedmodel(model) +--~ if model == 2 then +--~ return format("[%s]",cv[2]) +--~ elseif model == 3 then +--~ return format("[%s %s %s]",cv[3],cv[4],cv[5]) +--~ elseif model == 4 then +--~ return format("[%s %s %s %s]",cv[6],cv[7],cv[8],cv[9]) +--~ elseif model == 4 then +--~ return format("[%s]",cv[13]) +--~ end +--~ else +--~ return format("[%s]",default or 0) +--~ end +--~ end + +function lpdf.colorvalues(model,ca,default) local cv = colorsvalue(ca) if cv then if model == 1 then @@ -108,16 +129,25 @@ function lpdf.fdfcolor(model,ca,default) end model = forcedmodel(model) if model == 2 then - return format("[%s]",cv[2]) + return cv[2] elseif model == 3 then - return format("[%s %s %s]",cv[3],cv[4],cv[5]) + return cv[3], cv[4], cv[5] elseif model == 4 then - return format("[%s %s %s %s]",cv[6],cv[7],cv[8],cv[9]) + return cv[6], cv[7], cv[8], cv[9] elseif model == 4 then - return format("[%s]",cv[13]) + return cv[13] end else - return format("[%s]",default or 0) + return default or 0 + end +end + +function lpdf.transparencyvalue(ta,default) + local tv = transparenciesvalue(ta) + if tv then + return tv[2] + else + return default or 1 end end diff --git a/tex/context/base/lpdf-fld.lua b/tex/context/base/lpdf-fld.lua index f59ef2d3e..12f8a9f46 100644 --- a/tex/context/base/lpdf-fld.lua +++ b/tex/context/base/lpdf-fld.lua @@ -6,15 +6,25 @@ if not modules then modules = { } end modules ['lpdf-fld'] = { license = "see context related readme files" } --- cleaned up, e.g. no longer older viewers --- always kids so no longer explicit main / clone / copy --- some optimizations removed (will come bakc if needed) +-- 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. 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("widgets.fields", function(v) trace_fields = v end) +local trace_fields = false trackers.register("backends.fields", function(v) trace_fields = v end) local report_fields = logs.reporter("backend","fields") @@ -26,9 +36,11 @@ local context = context local references = structures.references 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 pdfbackend = backends.pdf + +local nodeinjections = pdfbackend.nodeinjections +local codeinjections = pdfbackend.codeinjections +local registrations = pdfbackend.registrations local registeredsymbol = codeinjections.registeredsymbol @@ -48,11 +60,24 @@ 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 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") +local pdf_yes = pdfconstant("Yes") +local pdf_off = pdfconstant("Off") +local pdf_p = pdfconstant("P") -- None Invert Outline Push +local pdf_n = pdfconstant("N") -- None Invert Outline Push +-- +local pdf_no_rect = pdfarray { 0, 0, 0, 0 } local splitter = lpeg.splitat("=>") @@ -61,7 +86,7 @@ local formats = { } function codeinjections.setformsmethod(name) - submitoutputformat = formats[lower(name)] or 3 + submitoutputformat = formats[lower(name)] or formats.xml end local flag = { @@ -128,11 +153,6 @@ local function fieldplus(specification) return n end --- local function checked(what) --- local set, bug = references.identify("",what) --- return not bug and #set > 0 and lpdf.action(set) --- end - local function checked(what) local set, bug = references.identify("",what) if not bug and #set > 0 then @@ -141,26 +161,12 @@ local function checked(what) end end --- a dedicated hash is faster, but maybe overkill - ---~ local cache = { } ---~ ---~ local function checked(what) ---~ local set, bug = references.identify("",what) ---~ if not bug and #set > 0 then ---~ local r = cache[set] ---~ if not r then ---~ r = pdfreference(pdfimmediateobject(pdfaction(set))) ---~ cache[set] = r ---~ end ---~ return r ---~ end ---~ end - local function fieldactions(specification) -- share actions local d, a = { }, nil - a = specification.mousedown if a and a ~= "" then d.D = checked(a) end - a = specification.mouseup if a and a ~= "" then d.U = checked(a) end + 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 @@ -169,8 +175,8 @@ local function fieldactions(specification) -- share actions 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.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) @@ -220,6 +226,9 @@ local fontnames = { sl = "Courier-Oblique", bi = "Courier-BoldOblique", bs = "Courier-BoldOblique", + }, + symbol = { + dingbats = "ZapfDingbats", } } @@ -240,7 +249,9 @@ local function fieldsurrounding(specification) end local tag = fontstyle .. fontalternative fontsize = todimen(fontsize) - local fontcode = format("%0.4f Tf",(fontsize and (bpfactor * fontsize)) or 12) + 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) local colorcode = lpdf.color(3,colorvalue) -- we force an rgb color space if trace_fields then report_fields("fontcode : %s %s @ %s => %s => %s",fontstyle,fontalternative,fontsize,tag,fontcode) @@ -274,16 +285,6 @@ local function registerfonts() end end --- cache - -local function fieldattributes(specification) ---~ return pdfarray { ---~ -- BG = -- backgroundcolor ---~ -- BC = -- framecolor ---~ } - return nil -end - -- symbols local function fieldappearances(specification) @@ -303,16 +304,18 @@ local function fieldappearances(specification) else n, r, d = v[1], v[2], v[3] end - local appearance = pdfdictionary { -- cache this one + local appearance = pdfdictionary { N = registeredsymbol(n), R = registeredsymbol(r), D = registeredsymbol(d), } return pdfshareobjectreference(appearance) end +local YesorOn = "Yes" -- somehow On is not always working out well any longer (why o why this change) + local function fieldstates(specification,forceyes,values,default) -- 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 then + if not values or values == "" then -- error return end @@ -354,14 +357,14 @@ local function fieldstates(specification,forceyes,values,default) offvalue = offn end if forceyes == true then - forceyes = forceyes and "On" -- spec likes Yes more but we've used On for ages now + forceyes = forceyes and 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 = pdfconstant("Off") + default = pdf_off end local appearance = pdfdictionary { -- maybe also cache components N = pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) }, @@ -369,7 +372,20 @@ local function fieldstates(specification,forceyes,values,default) D = pdfdictionary { [forceyes or yesd] = registeredsymbol(yesd), Off = registeredsymbol(offd) } } local appearanceref = pdfshareobjectreference(appearance) - return appearanceref, default + 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) @@ -390,21 +406,32 @@ local function fieldoptions(specification) end end -local function radiodefault(parent,field,forceyes) - local default, values = parent.default, parent.values - if not default or default == "" then - values = settings_to_array(values) - default = values[1] +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 - local name = field.name - local fieldvalues = settings_to_array(field.values) - local yes, off = fieldvalues[1], fieldvalues[2] or fieldvalues[1] - if not default then - return pdfconstant((forceyes and "On") or yes) - elseif default == name then - return pdfconstant((forceyes and "On") or default) - else - return pdfconstant("Off") +end + +local function fieldrendering(specification) + local bvalue = tonumber(specification.backgroundcolorvalue) + local fvalue = tonumber(specification.framecolorvalue) + local svalue = specification.fontsymbol + if bvalue or fvalue or svalue then + return pdfdictionary { + BG = bvalue and pdfarray { lpdf.colorvalues(3,bvalue) } or nil, + BC = fvalue and pdfarray { lpdf.colorvalues(3,fvalue) } or nil, + CA = svalue and pdfstring (svalue) or nil, + } end end @@ -472,9 +499,7 @@ function codeinjections.getdefaultfieldvalue(name) default = a or symbol end end - if default then - context(default) - end + return default end end @@ -482,12 +507,12 @@ function codeinjections.definefield(specification) local n = specification.name local f = fields[n] if not f then - local kind = specification.kind - if not kind then + local fieldtype = specification.type + if not fieldtype then if trace_fields then report_fields("invalid definition of '%s': unknown type",n) end - elseif kind == "radio" then + elseif fieldtype == "radio" then local values = specification.values if values and values ~= "" then values = settings_to_array(values) @@ -501,7 +526,7 @@ function codeinjections.definefield(specification) elseif trace_fields then report_fields("invalid definition of radio '%s': missing values",n) end - elseif kind == "sub" then + elseif fieldtype == "sub" then -- not in main field list ! local radio = radios[n] if radio then @@ -517,10 +542,10 @@ function codeinjections.definefield(specification) report_fields("invalid definition of radio sub '%s': no parent",n) end predefinesymbols(specification) - elseif kind == "text" or kind == "line" then + elseif fieldtype == "text" or fieldtype == "line" then fields[n] = specification if trace_fields then - report_fields("defining '%s' as %s",n,kind) + report_fields("defining '%s' as %s",n,fieldtype) end if specification.values ~= "" and specification.default == "" then specification.default, specification.values = specification.values, nil @@ -528,7 +553,7 @@ function codeinjections.definefield(specification) else fields[n] = specification if trace_fields then - report_fields("defining '%s' as %s",n,kind) + report_fields("defining '%s' as %s",n,fieldtype) end predefinesymbols(specification) end @@ -537,67 +562,67 @@ function codeinjections.definefield(specification) end end -function codeinjections.clonefield(specification) - local p, c, v = specification.parent, specification.children, specification.variant +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: '%s', parent '%s', variant: '%s'",p or "?",c or "?", v or "?") + report_fields("invalid clone: children: '%s', parent '%s', alternative: '%s'",p or "?",c or "?", v or "?") end - else - for n in gmatch(c,"[^, ]+") do - local f, r, c, x = fields[n], radios[n], clones[n], fields[p] - if f or r or c then - if trace_fields then - report_fields("already cloned: child: '%s', parent '%s', variant: '%s'",p or "?",n or "?", v or "?") - end - elseif x then - if trace_fields then - report_fields("invalid clone: child: '%s', variant: '%s', no parent",n or "?", v or "?") - end - else - if trace_fields then - report_fields("cloning: child: '%s', parent '%s', variant: '%s'",p or "?",n or "?", v or "?") - end - clones[n] = specification - predefinesymbols(specification) + return + end + local x = fields[p] + if not x then + if trace_fields then + report_fields("cloning: unknown parent '%s'",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: '%s', parent '%s', alternative: '%s'",p,n, v or "?") end + else + if trace_fields then + report_fields("cloning: child: '%s', parent '%s', alternative: '%s'",p,n, v or "?") + end + clones[n] = specification + predefinesymbols(specification) end end end -function codeinjections.getfieldgroup(name) +function codeinjections.getfieldcategory(name) local f = fields[name] or radios[name] or clones[name] if f then - local g = f.group + local g = f.category if not g or g == "" then - local v, p, k = f.variant, f.parent, f.kind + 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.group - elseif k == "sub" then + g = f and f.category + elseif t == "sub" then f = fields[p] - g = f and f.group + g = f and f.category end end - if g then - context(g) - end + return g end end -- -function codeinjections.doiffieldgroupelse(name) - local f = fields[name] or radios[name] or clones[name] - commands.testcase(f and f.group) +function codeinjections.validfieldcategory(name) + return fields[name] or radios[name] or clones[name] end -function codeinjections.doiffieldsetelse(tag) - commands.testcase(fieldsets[tag]) +function codeinjections.validfieldset(name) + return fieldsets[tag] end -function codeinjections.doiffieldelse(name) - commands.testcase(fields[name]) +function codeinjections.validfield(name) + return fields[name] end -- @@ -630,14 +655,21 @@ local function finishfields() for name, field in next, fields do local kids = field.kids if kids then - pdfflushobject(field.kobj,kids) + pdfflushobject(field.kidsnum,kids) + end + local opt = field.opt + if opt then + pdfflushobject(field.optnum,opt) end - local pobj = field.pobj end for name, field in next, radios do local kids = field.kids if kids then - pdfflushobject(field.kobj,kids) + pdfflushobject(field.kidsnum,kids) + end + local opt = field.opt + if opt then + pdfflushobject(field.optnum,opt) end end if #collected > 0 then @@ -659,300 +691,367 @@ end lpdf.registerdocumentfinalizer(finishfields,"form fields") -local pdf_widget = pdfconstant("Widget") -local pdf_tx = pdfconstant("Tx") -local pdf_ch = pdfconstant("Ch") -local pdf_btn = pdfconstant("Btn") -local pdf_yes = pdfconstant("Yes") -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 methods = { } -function codeinjections.typesetfield(name,specification) +function nodeinjections.typesetfield(name,specification) local field = fields[name] or radios[name] or clones[name] if not field then report_fields( "unknown child '%s'",name) -- unknown field return end - local variant, parent = field.variant, field.parent - if variant == "copy" or variant == "clone" then -- only in clones + 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.kind] + local method = methods[field.type] if method then - method(name,specification,variant) + return method(name,specification,alternative) else - report_fields( "unknown method '%s' for child '%s'",field.kind,name) + report_fields( "unknown method '%s' for child '%s'",field.type,name) end end --- can be optional multipass optimization (share objects) - -local function save_parent(field,specification,d) - local kn = pdfreserveobject() - d.Kids = pdfreference(kn) - field.kobj = kn +local function save_parent(field,specification,d,hasopt) + local kidsnum = pdfreserveobject() + d.Kids = pdfreference(kidsnum) + field.kidsnum = kidsnum field.kids = pdfarray() - local pn = pdfflushobject(d) - field.pobj = pn - collected[#collected+1] = pdfreference(pn) + 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) ---~ local kn = pdfreserveobject() +local function save_kid(field,specification,d,optname) local kn = pdfreserveannotation() field.kids[#field.kids+1] = pdfreference(kn) - node.write(pdfannotation_node(specification.width,specification.height,0,d(),kn)) + if optname then + field.opt[#field.opt+1] = optname + 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 -function methods.line(name,specification,variant,extras) - local field = fields[name] - if variant == "copy" or variant == "clone" then - report_fields("todo: clones of text fields") - end - local kind = field.kind - if not field.pobj then - if trace_fields then - report_fields("using parent text '%s'",name) +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 '%s'",parent.name) + end + makelineparent(parent,specification) end - if extras then - enhance(specification,extras) + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent text '%s'",name) + end + makelineparent(parent,specification) end - local text = pdfunicode(field.default) - 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 = (specification.length == 0 and 1000) or specification.length, - DV = text, - V = text, - } - save_parent(field,specification,d) - field.specification = specification end - specification = field.specification or { } -- todo: radio spec if trace_fields then report_fields("using child text '%s'",name) end local d = pdfdictionary { Subtype = pdf_widget, - Parent = pdfreference(field.pobj), + Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), DA = fieldsurrounding(specification), AA = fieldactions(specification), + MK = fieldrendering(specification), Q = fieldalignment(specification), } - save_kid(field,specification,d) + return save_kid(parent,specification,d) end -function methods.text(name,specification,variant) - methods.line(name,specification,variant,"MultiLine") +function methods.line(name,specification) + return makelinechild(name,specification) end -function methods.choice(name,specification,variant,extras) - local field = fields[name] - if variant == "copy" or variant == "clone" then - report_fields("todo: clones of choice fields") - end - local kind = field.kind - local d - if not field.pobj then - if trace_fields then - report_fields("using parent choice '%s'",name) +function methods.text(name,specification) + return makelinechine(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 '%s'",parent.name) + end + makechoiceparent(parent,specification,extras) end - if extras then - enhance(specification,extras) + else + parent = fields[name] + field = parent + if not parent.pobj then + if trace_fields then + report_fields("using parent choice '%s'",name) + end + makechoiceparent(parent,specification,extras) end - 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), - } - save_parent(field,specification,d) - field.specification = specification end - specification = field.specification or { } if trace_fields then report_fields("using child choice '%s'",name) end local d = pdfdictionary { Subtype = pdf_widget, - Parent = pdfreference(field.pobj), + Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), } - save_kid(field,specification,d) + return save_kid(parent,specification,d) -- do opt here end -function methods.popup(name,specification,variant) - methods.choice(name,specification,variant,"PopUp") +function methods.choice(name,specification) + return makechoicechild(name,specification) end -function methods.combo(name,specification,variant) - methods.choice(name,specification,variant,"PopUp,Edit") + +function methods.popup(name,specification) + return makechoicechild(name,enhance(specification,"PopUp")) end --- Probably no default appearance needed for first kid and no javascripts for the --- parent ... I will look into it when I have to make a complex document. +function methods.combo(name,specification) + return makechoicechild(name,enhance(specification,"PopUp,Edit")) +end -function methods.check(name,specification,variant) - -- no /Opt because (1) it's messy - see pdf spec, (2) it discouples kids and - -- contrary to radio there is no way to associate then - local field = fields[name] - if variant == "copy" or variant == "clone" then - report_fields("todo: clones of check fields") - end - local kind = field.kind - local appearance, default = fieldstates(field,true) - if not field.pobj then - if trace_fields then - report_fields("using parent check '%s'",name) +local function makecheckparent(field,specification) + local d = pdfdictionary { + T = pdfunicode(specification.title), + 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 '%s'",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 '%s'",name) + end + makecheckparent(parent,specification,extras) end - 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, - DV = default, - V = default, - AS = default, - AP = appearance, - H = pdf_n, - } - save_parent(field,specification,d) - field.specification = specification end - specification = field.specification or { } -- todo: radio spec if trace_fields then report_fields("using child check '%s'",name) end local d = pdfdictionary { Subtype = pdf_widget, - Parent = pdfreference(field.pobj), + Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), - DV = default, - V = default, - AS = default, - AP = appearance, H = pdf_n, } - save_kid(field,specification,d) + 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(field,true) + d.AS = default + d.AP = appearance + return save_kid(parent,specification,d,value) + end end -function methods.push(name,specification,variant) - local field = fields[name] - if variant == "copy" or variant == "clone" then - report_fields("todo: clones of push fields") - end - local kind = field.kind - if not field.pobj then - if trace_fields then - report_fields("using parent push '%s'",name) +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 '%s'",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 '%s'",name) + end + makepushparent(parent,specification) end - enhance(specification,"PushButton") - 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) - field.specification = specification end - specification = field.specification or { } -- todo: radio spec if trace_fields then report_fields("using child push '%s'",name) end + local fontsymbol = specification.fontsymbol local d = pdfdictionary { Subtype = pdf_widget, Parent = pdfreference(field.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), - AP = fieldappearances(field), H = pdf_p, } - save_kid(field,specification,d) + 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") + 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 -function methods.sub(name,specification,variant) - local field = radios[name] or fields[name] or clones[name] -- fields in case of a clone, maybe use dedicated clones - local values - if variant == "copy" or variant == "clone" then - name = field.parent - values = field.values -- clone only, copy has nil so same as parent +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 '%s'",parent.name) + end + makeradioparent(parent,parent) + end field = radios[name] else - values = field.values - end - local parent = fields[field.parent] - if not parent then - return - end - local appearance = fieldstates(field,name,values) -- we need to force the 'On' name - local default = radiodefault(parent,field) - if not parent.pobj then - if trace_fields then - report_fields("using parent '%s' of radio '%s' with values '%s' and default '%s'",parent.name,name,parent.values or "?",parent.default or "?") + field = radios[name] + parent = fields[field.parent] + if not parent.pobj then + if trace_fields then + report_fields("using parent radio '%s'",name) + end + makeradioparent(parent,parent) end - local specification = parent.specification or { } - -- enhance(specification,"Radio,RadiosInUnison") - enhance(specification,"RadiosInUnison") -- maybe also PushButton as acrobat does - local d = pdfdictionary { - Subtype = pdf_widget, - T = parent.name, - FT = pdf_btn, - Rect = pdf_no_rect, - F = fieldplus(specification), - Ff = fieldflag(specification), - H = pdf_n, - V = default, - } - save_parent(parent,specification,d) end if trace_fields then - report_fields("using child radio '%s' with values '%s'",name,values or "?") + report_fields("using child radio '%s' with values '%s' and default '%s'",name,field.values or "?",field.default or "?") end + local fontsymbol = specification.fontsymbol local d = pdfdictionary { Subtype = pdf_widget, Parent = pdfreference(parent.pobj), F = fieldplus(specification), - DA = fieldattributes(specification), OC = fieldlayer(specification), AA = fieldactions(specification), - AS = default, - AP = appearance, H = pdf_n, } - save_kid(parent,specification,d) + 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(field,true) -- false is also ok + d.AS = default -- needed ? + d.AP = appearance + return save_kid(parent,specification,d,value) + end +end + +function methods.sub(name,specification) + return makeradiochild(name,enhance(specification,"Radio,RadiosInUnison")) end diff --git a/tex/context/base/lpdf-fmt.lua b/tex/context/base/lpdf-fmt.lua index 8250ce775..197e24ce9 100644 --- a/tex/context/base/lpdf-fmt.lua +++ b/tex/context/base/lpdf-fmt.lua @@ -17,7 +17,8 @@ local report_backend = logs.reporter("backend","profiles") local backends, lpdf = backends, lpdf -local codeinjections = backends.pdf.codeinjections -- normally it is registered +local codeinjections = backends.pdf.codeinjections + local variables = interfaces.variables local viewerlayers = attributes.viewerlayers local colors = attributes.colors @@ -626,10 +627,6 @@ end lpdf.registerdocumentfinalizer(flushoutputintents,2,"output intents") -directives.register("backend.format", function(v) - codeinjections.setformat(v) -end) - function codeinjections.setformat(s) local format, level, profile, intent, option, filename = s.format or "", s.level or "", s.profile or "", s.intent or "", s.option or "", s.file or "" @@ -713,6 +710,19 @@ function codeinjections.setformat(s) end end +directives.register("backend.format", function(v) -- table ! + local tv = type(v) + if tv == "table" then + codeinjections.setformat(v) + elseif tv == "string" then + codeinjections.setformat { format = v } + end +end) + +function commands.setformat(s) + codeinjections.setformat(s) +end + function codeinjections.getformatoption(key) return formatspecification and formatspecification[key] end diff --git a/tex/context/base/lpdf-ini.lua b/tex/context/base/lpdf-ini.lua index c8291ff99..206e44688 100644 --- a/tex/context/base/lpdf-ini.lua +++ b/tex/context/base/lpdf-ini.lua @@ -389,25 +389,23 @@ end function lpdf.flushobject(name,data) if data then - name = names[name] or name - if name then - if trace_objects then - if trace_detail then - report_objects("flushing data to reserved object with name '%s' -> %s",name,tostring(data)) - else - report_objects("flushing data to reserved object with name '%s'",name) - end + 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 '%s' -> %s",name,tostring(data)) + else + report_objects("flushing data to reserved object with name '%s'",name) end - return pdfimmediateobject(name,tostring(data)) + return pdfimmediateobject(named,tostring(data)) else - if trace_objects then - if trace_detail then - report_objects("flushing data to reserved object with number %s -> %s",name,tostring(data)) - else - report_objects("flushing data to reserved object with number %s",name) - end + if not trace_objects then + elseif trace_detail then + report_objects("flushing data to reserved object with number %s -> %s",name,tostring(data)) + else + report_objects("flushing data to reserved object with number %s",name) end - return pdfimmediateobject(tostring(data)) + return pdfimmediateobject(name,tostring(data)) end else if trace_objects and trace_detail then @@ -688,36 +686,36 @@ function lpdf.id() return format("%s.%s",tex.jobname,timestamp) end -function lpdf.checkedkey(t,key,kind) +function lpdf.checkedkey(t,key,variant) local pn = t[key] if pn then local tn = type(pn) - if tn == kind then - if kind == "string" then + if tn == variant then + if variant == "string" then return pn ~= "" and pn - elseif kind == "table" then + elseif variant == "table" then return next(pn) and pn else return pn end - elseif tn == "string" and kind == "number" then + elseif tn == "string" and variant == "number" then return tonumber(pn) end end end -function lpdf.checkedvalue(value,kind) -- code not shared +function lpdf.checkedvalue(value,variant) -- code not shared if value then local tv = type(value) - if tv == kind then - if kind == "string" then + if tv == variant then + if variant == "string" then return value ~= "" and value - elseif kind == "table" then + elseif variant == "table" then return next(value) and value else return value end - elseif tv == "string" and kind == "number" then + elseif tv == "string" and variant == "number" then return tonumber(value) end end diff --git a/tex/context/base/lpdf-mis.lua b/tex/context/base/lpdf-mis.lua index 39d7fbae8..d28439c26 100644 --- a/tex/context/base/lpdf-mis.lua +++ b/tex/context/base/lpdf-mis.lua @@ -109,12 +109,20 @@ end local openpage, closepage, opendocument, closedocument -function codeinjections.flushdocumentactions(open,close) - opendocument, closedocument = open, close +function codeinjections.registerdocumentopenaction(open) + opendocument = open end -function codeinjections.flushpageactions(open,close) - openpage, closepage = open, close +function codeinjections.registerdocumentcloseaction(close) + closedocument = close +end + +function codeinjections.flushpageopenaction(open) + openpage = open +end + +function codeinjections.flushpagecloseaction(close) + closepage = close end local function flushdocumentactions() @@ -139,30 +147,41 @@ local function flushpageactions() end end -lpdf.registerpagefinalizer(flushpageactions,"page actions") +lpdf.registerpagefinalizer (flushpageactions, "page actions") lpdf.registerdocumentfinalizer(flushdocumentactions,"document actions") ---- info +--- info : this can change and move elsewhere + +local identity = { } function codeinjections.setupidentity(specification) - local title = specification.title or "" - if title ~= "" then - lpdf.addtoinfo("Title", pdfunicode(title), title) + for k, v in next, specification do + if v ~= "" then + identity[k] = v + end end - local subject = specification.subject or "" - if subject ~= "" then - lpdf.addtoinfo("Subject", pdfunicode(subject), subject) +end + +local function setupidentity() + local title = identity.title + if not title or title == "" then + title = tex.jobname end - local author = specification.author or "" + lpdf.addtoinfo("Title", pdfunicode(title), title) + local subtitle = identity.subtitle or "" + if subtitle ~= "" then + lpdf.addtoinfo("Subject", pdfunicode(subtitle), subtitle) + end + local author = identity.author or "" if author ~= "" then lpdf.addtoinfo("Author", pdfunicode(author), author) -- '/Author' in /Info, 'Creator' in XMP end - local creator = specification.creator or "" + local creator = identity.creator or "" if creator ~= "" then lpdf.addtoinfo("Creator", pdfunicode(creator), creator) -- '/Creator' in /Info, 'CreatorTool' in XMP end lpdf.addtoinfo("CreationDate", pdfstring(lpdf.pdftimestamp(lpdf.timestamp()))) - local date = specification.date or "" + local date = identity.date or "" local pdfdate = lpdf.pdftimestamp(date) if pdfdate then lpdf.addtoinfo("ModDate", pdfstring(pdfdate), date) @@ -172,15 +191,18 @@ function codeinjections.setupidentity(specification) date = lpdf.timestamp() lpdf.addtoinfo("ModDate", pdfstring(lpdf.pdftimestamp(date)), date) end - local keywords = specification.keywords or "" + local keywords = identity.keywords or "" if keywords ~= "" then keywords = string.gsub(keywords, "[%s,]+", " ") lpdf.addtoinfo("Keywords",pdfunicode(keywords), keywords) end local id = lpdf.id() lpdf.addtoinfo("ID", pdfstring(id), id) -- needed for pdf/x + setupidentity = function() end end +lpdf.registerpagefinalizer(setupidentity,"identity") + local function flushjavascripts() local t = interactions.javascripts.flushpreambles() if #t > 0 then @@ -279,7 +301,7 @@ local function pagespecification() local pageheight = tex.pdfpageheight local box = pdfarray { -- can be cached boxvalue(leftoffset), - boxvalue(pageheight-topoffset-height), + boxvalue(pageheight+topoffset-height), boxvalue(width-leftoffset), boxvalue(pageheight-topoffset), } diff --git a/tex/context/base/lpdf-ren.lua b/tex/context/base/lpdf-ren.lua index 404c45e11..2fc1bf23c 100644 --- a/tex/context/base/lpdf-ren.lua +++ b/tex/context/base/lpdf-ren.lua @@ -26,6 +26,14 @@ 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 @@ -73,8 +81,8 @@ local function useviewerlayer(name) local nd = pdfdictionary { Type = pdf_ocg, Name = specification.title or "unknown", - Intent = ((specification.kind > 0) and pdf_design) or nil, -- disable layer hiding by user - Usage = ((specification.printable == variables.no) and lpdf_usage) or nil , -- printable or not + 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 @@ -87,7 +95,7 @@ local function useviewerlayer(name) cache[#cache+1] = { dn, dd } pdfld[tag] = dr textlayers[#textlayers+1] = nr - if specification.visible == variables.start then + if specification.visible == v_start then videlayers[#videlayers+1] = nr else hidelayers[#hidelayers+1] = nr @@ -212,17 +220,19 @@ local last = 0 function codeinjections.setpagetransition(specification) local n, delay = specification.n, specification.delay - if n == variables.auto then + 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 == variables.stop then + elseif n == v_stop then return - elseif n == variables.reset then + elseif n == v_reset then last = 0 return - elseif n == variables.random then + elseif n == v_random then n = math.random(1,#pagetransitions) else n = tonumber(n) diff --git a/tex/context/base/lpdf-wid.lua b/tex/context/base/lpdf-wid.lua index d0e441c26..48beacfd8 100644 --- a/tex/context/base/lpdf-wid.lua +++ b/tex/context/base/lpdf-wid.lua @@ -6,112 +6,152 @@ if not modules then modules = { } end modules ['lpdf-wid'] = { license = "see context related readme files" } -local gmatch, gsub, find = string.gmatch, string.gsub, string.find +local gmatch, gsub, find, lower, format = string.gmatch, string.gsub, string.find, string.lower, string.format 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_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 nodeinjections = backends.pdf.nodeinjections +local codeinjections = backends.pdf.codeinjections +local registrations = backends.pdf.registrations -local executers = structures.references.executers -local variables = interfaces.variables +local executers = structures.references.executers +local variables = interfaces.variables -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 pdfcolorspec = lpdf.colorspec -local pdfflushobject = lpdf.flushobject -local pdfreserveannotation = lpdf.reserveannotation -local pdfreserveobject = lpdf.reserveobject -local pdfimmediateobject = lpdf.immediateobject -local pdfpagereference = lpdf.pagereference +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 nodepool = nodes.pool +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 pdfcolorspec = lpdf.colorspec +local pdfflushobject = lpdf.flushobject +local pdfreserveannotation = lpdf.reserveannotation +local pdfreserveobject = lpdf.reserveobject +local pdfimmediateobject = lpdf.immediateobject +local pdfpagereference = lpdf.pagereference +local pdfshareobjectreference = lpdf.shareobjectreference -local pdfannotation_node = nodepool.pdfannotation +local nodepool = nodes.pool -local hpack_node, write_node = node.hpack, node.write +local pdfannotation_node = nodepool.pdfannotation -local pdf_border = pdfarray { 0, 0, 0 } -- can be shared +local hpack_node = node.hpack +local write_node = node.write + +local pdf_border = pdfarray { 0, 0, 0 } -- can be shared -- symbols local presets = { } -- xforms -function codeinjections.registersymbol(name,n) +local function registersymbol(name,n) presets[name] = pdfreference(n) end -function codeinjections.registeredsymbol(name) +local function registeredsymbol(name) return presets[name] end -function codeinjections.presetsymbol(symbol) +local function presetsymbol(symbol) if not presets[symbol] then context.predefinesymbol { symbol } end end -function codeinjections.presetsymbollist(list) +local function presetsymbollist(list) if list then for symbol in gmatch(list,"[^, ]+") do - codeinjections.presetsymbol(symbol) + presetsymbol(symbol) end end end +codeinjections.registersymbol = registersymbol +codeinjections.registeredsymbol = registeredsymbol +codeinjections.presetsymbol = presetsymbol +codeinjections.presetsymbollist = presetsymbollist + -- comments -local symbols = { - New = pdfconstant("Insert"), - Insert = pdfconstant("Insert"), - Balloon = pdfconstant("Comment"), +-- 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"), - Text = pdfconstant("Note"), - Addition = pdfconstant("NewParagraph"), - NewParagraph = pdfconstant("NewParagraph"), Help = pdfconstant("Help"), - Paragraph = pdfconstant("Paragraph"), + Insert = pdfconstant("Insert"), Key = pdfconstant("Key"), - Graph = pdfconstant("Graph"), - Paperclip = pdfconstant("Paperclip"), - Attachment = pdfconstant("Attachment"), - Tag = pdfconstant("Tag"), + Newparagraph = pdfconstant("NewParagraph"), + Note = pdfconstant("Note"), + Paragraph = pdfconstant("Paragraph"), } -symbols[variables.normal] = pdfconstant("Note") +comment_symbols.NewParagraph = Newparagraph +comment_symbols.Default = Note -local nofcomments, usepopupcomments, stripleading = 0, true, true - -local function analyzesymbol(symbol) +local function analyzesymbol(symbol,collection) if not symbol or symbol == "" then - return symbols.normal, nil - elseif symbols[symbol] then - return symbols[symbol], nil + return collection.Default, nil + elseif collection[symbol] then + return collection[symbol], nil else + local setn, setr, setd local set = settings_to_array(symbol) - local normal, down = set[1], set[2] - if normal then - normal = codeinjections.registeredsymbol(down or normal) - end - if down then - down = codeinjections.registeredsymbol(normal) - end - if down or normal then - return nil, pdfdictionary { - N = normal, - D = down, - } + 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 @@ -119,67 +159,34 @@ local function analyzelayer(layer) -- todo: (specification.layer ~= "" and pdfreference(specification.layer)) or nil, -- todo: ref to layer end -function codeinjections.registercomment(specification) - nofcomments = nofcomments + 1 - local text = buffers.collectcontent(specification.buffer) - text = string.strip(text) - if stripleading then -- maybe just strip all leading and trailing spacing - text = gsub(text,"[\n\r] *","\n") - end - local name, appearance = analyzesymbol(specification.symbol) - local d = pdfdictionary { - Subtype = pdfconstant("Text"), - Open = specification.open, - Contents = pdfunicode(text), - T = (specification.title ~= "" and pdfunicode(specification.title)) or nil, - C = pdfcolorspec(specification.colormodel,specification.colorvalue), - OC = analyzelayer(specification.layer), - Name = name, - AP = appearance, - } - -- - -- watch the nice feed back to tex hack - -- - -- we can consider replacing nodes by user nodes that do a latelua - -- so that we get rid of all annotation whatsits - if usepopupcomments then - local nd = pdfreserveannotation() - local nc = pdfreserveannotation() - local c = pdfdictionary { - Subtype = pdfconstant("Popup"), - Parent = pdfreference(nd), - } - d.Popup = pdfreference(nc) - texbox["commentboxone"] = hpack_node(pdfannotation_node(0,0,0,d(),nd)) -- current dir - texbox["commentboxtwo"] = hpack_node(pdfannotation_node(specification.width,specification.height,0,c(),nc)) -- current dir - else - texbox["commentboxone"] = hpack_node(pdfannotation_node(0,0,0,d())) -- current dir - texbox["commentboxtwo"] = nil - 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 -local nofattachments, attachments, filestreams, referenced = 0, { }, { }, { } +-- Attachments --- todo: hash and embed once +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 name, reference in next, filestreams do - if reference then - if ignorereferenced and referenced[name] then - reference = nil - end - if reference then - e[#e+1] = pdfstring(name) - e[#e+1] = reference -- already a reference - end + for tag, reference in next, filestreams do + if not reference then + report_attachment("unreferenced file: tag '%s'",tag) + elseif referenced[name] == "hidden" then + e[#e+1] = pdfstring(tag) + e[#e+1] = reference -- already a reference else - -- we can issue a message + -- messy spec ... when annot not in named else twice in menu list acrobat end end lpdf.addtonames("EmbeddedFiles",pdfreference(pdfflushobject(pdfdictionary{ Names = e }))) @@ -188,78 +195,201 @@ end lpdf.registerdocumentfinalizer(flushembeddedfiles,"embeddedfiles") -function codeinjections.embedfile(filename) - local r = filestreams[filename] - if r == false then - return nil - elseif r then - return r - elseif not lfs.isfile(filename) then - interfaces.showmessage("interactions",5,filename) - filestreams[filename] = false - return nil +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 + 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 - local basename = file.basename(filename) - local a = pdfdictionary { Type = pdfconstant("EmbeddedFile") } - local f = pdfimmediateobject("streamfile",filename,a()) - local d = pdfdictionary { - Type = pdfconstant("Filespec"), - F = pdfstring(newname or basename), - UF = pdfstring(newname or basename), - EF = pdfdictionary { F = pdfreference(f) }, - } - local r = pdfreference(pdfflushobject(d)) - filestreams[filename] = r - return r + if not filename then + return nil + end + local r = filestreams[hash] + if r == false then + return nil + elseif r then + return r + elseif not lfs.isfile(filename) then + filestreams[filename] = false + return nil + end end + local basename = file.basename(filename) + 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 = pdfimmediateobject("stream",data,a()) + specification.data = true -- signal that still data but already flushed + else + f = pdfimmediateobject("streamfile",filename,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 codeinjections.attachfile(specification) - local attachment = interactions.attachments.attachment(specification.label) - if not attachment then - -- todo: message - return - end - local filename = attachment.filename - if not filename or filename == "" then - -- todo: message - return +function nodeinjections.attachfile(specification) + local registered = specification.registered or "" + local data = specification.data + local hash + if data then + hash = md5.HEX(data) + else + local filename = specification.file + if not filename or filename == "" then + report_attachment("missing file specification: registered '%s', using registered instead",registered) + filename = registered + specification.file = registered + end + if not lfs.isfile(filename) then + report_attachment("invalid file specification: registered '%s', filename '%s'",registered,filename) + return + end + hash = filename end - referenced[filename] = true + specification.hash = hash nofattachments = nofattachments + 1 - local label = attachment.label or "" - local title = attachment.title or "" - local newname = attachment.newname or "" - if label == "" then label = filename end - if title == "" then title = label end - if newname == "" then newname = filename end - local aref = attachments[label] + 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 = v_unknown + end + if title == "" then + title = registered + end + local aref = attachments[registered] if not aref then - aref = codeinjections.embedfile(filename,newname) - attachments[label] = aref + aref = codeinjections.embedfile(specification) + attachments[registered] = aref + end + if not aref then + report_attachment("skipping: registered '%s'",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 - local name, appearance = analyzesymbol(specification.symbol) - local d = pdfdictionary { - Subtype = pdfconstant("FileAttachment"), - FS = aref, - Contents = pdfunicode(title), - Name = name, - AP = appearance, - OC = analyzelayer(specification.layer), - C = pdfcolorspec(specification.colormodel,specification.colorvalue), - } - -- as soon as we can ask for the dimensions of an xform we can - -- use them here - local width = specification.width or 0 - local height = specification.height or 0 - local depth = specification.depth or 0 - write_node(pdfannotation_node(width,height,depth,d())) -- somehow the dimensions come out wrong end -function codeinjections.attachmentid(filename) +function codeinjections.attachmentid(filename) -- not used in context return filestreams[filename] end +local nofcomments, usepopupcomments, stripleading = 0, false, true + +function nodeinjections.comment(specification) + nofcomments = nofcomments + 1 + local text = string.strip(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 "" + 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 d = pdfdictionary { + Subtype = pdfconstant("Text"), + -- Open = specification.open, -- now options + Contents = pdfunicode(text), + 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 -> <> >> @@ -308,7 +438,7 @@ local function insertrenderingwindow(specification) local label = specification.label --~ local openpage = specification.openpage --~ local closepage = specification.closepage - if specification.options == variables.auto then + if specification.options == v_auto then if openpageaction then -- \handlereferenceactions{\v!StartRendering{#2}} end @@ -384,7 +514,7 @@ local function insertrendering(specification) } if isurl then descriptor.FS = pdfconstant("URL") - elseif options[variables.embed] then + elseif options[v_embed] then descriptor.EF = codeinjections.embedfile(filename) end local clip = pdfdictionary { @@ -430,7 +560,7 @@ function codeinjections.processrendering(label) local specification = interactions.renderings.rendering(label) if not specification then -- error - elseif specification.kind == "external" then + elseif specification.type == "external" then insertrendering(specification) else insertrenderingobject(specification) diff --git a/tex/context/base/lpdf-xmp.lua b/tex/context/base/lpdf-xmp.lua index 160a5ece1..c9bead8a5 100644 --- a/tex/context/base/lpdf-xmp.lua +++ b/tex/context/base/lpdf-xmp.lua @@ -14,7 +14,9 @@ local trace_xmp = false trackers.register("backend.xmp", function(v) trace_xmp local report_xmp = logs.reporter("backend","xmp") -local lpdf = lpdf +local backends, lpdf = backends, lpdf + +local codeinjections = backends.pdf.codeinjections -- normally it is registered local pdfdictionary = lpdf.dictionary local pdfconstant = lpdf.constant @@ -77,11 +79,7 @@ local mapping = { local xmp, xmpfile, xmpname = nil, nil, "lpdf-pdx.xml" -function lpdf.setxmpfile(name) - -- xmpfile = resolvers.findctxfile(name) or "" - -- if xmpfile == "" then - -- xmpfile = nil - -- end +local function setxmpfile(name) if xmp then report_xmp("discarding loaded file '%s'",xmpfile) xmp = nil @@ -89,6 +87,9 @@ function lpdf.setxmpfile(name) xmpfile = name ~= "" and name end +codeinjections.setxmpfile = setxmpfile +commands.setxmpfile = setxmpfile + local function valid_xmp() if not xmp then -- local xmpfile = xmpfile or resolvers.findfile(xmpname) or "" diff --git a/tex/context/base/luat-dum.lua b/tex/context/base/luat-dum.lua deleted file mode 100644 index e0b629248..000000000 --- a/tex/context/base/luat-dum.lua +++ /dev/null @@ -1,207 +0,0 @@ -if not modules then modules = { } end modules ['luat-dum'] = { - version = 1.100, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local dummyfunction = function() end -local dummyreporter = function(c) return function(...) texio.write(c .. " : " .. string.format(...)) end end - -statistics = { - register = dummyfunction, - starttiming = dummyfunction, - stoptiming = dummyfunction, - elapsedtime = nil, -} -directives = { - register = dummyfunction, - enable = dummyfunction, - disable = dummyfunction, -} -trackers = { - register = dummyfunction, - enable = dummyfunction, - disable = dummyfunction, -} -experiments = { - register = dummyfunction, - enable = dummyfunction, - disable = dummyfunction, -} -storage = { -- probably no longer needed - register = dummyfunction, - shared = { }, -} -logs = { - new = dummyreporter, - reporter = dummyreporter, - messenger = dummyreporter, - report = dummyfunction, -} -callbacks = { - register = function(n,f) return callback.register(n,f) end, -} -utilities = { - storage = { - allocate = function(t) return t or { } end, - mark = function(t) return t or { } end, - }, -} - -characters = characters or { - data = { } -} - --- we need to cheat a bit here - -texconfig.kpse_init = true - -resolvers = resolvers or { } -- no fancy file helpers used - -local remapper = { - otf = "opentype fonts", - ttf = "truetype fonts", - ttc = "truetype fonts", - dfont = "truetype fonts", -- "truetype dictionary", - cid = "cid maps", - fea = "font feature files", -} - -function resolvers.findfile(name,kind) - name = string.gsub(name,"\\","\/") - kind = kind and string.lower(kind) - local found = kpse.find_file(name,(kind and kind ~= "" and (remapper[kind] or kind)) or file.extname(name,"tex")) - if not found or found == "" then - found = kpse.find_file(name,"other text file") - end - return found -end - -function resolvers.findbinfile(name,kind) - if not kind or kind == "" then - kind = file.extname(name) -- string.match(name,"%.([^%.]-)$") - end - return resolvers.findfile(name,(kind and remapper[kind]) or kind) -end - -function resolvers.resolve(s) - return s -end - -function resolvers.unresolve(s) - return s -end - --- Caches ... I will make a real stupid version some day when I'm in the --- mood. After all, the generic code does not need the more advanced --- ConTeXt features. Cached data is not shared between ConTeXt and other --- usage as I don't want any dependency at all. Also, ConTeXt might have --- different needs and tricks added. - ---~ containers.usecache = true - -caches = { } - -local writable, readables = nil, { } - -if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then - caches.namespace = 'generic' -end - -do - - local cachepaths = kpse.expand_path('$TEXMFCACHE') or "" - - if cachepaths == "" then - cachepaths = kpse.expand_path('$TEXMFVAR') - end - - if cachepaths == "" then - cachepaths = kpse.expand_path('$VARTEXMF') - end - - if cachepaths == "" then - cachepaths = "." - end - - cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") - - for i=1,#cachepaths do - if file.is_writable(cachepaths[i]) then - writable = file.join(cachepaths[i],"luatex-cache") - lfs.mkdir(writable) - writable = file.join(writable,caches.namespace) - lfs.mkdir(writable) - break - end - end - - for i=1,#cachepaths do - if file.is_readable(cachepaths[i]) then - readables[#readables+1] = file.join(cachepaths[i],"luatex-cache",caches.namespace) - end - end - - if not writable then - texio.write_nl("quiting: fix your writable cache path") - os.exit() - elseif #readables == 0 then - texio.write_nl("quiting: fix your readable cache path") - os.exit() - elseif #readables == 1 and readables[1] == writable then - texio.write(string.format("(using cache: %s)",writable)) - else - texio.write(string.format("(using write cache: %s)",writable)) - texio.write(string.format("(using read cache: %s)",table.concat(readables, " "))) - end - -end - -function caches.getwritablepath(category,subcategory) - local path = file.join(writable,category) - lfs.mkdir(path) - path = file.join(path,subcategory) - lfs.mkdir(path) - return path -end - -function caches.getreadablepaths(category,subcategory) - local t = { } - for i=1,#readables do - t[i] = file.join(readables[i],category,subcategory) - end - return t -end - -local function makefullname(path,name) - if path and path ~= "" then - name = "temp-" .. name -- clash prevention - return file.addsuffix(file.join(path,name),"lua") - end -end - -function caches.is_writable(path,name) - local fullname = makefullname(path,name) - return fullname and file.is_writable(fullname) -end - -function caches.loaddata(paths,name) - for i=1,#paths do - local fullname = makefullname(paths[i],name) - if fullname then - texio.write(string.format("(load: %s)",fullname)) - local data = loadfile(fullname) - return data and data() - end - end -end - -function caches.savedata(path,name,data) - local fullname = makefullname(path,name) - if fullname then - texio.write(string.format("(save: %s)",fullname)) - table.tofile(fullname,data,'return',false,true,false) - end -end diff --git a/tex/context/base/luat-mac.lua b/tex/context/base/luat-mac.lua index 1b9e09951..775e8a3b5 100644 --- a/tex/context/base/luat-mac.lua +++ b/tex/context/base/luat-mac.lua @@ -6,13 +6,15 @@ if not modules then modules = { } end modules ['luat-mac'] = { license = "see context related readme files" } -local P, V, S, R, C, Cs, Cmt = lpeg.P, lpeg.V, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Cmt +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 pushtarget, poptarget = logs.pushtarget, logs.poptarget + local report_macros = logs.reporter("interface","macros") local stack, top, n, hashes = { }, nil, 0, { } @@ -37,8 +39,17 @@ local function set(s) end local function get(s) - local m = top and top[s] or s - return m + 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() @@ -64,24 +75,44 @@ 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 -- @?! -- utf? -local longname = (longleft/"") * (nolong^1) * (longright/"") -local variable = P("#") * Cs(name + longname) -local escapedname = escape * name -local definer = escape * (P("def") + P("egdx") * P("def")) -local startcode = P("\\starttexdefinition") -local stopcode = P("\\stoptexdefinition") -local anything = patterns.anything -local always = patterns.alwaysmatched - -local pushlocal = always / push -local poplocal = always / pop -local declaration = variable / set -local identifier = variable / get +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") + P("egx") * P("def")) -- tex +local setter = escape * P("set") * (P("u")^-1 * P("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 + +-- 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") +local 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 = leftbrace * ((identifier + (1-rightbrace))^0) * rightbrace local function matcherror(str,pos) report_macros("runaway definition at: %s",sub(str,pos-30,pos)) @@ -93,7 +124,7 @@ local grammar = { "converter", * spaces * name * spaces - * (declaration + (1 - newline - space))^0 + * (declaration + furthercomment + (1 - newline - space))^0 * V("texbody") * stopcode * poplocal, @@ -105,20 +136,33 @@ local grammar = { "converter", definition = pushlocal * definer * escapedname - * (declaration + (1-leftbrace))^0 + * (declaration + furthercomment + commentline + (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 + nobrace )^0 -- * rightbrace^-1, -- the -1 catches errors * (rightbrace + Cmt(always,matcherror)), - pattern = V("definition") + V("texcode") + anything, + pattern = leadingcomment + + V("definition") + + V("setcode") + + V("texcode") + + furthercomment + + anything, converter = V("pattern")^1, } @@ -132,8 +176,8 @@ local checker = P("%") * (1 - newline - P("macros"))^0 local macros = { } resolvers.macros = macros -function macros.preprocessed(str) - return lpegmatch(parser,str) +function macros.preprocessed(str,strip) + return lpegmatch(parser,str,1,strip) end function macros.convertfile(oldname,newname) -- beware, no testing on oldname == newname @@ -148,7 +192,11 @@ end function macros.processmkvi(str,filename) if (filename and file.suffix(filename) == "mkvi") or lpegmatch(checker,str) == "mkvi" then - return lpegmatch(parser,str) or str + local result = lpegmatch(parser,str,1,true) or str + pushtarget("log") + report_macros("processed file '%s', delta %s",filename,#str-#result) + poptarget("log") + return result else return str end @@ -160,9 +208,17 @@ if resolvers.schemes then local hashed = url.hashed(name) local path = hashed.path if path and path ~= "" then - local data = resolvers.loadtexfile(path) - data = lpegmatch(parser,data) or "" - io.savedata(cachename,data) + local str = resolvers.loadtexfile(path) + if file.suffix(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 '%s', delta %s",filename,#str-#result) + poptarget("log") + io.savedata(cachename,result) + end end return cachename end @@ -174,12 +230,32 @@ if resolvers.schemes then end +--~ print(macros.preprocessed([[\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([[\def\bla#bla{bla:#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}]])) ---~ 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([[\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}}} +--~ ]])) + diff --git a/tex/context/base/lxml-tex.lua b/tex/context/base/lxml-tex.lua index b4cbbbdd3..26a8b2996 100644 --- a/tex/context/base/lxml-tex.lua +++ b/tex/context/base/lxml-tex.lua @@ -482,17 +482,16 @@ end local pihandlers = { } xml.pihandlers = pihandlers -local kind = 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 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 = kind * spaces * class * spaces * key * spaces * value +local parser = category * spaces * class * spaces * key * spaces * value pihandlers[#pihandlers+1] = function(str) --- local kind, class, key, value = lpegmatch(parser,str) if str then local a, b, c, d = lpegmatch(parser,str) if d then diff --git a/tex/context/base/m-barcodes.mkiv b/tex/context/base/m-barcodes.mkiv index fa230ba57..fd98cec75 100644 --- a/tex/context/base/m-barcodes.mkiv +++ b/tex/context/base/m-barcodes.mkiv @@ -1,5 +1,5 @@ %D \module -%D [ file=m-pstricks, +%D [ file=m-barcodes, %D version=2010.03.14, %D title=\CONTEXT\ Extra Modules, %D subtitle=Barcodes, diff --git a/tex/context/base/m-chart.mkiv b/tex/context/base/m-chart.mkiv index 6b947b23f..114e7d553 100644 --- a/tex/context/base/m-chart.mkiv +++ b/tex/context/base/m-chart.mkiv @@ -1051,17 +1051,20 @@ %D A hook into the help system. +% \def\showFLOWhelp#1% +% {\doifhelpinfo\FLOWhelp +% {\setbox#1=\hbox +% {\setbox\scratchbox=\hbox{\lower\@@FLOWdy\hbox +% {\helpbutton +% [\c!width=\wd0,\c!color=,\c!height=\@@FLOWdy,\c!frame=\v!no]% +% [\FLOWhelp]}}% +% \smashbox\scratchbox +% \setbox#1=\vbox +% {\forgetall\offinterlineskip\box#1\box\scratchbox}% +% \box#1}}} + \def\showFLOWhelp#1% - {\doifhelpinfo\FLOWhelp - {\setbox#1=\hbox - {\setbox\scratchbox=\hbox{\lower\@@FLOWdy\hbox - {\helpbutton - [\c!width=\wd0,\c!color=,\c!height=\@@FLOWdy,\c!frame=\v!no]% - [\FLOWhelp]}}% - \smashbox\scratchbox - \setbox#1=\vbox - {\forgetall\offinterlineskip\box#1\box\scratchbox}% - \box#1}}} + {} %D The next section is dedicated to splitting up charts. @@ -2317,20 +2320,6 @@ \def\FLOWchart% {\dodoubleempty\doFLOWchart} -%D A hook into the help system. - -\def\showFLOWhelp#1% - {\doifhelpinfo\FLOWhelp - {\setbox#1=\hbox - {\setbox\scratchbox=\hbox{\lower\@@FLOWdy\hbox - {\helpbutton - [\c!width=\wd0,\c!color=,\c!height=\@@FLOWdy,\c!frame=\v!no]% - [\FLOWhelp]}}% - \smashbox\scratchbox - \setbox#1=\vbox - {\forgetall\offinterlineskip\box#1\box\scratchbox}% - \box#1}}} - %D The next section is dedicated to splitting up charts. \def\getFLOWsize[#1]% diff --git a/tex/context/base/m-fields.mkiv b/tex/context/base/m-fields.mkiv new file mode 100644 index 000000000..cd840f377 --- /dev/null +++ b/tex/context/base/m-fields.mkiv @@ -0,0 +1,70 @@ +%D \module +%D [ file=m-fields, +%D version=2010.03.14, +%D title=\CONTEXT\ Extra Modules, +%D subtitle=Fields, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\unprotect + +%D A rather old example of field usage is the following. It +%D makes no sense to have this in the core. +%D +%D \starttyping +%D before \fillinfield[oeps]{whatever} after +%D \stoptyping + +\startJSpreamble{FillInField} used later + function CheckFillInField(right) { + if (event.value.toLowerCase() == right.toLowerCase()) { + event.target.hidden = true ; + } + event.value = "" + } +\stopJSpreamble + +\newcount\noffillinfields + +\definefieldcategory + [fillinfield] + [\c!n=1024, + \c!height=\strutht, + \c!depth=\strutdp, + \c!align=\v!middle, + \c!color=red, + \c!fieldframecolor=blue, + \c!fieldbackgroundcolor=\s!white, + \c!validate=JS(CheckFillInField{\therightanswer})] + +\def\fillinfield + {\dosingleempty\dofillinfield} + +\def\dofillinfield[#1]#2% + {\dontleavehmode + \hbox + {\forgetall + \global\advance\noffillinfields\plusone + \edef\currentfillinfieldname{fillinfield:\number\noffillinfields}% + \useJSscripts[ans]% + \definefieldbody + [\currentfillinfieldname] + [\c!type=\v!line, + \c!category=fillinfield]% + \doifelsenothing{#1} + {\def\therightanswer{#2}} + {\def\therightanswer{#1}}% + \setbox0\hbox{\strut#2}% + \setbox2\hbox{\strut\therightanswer}% + \dimen0=\dimexpr\ifdim\wd0>\wd2 \wd0 \else \wd2 \fi + .2em\relax + \hbox to \wd0 + {\wd0\zeropoint + \box0 + \hss\fieldbody[\currentfillinfieldname][\c!width=\dimen0]\hss}}} + +\protect \endinput diff --git a/tex/context/base/m-newmat.tex b/tex/context/base/m-newmat.tex index 08ce33b4c..3fb75df5d 100644 --- a/tex/context/base/m-newmat.tex +++ b/tex/context/base/m-newmat.tex @@ -11,6 +11,12 @@ %C therefore copyrighted by \PRAGMA. See licen-en.pdf for %C details. +\unprotect + +%D Code has been integrated. + +\protect \endinput + %D This module collects macros that \TEX\ users kind of expect %D to be available when typesetting math. Most of them %D originate in the \AMS\ macro packages. We have taken the @@ -19,269 +25,6 @@ %D derived from AMS math modules) and adapted|/|extended by %D Hans Hagen. -%D Here we will add code on demand. So, just let us know what -%D should go in here. - -%M \usemodule[newmat] - -\unprotect - -%D \macros -%D {qedsymbol} -%D -%D [HH] The general Quod Erat Domonstrandum symbol is defined -%D in such a way that we can configure it. Because this symbol -%D is also used in text mode, we make it a normal text symbol -%D with special behavior. - -\def\qedsymbol#1% - {\ifhmode - \unskip~\hfill#1\par - \else\ifmmode - \eqno#1\relax % Do we really need the \eqno here? - \else - \leavevmode\hbox{}\hfill#1\par - \fi\fi} - -\definesymbol [qed] [\qedsymbol{\mathematics{\square}}] - -%D \macros -%D {QED} -%D -%D [HH] For compatbility reasons we also provide the \type -%D {\QED} command. In case this command is overloaded, we still -%D have the symbol available. \symbol[qed] - -\def\QED{\symbol[qed]} - -%D \macros -%D {genfrac} -%D -%D [TH] The definition of \type {\genfrac} \& co. is not -%D trivial, because it allows some flexibility. This is -%D supposed to be a user||level command, but will fail quite -%D desparately if called outside math mode (\CONTEXT\ redefines -%D \type {\over}) -%D -%D [HH] We clean up this macro a bit and (try) to make it -%D understandable. The expansion is needed for generating -%D the second argument to \type {\dogenfrac}, which is to -%D be a control sequence like \type {\over}. - -\unexpanded\def\genfrac#1#2#3#4% - {\edef\!!stringa - {#1#2}% - \expanded - {\dogenfrac{#4}% - \csname - \ifx @#3@% - \ifx\!!stringa\empty - \strippedcsname\normalover - \else - \strippedcsname\normaloverwithdelims - \fi - \else - \ifx\!!stringa\empty - \strippedcsname\normalabove - \else - \strippedcsname\normalabovewithdelims - \fi - \fi - \endcsname}% - {#1#2#3}} - -\def\dogenfrac#1#2#3#4#5% - {{#1{\begingroup#4\endgroup#2#3\relax#5}}} - -%D \macros -%D {dfrac, tfrac, frac, dbinom, tbinom, binom} -%D -%D [TH] No need to make these \type {\unexpanded} as well. - -%\def\dfrac {\genfrac\empty\empty\empty\displaystyle} -%\def\tfrac {\genfrac\empty\empty\empty\textstyle} -%\def\frac {\genfrac\empty\empty\empty\donothing} - -\def\dfrac {\genfrac\empty\empty{}\displaystyle} -\def\tfrac {\genfrac\empty\empty{}\textstyle} -\def\frac {\genfrac\empty\empty{}\donothing} - -\def\dbinom{\genfrac()\zeropoint\displaystyle} -\def\tbinom{\genfrac()\zeropoint\textstyle} -\def\binom {\genfrac()\zeropoint\donothing} - -\def\xfrac {\genfrac\empty\empty{}\scriptstyle} -\def\xxfrac{\genfrac\empty\empty{}\scriptscriptstyle} - -%D Better: - -\unexpanded\def\frac#1#2{\mathematics{\genfrac\empty\empty{}\donothing{#1}{#2}}} - -%D [HH] This shows up as: -%D -%D \startbuffer -%D $\dfrac {1}{2} \tfrac {1}{2} \frac {1}{2}$ -%D $\dbinom{1}{2} \tbinom{1}{2} \binom{1}{2}$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D \getbuffer - -%D \macros -%D {text} -%D -%D [TH] \type {\text} is a command to typeset more or less -%D ordinary text inside of super- and sub|-|scripts. It has to -%D do a full font switch to get the sides right, so it will be -%D quite slow. \type {\text} kind of replaces \CONTEXT's \type -%D {\mathstyle} command. - -%D [HH] This macro is now also moved to the core, but we -%D keep it here as well for completeness. -%D -%D \starttyping -%D \unexpanded\def\mathtext -%D {\mathortext\domathtext\hbox} % {\ifmmode\@EA\dotext\else\@EA\hbox\fi} -%D -%D \def\domathtext#1% -%D {\mathchoice -%D {\dodomathtext\displaystyle\textface {#1}}% -%D {\dodomathtext\textstyle \textface {#1}}% -%D {\dodomathtext\textstyle \scriptface {#1}}% -%D {\dodomathtext\textstyle \scriptscriptface{#1}}} -%D -%D \def\dodomathtext#1#2#3% no \everymath ! -%D %{\hbox{\everymath{#1}\switchtobodyfont [#2]#3}} % 15 sec -%D {\hbox{\everymath{#1}\setcurrentfontbody{#2}#3}} % 3 sec (no math) -%D \stoptyping - -%D [HH] We use the following indirectness because \type {\text} -%D is a natural candidate for user macros (actually, it is -%D used in some modules). -%D -%D \starttyping -%D \let\text\mathtext -%D \stoptyping - -%D [HH] Actually, the font switch is not that slow when -%D typefaces are used. If needed this macro can be sped up. -%D -%D \startbuffer -%D ordinary text $x^{\text{extra ordinary text}}$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D \getbuffer - -%D \macros -%D {mathhexbox} -%D -%D [TH] \type {\mathhexbox} is also user||level (already -%D defined in Plain \TEX). It allows to get a math character -%D inserted as if it was a text character. - -\gdef\mathhexbox#1#2#3{\mathtext{$\mathsurround\zeropoint\mathchar"#1#2#3$}} - -%D \macros -%D {boxed} -%D -%D [HH] Another macro that users expect (slightly adapted): - -\def\boxed - {\ifmmode\expandafter\mframed\else\expandafter\framed\fi} - -%D \macros -%D {cfrac} -%D -%D [HH] Now let us see what this one does: -%D -%D \startbuffer -%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$ -%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D \getbuffer - -\definecomplexorsimple\cfrac - -\def\simplecfrac - {\complexcfrac[c]} - -\def\complexcfrac[#1]#2#3% - {{\displaystyle - \frac - {\strut\ifx r#1\hfill\fi#2\ifx l#1\hfill\fi}% - {#3}}% - \kern-\nulldelimiterspace} - -%D [HH] The next alternative is nicer: - -\def\simplecfrac {\docfrac[cc]} -\def\complexcfrac[#1]{\docfrac[#1cc]} - -\def\docfrac[#1#2#3]#4#5% - {{\displaystyle - \frac - {\strut - \ifx r#1\hfill\fi#4\ifx l#1\hfill\fi}% - {\ifx r#2\hfill\fi#5\ifx l#2\hfill\fi}% - \kern-\nulldelimiterspace}} - -%D [HH] Now we can align every combination we want: -%D -%D \startbuffer -%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$ -%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$ -%D $\cfrac[cl]{12}{3} \cfrac[cc]{12}{3} \cfrac[cr]{12}{3}$ -%D $\cfrac[lc]{1}{23} \cfrac[cc]{1}{23} \cfrac[rc]{1}{23}$ -%D \stopbuffer -%D -%D \typebuffer -%D -%D \getbuffer - -%D \macros -%D {splitfrac, splitdfrac} -%D -%D Occasionally one needs to typeset multi||line fractions. -%D These commands use \tex{genfrac} to create such fractions. -%D -%D \startbuffer -%D \startformula -%D a=\frac{ -%D \splitfrac{xy + xy + xy + xy + xy} -%D {+ xy + xy + xy + xy} -%D } -%D {z} -%D =\frac{ -%D \splitdfrac{xy + xy + xy + xy + xy} -%D {+ xy + xy + xy + xy} -%D } -%D {z} -%D \stopformula -%D \stopbuffer -%D -%D \typebuffer \getbuffer -%D -%D These macros are based on Michael J.~Downes posting on -%D comp.text.tex on 2001/12/06 - -\def\splitfrac#1#2% - {\genfrac\empty\empty\zeropoint\textstyle% - {\textstyle#1\quad\hfill}% - {\textstyle\hfill\quad\mathstrut#2}} - -\def\splitdfrac#1#2% - {\genfrac\empty\empty\zeropoint\displaystyle% - {#1\quad\hfill} - {\hfill\quad\mathstrut #2}} - -\protect \endinput - %D \macros %D {startsubarray,substack,startsmallmatrix} %D diff --git a/tex/context/base/m-obsolete.tex b/tex/context/base/m-obsolete.tex index a97002cf6..2d4518181 100644 --- a/tex/context/base/m-obsolete.tex +++ b/tex/context/base/m-obsolete.tex @@ -1,5 +1,5 @@ \unprotect -\writestatus\m!systems{skipping obsolete module} +\writestatus\m!system{skipping obsolete module} \protect \endinput diff --git a/tex/context/base/m-punk.mkiv b/tex/context/base/m-punk.mkiv index dc7692144..71158d2a6 100644 --- a/tex/context/base/m-punk.mkiv +++ b/tex/context/base/m-punk.mkiv @@ -19,7 +19,7 @@ \startluacode local concat = table.concat local chardata = characters.data -local fontdata = fonts.identifiers +local fontdata = fonts.hashes.identifiers fonts.mp = fonts.mp or { } @@ -53,11 +53,11 @@ local flusher = { local cd = chardata[n] if inline then descriptions[n] = { - -- unicode = n, - name = cd and cd.adobename, - width = w*100, - height = h*100, - depth = d*100, + -- unicode = n, + name = cd and cd.adobename, + width = w*100, + height = h*100, + depth = d*100, boundingbox = { 0, -d, w, h }, } characters[n] = { @@ -100,7 +100,8 @@ function metapost.characters.process(mpxformat, name, instances, scalefactor) metapost.setoutercolor(2) -- no outer color and no reset either lists = { } for i=1,instances do - characters, descriptions = { }, { } + characters = { } + descriptions = { } metapost.process( mpxformat, { @@ -112,26 +113,23 @@ function metapost.characters.process(mpxformat, name, instances, scalefactor) flusher ) lists[i] = { - designsize = 655360, - name = string.format("%s-%03i",hash,i), - parameters = { - slant = 0, - space = 333 * scalefactor, - space_stretch = 166.5 * scalefactor, - space_shrink = 111 * scalefactor, - x_height = 431 * scalefactor, - quad =1000 * scalefactor, - extra_space = 0 - }, - ["type"] = "virtual", - characters = characters, + characters = characters, descriptions = descriptions, - -- embedding = "subset", - -- mkiv: - spacer = "space", - unit = 1000, - shared = { }, - unique = { }, + parameters = { + designsize = 655360, + slant = 0, + space = 333 * scalefactor, + space_stretch = 166.5 * scalefactor, + space_shrink = 111 * scalefactor, + x_height = 431 * scalefactor, + quad = 1000 * scalefactor, + extra_space = 0, + }, + properties = { + name = string.format("%s-%03i",hash,i), + virtualized = true, + spacer = "space", + } } end metapost.reset(mpxformat) -- saves memory @@ -143,23 +141,23 @@ function metapost.characters.process(mpxformat, name, instances, scalefactor) return lists end -function fonts.vf.aux.combine.commands.metafont(g,v) +function fonts.handlers.vf.combiner.commands.metafont(g,v) local size = g.specification.size local data = metapost.characters.process(v[2],v[3],v[4],size/655360) local list, t = { }, { } for d=1,#data do t = data[d] - t = fonts.tfm.scale(t, -1000) + t = fonts.constructors.scale(t, -1000) local id = font.nextid() t.fonts = { { id = id } } fontdata[id] = t - fonts.vf.aux.compose_characters(t) + fonts.handlers.vf.helpers.composecharacters(t) list[d] = font.define(t) end for k, v in next, t do g[k] = v -- kind of replace, when not present, make nil end - g.virtualized = true + g.properties.virtualized = true g.variants = list end diff --git a/tex/context/base/math-dim.lua b/tex/context/base/math-dim.lua index 604d390da..1d3f93ad3 100644 --- a/tex/context/base/math-dim.lua +++ b/tex/context/base/math-dim.lua @@ -6,8 +6,8 @@ if not modules then modules = { } end modules ['math-dim'] = { license = "see context related readme files" } --- Beware: only Taco really understands in depth what these dimensions do so --- if you run into problems ... +-- 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 diff --git a/tex/context/base/math-ext.lua b/tex/context/base/math-ext.lua index 75c6e4b26..ac994ea35 100644 --- a/tex/context/base/math-ext.lua +++ b/tex/context/base/math-ext.lua @@ -29,51 +29,48 @@ function extras.add(unicode,t) end end -function extras.copy(tfmdata) - local mathparameters = tfmdata.mathparameters - local MathConstants = tfmdata.MathConstants - if (mathparameters and next(mathparameters)) or (MathConstants and next(MathConstants)) then - local characters = tfmdata.characters - 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 - for i=1,#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 - if trace_virtual then - report_math("extra U+%04X in %s at %s maps on U+%04X (class: %s, name: %s)",unicode,file.basename(tfmdata.fullname),tfmdata.size,nextslot,extradesc.mathclass or "?",extradesc.mathname or "?") - end - characters[unicode] = nextchar - break +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 + for i=1,#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 + if trace_virtual then + report_math("extra U+%04X in %s at %s maps on U+%04X (class: %s, name: %s)",unicode, + file.basename(properties.fullname),parameters.size,nextslot,extradesc.mathclass or "?",extradesc.mathname or "?") end + characters[unicode] = nextchar + break end end end - if not characters[unicode] then -- can be set in previous loop - for i=1,#nextinsize do - local nextslot = nextinsize[i] - local nextbase = characters[nextslot] - if nextbase then - characters[unicode] = nextbase -- still ok? - break - end + end + if not characters[unicode] then -- can be set in previous loop + for i=1,#nextinsize do + local nextslot = nextinsize[i] + local nextbase = characters[nextslot] + if nextbase then + characters[unicode] = nextbase -- still ok? + break end end end end - else - -- let's not waste time on non-math end end -table.insert(fonts.tfm.mathactions,extras.copy) +utilities.sequencers.appendaction(mathactions,"system","mathematics.extras.copy") -- 0xFE302 -- 0xFE320 for accents diff --git a/tex/context/base/math-frc.mkii b/tex/context/base/math-frc.mkii index fa319bc4a..767c5ec5c 100644 --- a/tex/context/base/math-frc.mkii +++ b/tex/context/base/math-frc.mkii @@ -15,7 +15,8 @@ \unprotect -\def\exmthfont#1{\symbolicsizedfont#1\plusone{MathExtension}} +\unexpanded\def\exmthfont#1% + {\symbolicsizedfont#1\plusone{MathExtension}} \def\domthfrac#1#2#3#4#5#6#7% {\begingroup @@ -49,18 +50,204 @@ \rlap{\raise\dimexpr\ht2-\ht4\relax\copy4}\copy0}}}% \endgroup} -\def\mthfrac#1#2#3#4#5{\mathchoice +\unexpanded\def\mthfrac#1#2#3#4#5{\mathchoice {\domthfrac\displaystyle \textface {#1}{#2}{#3}{#4}{#5}} {\domthfrac\textstyle \textface {#1}{#2}{#3}{#4}{#5}} {\domthfrac\scriptstyle \scriptface {#1}{#2}{#3}{#4}{#5}} {\domthfrac\scriptscriptstyle\scriptscriptface{#1}{#2}{#3}{#4}{#5}}} -\def\mthsqrt#1#2#3{\mathchoice +\unexpanded\def\mthsqrt#1#2#3{\mathchoice {\domthsqrt\displaystyle \textface {#1}{#2}{#3}} {\domthsqrt\textstyle \textface {#1}{#2}{#3}} {\domthsqrt\scriptstyle \textface {#1}{#2}{#3}} {\domthsqrt\scriptscriptstyle\textface {#1}{#2}{#3}}} -% temp here +%D Moved from math-new.tex (not that new anyway: + +%D \macros +%D {genfrac} +%D +%D [TH] The definition of \type {\genfrac} \& co. is not +%D trivial, because it allows some flexibility. This is +%D supposed to be a user||level command, but will fail quite +%D desparately if called outside math mode (\CONTEXT\ redefines +%D \type {\over}) +%D +%D [HH] We clean up this macro a bit and (try) to make it +%D understandable. The expansion is needed for generating +%D the second argument to \type {\dogenfrac}, which is to +%D be a control sequence like \type {\over}. + +\unexpanded\def\genfrac#1#2#3#4% + {\edef\!!stringa + {#1#2}% + \expanded + {\dogenfrac{#4}% + \csname + \ifx @#3@% + \ifx\!!stringa\empty + \strippedcsname\normalover + \else + \strippedcsname\normaloverwithdelims + \fi + \else + \ifx\!!stringa\empty + \strippedcsname\normalabove + \else + \strippedcsname\normalabovewithdelims + \fi + \fi + \endcsname}% + {#1#2#3}} + +\def\dogenfrac#1#2#3#4#5% + {{#1{\begingroup#4\endgroup#2#3\relax#5}}} + +%D \macros +%D {dfrac, tfrac, frac, dbinom, tbinom, binom} +%D +%D \startbuffer +%D $\dfrac {1}{2} \tfrac {1}{2} \frac {1}{2}$ +%D $\dbinom{1}{2} \tbinom{1}{2} \binom{1}{2}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \getbuffer + +\unexpanded\def\dfrac {\genfrac\empty\empty{}\displaystyle} +\unexpanded\def\tfrac {\genfrac\empty\empty{}\textstyle} +\unexpanded\def\frac {\genfrac\empty\empty{}\donothing} + +\unexpanded\def\dbinom{\genfrac()\zeropoint\displaystyle} +\unexpanded\def\tbinom{\genfrac()\zeropoint\textstyle} +\unexpanded\def\binom {\genfrac()\zeropoint\donothing} + +\unexpanded\def\xfrac {\genfrac\empty\empty{}\scriptstyle} +\unexpanded\def\xxfrac{\genfrac\empty\empty{}\scriptscriptstyle} + +\unexpanded\def\frac#1#2{\mathematics{\genfrac\empty\empty{}\donothing{#1}{#2}}} + +%D \macros +%D {cfrac} +%D +%D \startbuffer +%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$ +%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \getbuffer +%D +%D Now we can align every combination we want: +%D +%D \startbuffer +%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$ +%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$ +%D $\cfrac[cl]{12}{3} \cfrac[cc]{12}{3} \cfrac[cr]{12}{3}$ +%D $\cfrac[lc]{1}{23} \cfrac[cc]{1}{23} \cfrac[rc]{1}{23}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \getbuffer + +\definecomplexorsimple\cfrac + +\def\simplecfrac {\docfrac[cc]} +\def\complexcfrac[#1]{\docfrac[#1cc]} + +\def\docfrac[#1#2#3]#4#5% + {{\displaystyle + \frac + {\strut + \ifx r#1\hfill\fi#4\ifx l#1\hfill\fi}% + {\ifx r#2\hfill\fi#5\ifx l#2\hfill\fi}% + \kern-\nulldelimiterspace}} + +%D \macros +%D {splitfrac, splitdfrac} +%D +%D Occasionally one needs to typeset multi||line fractions. +%D These commands use \tex{genfrac} to create such fractions. +%D +%D \startbuffer +%D \startformula +%D a=\frac{ +%D \splitfrac{xy + xy + xy + xy + xy} +%D {+ xy + xy + xy + xy} +%D } +%D {z} +%D =\frac{ +%D \splitdfrac{xy + xy + xy + xy + xy} +%D {+ xy + xy + xy + xy} +%D } +%D {z} +%D \stopformula +%D \stopbuffer +%D +%D \typebuffer \getbuffer +%D +%D These macros are based on Michael J.~Downes posting on +%D comp.text.tex on 2001/12/06 + +\unexpanded\def\splitfrac#1#2% + {\genfrac\empty\empty\zeropoint\textstyle% + {\textstyle#1\quad\hfill}% + {\textstyle\hfill\quad\mathstrut#2}} + +\unexpanded\def\splitdfrac#1#2% + {\genfrac\empty\empty\zeropoint\displaystyle% + {#1\quad\hfill} + {\hfill\quad\mathstrut #2}} + +%D For thee moment here, but it might move: + +%D \macros +%D {qedsymbol} +%D +%D [HH] The general Quod Erat Domonstrandum symbol is defined +%D in such a way that we can configure it. Because this symbol +%D is also used in text mode, we make it a normal text symbol +%D with special behavior. + +\unexpanded\def\qedsymbol#1% + {\ifhmode + \unskip~\hfill#1\par + \else\ifmmode + \eqno#1\relax % Do we really need the \eqno here? + \else + \leavevmode\hbox{}\hfill#1\par + \fi\fi} + +\definesymbol [qed] [\qedsymbol{\mathematics{\square}}] + +%D \macros +%D {QED} +%D +%D [HH] For compatbility reasons we also provide the \type +%D {\QED} command. In case this command is overloaded, we still +%D have the symbol available. \symbol[qed] + +\unexpanded\def\QED{\symbol[qed]} + +%D \macros +%D {mathhexbox} +%D +%D [TH] \type {\mathhexbox} is also user||level (already +%D defined in Plain \TEX). It allows to get a math character +%D inserted as if it was a text character. + +\unexpanded\def\mathhexbox#1#2#3% + {\mathtext{$\mathsurround\zeropoint\mathchar"#1#2#3$}} + +%D \macros +%D {boxed} +%D +%D [HH] Another macro that users expect (slightly adapted): + +\unexpanded\def\boxed + {\ifmmode\expandafter\mframed\else\expandafter\framed\fi} \protect \endinput diff --git a/tex/context/base/math-frc.mkiv b/tex/context/base/math-frc.mkiv index 9ef2cc6cf..f6331126f 100644 --- a/tex/context/base/math-frc.mkiv +++ b/tex/context/base/math-frc.mkiv @@ -160,7 +160,8 @@ % to be checked: -\def\exmthfont#1{\symbolicsizedfont#1\plusone{MathExtension}} +\unexpanded\def\exmthfont#1% + {\symbolicsizedfont#1\plusone{MathExtension}} \def\domthfrac#1#2#3#4#5#6#7% {\begingroup @@ -194,18 +195,204 @@ \rlap{\raise\dimexpr\ht2-\ht4\relax\copy4}\copy0}}}% \endgroup} -\def\mthfrac#1#2#3#4#5{\mathchoice +\unexpanded\def\mthfrac#1#2#3#4#5{\mathchoice {\domthfrac\displaystyle \textface {#1}{#2}{#3}{#4}{#5}} {\domthfrac\textstyle \textface {#1}{#2}{#3}{#4}{#5}} {\domthfrac\scriptstyle \scriptface {#1}{#2}{#3}{#4}{#5}} {\domthfrac\scriptscriptstyle\scriptscriptface{#1}{#2}{#3}{#4}{#5}}} -\def\mthsqrt#1#2#3{\mathchoice +\unexpanded\def\mthsqrt#1#2#3{\mathchoice {\domthsqrt\displaystyle \textface {#1}{#2}{#3}} {\domthsqrt\textstyle \textface {#1}{#2}{#3}} {\domthsqrt\scriptstyle \textface {#1}{#2}{#3}} {\domthsqrt\scriptscriptstyle\textface {#1}{#2}{#3}}} -% temp here +%D Moved from math-new.tex (not that new anyway): + +%D \macros +%D {genfrac} +%D +%D [TH] The definition of \type {\genfrac} \& co. is not +%D trivial, because it allows some flexibility. This is +%D supposed to be a user||level command, but will fail quite +%D desparately if called outside math mode (\CONTEXT\ redefines +%D \type {\over}) +%D +%D [HH] We clean up this macro a bit and (try) to make it +%D understandable. The expansion is needed for generating +%D the second argument to \type {\dogenfrac}, which is to +%D be a control sequence like \type {\over}. + +\unexpanded\def\genfrac#1#2#3#4% + {\edef\!!stringa + {#1#2}% + \expanded + {\dogenfrac{#4}% + \csname + \ifx @#3@% + \ifx\!!stringa\empty + \strippedcsname\normalover + \else + \strippedcsname\normaloverwithdelims + \fi + \else + \ifx\!!stringa\empty + \strippedcsname\normalabove + \else + \strippedcsname\normalabovewithdelims + \fi + \fi + \endcsname}% + {#1#2#3}} + +\def\dogenfrac#1#2#3#4#5% + {{#1{\begingroup#4\endgroup#2#3\relax#5}}} + +%D \macros +%D {dfrac, tfrac, frac, dbinom, tbinom, binom} +%D +%D \startbuffer +%D $\dfrac {1}{2} \tfrac {1}{2} \frac {1}{2}$ +%D $\dbinom{1}{2} \tbinom{1}{2} \binom{1}{2}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \getbuffer + +\unexpanded\def\dfrac {\genfrac\empty\empty{}\displaystyle} +\unexpanded\def\tfrac {\genfrac\empty\empty{}\textstyle} +\unexpanded\def\frac {\genfrac\empty\empty{}\donothing} + +\unexpanded\def\dbinom{\genfrac()\zeropoint\displaystyle} +\unexpanded\def\tbinom{\genfrac()\zeropoint\textstyle} +\unexpanded\def\binom {\genfrac()\zeropoint\donothing} + +\unexpanded\def\xfrac {\genfrac\empty\empty{}\scriptstyle} +\unexpanded\def\xxfrac{\genfrac\empty\empty{}\scriptscriptstyle} + +\unexpanded\def\frac#1#2{\mathematics{\genfrac\empty\empty{}\donothing{#1}{#2}}} + +%D \macros +%D {cfrac} +%D +%D \startbuffer +%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$ +%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \getbuffer +%D +%D Now we can align every combination we want: +%D +%D \startbuffer +%D $\cfrac{12}{3} \cfrac[l]{12}{3} \cfrac[c]{12}{3} \cfrac[r]{12}{3}$ +%D $\cfrac{1}{23} \cfrac[l]{1}{23} \cfrac[c]{1}{23} \cfrac[r]{1}{23}$ +%D $\cfrac[cl]{12}{3} \cfrac[cc]{12}{3} \cfrac[cr]{12}{3}$ +%D $\cfrac[lc]{1}{23} \cfrac[cc]{1}{23} \cfrac[rc]{1}{23}$ +%D \stopbuffer +%D +%D \typebuffer +%D +%D \getbuffer + +\definecomplexorsimple\cfrac + +\def\simplecfrac {\docfrac[cc]} +\def\complexcfrac[#1]{\docfrac[#1cc]} + +\def\docfrac[#1#2#3]#4#5% + {{\displaystyle + \frac + {\strut + \ifx r#1\hfill\fi#4\ifx l#1\hfill\fi}% + {\ifx r#2\hfill\fi#5\ifx l#2\hfill\fi}% + \kern-\nulldelimiterspace}} + +%D \macros +%D {splitfrac, splitdfrac} +%D +%D Occasionally one needs to typeset multi||line fractions. +%D These commands use \tex{genfrac} to create such fractions. +%D +%D \startbuffer +%D \startformula +%D a=\frac{ +%D \splitfrac{xy + xy + xy + xy + xy} +%D {+ xy + xy + xy + xy} +%D } +%D {z} +%D =\frac{ +%D \splitdfrac{xy + xy + xy + xy + xy} +%D {+ xy + xy + xy + xy} +%D } +%D {z} +%D \stopformula +%D \stopbuffer +%D +%D \typebuffer \getbuffer +%D +%D These macros are based on Michael J.~Downes posting on +%D comp.text.tex on 2001/12/06 + +\unexpanded\def\splitfrac#1#2% + {\genfrac\empty\empty\zeropoint\textstyle% + {\textstyle#1\quad\hfill}% + {\textstyle\hfill\quad\mathstrut#2}} + +\unexpanded\def\splitdfrac#1#2% + {\genfrac\empty\empty\zeropoint\displaystyle% + {#1\quad\hfill} + {\hfill\quad\mathstrut #2}} + +%D For thee moment here, but it might move: + +%D \macros +%D {qedsymbol} +%D +%D [HH] The general Quod Erat Domonstrandum symbol is defined +%D in such a way that we can configure it. Because this symbol +%D is also used in text mode, we make it a normal text symbol +%D with special behavior. + +\unexpanded\def\qedsymbol#1% + {\ifhmode + \unskip~\hfill#1\par + \else\ifmmode + \eqno#1\relax % Do we really need the \eqno here? + \else + \leavevmode\hbox{}\hfill#1\par + \fi\fi} + +\definesymbol [qed] [\qedsymbol{\mathematics{\square}}] + +%D \macros +%D {QED} +%D +%D [HH] For compatbility reasons we also provide the \type +%D {\QED} command. In case this command is overloaded, we still +%D have the symbol available. \symbol[qed] + +\unexpanded\def\QED{\symbol[qed]} + +%D \macros +%D {mathhexbox} +%D +%D [TH] \type {\mathhexbox} is also user||level (already +%D defined in Plain \TEX). It allows to get a math character +%D inserted as if it was a text character. + +\unexpanded\def\mathhexbox#1#2#3% + {\mathtext{$\mathsurround\zeropoint\mathchar"#1#2#3$}} + +%D \macros +%D {boxed} +%D +%D [HH] Another macro that users expect (slightly adapted): + +\unexpanded\def\boxed + {\ifmmode\expandafter\mframed\else\expandafter\framed\fi} \protect \endinput diff --git a/tex/context/base/math-ini.lua b/tex/context/base/math-ini.lua index c34e39bea..b211f7dd2 100644 --- a/tex/context/base/math-ini.lua +++ b/tex/context/base/math-ini.lua @@ -367,30 +367,135 @@ function mathematics.big(tfmdata,unicode,n) return unicode end --- plugins +-- plugins (will be proper handler, once we have separated generic from context) -local hvars = table.tohash { - --~ "RadicalKernBeforeDegree", - --~ "RadicalKernAfterDegree", +local sequencers = utilities.sequencers +local appendgroup = sequencers.appendgroup +local appendaction = sequencers.appendaction +local mathprocessor = nil + +local mathactions = sequencers.reset { + arguments = "target,original,directives", } -function mathematics.scaleparameters(t,tfmtable,delta,hdelta,vdelta) - local mathparameters = tfmtable.mathparameters +function fonts.constructors.mathactions(original,target,directives) + if mathactions.dirty then -- maybe use autocompile + mathprocessor = sequencers.compile(mathactions) + end + mathprocessor(original,target,directives or {}) +end + +appendgroup(mathactions,"before") -- user +appendgroup(mathactions,"system") -- private +appendgroup(mathactions,"after" ) -- user + +function mathematics.initializeparameters(target,original,directives) + local mathparameters = original.mathparameters if mathparameters and next(mathparameters) then - delta = delta or 1 - hdelta, vdelta = hdelta or delta, vdelta or delta local _, mp = mathematics.dimensions(mathparameters) - for name, value in next, mp do - if name == "RadicalDegreeBottomRaisePercent" then - mp[name] = value - elseif hvars[name] then - mp[name] = hdelta * value - else - mp[name] = vdelta * value + target.mathparameters = mp -- for ourselves + target.MathConstants = mp -- for luatex + end +end + +sequencers.appendaction(mathactions,"system","mathematics.initializeparameters") + +local how = { + -- RadicalKernBeforeDegree = "horizontal", + -- RadicalKernAfterDegree = "horizontal", + RadicalDegreeBottomRaisePercent = "unscaled" +} + +function mathematics.scaleparameters(target,original,directives) + if not directives.disablescaling 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 + mathparameters[name] = value + elseif h == "horizontal" then + mathparameters[name] = value * hfactor + elseif h == "vertical"then + mathparameters[name] = value * vfactor + else + mathparameters[name] = value * factor + end + end + end + end +end + +sequencers.appendaction(mathactions,"system","mathematics.scaleparameters") + +function mathematics.checkaccentbaseheight(target,original,directives) + local MathConstants = target.MathConstants + if MathConstants then + MathConstants.AccentBaseHeight = nil -- safeguard + end +end + +sequencers.appendaction(mathactions,"system","mathematics.checkaccentbaseheight") + +function mathematics.checkprivateparameters(target,original,directives) + local MathConstants = target.MathConstants + if MathConstants then + if not MathConstants.FractionDelimiterSize then + MathConstants.FractionDelimiterSize = 0 + end + if not MathConstants.FractionDelimiterDisplayStyleSize then + MathConstants.FractionDelimiterDisplayStyleSize = 0 + end + end +end + +sequencers.appendaction(mathactions,"system","mathematics.checkprivateparameters") + +function mathematics.overloadparameters(target,original,directives) + 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 '%s' @ %s",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 '%s': %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 '%s': %s => %s",name,tostring(oldvalue),tostring(newvalue)) + end + else + report_math("invalid math parameter '%s'",name) + end + mathparameters[name] = newvalue + end + end + end end end - t.MathConstants = mp end end -table.insert(fonts.tfm.mathactions,mathematics.scaleparameters) +sequencers.appendaction(mathactions,"system","mathematics.overloadparameters") diff --git a/tex/context/base/math-ini.mkiv b/tex/context/base/math-ini.mkiv index ce487702d..43e091969 100644 --- a/tex/context/base/math-ini.mkiv +++ b/tex/context/base/math-ini.mkiv @@ -498,8 +498,8 @@ \appendtoks \doifelse{\mathematicsparameter\v!compact}\v!yes - {\ctxlua{fonts.vf.math.optional=true}} - {\ctxlua{fonts.vf.math.optional=false}}% + {\ctxlua{fonts.handlers.vf.math.optional=true}} + {\ctxlua{fonts.handlers.vf.math.optional=false}}% \to \everysetupmathematics \setupmathematics diff --git a/tex/context/base/math-noa.lua b/tex/context/base/math-noa.lua index 6736181e6..8c9676ca4 100644 --- a/tex/context/base/math-noa.lua +++ b/tex/context/base/math-noa.lua @@ -22,51 +22,57 @@ local utfchar, utfbyte = utf.char, utf.byte local fonts, nodes, node, mathematics = fonts, nodes, node, mathematics -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute -local mlist_to_hlist = node.mlist_to_hlist -local font_of_family = node.family_font -local fontdata = fonts.identifiers +local otf = fonts.handlers.otf +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -noads = noads or { } -local noads = noads +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) -noads.processors = noads.processors or { } -local processors = noads.processors +local report_processing = logs.reporter("mathematics","processing") +local report_remapping = logs.reporter("mathematics","remapping") -noads.handlers = noads.handlers or { } -local handlers = noads.handlers +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local mlist_to_hlist = node.mlist_to_hlist +local font_of_family = node.family_font -local tasks = nodes.tasks +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers -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) +noads = noads or { } -- todo: only here +local noads = noads -local report_processing = logs.reporter("mathematics","processing") -local report_remapping = logs.reporter("mathematics","remapping") +noads.processors = noads.processors or { } +local processors = noads.processors -local nodecodes = nodes.nodecodes -local noadcodes = nodes.noadcodes +noads.handlers = noads.handlers or { } +local handlers = noads.handlers -local noad_ord = noadcodes.ord -local noad_rel = noadcodes.rel -local noad_punct = noadcodes.punct +local tasks = nodes.tasks -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 nodecodes = nodes.nodecodes +local noadcodes = nodes.noadcodes -local left_fence_code = 1 +local noad_ord = noadcodes.ord +local noad_rel = noadcodes.rel +local noad_punct = noadcodes.punct + +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 left_fence_code = 1 local function process(start,what,n,parent) if n then n = n + 1 else n = 0 end @@ -147,7 +153,7 @@ local function report_remap(tag,id,old,new,extra) end local remapalphabets = mathematics.remapalphabets -local fcs = fonts.colors.set +local setnodecolor = nodes.tracers.colors.set -- we can have a global famdata == fonts.famdata @@ -168,7 +174,7 @@ local fcs = fonts.colors.set --~ report_remap("fallback",id,char,newchar) --~ end --~ if trace_analyzing then ---~ fcs(pointer,"font:isol") +--~ setnodecolor(pointer,"font:isol") --~ end --~ pointer.char = newchar --~ return true @@ -206,7 +212,7 @@ processors.relocate[math_char] = function(pointer) report_remap("char",id,char,newchar) end if trace_analyzing then - fcs(pointer,"font:isol") + setnodecolor(pointer,"font:isol") end pointer.char = newchar return true @@ -220,19 +226,19 @@ processors.relocate[math_char] = function(pointer) -- return checked(pointer) end if trace_analyzing then - fcs(pointer,"font:medi") + setnodecolor(pointer,"font:medi") end end processors.relocate[math_textchar] = function(pointer) if trace_analyzing then - fcs(pointer,"font:init") + setnodecolor(pointer,"font:init") end end processors.relocate[math_delim] = function(pointer) if trace_analyzing then - fcs(pointer,"font:fina") + setnodecolor(pointer,"font:fina") end end @@ -390,11 +396,12 @@ function noads.handlers.collapse(head,style,penalties) return true end --- math alternates +-- math alternates: (in xits lgf: $ABC$ $\cal ABC$ $\mathalternate{cal}\cal ABC$) -function fonts.initializers.common.mathalternates(tfmdata) +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) @@ -407,23 +414,24 @@ function fonts.initializers.common.mathalternates(tfmdata) v.attribute = lastattribute attributes[lastattribute] = v end - tfmdata.shared.mathalternates = alternates -- to be checked if shared is ok here - tfmdata.shared.mathalternatesattributes = attributes -- to be checked if shared is ok here + 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 -fonts.otf.tables.features['mathalternates'] = 'Additional math alternative shapes' - -fonts.otf.features.register('mathalternates') -- true -table.insert(fonts.triggers,"mathalternates") - -fonts.initializers.base.otf.mathalternates = fonts.initializers.common.mathalternates -fonts.initializers.node.otf.mathalternates = fonts.initializers.common.mathalternates +registerotffeature { + name = "mathalternates", + description = "additional math alternative shapes", + initializers = { + base = initializemathalternates, + node = initializemathalternates, + } +} -local getalternate = fonts.otf.getalternate +local getalternate = otf.getalternate local mathalternate = attributes.private("mathalternate") diff --git a/tex/context/base/math-vfu.lua b/tex/context/base/math-vfu.lua index e072fe83b..b13f1804e 100644 --- a/tex/context/base/math-vfu.lua +++ b/tex/context/base/math-vfu.lua @@ -20,16 +20,16 @@ local report_virtual = logs.reporter("fonts","virtual math") local fonts, nodes, mathematics = fonts, nodes, mathematics -local mathencodings = utilities.storage.allocate { } +local allocate = utilities.storage.allocate -fonts.enc.math = mathencodings -- better is then: fonts.enc.vectors +local mathencodings = allocate() +fonts.encodings.math = mathencodings -- better is then: fonts.encodings.vectors +local vfmath = allocate() +fonts.handlers.vf.math = vfmath -local shared = { } +vfmath.optional = false -fonts.vf.math = fonts.vf.math or { } -local vfmath = fonts.vf.math - -vfmath.optional = false +local shared = { } --~ local push, pop, back = { "push" }, { "pop" }, { "slot", 1, 0x2215 } @@ -293,9 +293,10 @@ local function stack(main,characters,id,size,unicode,u1,d12,u2) end end -function vfmath.alas(main,id,size,variables) +function vfmath.alas(main,id,size) local characters = main.characters local shared = main.shared + local variables = main.goodies.mathematics and main.goodies.mathematics.variables or { } local joinrelfactor = variables.joinrelfactor or 3 for i=0x7A,0x7D do make(main,characters,id,size,i,1) @@ -342,36 +343,6 @@ end local unique = 0 -- testcase: \startTEXpage \math{!\text{-}\text{-}\text{-}} \stopTEXpage -function fonts.basecopy(tfmtable,name) - local characters, parameters, fullname = tfmtable.characters, tfmtable.parameters, tfmtable.fullname - local t, c, p = { }, { }, { } - for k, v in next, tfmtable do - t[k] = v - end - if characters then - for k, v in next, characters do - c[k] = v - end - t.characters = c - else - report_virtual("font %s has no characters",name) - end - if parameters then - for k, v in next, parameters do - p[k] = v - end - t.parameters = p - else - report_virtual("font %s has no parameters",name) - end - -- tricky ... what if fullname does not exist - if fullname then - unique = unique + 1 - t.fullname = fullname .. "-" .. unique - end - return t -end - local reported = { } local reverse = { } -- index -> unicode @@ -387,11 +358,14 @@ setmetatable ( reverse, { __index = function(t,name) return r end } ) -function vfmath.define(specification,set,variables) - variables = variables or { } +local mathdirectives = { + disablescaling = true +} + +function vfmath.define(specification,set,goodies) local name = specification.name -- symbolic name local size = specification.size -- given size - local fnt, lst, main = { }, { }, nil + local loaded, fontlist, main = { }, { }, nil local start = (trace_virtual or trace_timings) and os.clock() local okset, n = { }, 0 for s=1,#set do @@ -402,16 +376,20 @@ function vfmath.define(specification,set,variables) report_virtual("loading font %s subfont %s with name %s at %s is skipped",name,s,ssname,size) end else - if ss.features then ssname = ssname .. "*" .. ss.features end - if ss.main then main = s end - local f, id = fonts.tfm.readanddefine(ssname,size) + if ss.features then + ssname = ssname .. "*" .. ss.features + end + if ss.main then + main = s + end + local f, id = fonts.constructors.readanddefine(ssname,size) if not f then report_virtual("loading font %s subfont %s with name %s at %s is skipped, not found",name,s,ssname,size) else n = n + 1 okset[n] = ss - fnt[n] = f - lst[n] = { id = id, size = size } + loaded[n] = f + fontlist[n] = { id = id, size = size } if not shared[s] then shared[n] = { } end if trace_virtual then report_virtual("loading font %s subfont %s with name %s at %s as id %s using encoding %s",name,s,ssname,size,id,ss.vector or "none") @@ -442,55 +420,117 @@ function vfmath.define(specification,set,variables) end end end - -- beware, fnt[1] is already passed to tex (we need to make a simple copy then .. todo) - main = fonts.basecopy(fnt[1],name) - main.name, main.fonts, main.virtualized, main.mathparameters = name, lst, true, { } - local characters, descriptions = main.characters, main.descriptions - local mp = main.parameters - if mp then - mp.x_height = mp.x_height or 0 + -- beware, loaded[1] is already passed to tex (we need to make a simple copy then .. todo) + local parent = loaded[1] -- a text font + local characters = { } + local parameters = { } + local mathparameters = { } + local descriptions = { } + local metadata = { } + local properties = { } + local goodies = { } + local main = { + metadata = metadata, + properties = properties, + characters = characters, + descriptions = descriptions, + parameters = parameters, + mathparameters = mathparameters, + fonts = fontlist, + goodies = goodies, + } + -- + -- + for key, value in next, parent do + if type(value) ~= "table" then + main[key] = value + end + end + -- + if parent.characters then + for unicode, character in next, parent.characters do + characters[unicode] = character + end + else + report_virtual("font %s has no characters",name) + end + -- + if parent.parameters then + for key, value in next, parent.parameters do + parameters[key] = value + end + else + report_virtual("font %s has no parameters",name) + end + -- + local description = { name = "" } + setmetatable(descriptions, { __index = function() return description end }) + -- + if parent.properties then + setmetatable(properties, { __index = parent.properties }) + end + -- + if parent.goodies then + setmetatable(goodies, { __index = parent.goodies }) end + -- + properties.virtualized = true + properties.italic_correction = true + properties.has_math = true + -- + local fullname = properties.fullname -- parent via mt + if fullname then + unique = unique + 1 + properties.fullname = fullname .. "-" .. unique + end + -- + -- we need to set some values in main as well (still?) + -- + main.fullname = properties.fullname + main.type = "virtual" + main.nomath = false + -- + parameters.x_height = parameters.x_height or 0 + -- local already_reported = false for s=1,n do - local ss, fs = okset[s], fnt[s] + local ss, fs = okset[s], loaded[s] if not fs then -- skip, error elseif ss.optional and vfmath.optional then -- skip, redundant else - local mm, fp = main.mathparameters, fs.parameters - if mm and fp and mp then - if ss.extension then - mm.math_x_height = fp.x_height or 0 -- math_x_height height of x - mm.default_rule_thickness = fp[ 8] or 0 -- default_rule_thickness thickness of \over bars - mm.big_op_spacing1 = fp[ 9] or 0 -- big_op_spacing1 minimum clearance above a displayed op - mm.big_op_spacing2 = fp[10] or 0 -- big_op_spacing2 minimum clearance below a displayed op - mm.big_op_spacing3 = fp[11] or 0 -- big_op_spacing3 minimum baselineskip above displayed op - mm.big_op_spacing4 = fp[12] or 0 -- big_op_spacing4 minimum baselineskip below displayed op - mm.big_op_spacing5 = fp[13] or 0 -- big_op_spacing5 padding above and below displayed limits - -- report_virtual("loading and virtualizing font %s at size %s, setting ex parameters",name,size) - elseif ss.parameters then - mp.x_height = fp.x_height or mp.x_height - mm.x_height = mm.x_height or fp.x_height or 0 -- x_height height of x - mm.num1 = fp[ 8] or 0 -- num1 numerator shift-up in display styles - mm.num2 = fp[ 9] or 0 -- num2 numerator shift-up in non-display, non-\atop - mm.num3 = fp[10] or 0 -- num3 numerator shift-up in non-display \atop - mm.denom1 = fp[11] or 0 -- denom1 denominator shift-down in display styles - mm.denom2 = fp[12] or 0 -- denom2 denominator shift-down in non-display styles - mm.sup1 = fp[13] or 0 -- sup1 superscript shift-up in uncramped display style - mm.sup2 = fp[14] or 0 -- sup2 superscript shift-up in uncramped non-display - mm.sup3 = fp[15] or 0 -- sup3 superscript shift-up in cramped styles - mm.sub1 = fp[16] or 0 -- sub1 subscript shift-down if superscript is absent - mm.sub2 = fp[17] or 0 -- sub2 subscript shift-down if superscript is present - mm.sup_drop = fp[18] or 0 -- sup_drop superscript baseline below top of large box - mm.sub_drop = fp[19] or 0 -- sub_drop subscript baseline below bottom of large box - mm.delim1 = fp[20] or 0 -- delim1 size of \atopwithdelims delimiters in display styles - mm.delim2 = fp[21] or 0 -- delim2 size of \atopwithdelims delimiters in non-displays - mm.axis_height = fp[22] or 0 -- axis_height height of fraction lines above the baseline - -- report_virtual("loading and virtualizing font %s at size %s, setting sy parameters",name,size) - end - else + local newparameters = fs.parameters + if not newparameters then report_virtual("font %s, no parameters set",name) + elseif ss.extension then + mathparameters.math_x_height = newparameters.x_height or 0 -- math_x_height : height of x + mathparameters.default_rule_thickness = newparameters[ 8] or 0 -- default_rule_thickness : thickness of \over bars + mathparameters.big_op_spacing1 = newparameters[ 9] or 0 -- big_op_spacing1 : minimum clearance above a displayed op + mathparameters.big_op_spacing2 = newparameters[10] or 0 -- big_op_spacing2 : minimum clearance below a displayed op + mathparameters.big_op_spacing3 = newparameters[11] or 0 -- big_op_spacing3 : minimum baselineskip above displayed op + mathparameters.big_op_spacing4 = newparameters[12] or 0 -- big_op_spacing4 : minimum baselineskip below displayed op + mathparameters.big_op_spacing5 = newparameters[13] or 0 -- big_op_spacing5 : padding above and below displayed limits + -- report_virtual("loading and virtualizing font %s at size %s, setting ex parameters",name,size) + elseif ss.parameters then + mathparameters.x_height = newparameters.x_height or mathparameters.x_height + mathparameters.x_height = mathparameters.x_height or fp.x_height or 0 -- x_height : height of x + mathparameters.num1 = newparameters[ 8] or 0 -- num1 : numerator shift-up in display styles + mathparameters.num2 = newparameters[ 9] or 0 -- num2 : numerator shift-up in non-display, non-\atop + mathparameters.num3 = newparameters[10] or 0 -- num3 : numerator shift-up in non-display \atop + mathparameters.denom1 = newparameters[11] or 0 -- denom1 : denominator shift-down in display styles + mathparameters.denom2 = newparameters[12] or 0 -- denom2 : denominator shift-down in non-display styles + mathparameters.sup1 = newparameters[13] or 0 -- sup1 : superscript shift-up in uncramped display style + mathparameters.sup2 = newparameters[14] or 0 -- sup2 : superscript shift-up in uncramped non-display + mathparameters.sup3 = newparameters[15] or 0 -- sup3 : superscript shift-up in cramped styles + mathparameters.sub1 = newparameters[16] or 0 -- sub1 : subscript shift-down if superscript is absent + mathparameters.sub2 = newparameters[17] or 0 -- sub2 : subscript shift-down if superscript is present + mathparameters.sup_drop = newparameters[18] or 0 -- sup_drop : superscript baseline below top of large box + mathparameters.sub_drop = newparameters[19] or 0 -- sub_drop : subscript baseline below bottom of large box + mathparameters.delim1 = newparameters[20] or 0 -- delim1 : size of \atopwithdelims delimiters in display styles + mathparameters.delim2 = newparameters[21] or 0 -- delim2 : size of \atopwithdelims delimiters in non-displays + mathparameters.axis_height = newparameters[22] or 0 -- axis_height : height of fraction lines above the baseline + -- report_virtual("loading and virtualizing font %s at size %s, setting sy parameters",name,size) end local vectorname = ss.vector if vectorname then @@ -503,7 +543,7 @@ function vfmath.define(specification,set,variables) for unicode, index in next, vector do local fci = fc[index] if not fci then - local fontname = fs.name or "unknown" + local fontname = fs.properties.name or "unknown" local rf = reported[fontname] if not rf then rf = { } reported[fontname] = rf end local rv = rf[vectorname] @@ -672,26 +712,30 @@ function vfmath.define(specification,set,variables) mathematics.extras.copy(main) --not needed here (yet) end end - lst[#lst+1] = { id = font.nextid(), size = size } - if mp then -- weak catch - vfmath.alas(main,#lst,size,variables) + -- + fontlist[#fontlist+1] = { + id = font.nextid(), + size = size, + } + -- + if mathparameters then -- weak catch ? ? ? + vfmath.alas(main,#fontlist,size) end + -- mathematics.addfallbacks(main) + -- if trace_virtual or trace_timings then report_virtual("loading and virtualizing font %s at size %s took %0.3f seconds",name,size,os.clock()-start) end - main.has_italic = true - main.type = "virtual" -- not needed - mathematics.scaleparameters(main,main,1) - main.nomath = false - -- table.print(characters[0x222B]) - -- table.print(main.MathConstants) + -- + fonts.constructors.mathactions(main,main,mathdirectives) + -- return main end -function mathematics.makefont(name, set, variables) +function mathematics.makefont(name,set,goodies) fonts.definers.methods.variants[name] = function(specification) - return vfmath.define(specification,set,variables) + return vfmath.define(specification,set,goodies) end end diff --git a/tex/context/base/meta-imp-dum.mkiv b/tex/context/base/meta-imp-dum.mkiv index bc19f3c5f..60ab0a41d 100644 --- a/tex/context/base/meta-imp-dum.mkiv +++ b/tex/context/base/meta-imp-dum.mkiv @@ -34,26 +34,50 @@ % fractions like reduction % June 22, 2003, this definition was patched to adapt itself -% to transparent colors +% to transparent colors, but ... in 2011 we no longer have +% is_transparent so we revert. +% +% \startuseMPgraphic{placeholder}{width,height,reduction,color} +% numeric w, h, d, r ; color c, b, cc ; path p ; boolean t ; +% t := is_transparent(\MPvar{color}) ; +% c := not_transparent(\MPvar{color}) ; +% b := not_transparent(white) ; +% w := \MPvar{width} ; +% h := \MPvar{height} ; +% r := \MPvar{reduction} ; +% d := max(w,h) ; +% p := unitsquare xyscaled (w,h) ; +% cc := r[.5c,b] ; +% fill p withcolor if t : transparent(1,.5,cc) else : cc fi ; +% for i := 1 upto 60 : +% cc := r[c randomized(.3,.9),b] ; +% fill fullcircle +% scaled (d/5 randomized (d/5)) +% shifted (center p randomized (d)) +% withcolor if t : transparent(1,.5,cc) else : cc fi ; +% endfor ; +% clip currentpicture to p ; +% \stopuseMPgraphic \startuseMPgraphic{placeholder}{width,height,reduction,color} - numeric w, h, d, r ; color c, b, cc ; path p ; boolean t ; - t := is_transparent(\MPvar{color}) ; - c := not_transparent(\MPvar{color}) ; - b := not_transparent(white) ; + numeric w, h, d, r ; path p ; + if cmykcolor \MPvar{color} : + cmykcolor c, b ; b := (0,0,0,0) + else : + color c, b ; ; b := (1,1,1) + fi ; + c := \MPvar{color} ; w := \MPvar{width} ; h := \MPvar{height} ; r := \MPvar{reduction} ; d := max(w,h) ; p := unitsquare xyscaled (w,h) ; - cc := r[.5c,b] ; - fill p withcolor if t : transparent(1,.5,cc) else : cc fi ; + fill p withcolor r[.5c,b] ; for i := 1 upto 60 : - cc := r[c randomized(.3,.9),b] ; fill fullcircle scaled (d/5 randomized (d/5)) shifted (center p randomized (d)) - withcolor if t : transparent(1,.5,cc) else : cc fi ; + withcolor r[c randomized(.3,.9),b] ; endfor ; clip currentpicture to p ; \stopuseMPgraphic diff --git a/tex/context/base/mlib-pps.lua b/tex/context/base/mlib-pps.lua index a8cdf7275..e1f85e804 100644 --- a/tex/context/base/mlib-pps.lua +++ b/tex/context/base/mlib-pps.lua @@ -19,7 +19,7 @@ local lpegmatch = lpeg.match local texbox = tex.box local copy_list, free_list = node.copy_list, node.flush_list -local Cs, Cf, C, Cg, Ct, P, S, V = lpeg.Cs, lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.V +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 starttiming, stoptiming = statistics.starttiming, statistics.stoptiming @@ -95,9 +95,9 @@ end --~ -local specificationsplitter = lpeg.Ct(lpeg.splitat(" ")) -local colorsplitter = lpeg.Ct(lpeg.splitter(":",tonumber)) -- no need for : -local domainsplitter = lpeg.Ct(lpeg.splitter(" ",tonumber)) +local specificationsplitter = Ct(lpeg.splitat(" ")) +local colorsplitter = Ct(lpeg.splitter(":",tonumber)) -- no need for : +local domainsplitter = Ct(lpeg.splitter(" ",tonumber)) local centersplitter = domainsplitter local coordinatesplitter = domainsplitter @@ -821,24 +821,23 @@ end local function fg_process(object,prescript,before,after) local fg_name = prescript.fg_name if fg_name then - -- now uses textext - local op = object.path - 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 - object.path = nil + before[#before+1] = format("q %f %f %f %f %f %f cm",cm(object)) before[#before+1] = function() - context.MPLIBfigure(sx,rx,ry,sy,tx,ty,fg_name) + 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 +) + local function tr_process(object,prescript,before,after) local cs = object.color if cs and #cs > 0 then @@ -853,8 +852,12 @@ local function tr_process(object,prescript,before,after) if sp_name then local sp_fractions = prescript.sp_fractions or 1 local sp_components = prescript.sp_components or "" - local sp_value = prescript.sp_value or 1 - sp_value = tonumber(sp_value) * cs[1] + 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 = lpeg.match(value,sp_value,1,cf) or sp_value + end before[#before+1], after[#after+1] = spotcolorconverter(sp_name,sp_fractions,sp_components,sp_value) else before[#before+1], after[#after+1] = colorconverter(cs) diff --git a/tex/context/base/mlib-pps.mkiv b/tex/context/base/mlib-pps.mkiv index d131e9117..357f85b1a 100644 --- a/tex/context/base/mlib-pps.mkiv +++ b/tex/context/base/mlib-pps.mkiv @@ -33,12 +33,10 @@ \newbox \MPtextbox \newtoks\everyMPLIBsettext -\def\MPLIBfigure#1#2#3#4#5#6#7% todo: move Q q to lua - {\setbox\scratchbox\hbox{\externalfigure[#7]}% +\def\MPLIBfigure#1#2% + {\setbox\scratchbox\hbox{\externalfigure[#1][\c!mask=#2]}% \ctxlua{metapost.edefsxsy(\number\wd\scratchbox,\number\ht\scratchbox,0)}% - \pdfliteral direct{q #1 #2 #3 #4 #5 #6 cm}% no direct - \vbox to \zeropoint{\vss\hbox to \zeropoint{\scale[sx=\sx,sy=\sy]{\box\scratchbox}\hss}}% - \pdfliteral direct{Q}} + \vbox to \zeropoint{\vss\hbox to \zeropoint{\scale[\c!sx=\sx,\c!sy=\sy]{\box\scratchbox}\hss}}} \def\MPLIBsettext#1% #2% {\dowithnextbox{\ctxlua{metapost.settext(\number\nextbox,#1)}}\hbox} diff --git a/tex/context/base/mult-aux.mkiv b/tex/context/base/mult-aux.mkiv index 473a18e08..4087180c0 100644 --- a/tex/context/base/mult-aux.mkiv +++ b/tex/context/base/mult-aux.mkiv @@ -52,23 +52,26 @@ %D \stoptyping % problem: every* could clash - +% % There can be less {} in the following definitions if we assume \??aa and \c!somecs - +% % todo: \def\detokenized...parameter#1{\detokenize\expandafter\expandafter\expandafter{\csname#1#2\endcsname}} % always root +% +% it might be more efficient to do this at the lua and +% +% watch the push/pop and predefinition of current .. this is needed for nested +% definitions and overloaded defines using the predefined one -\unexpanded\def\doinstallparameterhandler#1#2#3#4#5#6#7#8#9% - {\def#3##1{\csname#4{#1#2}{##1}\endcsname}% +\unexpanded\def\doinstallparameterhandler#1#2#3#4#5#6#7% + {\ifx#2\relax\let#2\empty\fi + \def#3##1{\csname#4{#1#2}{##1}\endcsname}% \def#4##1##2{\ifcsname##1##2\endcsname##1##2\else\expandafter#5\csname##1\s!parent\endcsname{##2}\fi}% \def#5##1##2{\ifx##1\relax\s!empty\else#4{##1}{##2}\fi}% \def#6##1##2{\csname#4{#1##1}{##2}\endcsname}% - \def#7##1{\detokenize\expandafter\expandafter\expandafter{\csname#1##1\endcsname}}% always root - \def#8{\dosetvalue{#1}}% ##1 {##2} (braces are mandate) - \def#9{\doletvalue{#1}}}% ##1 ##2 + \def#7##1{\detokenize\expandafter\expandafter\expandafter{\csname#1##1\endcsname}}} % always root \unexpanded\def\installparameterhandler#1#2% - {%\message{\detokenize{#1}/\detokenize{#2}}% - \normalexpanded + {\normalexpanded {\doinstallparameterhandler {\noexpand#1}% \??aa \expandafter\noexpand\csname current#2\endcsname @@ -76,12 +79,11 @@ \expandafter\noexpand\csname do#2parameter\endcsname \expandafter\noexpand\csname do#2parentparameter\endcsname \expandafter\noexpand\csname named#2parameter\endcsname - \expandafter\noexpand\csname detokenized#2parameter\endcsname - \expandafter\noexpand\csname doset#2parameter\endcsname - \expandafter\noexpand\csname dolet#2parameter\endcsname}} + \expandafter\noexpand\csname detokenized#2parameter\endcsname}} \unexpanded\def\doinstallparameterhashhandler#1#2#3#4#5% - {\def#3##1{#4{#1#2}{##1}}% + {\ifx#2\relax\let#2\empty\fi + \def#3##1{#4{#1#2}{##1}}% \def#4##1##2{\ifcsname##1##2\endcsname##1\else\expandafter#5\csname##1\s!parent\endcsname{##2}\fi}% \def#5##1##2{\ifx##1\relax\else#4{##1}{##2}\fi}} @@ -94,6 +96,21 @@ \expandafter\noexpand\csname do#2parameterhash\endcsname \expandafter\noexpand\csname do#2parentparameterhash\endcsname}} +\unexpanded\def\doinstallparametersethandler#1#2#3#4#5% + {\ifx#2\relax\let#2\empty\fi + \def#3{\dosetvalue{#1#2}}% ##1 {##2} (braces are mandate) + \def#4{\doletvalue{#1#2}}% ##1 ##2 + \def#5{\doletvalue{#1#2}\empty}}% ##1 + +\unexpanded\def\installparametersethandler#1#2% + {\normalexpanded + {\doinstallparametersethandler + {\noexpand#1}% \??aa + \expandafter\noexpand\csname current#2\endcsname + \expandafter\noexpand\csname set#2parameter\endcsname + \expandafter\noexpand\csname let#2parameter\endcsname + \expandafter\noexpand\csname reset#2parameter\endcsname}} + \unexpanded\def\doinstallattributehandler#1#2#3% #1 not used here {\def#2##1##2% style color {\edef\fontattributehash {#3{##1}}% @@ -108,23 +125,32 @@ \expandafter\noexpand\csname doset#2attributes\endcsname \expandafter\noexpand\csname #2parameterhash\endcsname}} -\unexpanded\def\doinstalldefinehandler#1#2#3#4#5#6#7% - {\unexpanded\def#2{\dotripleempty#5}% +\let\definehandlerparent\empty + +\unexpanded\def\doinstalldefinehandler#1#2#3#4#5#6#7#8% + {\ifx#4\relax\let#4\empty\fi + \unexpanded\def#2{\dotripleempty#5}% \newtoks#6% \newtoks#7% - \def#5[##1][##2][##3]% [child][parent][settings] - {\edef#4{##1}% % [child] [settings] - \the#6% predefine % [child][parent] - \ifthirdargument % [child] + \def#5[##1][##2][##3]% [child][parent][settings] | [child][settings] | [child][parent] | [child] + {\let\saveddefinewhatever#4% + \edef#4{##1}% + \the#6% predefine + \ifthirdargument + \edef#8{##2}% \getparameters[#1#4][\s!parent=#1##2,##3]% \else\ifsecondargument \doifassignmentelse{##2} - {\getparameters[#1#4][\s!parent=#3,##2]} - {\getparameters[#1#4][\s!parent=#1##2]}% + {\let#8\empty + \getparameters[#1#4][\s!parent=#3,##2]} + {\edef#8{##2}% + \getparameters[#1#4][\s!parent=#1##2]}% \else + \let#8\empty \getparameters[#1#4][\s!parent=#3]% \fi\fi - \the#7}} + \the#7% + \let#4\saveddefinewhatever}} \unexpanded\def\installdefinehandler#1#2#3% {\normalexpanded @@ -135,13 +161,17 @@ \expandafter\noexpand\csname current#2\endcsname \expandafter\noexpand\csname d@define#2\endcsname \expandafter\noexpand\csname everypreset#2\endcsname - \expandafter\noexpand\csname everydefine#2\endcsname}} + \expandafter\noexpand\csname everydefine#2\endcsname + \expandafter\noexpand\csname current#2parent\endcsname}} -\unexpanded\def\doinstallsetuphandler#1#2#3#4#5% - {\unexpanded\def#2{\dodoubleempty#4}% +\unexpanded\def\doinstallsetuphandler#1#2#3#4#5#6% + {\ifx#3\relax\let#3\empty\fi + \unexpanded\def#2{\dodoubleempty#4}% + \unexpanded\def#6{\getparameters[#1#3]}% \newtoks#5% \def#4[##1][##2]% maybe helper - {\ifsecondargument + {\let\savedsetupwhatever#3% + \ifsecondargument \def\docommand####1% we will have a simple one as well {\edef#3{####1}% \getparameters[#1#3][##2]% @@ -151,7 +181,8 @@ \let#3\empty \getparameters[#1][##1]% \the#5% - \fi}} + \fi + \let#3\savedsetupwhatever}} \unexpanded\def\installsetuphandler#1#2% {\normalexpanded @@ -160,11 +191,13 @@ \expandafter\noexpand\csname setup#2\endcsname \expandafter\noexpand\csname current#2\endcsname \expandafter\noexpand\csname d@setup#2\endcsname - \expandafter\noexpand\csname everysetup#2\endcsname}} + \expandafter\noexpand\csname everysetup#2\endcsname + \expandafter\noexpand\csname setupcurrent#2\endcsname}} \unexpanded\def\installcommandhandler#1#2#3% \??self name \??parent (can be \??self) {\installparameterhandler {#1}{#2}% \installparameterhashhandler{#1}{#2}% + \installparametersethandler {#1}{#2}% \installdefinehandler {#1}{#2}{#3}% \installsetuphandler {#1}{#2}% \installattributehandler {#1}{#2}} @@ -216,5 +249,20 @@ \def\listnamespaces {\ctxlua{interfaces.namespaces.list()}} +%D Helper: +%D +%D \starttyping +%D \showparentchain{@@am}{left} +%D \stoptyping + +\def\doshowparentchain#1% + {#1 => % + \ifcsname#1\s!parent\endcsname + \expandafter\doshowparentchain\csname#1\s!parent\endcsname + \fi} + +\def\showparentchain#1#2% + {\writestatus\m!system{chain: [ \doshowparentchain{#1#2}]}} + \protect diff --git a/tex/context/base/mult-chk.lua b/tex/context/base/mult-chk.lua index efe17db34..4b1ba12ee 100644 --- a/tex/context/base/mult-chk.lua +++ b/tex/context/base/mult-chk.lua @@ -22,14 +22,14 @@ interfaces.syntax = allocate { test = { keys = table.tohash { "a","b","c","d","e","f","g" } } } -function interfaces.invalidkey(kind,key) - report_interface("invalid key '%s' for '%s' in line %s",key,kind,tex.inputlineno) +function interfaces.invalidkey(category,key) + report_interface("invalid key '%s' for '%s' in line %s",key,category,tex.inputlineno) end -function interfaces.setvalidkeys(kind,list) - local s = interfaces.syntax[kind] +function interfaces.setvalidkeys(category,list) + local s = interfaces.syntax[category] if not s then - interfaces.syntax[kind] = { + interfaces.syntax[category] = { keys = settings_to_set(list) } else @@ -37,10 +37,10 @@ function interfaces.setvalidkeys(kind,list) end end -function interfaces.addvalidkeys(kind,list) - local s = interfaces.syntax[kind] +function interfaces.addvalidkeys(category,list) + local s = interfaces.syntax[category] if not s then - interfaces.syntax[kind] = { + interfaces.syntax[category] = { keys = settings_to_set(list) } else @@ -50,11 +50,11 @@ end -- weird code, looks incomplete ... probbably an experiment -local prefix, kind, keys +local prefix, category, keys local function set(key,value) if keys and not keys[key] then - interfaces.invalidkey(kind,key) + interfaces.invalidkey(category,key) else context.setsomevalue(prefix,key,value) end @@ -64,7 +64,7 @@ local pattern = make_settings_to_hash_pattern(set,"tolerant") function interfaces.getcheckedparameters(k,p,s) if s and s ~= "" then - prefix, kind = p, k + prefix, category = p, k keys = k and k ~= "" and interfaces.syntax[k].keys lpegmatch(pattern,s) end diff --git a/tex/context/base/mult-de.mkii b/tex/context/base/mult-de.mkii index 494df52bd..1ac862210 100644 --- a/tex/context/base/mult-de.mkii +++ b/tex/context/base/mult-de.mkii @@ -75,6 +75,7 @@ \setinterfacevariable{april}{April} \setinterfacevariable{atmargin}{amrand} \setinterfacevariable{atpage}{aufseite} +\setinterfacevariable{attachment}{attachment} \setinterfacevariable{august}{August} \setinterfacevariable{author}{autor} \setinterfacevariable{auto}{auto} @@ -525,6 +526,7 @@ \setinterfaceconstant{after}{nach} \setinterfaceconstant{afterhead}{nachkopf} \setinterfaceconstant{afterkey}{nachtaste} +\setinterfaceconstant{aftersection}{aftersection} \setinterfaceconstant{align}{ausrichtung} \setinterfaceconstant{aligncharacter}{aligncharacter} \setinterfaceconstant{alignmentcharacter}{alignmentcharacter} @@ -563,6 +565,7 @@ \setinterfaceconstant{balance}{ausgleichen} \setinterfaceconstant{before}{vor} \setinterfaceconstant{beforehead}{vorkopf} +\setinterfaceconstant{beforesection}{beforesection} \setinterfaceconstant{bet}{bet} \setinterfaceconstant{big}{gross} \setinterfaceconstant{blank}{blanko} @@ -577,6 +580,7 @@ \setinterfaceconstant{bottomoffset}{untenoffset} \setinterfaceconstant{bottomspace}{bottomspace} \setinterfaceconstant{bottomstate}{untenstatus} +\setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{cache} \setinterfaceconstant{calculate}{berechnen} \setinterfaceconstant{category}{category} @@ -587,6 +591,7 @@ \setinterfaceconstant{clipoffset}{clipoffset} \setinterfaceconstant{closeaction}{schliessenaktion} \setinterfaceconstant{closecommand}{closecommand} +\setinterfaceconstant{closepage}{closepage} \setinterfaceconstant{closepageaction}{closepageaction} \setinterfaceconstant{closesymbol}{closesymbol} \setinterfaceconstant{color}{farbe} @@ -811,6 +816,7 @@ \setinterfaceconstant{oddmargin}{ungeraderand} \setinterfaceconstant{offset}{offset} \setinterfaceconstant{openaction}{oeffenaktion} +\setinterfaceconstant{openpage}{openpage} \setinterfaceconstant{openpageaction}{openpageaction} \setinterfaceconstant{option}{option} \setinterfaceconstant{order}{order} @@ -1006,6 +1012,7 @@ \setinterfaceconstant{urlalternative}{urlalternative} \setinterfaceconstant{urlspace}{urlspatium} \setinterfaceconstant{validate}{validieren} +\setinterfaceconstant{values}{values} \setinterfaceconstant{vcommand}{vbefehl} \setinterfaceconstant{veroffset}{kopfoffset} \setinterfaceconstant{vfil}{vfil} @@ -1098,7 +1105,6 @@ \setinterfacecommand{colorbar}{farbbalken} \setinterfacecommand{colorvalue}{farbewert} \setinterfacecommand{column}{spalte} -\setinterfacecommand{comment}{kommentar} \setinterfacecommand{comparecolorgroup}{vergleichefarbengruppe} \setinterfacecommand{comparepalet}{vergleichepalette} \setinterfacecommand{completepagenumber}{completepagenumber} diff --git a/tex/context/base/mult-def.lua b/tex/context/base/mult-def.lua index a119c00c0..ad8618c09 100644 --- a/tex/context/base/mult-def.lua +++ b/tex/context/base/mult-def.lua @@ -430,16 +430,6 @@ return { ["pe"]="ستون", ["ro"]="coloana", }, - ["comment"]={ - ["cs"]="komentar", - ["de"]="kommentar", - ["en"]="comment", - ["fr"]="commentaire", - ["it"]="commento", - ["nl"]="commentaar", - ["pe"]="توضیح", - ["ro"]="comentariu", - }, ["comparecolorgroup"]={ ["cs"]="porovnejskupinubarev", ["de"]="vergleichefarbengruppe", @@ -6579,6 +6569,14 @@ return { ["maybeyear"]={ ["en"]="maybeyear", }, +--["group"]={ +-- ["en"]="group", +-- ["nl"]="groep", +--}, + ["values"]={ + ["en"]="values", + ["nl"]="waarden", + }, ["action"]={ ["cs"]="akce", ["de"]="aktion", @@ -6619,6 +6617,10 @@ return { ["pe"]="بعدازسر", ["ro"]="dupatitlu", }, + ["aftersection"]={ + ["en"]="aftersection", + ["nl"]="nasectie", + }, ["afterkey"]={ ["cs"]="klavesapo", ["de"]="nachtaste", @@ -6897,6 +6899,10 @@ return { ["pe"]="قبل‌از", ["ro"]="inainte", }, + ["beforesection"]={ + ["en"]="beforesection", + ["nl"]="voorsectie", + }, ["beforehead"]={ ["cs"]="predhlavickou", ["de"]="vorkopf", @@ -7033,6 +7039,10 @@ return { ["pe"]="وضعیت‌پایین", ["ro"]="starejos", }, + ["buffer"]={ + ["en"]="buffer", + ["nl"]="buffer", + }, ["cache"]={ ["cs"]="cache", ["de"]="cache", @@ -7137,6 +7147,10 @@ return { ["pe"]="بستن‌عمل‌صفحه", ["ro"]="actiuneinchiderepagina", }, + ["closepage"]={ + ["en"]="closepage", + ["nl"]="sluitpagina", + }, ["closesymbol"]={ ["cs"]="closesymbol", ["de"]="closesymbol", @@ -9121,6 +9135,10 @@ return { ["pe"]="عمل‌صفحه‌باز", ["ro"]="actiunedeschiderepagina", }, + ["openpage"]={ + ["en"]="openpage", + ["nl"]="openpagina", + }, ["option"]={ ["cs"]="volba", ["de"]="option", @@ -12011,6 +12029,10 @@ return { ["pe"]="آوریل", ["ro"]="aprilie", }, + ["attachment"]={ + ["en"]="attachment", + ["nl"]="aanhangsel", + }, ["atmargin"]={ ["cs"]="naokraji", ["de"]="amrand", diff --git a/tex/context/base/mult-en.mkii b/tex/context/base/mult-en.mkii index 1fc61060c..cbb3bad57 100644 --- a/tex/context/base/mult-en.mkii +++ b/tex/context/base/mult-en.mkii @@ -75,6 +75,7 @@ \setinterfacevariable{april}{April} \setinterfacevariable{atmargin}{atmargin} \setinterfacevariable{atpage}{atpage} +\setinterfacevariable{attachment}{attachment} \setinterfacevariable{august}{August} \setinterfacevariable{author}{author} \setinterfacevariable{auto}{auto} @@ -525,6 +526,7 @@ \setinterfaceconstant{after}{after} \setinterfaceconstant{afterhead}{afterhead} \setinterfaceconstant{afterkey}{afterkey} +\setinterfaceconstant{aftersection}{aftersection} \setinterfaceconstant{align}{align} \setinterfaceconstant{aligncharacter}{aligncharacter} \setinterfaceconstant{alignmentcharacter}{alignmentcharacter} @@ -563,6 +565,7 @@ \setinterfaceconstant{balance}{balance} \setinterfaceconstant{before}{before} \setinterfaceconstant{beforehead}{beforehead} +\setinterfaceconstant{beforesection}{beforesection} \setinterfaceconstant{bet}{bet} \setinterfaceconstant{big}{big} \setinterfaceconstant{blank}{blank} @@ -577,6 +580,7 @@ \setinterfaceconstant{bottomoffset}{bottomoffset} \setinterfaceconstant{bottomspace}{bottomspace} \setinterfaceconstant{bottomstate}{bottomstate} +\setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{cache} \setinterfaceconstant{calculate}{calculate} \setinterfaceconstant{category}{category} @@ -587,6 +591,7 @@ \setinterfaceconstant{clipoffset}{clipoffset} \setinterfaceconstant{closeaction}{closeaction} \setinterfaceconstant{closecommand}{closecommand} +\setinterfaceconstant{closepage}{closepage} \setinterfaceconstant{closepageaction}{closepageaction} \setinterfaceconstant{closesymbol}{closesymbol} \setinterfaceconstant{color}{color} @@ -811,6 +816,7 @@ \setinterfaceconstant{oddmargin}{oddmargin} \setinterfaceconstant{offset}{offset} \setinterfaceconstant{openaction}{openaction} +\setinterfaceconstant{openpage}{openpage} \setinterfaceconstant{openpageaction}{openpageaction} \setinterfaceconstant{option}{option} \setinterfaceconstant{order}{order} @@ -1006,6 +1012,7 @@ \setinterfaceconstant{urlalternative}{urlalternative} \setinterfaceconstant{urlspace}{urlspace} \setinterfaceconstant{validate}{validate} +\setinterfaceconstant{values}{values} \setinterfaceconstant{vcommand}{vcommand} \setinterfaceconstant{veroffset}{veroffset} \setinterfaceconstant{vfil}{vfil} @@ -1098,7 +1105,6 @@ \setinterfacecommand{colorbar}{colorbar} \setinterfacecommand{colorvalue}{colorvalue} \setinterfacecommand{column}{column} -\setinterfacecommand{comment}{comment} \setinterfacecommand{comparecolorgroup}{comparecolorgroup} \setinterfacecommand{comparepalet}{comparepalet} \setinterfacecommand{completepagenumber}{completepagenumber} diff --git a/tex/context/base/mult-fr.mkii b/tex/context/base/mult-fr.mkii index f5c75dec4..e86f13c8b 100644 --- a/tex/context/base/mult-fr.mkii +++ b/tex/context/base/mult-fr.mkii @@ -75,6 +75,7 @@ \setinterfacevariable{april}{avril} \setinterfacevariable{atmargin}{alamarge} \setinterfacevariable{atpage}{alapage} +\setinterfacevariable{attachment}{attachment} \setinterfacevariable{august}{aout} \setinterfacevariable{author}{auteur} \setinterfacevariable{auto}{auto} @@ -525,6 +526,7 @@ \setinterfaceconstant{after}{apres} \setinterfaceconstant{afterhead}{aprestete} \setinterfaceconstant{afterkey}{aprescle} +\setinterfaceconstant{aftersection}{aftersection} \setinterfaceconstant{align}{alignement} \setinterfaceconstant{aligncharacter}{caracterealigne} \setinterfaceconstant{alignmentcharacter}{alignementcaractere} @@ -563,6 +565,7 @@ \setinterfaceconstant{balance}{equilibre} \setinterfaceconstant{before}{avant} \setinterfaceconstant{beforehead}{avanttete} +\setinterfaceconstant{beforesection}{beforesection} \setinterfaceconstant{bet}{bet} \setinterfaceconstant{big}{grand} \setinterfaceconstant{blank}{vide} @@ -577,6 +580,7 @@ \setinterfaceconstant{bottomoffset}{decalageinf} \setinterfaceconstant{bottomspace}{espaceinf} \setinterfaceconstant{bottomstate}{etatinf} +\setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{cache} \setinterfaceconstant{calculate}{calculer} \setinterfaceconstant{category}{category} @@ -587,6 +591,7 @@ \setinterfaceconstant{clipoffset}{clipoffset} \setinterfaceconstant{closeaction}{actionfermeture} \setinterfaceconstant{closecommand}{closecommand} +\setinterfaceconstant{closepage}{closepage} \setinterfaceconstant{closepageaction}{actionfermeturepage} \setinterfaceconstant{closesymbol}{closesymbol} \setinterfaceconstant{color}{couleur} @@ -811,6 +816,7 @@ \setinterfaceconstant{oddmargin}{margepaire} \setinterfaceconstant{offset}{offset} \setinterfaceconstant{openaction}{actionouverture} +\setinterfaceconstant{openpage}{openpage} \setinterfaceconstant{openpageaction}{actionouverturepage} \setinterfaceconstant{option}{option} \setinterfaceconstant{order}{order} @@ -1006,6 +1012,7 @@ \setinterfaceconstant{urlalternative}{alternativeurl} \setinterfaceconstant{urlspace}{espaceurl} \setinterfaceconstant{validate}{valider} +\setinterfaceconstant{values}{values} \setinterfaceconstant{vcommand}{vcommande} \setinterfaceconstant{veroffset}{veroffset} \setinterfaceconstant{vfil}{vfil} @@ -1098,7 +1105,6 @@ \setinterfacecommand{colorbar}{barrecouleur} \setinterfacecommand{colorvalue}{valeurcouleur} \setinterfacecommand{column}{colonne} -\setinterfacecommand{comment}{commentaire} \setinterfacecommand{comparecolorgroup}{comparegroupecouleur} \setinterfacecommand{comparepalet}{comparepalette} \setinterfacecommand{completepagenumber}{completenumeropage} diff --git a/tex/context/base/mult-it.mkii b/tex/context/base/mult-it.mkii index a31cb9689..f88b5f49b 100644 --- a/tex/context/base/mult-it.mkii +++ b/tex/context/base/mult-it.mkii @@ -75,6 +75,7 @@ \setinterfacevariable{april}{aprile} \setinterfacevariable{atmargin}{almargine} \setinterfacevariable{atpage}{apagina} +\setinterfacevariable{attachment}{attachment} \setinterfacevariable{august}{agosto} \setinterfacevariable{author}{autore} \setinterfacevariable{auto}{auto} @@ -525,6 +526,7 @@ \setinterfaceconstant{after}{dopo} \setinterfaceconstant{afterhead}{dopotesta} \setinterfaceconstant{afterkey}{dopotasto} +\setinterfaceconstant{aftersection}{aftersection} \setinterfaceconstant{align}{allinea} \setinterfaceconstant{aligncharacter}{allineacarattere} \setinterfaceconstant{alignmentcharacter}{carattereallineamento} @@ -563,6 +565,7 @@ \setinterfaceconstant{balance}{bilanciamento} \setinterfaceconstant{before}{prima} \setinterfaceconstant{beforehead}{primaditesta} +\setinterfaceconstant{beforesection}{beforesection} \setinterfaceconstant{bet}{bet} \setinterfaceconstant{big}{grande} \setinterfaceconstant{blank}{rigovuoto} @@ -577,6 +580,7 @@ \setinterfaceconstant{bottomoffset}{offsetfondo} \setinterfaceconstant{bottomspace}{spaziofondo} \setinterfaceconstant{bottomstate}{statofondo} +\setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{cache} \setinterfaceconstant{calculate}{calcola} \setinterfaceconstant{category}{category} @@ -587,6 +591,7 @@ \setinterfaceconstant{clipoffset}{clipoffset} \setinterfaceconstant{closeaction}{azionechiudi} \setinterfaceconstant{closecommand}{closecommand} +\setinterfaceconstant{closepage}{closepage} \setinterfaceconstant{closepageaction}{azionechiudipagina} \setinterfaceconstant{closesymbol}{closesymbol} \setinterfaceconstant{color}{colore} @@ -811,6 +816,7 @@ \setinterfaceconstant{oddmargin}{marginedispari} \setinterfaceconstant{offset}{offset} \setinterfaceconstant{openaction}{azioneapri} +\setinterfaceconstant{openpage}{openpage} \setinterfaceconstant{openpageaction}{azioneapripagina} \setinterfaceconstant{option}{opzione} \setinterfaceconstant{order}{order} @@ -1006,6 +1012,7 @@ \setinterfaceconstant{urlalternative}{alternativaurl} \setinterfaceconstant{urlspace}{spaziourl} \setinterfaceconstant{validate}{verifica} +\setinterfaceconstant{values}{values} \setinterfaceconstant{vcommand}{vcomando} \setinterfaceconstant{veroffset}{veroffset} \setinterfaceconstant{vfil}{vfil} @@ -1098,7 +1105,6 @@ \setinterfacecommand{colorbar}{barracolori} \setinterfacecommand{colorvalue}{valorecolore} \setinterfacecommand{column}{colonna} -\setinterfacecommand{comment}{commento} \setinterfacecommand{comparecolorgroup}{confrontagruppocolori} \setinterfacecommand{comparepalet}{confrontatavolozza} \setinterfacecommand{completepagenumber}{numeropaginacompleto} diff --git a/tex/context/base/mult-nl.mkii b/tex/context/base/mult-nl.mkii index 8fef745f8..68768df57 100644 --- a/tex/context/base/mult-nl.mkii +++ b/tex/context/base/mult-nl.mkii @@ -75,6 +75,7 @@ \setinterfacevariable{april}{april} \setinterfacevariable{atmargin}{opmarge} \setinterfacevariable{atpage}{oppagina} +\setinterfacevariable{attachment}{aanhangsel} \setinterfacevariable{august}{augustus} \setinterfacevariable{author}{auteur} \setinterfacevariable{auto}{auto} @@ -525,6 +526,7 @@ \setinterfaceconstant{after}{na} \setinterfaceconstant{afterhead}{kopna} \setinterfaceconstant{afterkey}{natoets} +\setinterfaceconstant{aftersection}{nasectie} \setinterfaceconstant{align}{uitlijnen} \setinterfaceconstant{aligncharacter}{karakteruitlijnen} \setinterfaceconstant{alignmentcharacter}{uitlijnkarakter} @@ -563,6 +565,7 @@ \setinterfaceconstant{balance}{balanceren} \setinterfaceconstant{before}{voor} \setinterfaceconstant{beforehead}{kopvoor} +\setinterfaceconstant{beforesection}{voorsectie} \setinterfaceconstant{bet}{bet} \setinterfaceconstant{big}{groot} \setinterfaceconstant{blank}{blanko} @@ -577,6 +580,7 @@ \setinterfaceconstant{bottomoffset}{onderoffset} \setinterfaceconstant{bottomspace}{bodemwit} \setinterfaceconstant{bottomstate}{onderstatus} +\setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{cache} \setinterfaceconstant{calculate}{bereken} \setinterfaceconstant{category}{categorie} @@ -587,6 +591,7 @@ \setinterfaceconstant{clipoffset}{clipoffset} \setinterfaceconstant{closeaction}{sluitactie} \setinterfaceconstant{closecommand}{sluitcommando} +\setinterfaceconstant{closepage}{sluitpagina} \setinterfaceconstant{closepageaction}{sluitpaginaactie} \setinterfaceconstant{closesymbol}{sluitsymbool} \setinterfaceconstant{color}{kleur} @@ -811,6 +816,7 @@ \setinterfaceconstant{oddmargin}{onevenmarge} \setinterfaceconstant{offset}{offset} \setinterfaceconstant{openaction}{openactie} +\setinterfaceconstant{openpage}{openpagina} \setinterfaceconstant{openpageaction}{openpaginaactie} \setinterfaceconstant{option}{optie} \setinterfaceconstant{order}{order} @@ -1006,6 +1012,7 @@ \setinterfaceconstant{urlalternative}{urlvariant} \setinterfaceconstant{urlspace}{urlspatie} \setinterfaceconstant{validate}{valideer} +\setinterfaceconstant{values}{waarden} \setinterfaceconstant{vcommand}{vcommando} \setinterfaceconstant{veroffset}{kopoffset} \setinterfaceconstant{vfil}{vfil} @@ -1098,7 +1105,6 @@ \setinterfacecommand{colorbar}{kleurenbalk} \setinterfacecommand{colorvalue}{kleurwaarde} \setinterfacecommand{column}{kolom} -\setinterfacecommand{comment}{commentaar} \setinterfacecommand{comparecolorgroup}{vergelijkkleurgroep} \setinterfacecommand{comparepalet}{vergelijkpalet} \setinterfacecommand{completepagenumber}{volledigepaginanummer} diff --git a/tex/context/base/mult-pe.mkii b/tex/context/base/mult-pe.mkii index e003e15a9..66c7529c5 100644 --- a/tex/context/base/mult-pe.mkii +++ b/tex/context/base/mult-pe.mkii @@ -75,6 +75,7 @@ \setinterfacevariable{april}{آوریل} \setinterfacevariable{atmargin}{درحاشیه} \setinterfacevariable{atpage}{درصفحه} +\setinterfacevariable{attachment}{attachment} \setinterfacevariable{august}{آگوست} \setinterfacevariable{author}{author} \setinterfacevariable{auto}{خودکار} @@ -525,6 +526,7 @@ \setinterfaceconstant{after}{بعداز} \setinterfaceconstant{afterhead}{بعدازسر} \setinterfaceconstant{afterkey}{بعدازکلید} +\setinterfaceconstant{aftersection}{aftersection} \setinterfaceconstant{align}{تنظیم} \setinterfaceconstant{aligncharacter}{حرف‌تنظیم} \setinterfaceconstant{alignmentcharacter}{حرف‌تنظیم‌کردن} @@ -563,6 +565,7 @@ \setinterfaceconstant{balance}{تعادل} \setinterfaceconstant{before}{قبل‌از} \setinterfaceconstant{beforehead}{قبل‌ازسر} +\setinterfaceconstant{beforesection}{beforesection} \setinterfaceconstant{bet}{bet} \setinterfaceconstant{big}{بزرگ} \setinterfaceconstant{blank}{خالی} @@ -577,6 +580,7 @@ \setinterfaceconstant{bottomoffset}{آفست‌پایین} \setinterfaceconstant{bottomspace}{فضای‌پایین} \setinterfaceconstant{bottomstate}{وضعیت‌پایین} +\setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{میانگیر} \setinterfaceconstant{calculate}{محاسبه} \setinterfaceconstant{category}{category} @@ -587,6 +591,7 @@ \setinterfaceconstant{clipoffset}{آفست‌کلیپ} \setinterfaceconstant{closeaction}{بستن‌کنش} \setinterfaceconstant{closecommand}{بستن‌فرمان} +\setinterfaceconstant{closepage}{closepage} \setinterfaceconstant{closepageaction}{بستن‌عمل‌صفحه} \setinterfaceconstant{closesymbol}{بستن‌نماد} \setinterfaceconstant{color}{رنگ} @@ -811,6 +816,7 @@ \setinterfaceconstant{oddmargin}{حاشیه‌فرد} \setinterfaceconstant{offset}{آفست} \setinterfaceconstant{openaction}{عمل‌باز} +\setinterfaceconstant{openpage}{openpage} \setinterfaceconstant{openpageaction}{عمل‌صفحه‌باز} \setinterfaceconstant{option}{گزینه} \setinterfaceconstant{order}{order} @@ -1006,6 +1012,7 @@ \setinterfaceconstant{urlalternative}{urlalternative} \setinterfaceconstant{urlspace}{urlspace} \setinterfaceconstant{validate}{تاییداعتبار} +\setinterfaceconstant{values}{values} \setinterfaceconstant{vcommand}{vcommand} \setinterfaceconstant{veroffset}{آفست‌عم} \setinterfaceconstant{vfil}{vfil} @@ -1098,7 +1105,6 @@ \setinterfacecommand{colorbar}{میله‌رنگ} \setinterfacecommand{colorvalue}{مقداررنگ} \setinterfacecommand{column}{ستون} -\setinterfacecommand{comment}{توضیح} \setinterfacecommand{comparecolorgroup}{مقایسه‌گروه‌رنگ} \setinterfacecommand{comparepalet}{لوح‌مقایسه} \setinterfacecommand{completepagenumber}{شماره‌صفحه‌کامل} diff --git a/tex/context/base/mult-ro.mkii b/tex/context/base/mult-ro.mkii index 88491aade..4455a5100 100644 --- a/tex/context/base/mult-ro.mkii +++ b/tex/context/base/mult-ro.mkii @@ -75,6 +75,7 @@ \setinterfacevariable{april}{aprilie} \setinterfacevariable{atmargin}{lamargine} \setinterfacevariable{atpage}{lapagina} +\setinterfacevariable{attachment}{attachment} \setinterfacevariable{august}{august} \setinterfacevariable{author}{autor} \setinterfacevariable{auto}{auto} @@ -525,6 +526,7 @@ \setinterfaceconstant{after}{dupa} \setinterfaceconstant{afterhead}{dupatitlu} \setinterfaceconstant{afterkey}{dupatasta} +\setinterfaceconstant{aftersection}{aftersection} \setinterfaceconstant{align}{aliniere} \setinterfaceconstant{aligncharacter}{aliniazacaracter} \setinterfaceconstant{alignmentcharacter}{alierecaracter} @@ -563,6 +565,7 @@ \setinterfaceconstant{balance}{balanta} \setinterfaceconstant{before}{inainte} \setinterfaceconstant{beforehead}{inaintetitlu} +\setinterfaceconstant{beforesection}{beforesection} \setinterfaceconstant{bet}{bet} \setinterfaceconstant{big}{mare} \setinterfaceconstant{blank}{blanc} @@ -577,6 +580,7 @@ \setinterfaceconstant{bottomoffset}{offsetjos} \setinterfaceconstant{bottomspace}{spatiujos} \setinterfaceconstant{bottomstate}{starejos} +\setinterfaceconstant{buffer}{buffer} \setinterfaceconstant{cache}{cache} \setinterfaceconstant{calculate}{calculeaza} \setinterfaceconstant{category}{category} @@ -587,6 +591,7 @@ \setinterfaceconstant{clipoffset}{clipoffset} \setinterfaceconstant{closeaction}{actiuneinchidere} \setinterfaceconstant{closecommand}{closecommand} +\setinterfaceconstant{closepage}{closepage} \setinterfaceconstant{closepageaction}{actiuneinchiderepagina} \setinterfaceconstant{closesymbol}{closesymbol} \setinterfaceconstant{color}{culoare} @@ -811,6 +816,7 @@ \setinterfaceconstant{oddmargin}{margineimpara} \setinterfaceconstant{offset}{offset} \setinterfaceconstant{openaction}{actiunedeschidere} +\setinterfaceconstant{openpage}{openpage} \setinterfaceconstant{openpageaction}{actiunedeschiderepagina} \setinterfaceconstant{option}{optiune} \setinterfaceconstant{order}{order} @@ -1006,6 +1012,7 @@ \setinterfaceconstant{urlalternative}{urlalternativ} \setinterfaceconstant{urlspace}{spatiuurl} \setinterfaceconstant{validate}{verifica} +\setinterfaceconstant{values}{values} \setinterfaceconstant{vcommand}{comandav} \setinterfaceconstant{veroffset}{veroffset} \setinterfaceconstant{vfil}{vfil} @@ -1098,7 +1105,6 @@ \setinterfacecommand{colorbar}{baraculoare} \setinterfacecommand{colorvalue}{valoareculoare} \setinterfacecommand{column}{coloana} -\setinterfacecommand{comment}{comentariu} \setinterfacecommand{comparecolorgroup}{comparagrupculoare} \setinterfacecommand{comparepalet}{comparapaleta} \setinterfacecommand{completepagenumber}{completeazanumarpagina} diff --git a/tex/context/base/mult-sys.mkiv b/tex/context/base/mult-sys.mkiv index 564e3d0f4..4e3138c86 100644 --- a/tex/context/base/mult-sys.mkiv +++ b/tex/context/base/mult-sys.mkiv @@ -518,8 +518,9 @@ \definesystemvariable {et} % EffecT \definesystemvariable {ex} % ExterneFiguren \definesystemvariable {fa} % font feature +\definesystemvariable {fb} % FieldBody \definesystemvariable {fc} % FramedContent -\definesystemvariable {fd} % FielD +\definesystemvariable {fd} % FielDgroup \definesystemvariable {fe} % FoxetExtensions \definesystemvariable {ff} % FontFile \definesystemvariable {fg} % FiGuurmaten @@ -585,8 +586,10 @@ \definesystemvariable {ly} % LaYout \definesystemvariable {ma} % MargeAchtergrond \definesystemvariable {mb} % MargeBlokken +\definesystemvariable {mc} % MarginCategory \definesystemvariable {md} % MoDule \definesystemvariable {me} % MultilingualElement (tags) +\definesystemvariable {mf} % MarginFramed \definesystemvariable {mg} % Metapost paGe \definesystemvariable {mh} % MultilingualHead \definesystemvariable {mk} % MarKering @@ -676,12 +679,12 @@ \definesystemvariable {tk} % Teksten \definesystemvariable {tl} % TekstLijnen \definesystemvariable {tm} % TypesynonyM -\definesystemvariable {tp} % TyPen -\definesystemvariable {tx} % TeXtflow \definesystemvariable {to} % TOlerance +\definesystemvariable {tp} % TyPen \definesystemvariable {tr} % TRacer \definesystemvariable {ts} % TypeScript \definesystemvariable {tt} % TabulaTe +\definesystemvariable {tx} % TeXtflow \definesystemvariable {ty} % TYpe \definesystemvariable {uc} % Unicode \definesystemvariable {ui} % UItvoer @@ -692,10 +695,16 @@ \definesystemvariable {vn} % VoetNoten \definesystemvariable {vs} % VSpacing \definesystemvariable {vt} % VerTical -\definesystemvariable {wr} % WitRuimte \definesystemvariable {wl} % WordList +\definesystemvariable {wr} % WitRuimte \definesystemvariable {xf} % XML File \definesystemvariable {xl} % lxml (mkiv) +\definesystemvariable {wl} % WidgetLabel +\definesystemvariable {wc} % WidgetContent +\definesystemvariable {wt} % WidgetTotal +\definesystemvariable {ws} % WidgetStack +\definesystemvariable {wh} % WidgetHelp +\definesystemvariable {wp} % WidgetPopuphelp \definesystemvariable {xm} % xml (mkiv) \definesystemvariable {xp} % XML Processing \definesystemvariable {xy} % schaal @@ -802,20 +811,20 @@ \definefileconstant {encodingprefix} {enco-} \definefileconstant {filterprefix} {filt-} \definefileconstant {fontprefix} {font-} -\definefileconstant {handlingprefix} {hand-} -\definefileconstant {javascriptprefix} {java-} -\definefileconstant {languageprefix} {lang-} +%definefileconstant {handlingprefix} {hand-} +%definefileconstant {javascriptprefix} {java-} +%definefileconstant {languageprefix} {lang-} \definefileconstant {mathprefix} {math-} \definefileconstant {metapostprefix} {meta-} -\definefileconstant {regimeprefix} {regi-} -\definefileconstant {specialprefix} {spec-} +%definefileconstant {regimeprefix} {regi-} +%definefileconstant {specialprefix} {spec-} \definefileconstant {symbolprefix} {symb-} \definefileconstant {typeprefix} {type-} \definefileconstant {xtagprefix} {xtag-} -\definefileconstant {propprefix} {prop-} -\definefileconstant {unicprefix} {unic-} -\definefileconstant {sortprefix} {sort-} -\definefileconstant {prettyprefix} {pret-} +%definefileconstant {propprefix} {prop-} +%definefileconstant {unicprefix} {unic-} +%definefileconstant {sortprefix} {sort-} +%definefileconstant {prettyprefix} {pret-} \definefileconstant {moduleprefix} {m-} \definefileconstant {styleprefix} {s-} diff --git a/tex/context/base/node-aux.lua b/tex/context/base/node-aux.lua index 51293e78a..e6f698950 100644 --- a/tex/context/base/node-aux.lua +++ b/tex/context/base/node-aux.lua @@ -241,7 +241,7 @@ local function tonodes(str,fnt,attr) -- (str,template_glyph) -- moved from blob- if space then n = copy_node(space) elseif fonts then -- depedency - local parameters = fonts.identifiers[fnt].parameters + local parameters = fonts.hashes.identifiers[fnt].parameters space = new_glue(parameters.space,parameters.space_stretch,parameters.space_shrink) n = space end diff --git a/tex/context/base/node-dum.lua b/tex/context/base/node-dum.lua deleted file mode 100644 index 512748151..000000000 --- a/tex/context/base/node-dum.lua +++ /dev/null @@ -1,140 +0,0 @@ -if not modules then modules = { } end modules ['node-dum'] = { - version = 1.001, - comment = "companion to luatex-*.tex", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -nodes = nodes or { } -fonts = fonts or { } -attributes = attributes or { } - -nodes.pool = nodes.pool or { } -nodes.handlers = nodes.handlers or { } - -local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end -local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end -local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } - -nodes.nodecodes = nodecodes -nodes.whatcodes = whatcodes -nodes.whatsitcodes = whatcodes -nodes.glyphcodes = glyphcodes - -local traverse_id = node.traverse_id -local free_node = node.free -local remove_node = node.remove -local new_node = node.new - -local glyph_code = nodecodes.glyph - -function nodes.simple_font_handler(head) --- lang.hyphenate(head) - head = nodes.handlers.characters(head) - nodes.injections.handler(head) - nodes.handlers.protectglyphs(head) - head = node.ligaturing(head) - head = node.kerning(head) - return head -end - -if tex.attribute[0] ~= 0 then - - texio.write_nl("log","!") - texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") - texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") - texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.") - texio.write_nl("log","!") - - tex.attribute[0] = 0 -- else no features - -end - -nodes.handlers.protectglyphs = node.protect_glyphs -nodes.handlers.unprotectglyphs = node.unprotect_glyphs - -function nodes.handlers.characters(head) - local fontdata = fonts.identifiers - if fontdata then - local usedfonts, done, prevfont = { }, false, nil - for n in traverse_id(glyph_code,head) do - local font = n.font - if font ~= prevfont then - prevfont = font - local used = usedfonts[font] - if not used then - local tfmdata = fontdata[font] -- - if tfmdata then - local shared = tfmdata.shared -- we need to check shared, only when same features - if shared then - local processors = shared.processes - if processors and #processors > 0 then - usedfonts[font] = processors - done = true - end - end - end - end - end - end - if done then - for font, processors in next, usedfonts do - for i=1,#processors do - local h, d = processors[i](head,font,0) - head, done = h or head, done or d - end - end - end - return head, true - else - return head, false - end -end - --- helper - -function nodes.pool.kern(k) - local n = new_node("kern",1) - n.kern = k - return n -end - -function nodes.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, t.prev = nil, nil - end - end - return head, current, t -end - -function nodes.delete(head,current) - return nodes.remove(head,current,true) -end - -nodes.before = node.insert_before -nodes.after = node.insert_after - --- attributes - -attributes.unsetvalue = -0x7FFFFFFF - -local numbers, last = { }, 127 - -function attributes.private(name) - local number = numbers[name] - if not number then - if last < 255 then - last = last + 1 - end - number = last - numbers[name] = number - end - return number -end diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua index 8348e05c3..23cf0c04d 100644 --- a/tex/context/base/node-fnt.lua +++ b/tex/context/base/node-fnt.lua @@ -16,18 +16,18 @@ local trace_fontrun = false trackers.register("nodes.fontrun", function(v local report_fonts = logs.reporter("fonts","processing") -local nodes, node = nodes, node +local nodes, node, fonts = nodes, node, fonts -fonts = fonts or { } -fonts.tfm = fonts.tfm or { } -fonts.identifiers = fonts.identifiers or { } +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers + +local otf = fonts.handlers.otf local traverse_id = node.traverse_id local has_attribute = node.has_attribute local starttiming = statistics.starttiming local stoptiming = statistics.stoptiming local nodecodes = nodes.nodecodes -local fontdata = fonts.identifiers local handlers = nodes.handlers local glyph_code = nodecodes.glyph @@ -44,6 +44,37 @@ local glyph_code = nodecodes.glyph local run = 0 +local setfontdynamics = { } +local fontprocesses = { } + +setmetatable(setfontdynamics, { __index = + 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 +}) + +setmetatable(fontprocesses, { __index = + 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) @@ -57,7 +88,8 @@ function handlers.characters(head) local n = head while n do if n.id == glyph_code then - local font, attr = n.font, has_attribute(n,0) or 0 + local font = n.font + local attr = has_attribute(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]) @@ -75,35 +107,22 @@ function handlers.characters(head) attrfonts[font] = used end if not used[attr] then - -- we do some testing outside the function - local tfmdata = fontdata[font] - local shared = tfmdata.shared - if shared then - local dynamics = shared.dynamics - if dynamics then - local d = shared.setdynamics(font,dynamics,attr) - if d then - used[attr] = d - a = a + 1 - end + 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 end end end else local used = usedfonts[font] if not used then - local tfmdata = fontdata[font] - if tfmdata then - local shared = tfmdata.shared -- we need to check shared, only when same features - if shared then - local processors = shared.processes - if processors and #processors > 0 then - usedfonts[font] = processors - u = u + 1 - end - end - else - -- probably nullfont + local fp = fontprocesses[font] + if fp then + usedfonts[font] = fp + u = u + 1 end end end diff --git a/tex/context/base/node-ini.mkiv b/tex/context/base/node-ini.mkiv index 3bc258ce6..8d048122f 100644 --- a/tex/context/base/node-ini.mkiv +++ b/tex/context/base/node-ini.mkiv @@ -29,7 +29,7 @@ \registerctxluafile{node-shp}{1.001} \registerctxluafile{node-ser}{1.001} \registerctxluafile{node-ext}{1.001} -\registerctxluafile{node-inj}{1.001} % we might split it off +%registerctxluafile{node-inj}{1.001} % we might split it off \registerctxluafile{node-typ}{1.001} % experimental \registerctxluafile{node-acc}{1.001} % experimental diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua index bf6a60987..80360b47b 100644 --- a/tex/context/base/node-inj.lua +++ b/tex/context/base/node-inj.lua @@ -6,8 +6,6 @@ if not modules then modules = { } end modules ['node-inj'] = { license = "see context related readme files" } --- tricky ... fonts.identifiers is not yet defined .. to be solved (maybe general tex ini) - -- 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 @@ -21,14 +19,12 @@ local report_injections = logs.reporter("nodes","injections") local attributes, nodes, node = attributes, nodes, node -fonts = fonts or { } -fonts.tfm = fonts.tfm or { } -fonts.identifiers = fonts.identifiers or { } +fonts = fonts +local fontdata = fonts.hashes.identifiers nodes.injections = nodes.injections or { } local injections = nodes.injections -local fontdata = fonts.identifiers local nodecodes = nodes.nodecodes local glyph_code = nodecodes.glyph local nodepool = nodes.pool @@ -180,10 +176,11 @@ 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 + 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 has_marks or has_cursives or has_kerns then if trace_injections then trace(head) end @@ -191,18 +188,19 @@ function injections.handler(head,where,keep) 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 + 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].marks + tm = fontdata[nf].resources.marks + end + if tm then + mk[n] = tm[n.char] end - mk[n] = tm[n.char] local k = has_attribute(n,kernpair) if k then ---~ unset_attribute(k,kernpair) 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 @@ -226,9 +224,11 @@ function injections.handler(head,where,keep) valid[nofvalid] = n if n.font ~= nf then nf = n.font - tm = fontdata[nf].marks + tm = fontdata[nf].resources.marks + end + if tm then + mk[n] = tm[n.char] end - mk[n] = tm[n.char] end end end @@ -322,12 +322,18 @@ function injections.handler(head,where,keep) if d then local rlmode = d[3] if rlmode and rlmode > 0 then - -- new per 2010-10-06 + -- new per 2010-10-06, width adapted per 2010-02-03 + -- we used to negate the width of marks because in tfm + -- that makes sense but we no longer do that so as a + -- consequence the sign of p.width was changed (we need + -- to keep an eye on it as we don't have that many fonts + -- that enter this branch .. i'm still not sure if this + -- one is right local k = wx[p] - if k then -- maybe (d[1] - p.width) and/or + k[2] - n.xoffset = p.xoffset - (p.width - d[1]) - k[2] + if k then + n.xoffset = p.xoffset + p.width + d[1] - k[2] else - n.xoffset = p.xoffset - (p.width - d[1]) + n.xoffset = p.xoffset + p.width + d[1] end else local k = wx[p] diff --git a/tex/context/base/node-ref.lua b/tex/context/base/node-ref.lua index c54614359..fbf528629 100644 --- a/tex/context/base/node-ref.lua +++ b/tex/context/base/node-ref.lua @@ -220,7 +220,11 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx elseif id == glue_code and current.subtype == leftskip_code then -- any glue at the left? -- elseif id == hlist_code or id == vlist_code then - if not reference and r and (not skip or r > skip) then +-- somehow reference is true so teh 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 @@ -241,7 +245,7 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx 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 + 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 @@ -552,33 +556,6 @@ local function checkboth(open,close) return open, close end --- expansion is temp hack - -local opendocument, closedocument, openpage, closepage - -local function check(what) - if what and what ~= "" then - local set, bug = references.identify("",what) - return not bug and #set > 0 and set - end -end - -function references.checkopendocumentactions (open) opendocument = check(open) end -function references.checkclosedocumentactions(close) closedocument = check(close) end -function references.checkopenpageactions (open) openpage = check(open) end -function references.checkclosepageactions (close) closepage = check(close) end - -function references.flushdocumentactions() - if opendocument or closedocument then - codeinjections.flushdocumentactions(opendocument,closedocument) -- backend - end -end -function references.flushpageactions() - if openpage or closepage then - codeinjections.flushpageactions(openpage,closepage) -- backend - end -end - -- end temp hack statistics.register("interactive elements", function() diff --git a/tex/context/base/node-rul.lua b/tex/context/base/node-rul.lua index deb5b4341..4cbd1ad0c 100644 --- a/tex/context/base/node-rul.lua +++ b/tex/context/base/node-rul.lua @@ -60,31 +60,39 @@ end -- todo: order and maybe other dimensions -local trace_ruled = false trackers.register("nodes.rules", function(v) trace_ruled = v end) +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 floor = math.floor -local n_tostring, n_tosequence = nodes.idstostring, 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_before, insert_after, striprange = node.insert_before, node.insert_after, nodes.striprange -local list_dimensions, has_attribute, set_attribute = node.dimensions, node.has_attribute, node.set_attribute -local hpack_nodes = node.hpack -local dimenfactor = fonts.dimenfactor -local texwrite = tex.write - -local fontdata = fonts.identifiers -local variables = interfaces.variables - -local nodecodes = nodes.nodecodes -local skipcodes = nodes.skipcodes -local whatcodes = nodes.whatcodes -local kerncodes = nodes.kerncodes +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 texwrite = tex.write + +local insert_before = node.insert_before +local insert_after = node.insert_after +local striprange = nodes.striprange +local list_dimensions = node.dimensions +local has_attribute = node.has_attribute +local set_attribute = node.set_attribute + +local hpack_nodes = node.hpack + +local fontdata = fonts.hashes.identifiers +local variables = interfaces.variables +local dimenfactor = fonts.helpers.dimenfactor + +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 diff --git a/tex/context/base/node-rul.mkiv b/tex/context/base/node-rul.mkiv index 8e6aa5790..54d0c22f1 100644 --- a/tex/context/base/node-rul.mkiv +++ b/tex/context/base/node-rul.mkiv @@ -55,6 +55,9 @@ %D %D \showsetup{setupunderbar} +%D todo: mkvi this file ... no redefine, just pass all parameters, too messy now +%D (due to grouping) so better hash settings at the lua end + \unprotect %definesystemattribute[ruled] @@ -62,41 +65,39 @@ \registerctxluafile{node-rul}{1.001} -\newtoks\checkalldefinedbars +\installcommandhandler \??on {bar} \??on -\def\barparameter #1{\csname\dobarparameter\currentbar#1\endcsname} -\def\dobarparameter #1#2{\ifcsname\??on#1#2\endcsname\??on#1#2\else\expandafter\dobarparentparameter\csname\??on#1\s!parent\endcsname#2\fi} -\def\dobarparentparameter#1#2{\ifx#1\relax\s!empty\else\dobarparameter#1#2\fi} +\newtoks\checkalldefinedbars -\unexpanded\def\definebar - {\dotripleempty\dodefinebar} +\let\setupbars\setupbar -\def\dodefinebar[#1][#2][#3]% - {\ifthirdargument - \getparameters[\??on#1][\s!parent=#2,#3]% +\appendtoks + \ifsecondargument + \dodefinebarindeed\currentbar \else - \getparameters[\??on#1][\s!parent=,#2]% + \the\checkalldefinedbars \fi - % - %\setvalue{\??on:#1}{0}% - % - \ifcsname\??on:#1:c\endcsname - \csname\??on:#1:c\endcsname\zerocount +\to \everysetupbar + +\appendtoks + \ifcsname\??on:\currentbar:c\endcsname + \csname\??on:\currentbar:c\endcsname\zerocount \else - \expandafter\newcount\csname\??on:#1:c\endcsname + \expandafter\newcount\csname\??on:\currentbar:c\endcsname \fi - \normalexpanded{\checkalldefinedbars{\noexpand\doredefinebar{#1}\the\checkalldefinedbars}}% - \dodefinebarindeed{#1}% - \setuvalue{#1}{\doruled{#1}}} + \normalexpanded{\checkalldefinedbars{\doredefinebar{\currentbar}\the\checkalldefinedbars}}% + \dodefinebarindeed\currentbar + \setuevalue\currentbar{\doruled{\currentbar}}% +\to \everydefinebar -\def\dodefinebarindeed#1% - {\bgroup - \def\currentbar{#1}% +\unexpanded\def\dodefinebarindeed#1% + {\begingroup + \edef\currentbar{#1}% \doifsomethingelse{\barparameter\c!color} {\donetrue\colored[\barparameter\c!color]} {\donefalse}% \normalexpanded - {\egroup + {\endgroup \scratchcounter\ctxlua{nodes.rules.define { method = \barparameter\c!method, offset = \barparameter\c!offset, @@ -114,7 +115,7 @@ \let\doredefinebar\dodefinebarindeed -\def\doruled#1% +\unexpanded\def\doruled#1% {\groupedcommand{\dodoruled{#1}}\relax} \def\dodoruled @@ -123,7 +124,7 @@ \dodoruled} \def\dodoruledindeed#1% maybe reverse the 1000 - {\advance\csname\??on:#1:c\endcsname\plusone + {\advance\csname\??on:#1:c\endcsname\plusone % local ? \scratchcounter\csname\??on:#1:c\endcsname \attribute\ruledattribute\numexpr 1000*\scratchcounter @@ -151,18 +152,6 @@ {\csname\??on:s:\number\currentbarnesting\endcsname \global\advance\currentbarnesting\minusone} -\unexpanded\def\setupbars - {\dodoubleempty\dosetupbars} - -\def\dosetupbars[#1][#2]% not that efficient - {\ifsecondargument - \getparameters[\??on#1][#2]% - \dodefinebarindeed{#1}% - \else - \getparameters[\??on][#1]% - \the\checkalldefinedbars - \fi} - \setupbars [\c!method=0, % new: 0=center nested, 1=stack nested \c!continue=\v!no, @@ -199,49 +188,36 @@ %D This will move: (a bit duplicated) -\newtoks\checkalldefinedshifts - -\def\shiftparameter #1{\csname\doshiftparameter\currentshift#1\endcsname} -\def\shiftparameterhash#1{\doshiftparameterhash{\??ra\currentshift}#1} +\installcommandhandler \??ra {shift} \??ra -\def\doshiftparameter #1#2{\ifcsname\??ra#1#2\endcsname\??ra#1#2\else\expandafter\doshiftparentparameter\csname\??ra#1\s!parent\endcsname#2\fi} -\def\doshiftparameterhash#1#2{\ifcsname#1#2\endcsname#1\else\expandafter\doshiftparentparameterhash\csname#1\s!parent\endcsname#2\fi} - -\def\doshiftparentparameter #1#2{\ifx#1\relax\s!empty\else\doshiftparameter #1#2\fi} -\def\doshiftparentparameterhash#1#2{\ifx#1\relax \else\doshiftparameterhash#1#2\fi} - -\def\dosetshiftattributes#1#2% style color - {\edef\fontattributehash {\shiftparameterhash#1}% - \edef\colorattributehash{\shiftparameterhash#2}% - \ifx\fontattributehash \empty\else\dosetfontattribute \fontattributehash #1\fi - \ifx\colorattributehash\empty\else\dosetcolorattribute\colorattributehash#2\fi} +\newtoks\checkalldefinedshifts -\unexpanded\def\defineshift - {\dotripleempty\dodefineshift} +\let\setupshifts\setupshift -\def\dodefineshift[#1][#2][#3]% - {\ifthirdargument - \getparameters[\??ra#1][\s!parent=#2,#3]% +\appendtoks + \ifsecondargument + \dodefineshiftindeed\currentshift \else - \getparameters[\??ra#1][\s!parent=,#2]% + \the\checkalldefinedshifts \fi - % - %\setvalue{\??ra:#1}{0}% - % - \ifcsname\??ra:#1:c\endcsname - \csname\??ra:#1:c\endcsname\zerocount +\to \everysetupshift + +\appendtoks + \ifcsname\??ra:\currentshift:c\endcsname + \csname\??ra:\currentshift:c\endcsname\zerocount \else - \expandafter\newcount\csname\??ra:#1:c\endcsname + \expandafter\newcount\csname\??ra:\currentshift:c\endcsname \fi - \normalexpanded{\checkalldefinedshifts{\noexpand\doredefineshift{#1}\the\checkalldefinedshifts}}% - \dodefineshiftindeed{#1}% - \setuvalue{#1}{\doshifted{#1}}} + \normalexpanded{\checkalldefinedshifts{\doredefineshift{\currentshift}\the\checkalldefinedshifts}}% + \dodefineshiftindeed{\currentshift}% + \setuevalue\currentshift{\doshifted{\currentshift}}% +\to \everydefineshift -\def\dodefineshiftindeed#1% - {\bgroup - \def\currentshift{#1}% +\unexpanded\def\dodefineshiftindeed#1% + {\begingroup + \edef\currentshift{#1}% \normalexpanded - {\egroup + {\endgroup \scratchcounter\ctxlua{nodes.shifts.define { method = \shiftparameter\c!method, continue = "\shiftparameter\c!continue", @@ -252,23 +228,14 @@ \let\doredefineshift\dodefineshiftindeed -\def\doshifted#1% - {\groupedcommand{\dodoshifted{#1}}\relax} +% \unexpanded\def\doshifted#1% +% {\groupedcommand{\dodoshifted{#1}}\relax} \def\dodoshifted {\ctxlua{nodes.shifts.enable()}% \glet\dodoshifted\dodoshiftedindeed \dodoshifted} -% \def\dodoshiftedindeed#1% -% {\def\currentshift{#1}% -% \advance\csname\??ra:#1:c\endcsname\plusone -% \scratchcounter\csname\??ra:#1:c\endcsname -% \attribute\shiftedattribute\numexpr1000*\scratchcounter -% +\csname\??ra#1\ifcsname\??ra#1:\number\scratchcounter\s!parent\endcsname:\number\scratchcounter\fi:a\endcsname -% \setupalign[\shiftparameter\c!align]% -% \dosetshiftattributes\c!style\c!color} - \def\dostartisolation{\char0 } \def\dostopisolation {\char0 } \def\doisolator {\char0 } @@ -291,7 +258,7 @@ \dosetshiftattributes\c!style\c!color \dosetupisolatedalign{\shiftparameter\c!align}} -\def\doshifted#1% +\unexpanded\def\doshifted#1% {\doisolatedgroupedalign{\dodoshifted{#1}}{}} \unexpanded\def\startshift[#1]% @@ -301,18 +268,6 @@ \unexpanded\def\stopshift {\endgroup} -\unexpanded\def\setupshifts - {\dodoubleempty\dosetupshifts} - -\def\dosetupshifts[#1][#2]% not that efficient - {\ifsecondargument - \getparameters[\??ra#1][#2]% - \dodefineshiftindeed{#1}% - \else - \getparameters[\??ra][#1]% - \the\checkalldefinedshifts - \fi} - \setupshifts [\c!method=0, \c!continue=\v!no, diff --git a/tex/context/base/node-spl.lua b/tex/context/base/node-spl.lua index c609f4150..ddb4a26a8 100644 --- a/tex/context/base/node-spl.lua +++ b/tex/context/base/node-spl.lua @@ -22,9 +22,6 @@ local gmatch, concat, format, remove = string.gmatch, table.concat, string.forma local next, tostring, tonumber = next, tostring, tonumber local utfchar = utf.char local random = math.random -local variables = interfaces.variables -local settings_to_array, settings_to_hash = utilities.parsers.settings_to_array, utilities.parsers.settings_to_hash -local fcs = fonts.colors.set 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) @@ -37,6 +34,11 @@ local report_optimizers = logs.reporter("nodes","optimizers") local nodes, node = nodes, node +local variables = interfaces.variables + +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 @@ -53,6 +55,8 @@ local insert_node_before = node.insert_before local insert_node_after = node.insert_after local repack_hlist = nodes.repack_hlist +local setnodecolor = nodes.tracers.colors.set + local nodecodes = nodes.nodecodes local whatsitcodes = nodes.whatsitcodes @@ -76,7 +80,9 @@ local starttiming = statistics.starttiming local stoptiming = statistics.stoptiming local process_characters = nodes.handlers.characters local inject_kerns = nodes.injections.handler -local fontdata = fonts.identifiers +local fontdata = fonts.hashes.identifiers +local setfontdynamics = fonts.hashes.setdynamics +local fontprocesses = fonts.hashes.processes local parbuilders = builders.paragraphs parbuilders.solutions = parbuilders.solutions or { } @@ -112,7 +118,7 @@ function splitters.setup(setups) criterium = tonumber(setups.criterium) or criterium end -local contextsetups = fonts.definers.specifiers.contextsetups +local contextsetups = fonts.specifiers.contextsetups local function convert(featuresets,name,set,what) local list, numbers, nofnumbers = set[what], { }, 0 @@ -333,29 +339,35 @@ local function doit(word,list,best,width,badness,line,set,listdir) end elseif set == "less" then for n in traverse_nodes(first) do - fcs(n,"font:isol") + setnodecolor(n,"font:isol") set_attribute(n,0,featurenumber) end else for n in traverse_nodes(first) do - fcs(n,"font:medi") + setnodecolor(n,"font:medi") set_attribute(n,0,featurenumber) end end local font = found.font - local dynamics = found.dynamics - local shared = fontdata[font].shared - if not dynamics then -- we cache this - dynamics = shared.dynamics - found.dynamics = dynamics - end - local processors = found[featurenumber] - if not processors then -- we cache this too - processors = shared.setdynamics(font,dynamics,featurenumber) - found[featurenumber] = processors - end - for i=1,#processors do -- often more than 1 - first = processors[i](first,font,featurenumber) -- we can make a special one that already passes the dynamics + -- local dynamics = found.dynamics + -- local shared = fontdata[font].shared + -- if not dynamics then -- we cache this + -- dynamics = shared.dynamics + -- found.dynamics = dynamics + -- end + -- local processors = found[featurenumber] + -- if not processors then -- we cache this too + -- processors = fonts.handlers.otf.setdynamics(font,featurenumber) + -- found[featurenumber] = processors + -- end + 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 %s",font) end first = inject_kerns(first) local h = word[2].next -- head of current word diff --git a/tex/context/base/node-tra.lua b/tex/context/base/node-tra.lua index 8b393df48..84d772670 100644 --- a/tex/context/base/node-tra.lua +++ b/tex/context/base/node-tra.lua @@ -24,13 +24,6 @@ nodes = nodes or { } local fonts, nodes, node, context = fonts, nodes, node, context -fonts.tfm = fonts.tfm or { } -fonts.identifiers = fonts.identifiers or { } -fonts.characters = fonts.characters or { } - -local fontdata = fonts.identifiers -local fontchar = fonts.characters - nodes.tracers = nodes.tracers or { } local tracers = nodes.tracers @@ -77,6 +70,7 @@ local nodepool = nodes.pool local new_glyph = nodepool.glyph function char_tracers.collect(head,list,tag,n) + local fontdata = fonts.hashes.identifiers n = n or 0 local ok, fn = false, nil while head do @@ -236,6 +230,7 @@ end function step_tracers.features() -- we cannot use first_glyph here as it only finds characters with subtype < 256 + local fontdata = fonts.hashes.identifiers local f = collection[1] while f do if f.id == glyph_code then @@ -265,12 +260,14 @@ function step_tracers.features() end function tracers.fontchar(font,char) + local fontchar = fonts.hashes.characters local n = new_glyph() n.font, n.char, n.subtype = font, char, 256 context(n) end function step_tracers.codes(i,command) + local fontdata = fonts.hashes.identifiers local c = collection[i] while c do local id = c.id @@ -581,6 +578,7 @@ local threshold = 65536 local function toutf(list,result,nofresult,stopcriterium) if list then + local fontchar = fonts.hashes.characters for n in traverse_nodes(list) do local id = n.id if id == glyph_code then @@ -656,3 +654,25 @@ number.points = points --~ local shrink_unit = (shrink_order ~= 0) and ("fi".. string.rep("l",shrink_order)) or "sp" --~ return string.format("%ssp+ %ssp - %ssp",s.width,s.stretch,stretch_unit,s.shrink,shrink_unit) --~ end + +local colors = { } +tracers.colors = colors + +local set_attribute = node.set_attribute +local unset_attribute = node.unset_attribute + +local attribute = attributes.private('color') +local mapping = attributes.list[attribute] or { } + +function colors.set(n,c) + local mc = mapping[c] + if not mc then + unset_attribute(n,attribute) + else + set_attribute(n,attribute,mc) + end +end + +function colors.reset(n) + unset_attribute(n,attribute) +end diff --git a/tex/context/base/pack-lyr.mkiv b/tex/context/base/pack-lyr.mkiv index 8335a07b9..5242b56de 100644 --- a/tex/context/base/pack-lyr.mkiv +++ b/tex/context/base/pack-lyr.mkiv @@ -140,15 +140,6 @@ \def\setlayer {\dotripleempty\dosetlayer} -% \def\dosetlayer[#1][#2][#3]% #4 == box do \fi is ok -% {\doifelsevalue{\??ll#1\c!state}\v!stop -% {\dowithnextbox\donothing\hbox} -% {\ifthirdargument -% \dodosetlayer[#1][#2][#3]% -% \else -% \dodosetlayer[#1][][#2]% -% \fi}} - \def\dosetlayer[#1][#2][#3]% #4 == box do \fi is ok {\doifelsevalue{\??ll#1\c!state}\v!stop {\dowithnextbox\donothing\hbox} @@ -424,20 +415,6 @@ % todo: setups before flush, handy hook -% \unexpanded\def\flushlayer[#1]% -% {\doifelsevalue{\??ll#1\c!state}\v!next -% {\letgvalue{\??ll#1\c!state}\v!start} % dangerous, stack-built-up -% {\doifelsevalue{\??ll#1\c!state}\v!continue -% {\letgvalue{\??ll#1\c!state}\v!repeat} % dangerous, stack-built-up -% {\doifelsevalue{\??ll#1\c!doublesided}\v!yes -% {\doifundefinedelse{\@@layerbox#1}% -% {\dodoflushlayerA[#1]} -% {\doifbothsidesoverruled -% {\dodoflushlayerB\v!left [#1]}% left -% {\dodoflushlayerB\v!right[#1]}% right -% {\dodoflushlayerB\v!left [#1]}}}% left -% {\dodoflushlayerA[#1]}}}} - \unexpanded\def\flushlayer[#1]% quite core, so optimized {\begingroup \forgetall diff --git a/tex/context/base/pack-mis.mkvi b/tex/context/base/pack-mis.mkvi new file mode 100644 index 000000000..ab6d2b334 --- /dev/null +++ b/tex/context/base/pack-mis.mkvi @@ -0,0 +1,84 @@ +%D \module +%D [ file=pack-mis, % moved from e.g. core-mis +%D version=1998.01.29, +%D title=\CONTEXT\ Core Macros, +%D subtitle=Miscelaneous, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Packing Macros / Misc Commands} + +\unprotect + +% a quite old mechanism already (but inheritance added) +% +% \defineplacement[name][settings] +% \setupplacement [name][settings] +% \placement [name][settings] +% \place [settings] + +\installcommandhandler \??pl {placement} \??pl + +\appendtoks + \setuevalue{\e!place\currentplacement}{\pack_placement{\currentplacement}}% +\to \everydefineplacement + +\setupplacement + [\c!left=\hss, + \c!right=\hss, + \c!linecorrection=\v!off, + \c!depthcorrection=\v!off, + \c!grid=\v!middle, + %\c!before=, + %\c!after=, + \c!margin=\v!standard] + +\unexpanded\def\placement[#tag]% + {\pack_placement{#tag}} + +\unexpanded\def\pack_placement#tag% + {\bgroup + \edef\currentplacement{#tag}% + \dosingleempty\pack_placement_indeed} + +\def\pack_placement_indeed[#settings]% set test can be sped up but non critical + {\iffirstargument + \setupcurrentplacement[#settings]% + \fi + \dowithnextboxcontent{\forgetall}{\pack_placement_flush\egroup}\vbox} + +\def\pack_placement_flush + {\setlocalhsize + \placementparameter\c!before + \begingroup + \disableparpositions + \setbox\nextbox\hbox to \localhsize + {\placementparameter\c!left + \flushnextbox + \placementparameter\c!right}% + \ifinsidefloat \else + \addlocalbackgroundtobox\nextbox + \fi + \ifgridsnapping + \doifinset{\placementparameter\c!margin}{\v!standard,\v!yes}\noindent % unchecked + \doifelsenothing{\placementparameter\c!grid} + {\snaptogrid[\v!middle]} + {\snaptogrid[\placementparameter\c!grid]}% + \hbox{\flushnextbox}% + \else + \doif{\placementparameter\c!linecorrection}\v!on \startbaselinecorrection + \doifinset{\placementparameter\c!margin}{\v!standard,\v!yes}\noindent + \flushnextbox + \doif{\placementparameter\c!depthcorrection}\v!on\baselinecorrection + \doif{\placementparameter\c!linecorrection }\v!on\stopbaselinecorrection + \fi + \endgroup + \placementparameter\c!after} + +\protect \endinput + diff --git a/tex/context/base/pack-rul.mkiv b/tex/context/base/pack-rul.mkiv index 053b217c5..d6299a292 100644 --- a/tex/context/base/pack-rul.mkiv +++ b/tex/context/base/pack-rul.mkiv @@ -328,6 +328,16 @@ \box\scratchbox \egroup} +%D \macros +%D {overlayfakebox} + +\def\overlayfakebox + {\hbox + {\setbox\scratchbox\emptyhbox + \wd\scratchbox\overlaywidth + \ht\scratchbox\overlayheight + \box\scratchbox}} + %D The empty case is: \let\executeoverlay\gobblesevenarguments @@ -2146,10 +2156,12 @@ %D %D \showsetup{blackrule} -\def\complexblackrule[#1]% +\definecomplexorsimple\blackrule + +\unexpanded\def\complexblackrule[#1]% {\hbox\bgroup\getparameters[\??bj][#1]\domakeblackrule\egroup} -\def\simpleblackrule +\unexpanded\def\simpleblackrule {\hbox\bgroup\domakeblackrule\egroup} \def\domakeblackrule @@ -2163,8 +2175,6 @@ \!!depth \@@bjdepth \stopcolor} -\definecomplexorsimple\blackrule - %D \macros %D {blackrules} %D @@ -2686,77 +2696,124 @@ %D %D The next definition shows the defaults. -\def\dodefineframedtext[#1][#2]% - {\presetlocalframed[\??kd#1]% - \getparameters[\??kd#1] - [\c!width=0.75\hsize, - \c!height=\v!fit, - \c!align=\v!yes, - \c!top=, - \c!bottom=\vfill, - \c!offset=1em, - \c!bodyfont=, - \c!style=, - \c!color=, - \c!left=, - \c!right=\hfill, - \c!before=\blank, - \c!after=\blank, - \c!inner=, - \c!frame=\v!on, - \c!topframe=, - \c!bottomframe=, - \c!leftframe=, - \c!rightframe=, - \c!radius=.5\bodyfontsize, - \c!corner=\v!rectangular, - \c!foregroundcolor=, - \c!foregroundstyle=, - \c!background=, - \c!backgroundcolor=, - \c!backgroundscreen=\@@rsscreen, - \c!linecorrection=\v!on, - \c!depthcorrection=\v!on, - \c!margin=\v!standard, - \c!orientation=, - \c!indenting=, - #2]% - \setuvalue{\e!start#1}{\dostartframedtext[#1]}% - \setuvalue{\e!stop #1}{\dostopframedtext }% - \setuvalue {#1}{\doframedtext [#1]}} - -\unexpanded\def\defineframedtext - {\dodoubleempty\dodefineframedtext} +\installcommandhandler \??kd {framedtext} \??kd -%D We define the general (and original) case by just saying: +\let\setupframedtexts\setupframedtext -\defineframedtext[\v!framedtext] +\presetlocalframed[\??kd] -%D We need several steps before the actual job is done, -%D because we have to handle an optional identifier (and -%D because these commands evolved out of a single case). +\setupframedtext + [\c!width=0.75\hsize, + \c!height=\v!fit, + \c!align=\v!yes, + %\c!top=, + \c!bottom=\vfill, + \c!offset=1em, + %\c!bodyfont=, + %\c!style=, + %\c!color=, + %\c!left=, + \c!right=\hfill, + \c!before=\blank, + \c!after=\blank, + %\c!inner=, + \c!frame=\v!on, + %\c!topframe=, + %\c!bottomframe=, + %\c!leftframe=, + %\c!rightframe=, + \c!radius=.5\bodyfontsize, + \c!corner=\v!rectangular, + %\c!orientation=, + %\c!indenting=, + %\c!foregroundcolor=, + %\c!foregroundstyle=, + %\c!background=, + %\c!backgroundcolor=, + \c!backgroundscreen=\@@rsscreen, + \c!linecorrection=\v!on, + \c!depthcorrection=\v!on, + \c!margin=\v!standard] -\def\framedtextparameter#1#2% todo: currentframedtext - {\csname\??kd#1#2\endcsname} +\appendtoks + \setuevalue{\e!start\currentframedtext}{\dostartframedtext{\currentframedtext}}% + \setuevalue{\e!stop \currentframedtext}{\dostopframedtext }% + \setuevalue {\currentframedtext}{\doframedtext {\currentframedtext}}% +\to \everydefineframedtext -\def\dosetupframedtexts[#1][#2]% - {\ifsecondargument - \def\docommand##1{\getparameters[\??kd##1][#2]}% - \processcommacommand[#1]\docommand % new, #1 may be macro - \else - \getparameters[\??kd\v!framedtext][#1]% - \fi} +\unexpanded\def\dostartframedtext#1% + {\bgroup + \edef\currentframedtext{#1}% + \dodoubleempty\dodostartframedtext} -\unexpanded\def\setupframedtexts - {\dodoubleempty\dosetupframedtexts} +\def\dodostartframedtext[#1][#2]% + {\doifassignmentelse{#1} + {\dododostartframedtext[][#1]} + {\dododostartframedtext[#1][#2]}} -\def\dostartframedtext - {\bgroup\dotripleempty\dodostartframedtext} +\def\dododostartframedtext[#1][#2]% #2 only passed to framed, not to framedtext + {\setupframedtexts[\currentframedtext][#2]% + \doifsomething{#1}{\setframedtextparameter\c!location{#1}}% does not listen to #3 + \setfalse\framedtextlocationnone + % somewhat messy ... needs to be redone + \processaction % \v!low en \v!depth are already taken ! + [\framedtextparameter\c!location] + [ \v!left=>\letframedtextparameter\c!left \relax + \letframedtextparameter\c!right\hfill, + \v!right=>\letframedtextparameter\c!left \hfill + \letframedtextparameter\c!right\relax, + \v!middle=>\letframedtextparameter\c!left \hfill + \letframedtextparameter\c!right\hfill, + \v!none=>\letframedtextparameter\c!left \relax % new + \letframedtextparameter\c!right\relax % new + \settrue\framedtextlocationnone]% + \resetframedtextparameter\c!location + % removed 06/2001 + % \forgetparindent + % added 06/2001 [see demo-bbv] + \localhsize\hsize \checkframedtext + % so far + \setbox\framebox\vbox + \startboxedcontent + \hsize\localhsize + % \insidefloattrue % ? better + \normalexpanded{\noexpand\switchtobodyfont[\framedtextparameter\c!bodyfont]}% + \startcolor[\framedtextparameter\c!color]% + \localframed[\??kd\currentframedtext][\c!strut=\v!no]% todo: use delayedstrut + \bgroup + \let\\=\endgraf + \framedtextparameter\c!inner % oud spul + \doif{\framedtextparameter\c!depthcorrection}\v!on\doftstartdepthcorrection + \doinhibitblank % \blank[\v!disable]% plaatst signal + \setupindenting[\framedtextparameter\c!indenting]% + %\doconvertfont{\framedtextparameter\c!style}\empty} %%%%% todo: attr setter + \dosetframedtextattributes\c!style\c!color} -\def\dodostartframedtext[#1][#2][#3]% - {\doifassignmentelse{#2} - {\dododostartframedtext[#1][][#2]} - {\dododostartframedtext[#1][#2][#3]}} +%D The \type {none} option is handy for nested usage, as +%D in the presentation styles, where we don't want +%D interference. + +\defineplacement[\??kd][\s!parent=\??kd\currentframedtext] + +\unexpanded\def\dostopframedtext % no \baselinecorrection, see faq docs + {\endgraf + \removelastskip + \doif{\framedtextparameter\c!depthcorrection}\v!on\doftstopdepthcorrection + \stopboxedcontent + \stopcolor + \ifconditional\framedtextlocationnone + \egroup + \box\framebox + \else\ifinsidefloat + \egroup + \box\framebox + \else + \egroup + \placement[\??kd][\c!depthcorrection=\v!off]{\box\framebox}% + \fi\fi + \egroup} + +%D We define the general (and original) case by just saying: \setfalse\framedtextlocationnone @@ -2793,64 +2850,6 @@ % \donegbotbaselinecorrection \verticalstrut} -\def\dododostartframedtext[#1][#2][#3]% #3 only passed to framed, not to framedtext - {\doifsomething{#2}{\setvalue{\??kd#1\c!location}{#2}}% does not listen to #3 - \setfalse\framedtextlocationnone - \processaction % \v!low en \v!depth are already taken ! - [\framedtextparameter{#1}\c!location] - [ \v!left=>\letvalue{\??kd#1\c!left }\relax - \letvalue{\??kd#1\c!right}\hfill, - \v!right=>\letvalue{\??kd#1\c!left }\hfill - \letvalue{\??kd#1\c!right}\relax, - \v!middle=>\letvalue{\??kd#1\c!left }\hfill - \letvalue{\??kd#1\c!right}\hfill, - \v!none=>\letvalue{\??kd#1\c!left }\relax % new - \letvalue{\??kd#1\c!right}\relax % new - \settrue\framedtextlocationnone]% - \letvalueempty{\??kd#1\c!location}% - % removed 06/2001 - % \forgetparindent - % added 06/2001 [see demo-bbv] - \localhsize\hsize \checkframedtext - % so far - \setbox\framebox\vbox - \startboxedcontent - \hsize\localhsize - % \insidefloattrue % ? better - \normalexpanded{\noexpand\switchtobodyfont[\framedtextparameter{#1}\c!bodyfont]}% - \startcolor[\framedtextparameter{#1}\c!color]% - \localframed[\??kd#1][\c!strut=\v!no,#3]% todo: use delayedstrut - \bgroup - \let\\=\endgraf - \framedtextparameter{#1}\c!inner % oud spul - \doifvalue{\??kd#1\c!depthcorrection}\v!on\doftstartdepthcorrection - \doinhibitblank % \blank[\v!disable]% plaatst signal - \setupindenting[\framedtextparameter{#1}\c!indenting]% - \doconvertfont{\framedtextparameter{#1}\c!style}\empty - \def\dostopframedtext{\dodostopframedtext{#1}{#2}}} - -%D The \type {none} option is handy for nested usage, as -%D in the presentation styles, where we don't want -%D interference. - -\def\dodostopframedtext#1#2% % no \baselinecorrection, see faq docs - {\endgraf - \removelastskip - \doifvalue{\??kd#1\c!depthcorrection}\v!on\doftstopdepthcorrection - \stopboxedcontent - \stopcolor - \ifconditional\framedtextlocationnone - \egroup - \box\framebox - \else\ifinsidefloat - \egroup - \box\framebox - \else - \egroup - \doplacement[\??kd#1][\c!depthcorrection=\v!off]{\box\framebox}% - \fi\fi - \egroup} - %D Placement can be ignored: %D %D \starttyping @@ -2869,17 +2868,19 @@ %D The simple brace (or group) delimited case is typeset %D slightly different and is not aligned. -\def\doframedtext - {\bgroup\dodoubleempty\dodoframedtext} +\unexpanded\def\doframedtext#1% + {\bgroup + \edef\currentframedtext{#1}% + \dosingleempty\dodoframedtext} -\def\dodoframedtext[#1][#2]% beware! - {\normalexpanded{\noexpand\switchtobodyfont[\getvalue{\??kd#1\c!bodyfont}]}% - \localframed[\??kd#1][\c!strut=\v!no,#2]% +\def\dodoframedtext[#1]% beware! + {\normalexpanded{\noexpand\switchtobodyfont[\\framedtextparameter\c!bodyfont]}% + \localframed[\??kd\currentframedtext][\c!strut=\v!no,#1]% \bgroup \blank[\v!disable]% \let\\=\endgraf - \getvalue{\??kd#1\c!inner}% % kleur naar outer level - \dostartattributes{\??kd#1}\c!style\c!color\empty + \framedtextparameter\c!inner + \dosetframedtextattributes\c!style\c!color \bgroup \aftergroup\docloseframedtext \let\next=} @@ -2890,6 +2891,8 @@ \egroup \egroup} +\defineframedtext[\v!framedtext] + %D \macros %D {defineframed} %D @@ -2902,17 +2905,6 @@ %D also simplified the \type {\setupframed} command. There are %D certainly more places where such improvements can be made. -% \unexpanded\def\defineframed -% {\dodoubleempty\dodefineframed} -% -% \def\dodefineframed[#1][#2]% -% {\iffirstargument -% \setuvalue{#1}{\dodoubleempty\doframed[#2]}% -% \fi} -% -% \def\doframed[#1][#2]% -% {\framed[#1,#2]} - \def\defineframed {\dodoubleempty\dodefineframed} diff --git a/tex/context/base/page-bck.mkiv b/tex/context/base/page-bck.mkiv index ddea18c5f..81392f5af 100644 --- a/tex/context/base/page-bck.mkiv +++ b/tex/context/base/page-bck.mkiv @@ -296,7 +296,7 @@ \box#1}% \fi} -\newdimen\pagebackgroundhoffset +\newdimen\pagebackgroundhoffset % THESE WILL BECOME OBSOLETE \newdimen\pagebackgroundvoffset \newdimen\pagebackgrounddepth \newdimen\pagebackgroundoffset diff --git a/tex/context/base/page-flt.lua b/tex/context/base/page-flt.lua index 67e2e5b6f..b691dbd45 100644 --- a/tex/context/base/page-flt.lua +++ b/tex/context/base/page-flt.lua @@ -10,7 +10,7 @@ if not modules then modules = { } end modules ['page-flt'] = { local insert, remove = table.insert, table.remove local find = string.find -local setdimen, setbox, setcount, texbox = tex.setdimen, tex.setbox, tex.setcount, tex.box +local setdimen, setcount, texbox = tex.setdimen, tex.setcount, tex.box local copy_node_list = node.copy_list diff --git a/tex/context/base/page-imp.mkii b/tex/context/base/page-imp.mkii index 11ef561e2..4c22d4d2d 100644 --- a/tex/context/base/page-imp.mkii +++ b/tex/context/base/page-imp.mkii @@ -3,7 +3,7 @@ %D version=1998.01.15, %D title=\CONTEXT\ Page Macros, %D subtitle=Pagebody Building (Imposition), -%D author=Hans Hagen, +%D author=Hans Hagen & Willi Egger, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] %C @@ -806,6 +806,364 @@ \setuppaper [\c!width =\dimexpr\printpaperwidth -2\dimexpr\@@ppbackspace\relax\relax, \c!height=\dimexpr\printpaperheight-2\dimexpr\@@pptopspace \relax\relax] + +%D Might be used if a printer is printing from a rol or creating mini-books from A4: +%D This section has 16 pages. The folding scheme is first a Z-fold and at the end +%D a final fold in the spine. +%D Coding: [2*8*Z] + +\installpagearrangement 2*8*Z + {\dosetuparrangement{2}{4}{8}{3}{5}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageSIXTEENZ\poparrangedpagesAB\relax} + +\def\pusharrangedpageSIXTEENZ#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}101\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}111\arrangedpageA % 4 + \or \handlearrangedpageXandY{#1}012\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}002\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}103\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}113\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}103\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}113\arrangedpageB % 10 + \or \handlearrangedpageXandY{#1}012\arrangedpageB % 11 + \or \handlearrangedpageXandY{#1}002\arrangedpageA % 12 + \or \handlearrangedpageXandY{#1}101\arrangedpageA % 13 + \or \handlearrangedpageXandY{#1}111\arrangedpageB % 14 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 15 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 16 + \poparrangedpages + \fi} + +%D Another Z-folded section with 12 pages +%D Coding: [2*6*Z] + +\installpagearrangement 2*6*Z + {\dosetuparrangement{2}{3}{6}{3}{4}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageTWELVEZ\poparrangedpagesAB\relax} + +\def\pusharrangedpageTWELVEZ#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1: rotation (0=upright),x (0=first column),y (0=first row) + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}101\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}111\arrangedpageA % 4 + \or \handlearrangedpageXandY{#1}012\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}002\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}012\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}002\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}101\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}111\arrangedpageB % 10 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 11 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 12 + \poparrangedpages + \fi} + +%D For Heinz' special greeting cards folding. This scheme is also used for the PocketDiary (module): +%D Coding: [1*8] + +\installpagearrangement 1*8 + {\dosetuparrangement{4}{2}{8}{5}{3} % X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageEIGHTSINGLESIDEDFOLDED\poparrangedpagesTWO\relax} + +\def\pusharrangedpageEIGHTSINGLESIDEDFOLDED#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 3 + \or \handlearrangedpageXandY{#1}131\arrangedpageA % 4 + \or \handlearrangedpageXandY{#1}121\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}111\arrangedpageA % 6 + \or \handlearrangedpageXandY{#1}101\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \poparrangedpages + \fi} + +%D This is not a section. \CONTEXT\ places 4 pages on a sheet of paper, singlesided +%D Coding: [1*4] + +\installpagearrangement 1*4 + {\dosetuparrangement{2}{2}{4}{3}{3} % X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageFOURSINGLESIDEDFOLDED\poparrangedpagesTWO\relax} + +\def\pusharrangedpageFOURSINGLESIDEDFOLDED#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}100\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}011\arrangedpageA % 3 + \or \handlearrangedpageXandY{#1}110\arrangedpageA % 4 + \poparrangedpages + \fi} + +%D This imposition scheme was requested by Hraban Ramm, by Willi Egger 21-07-2003 +%D Coding: [3SIDE] + +\installpagearrangement 3SIDE + {\dosetuparrangement{3}{1}{3}{4}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageTHREESIDE\poparrangedpagesAB\relax} + +\def\pusharrangedpageTHREESIDE#1% Willi's approach + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 3 + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 6 + \poparrangedpages + \fi} + +%D FLYER in three parts and 6 pages 22-10-2010 +%D Coding: [TRYPTICHON] + +\installpagearrangement TRYPTICHON + {\dosetuparrangement{3}{1}{3}{4}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageFOLDERSIX\poparrangedpagesAB\relax} + +\def\pusharrangedpageFOLDERSIX#1% Willi's approach + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 6 + \poparrangedpages + \fi} + +%D FLYER in Z-fold with 8 pages 22-01-2010 +%D Coding: [ZFLYER-8] + +\installpagearrangement ZFLYER-8 + {\dosetuparrangement{4}{1}{4}{5}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageZFOLDEREIGHT\poparrangedpagesAB\relax} + +\def\pusharrangedpageZFOLDEREIGHT#1% Willi's approach + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 6 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 8 + \poparrangedpages + \fi} + +%D FLYER in Z-fold with 10 pages 04-08-2010 +%D Coding: [ZFLYER-10] + +\installpagearrangement ZFLYER-10 + {\dosetuparrangement{5}{1}{5}{6}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageZFLYERTEN\poparrangedpagesAB\relax} + +\def\pusharrangedpageZFLYERTEN#1% Willi's approach + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}040\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}040\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 10 + \poparrangedpages + \fi} + + +%D FLYER in Z-fold with 12 pages 04-08-2010 +%D Coding: [ZFLYER-12] + +\installpagearrangement ZFLYER-12 + {\dosetuparrangement{6}{1}{6}{7}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageZFLYERTWELVE\poparrangedpagesAB\relax} + +\def\pusharrangedpageZFLYERTWELVE#1% Willi's approach + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}050\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}040\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}050\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 10 + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 11 + \or \handlearrangedpageXandY{#1}040\arrangedpageA % 12 + \poparrangedpages + \fi} + +%D FLYER folded as a map with 6 pages per side. +%D Coding: [MAPFLYER-12] + +\installpagearrangement MAPFLYER-12 + {\dosetuparrangement{3}{2}{6}{4}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageMFOLDERTWELVE\poparrangedpagesAB\relax} + +\def\pusharrangedpageMFOLDERTWELVE#1% Willi's approach + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}001\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}011\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}021\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 10 + \or \handlearrangedpageXandY{#1}011\arrangedpageA % 11 + \or \handlearrangedpageXandY{#1}021\arrangedpageA % 12 + \poparrangedpages + \fi} + +%D FLYER folded as double window with 4 pages per side. +%D Coding: [DOUBLEWINDOW] + +\installpagearrangement DOUBLEWINDOW + {\dosetuparrangement{4}{1}{4}{5}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageDOUBLEWINDOWEIGHT\poparrangedpagesAB\relax} + +\def\pusharrangedpageDOUBLEWINDOWEIGHT#1% Willi's approach + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 8 + \poparrangedpages + \fi} + +%D Imposition as requested by Jan Pohanka 26-08-2010, 4 pages, two verso, two recto, +%D uneven pages upright and down, even pages top and rotated 180. +%D Implementation with 2 pages for conference-name-display +%D Coding: [1*2-Conference] + +\installpagearrangement 1*2-Conference + {\dosetuparrangement{1}{2}{4}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageCONFERENCE2\poparrangedpagesAB\relax} + +\def\pusharrangedpageCONFERENCE2#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}100\arrangedpageA % 2 + \poparrangedpages + \fi} + +%D Implementation with 4 pages for conference-name-display +%D Coding: [1*4-Conference] + +\installpagearrangement 1*4-Conference + {\dosetuparrangement{1}{2}{4}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageCONFERENCE4\poparrangedpagesAB\relax} + +\def\pusharrangedpageCONFERENCE4#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}100\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}011\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}110\arrangedpageB % 4 + \poparrangedpages + \fi} + +% There should be arrangements for section made of heavy and thick paper. i.e. the heavier the paper +% the fewer pages per section: +% Section with 8 pages put on to sheets of paper. Each sheet carries recto 2 and verso 2 pages. +% Coding: [2*2*2] + +\installpagearrangement 2*2*2 + {\dosetuparrangement{2}{1}{2}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageEIGHTTWO\poparrangedpagesAtoD\relax} + +\def\pusharrangedpageEIGHTWO#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageC % 3 + \or \handlearrangedpageXandY{#1}000\arrangedpageD % 4 + \or \handlearrangedpageXandY{#1}010\arrangedpageD % 5 + \or \handlearrangedpageXandY{#1}000\arrangedpageC % 6 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \poparrangedpages + \fi} + +% Section with 12 pages, built from three sheets of paper. +% Each sheet carries 2 pages recto and verso. +% Coding: [2*2*3] + +\def\poparrangedpagesAtoF + {\ifnum\arrangedpageN>\zerocount + \paperwidth \arrangedpageX\paperwidth + \paperheight\arrangedpageY\paperheight + \outputarrangedbox\arrangedpageA + \outputarrangedbox\arrangedpageB + \outputarrangedbox\arrangedpageC + \outputarrangedbox\arrangedpageD + \outputarrangedbox\arrangedpageE + \outputarrangedbox\arrangedpageF + \global\arrangedpageN\zerocount + \fi} +\installpagearrangement 2*2*3 + {\dosetuparrangement{2}{1}{2}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageTWELVETWO\poparrangedpagesAtoD\relax} + +\def\pusharrangedpageTWELVETWO#1% + {\doglobal\increment\arrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageC % 3 + \or \handlearrangedpageXandY{#1}000\arrangedpageD % 4 + \or \handlearrangedpageXandY{#1}010\arrangedpageE % 5 + \or \handlearrangedpageXandY{#1}000\arrangedpageF % 6 + \or \handlearrangedpageXandY{#1}010\arrangedpageF % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageE % 8 + \or \handlearrangedpageXandY{#1}010\arrangedpageD % 9 + \or \handlearrangedpageXandY{#1}000\arrangedpageC % 10 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 11 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 12 + \poparrangedpages + \fi} % \definepageshift[test][horizontal][10pt,20pt,30pt,40pt,50pt] % \definepageshift[test][vertical] [10pt,20pt,30pt,40pt,50pt] diff --git a/tex/context/base/page-imp.mkiv b/tex/context/base/page-imp.mkiv index cc48b9359..e01202c81 100644 --- a/tex/context/base/page-imp.mkiv +++ b/tex/context/base/page-imp.mkiv @@ -3,7 +3,7 @@ %D version=1998.01.15, %D title=\CONTEXT\ Page Macros, %D subtitle=Pagebody Building (Imposition), -%D author=Hans Hagen, +%D author=Hans Hagen & Willi Egger, %D date=\currentdate, %D copyright={PRAGMA / Hans Hagen \& Ton Otten}] %C @@ -738,6 +738,365 @@ \or \handlearrangedpageXandY{#1}001\arrangedpageA % 16 \poparrangedpages \fi} + +%D Might be used if a printer is printing from a rol or creating mini-books from A4: +%D This section has 16 pages. The folding scheme is first a Z-fold and at the end +%D a final fold in the spine. +%D Coding: [2*8*Z] + +\installpagearrangement 2*8*Z + {\dosetuparrangement{2}{4}{8}{3}{5}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageSIXTEENZ\poparrangedpagesAB\relax} + + +\def\pusharrangedpageSIXTEENZ#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}101\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}111\arrangedpageA % 4 + \or \handlearrangedpageXandY{#1}012\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}002\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}103\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}113\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}103\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}113\arrangedpageB % 10 + \or \handlearrangedpageXandY{#1}012\arrangedpageB % 11 + \or \handlearrangedpageXandY{#1}002\arrangedpageA % 12 + \or \handlearrangedpageXandY{#1}101\arrangedpageA % 13 + \or \handlearrangedpageXandY{#1}111\arrangedpageB % 14 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 15 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 16 + \poparrangedpages + \fi} + +%D Another Z-folded section with 12 pages +%D Coding: [2*6*Z] + +\installpagearrangement 2*6*Z + {\dosetuparrangement{2}{3}{6}{3}{4}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageTWELVEZ\poparrangedpagesAB\relax} + +\def\pusharrangedpageTWELVEZ#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1: rotation (0=upright),x (0=first column),y (0=first row) + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}101\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}111\arrangedpageA % 4 + \or \handlearrangedpageXandY{#1}012\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}002\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}012\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}002\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}101\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}111\arrangedpageB % 10 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 11 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 12 + \poparrangedpages + \fi} + +%D For Heinz' special greeting cards folding. This scheme is also used for the PocketDiary (module): +%D Coding: [1*8] + +\installpagearrangement 1*8 + {\dosetuparrangement{4}{2}{8}{5}{3}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageEIGHTSINGLESIDEDFOLDED\poparrangedpagesTWO\relax} + +\def\pusharrangedpageEIGHTSINGLESIDEDFOLDED#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 3 + \or \handlearrangedpageXandY{#1}131\arrangedpageA % 4 + \or \handlearrangedpageXandY{#1}121\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}111\arrangedpageA % 6 + \or \handlearrangedpageXandY{#1}101\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \poparrangedpages + \fi} + +%D This is not a section. \CONTEXT\ places 4 pages on a sheet of paper, singlesided +%D Coding: [1*4] + +\installpagearrangement 1*4 + {\dosetuparrangement{2}{2}{4}{3}{3}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageFOURSINGLESIDEDFOLDED\poparrangedpagesTWO\relax} + +\def\pusharrangedpageFOURSINGLESIDEDFOLDED#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}100\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}011\arrangedpageA % 3 + \or \handlearrangedpageXandY{#1}110\arrangedpageA % 4 + \poparrangedpages + \fi} + +%D This imposition scheme was requested by Hraban Ramm, by Willi Egger 21-07-2003 +%D Coding: [3SIDE] + +\installpagearrangement 3SIDE + {\dosetuparrangement{3}{1}{3}{4}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageTHREESIDE\poparrangedpagesAB\relax} + +\def\pusharrangedpageTHREESIDE#1% Willi's approach + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 3 + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 6 + \poparrangedpages + \fi} + +%D FLYER in three parts and 6 pages 22-10-2010 +%D Coding: [TRYPTICHON] + +\installpagearrangement TRYPTICHON + {\dosetuparrangement{3}{1}{3}{4}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageFLYERSIX\poparrangedpagesAB\relax} + +\def\pusharrangedpageFLYERSIX#1% Willi's approach + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 5 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 6 + \poparrangedpages + \fi} + +%D FLYER in Z-fold with 8 pages 22-01-2010 +%D Coding: [ZFLYER-8] + +\installpagearrangement ZFLYER-8 + {\dosetuparrangement{4}{1}{4}{5}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageZFLYEREIGHT\poparrangedpagesAB\relax} + +\def\pusharrangedpageZFLYEREIGHT#1% Willi's approach + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 6 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 8 + \poparrangedpages + \fi} + +%D FLYER in Z-fold with 10 pages 04-08-2010 +%D Coding: [ZFLYER-10] + +\installpagearrangement ZFLYER-10 + {\dosetuparrangement{5}{1}{5}{6}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageZFLYERTEN\poparrangedpagesAB\relax} + +\def\pusharrangedpageZFLYERTEN#1% Willi's approach + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}040\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}040\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 10 + \poparrangedpages + \fi} + +%D FLYER in Z-fold with 12 pages 04-08-2010 +%D Coding: [ZFLYER-12] + +\installpagearrangement ZFLYER-12 + {\dosetuparrangement{6}{1}{6}{7}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageZFLYERTWELVE\poparrangedpagesAB\relax} + +\def\pusharrangedpageZFLYERTWELVE#1% Willi's approach + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}050\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}040\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}050\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 10 + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 11 + \or \handlearrangedpageXandY{#1}040\arrangedpageA % 12 + \poparrangedpages + \fi} + +%D FLYER folded as a map with 6 pages per side. +%D Coding: [MAPFLYER-12] + +\installpagearrangement MAPFLYER-12 + {\dosetuparrangement{3}{2}{6}{4}{3}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageMFLYERTWELVE\poparrangedpagesAB\relax} + +\def\pusharrangedpageMFLYERTWELVE#1% Willi's approach + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}001\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}011\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}021\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 9 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 10 + \or \handlearrangedpageXandY{#1}011\arrangedpageA % 11 + \or \handlearrangedpageXandY{#1}021\arrangedpageA % 12 + \poparrangedpages + \fi} + +%D FLYER folded as double window with 4 pages per side. +%D Coding: [DOUBLEWINDOW] + +\installpagearrangement DOUBLEWINDOW + {\dosetuparrangement{4}{1}{4}{5}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageDOUBLEWINDOWEIGHT\poparrangedpagesAB\relax} + +\def\pusharrangedpageDOUBLEWINDOWEIGHT#1% Willi's approach + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}020\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}030\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 4 + \or \handlearrangedpageXandY{#1}020\arrangedpageB % 5 + \or \handlearrangedpageXandY{#1}030\arrangedpageB % 6 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 7 + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 8 + \poparrangedpages + \fi} + +%D Imposition as requested by Jan Pohanka 26-08-2010, 4 pages, two verso, two recto, +%D uneven pages upright and down, even pages top and rotated 180. +%D Implementation with 2 pages for conference-name-display +%D Coding: [1*2-Conference] + +\installpagearrangement 1*2-Conference + {\dosetuparrangement{1}{2}{4}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageCONFERENCE2\poparrangedpagesAB\relax} + +\def\pusharrangedpageCONFERENCE2#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}100\arrangedpageA % 2 + \poparrangedpages + \fi} + +%D Implementation with 4 pages for conference-name-display +%D Coding: [1*4-Conference] + +\installpagearrangement 1*4-Conference + {\dosetuparrangement{1}{2}{4}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageCONFERENCE4\poparrangedpagesAB\relax} + +\def\pusharrangedpageCONFERENCE4#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}001\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}100\arrangedpageA % 2 + \or \handlearrangedpageXandY{#1}011\arrangedpageB % 3 + \or \handlearrangedpageXandY{#1}110\arrangedpageB % 4 + \poparrangedpages + \fi} + +%D There should be arrangements for sections made of heavy and thick paper. i.e. the heavier the paper +%D the fewer pages per section: +%D Section with 8 pages put on to sheets of paper. Each sheet carries recto 2 and verso 2 pages. +%D Coding: [2*2*2] + +\installpagearrangement 2*2*2 + {\dosetuparrangement{2}{1}{2}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageEIGHTTWO\poparrangedpagesAtoD\relax} + +\def\pusharrangedpageEIGHTTWO#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageC % 3 + \or \handlearrangedpageXandY{#1}000\arrangedpageD % 4 + \or \handlearrangedpageXandY{#1}010\arrangedpageD % 5 + \or \handlearrangedpageXandY{#1}000\arrangedpageC % 6 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 8 + \poparrangedpages + \fi} + +%D Section with 12 pages, built from three sheets of paper. +%D Each sheet carries 2 pages recto and verso. +%D Coding: [2*2*3] + +\def\poparrangedpagesAtoF + {\ifnum\arrangedpageN>\zerocount + \paperwidth \arrangedpageX\paperwidth + \paperheight\arrangedpageY\paperheight + \outputarrangedbox\arrangedpageA + \outputarrangedbox\arrangedpageB + \outputarrangedbox\arrangedpageC + \outputarrangedbox\arrangedpageD + \outputarrangedbox\arrangedpageE + \outputarrangedbox\arrangedpageF + \global\arrangedpageN\zerocount + \fi} + +\installpagearrangement 2*2*3 + {\dosetuparrangement{2}{1}{2}{3}{2}% X,Y,Total,hcutmarks,vcutmarks + \pusharrangedpageTWELVETWO\poparrangedpagesAtoF\relax} + +\def\pusharrangedpageTWELVETWO#1% + {\advancearrangedpageN + \reportarrangedpage\arrangedpageN + \ifcase\arrangedpageN + \or \handlearrangedpageXandY{#1}010\arrangedpageA % 1 rot,hskip,vskip + \or \handlearrangedpageXandY{#1}000\arrangedpageB % 2 + \or \handlearrangedpageXandY{#1}010\arrangedpageC % 3 + \or \handlearrangedpageXandY{#1}000\arrangedpageD % 4 + \or \handlearrangedpageXandY{#1}010\arrangedpageE % 5 + \or \handlearrangedpageXandY{#1}000\arrangedpageF % 6 + \or \handlearrangedpageXandY{#1}010\arrangedpageF % 7 + \or \handlearrangedpageXandY{#1}000\arrangedpageE % 8 + \or \handlearrangedpageXandY{#1}010\arrangedpageD % 9 + \or \handlearrangedpageXandY{#1}000\arrangedpageC % 10 + \or \handlearrangedpageXandY{#1}010\arrangedpageB % 11 + \or \handlearrangedpageXandY{#1}000\arrangedpageA % 12 + \poparrangedpages + \fi} % % handy for stickers etc, this way we can treat them as page % @@ -861,8 +1220,8 @@ \def\dosetuppageshift[#1][#2][#3]% page|paper horizontal vertical {\ifthirdargument % paper=arrange - \edef\hpageshifts{\ifcsname\??pt\v!horizontal:#2\endcsname\csname\??pt\v!horizontal:#2\endcsname}% - \edef\vpageshifts{\ifcsname\??pt\v!vertical :#3\endcsname\csname\??pt\v!vertical :#3\endcsname}% + \edef\hpageshifts{\ifcsname\??pt\v!horizontal:#2\endcsname\csname\??pt\v!horizontal:#2\endcsname\fi}% + \edef\vpageshifts{\ifcsname\??pt\v!vertical :#3\endcsname\csname\??pt\v!vertical :#3\endcsname\fi}% \doifelse{#1}\v!page {\let\shiftprintpagebox\shiftpagebox}{\let\shiftprintpagebox\gobbleoneargument}% \doifelse{#1}\v!paper{\let\shiftpaperpagebox\shiftpagebox}{\let\shiftpaperpagebox\gobbleoneargument}% \else\ifsecondargument diff --git a/tex/context/base/page-lay.mkiv b/tex/context/base/page-lay.mkiv index 6cc8afc9c..a8150cc51 100644 --- a/tex/context/base/page-lay.mkiv +++ b/tex/context/base/page-lay.mkiv @@ -13,13 +13,6 @@ \writestatus{loading}{ConTeXt Page Macros / Layout Specification} -%D This module is now etex dependent. - -% to be translated into english - -% hoofdhoogte wordt bij status=hoog niet aangepast op outer -% level, wel binnen bepaalde berekeningen - %D Before you start wondering why some of the page related %D modules skip upward or left in order to place elements, you %D must realize that the reference point is the top left @@ -323,7 +316,7 @@ {\doifelsenothing{#2} {\expanded{\dodosetuppapersize [\executeifdefined{\??pp:1:#1}{#1}]% - [\executeifdefined{\??pp:2:#1}{}]}} + [\executeifdefined{\??pp:2:#1}{\v!default}]}} {\doifassignmentelse{#2} {\getparameters[\??pp\executeifdefined{\??pp:1:#1}{#1}][#2]} {\expanded{\dodosetuppapersize @@ -1231,6 +1224,24 @@ %D since they could change while going to a new page, %D depending on the current font setting. +\setuppaper % (size) % only used in XY imposition + [\c!width=\zeropoint, + \c!height=\zeropoint, + \c!topspace=\zeropoint, + \c!backspace=\zeropoint, + \c!dx=\zeropoint, + \c!dy=\zeropoint, + \c!nx=1, + \c!ny=1, + \c!method=\v!normal] + +\setuppapersize + [\c!option=\v!max, + \c!top=, + \c!bottom=\vss, + \c!left=, + \c!right=\hss] + \setuplayout [ \c!topspace=.08417508418\paperheight, % 2.5cm \c!top=\zeropoint, @@ -1266,7 +1277,7 @@ \c!style=, \c!color=, \c!marking=\v!off, - \c!location=, % \v!singlesided, but empty is signal + \c!location=\v!middle, % \v!singlesided, but unset is signal \c!scale=1, \c!sx=1, \c!sy=1, @@ -1287,27 +1298,6 @@ %D First we define a whole range of (DIN) papersizes, %D of which the A-series makes most sense. We enable checking. -%D We also set some of the parameters that will be used when -%D positioning the typeset paper onto the print paper. - -\setuppaper % (size) % only used in XY imposition - [\c!width=\zeropoint, - \c!height=\zeropoint, - \c!topspace=\zeropoint, - \c!backspace=\zeropoint, - \c!dx=\zeropoint, - \c!dy=\zeropoint, - \c!nx=1, - \c!ny=1, - \c!method=\v!normal] - -\setuppapersize - [\c!option=\v!max, - \c!top=, - \c!bottom=\vss, - \c!left=, - \c!right=\hss] - \definepapersize [A0] [\c!width=841mm,\c!height=1189mm] \definepapersize [A1] [\c!width=594mm,\c!height=841mm] \definepapersize [A2] [\c!width=420mm,\c!height=594mm] @@ -1435,6 +1425,11 @@ %D paper size with the typeset paper size. This setting should %D come after the first layout specification (already done). +\definepapersize + [\v!default] + [ \c!width=\paperwidth, + \c!height=\paperheight] + \definepapersize [samesized] [ \c!width=\paperwidth, diff --git a/tex/context/base/page-run.mkiv b/tex/context/base/page-run.mkiv index 1c7595b03..0ea18dfbf 100644 --- a/tex/context/base/page-run.mkiv +++ b/tex/context/base/page-run.mkiv @@ -183,7 +183,9 @@ end [#1] \else \showframe - [\v!header,\v!text,\v!footer] + [\v!top,\v!header, + \v!text, + \v!footer,\v!bottom] [\v!leftedge,\v!leftmargin, \v!text, \v!rightmargin,\v!rightedge] diff --git a/tex/context/base/page-set.mkiv b/tex/context/base/page-set.mkiv index 8b689b284..8c3566bc0 100644 --- a/tex/context/base/page-set.mkiv +++ b/tex/context/base/page-set.mkiv @@ -2412,7 +2412,7 @@ \def\dodefinecolumnsetspan[#1][#2]% {%\ifsecondargument - \defineframedtext + \defineframedtext % we can have a parent [cs:#1] [\c!frame=\v!off, \c!before=, @@ -2466,7 +2466,7 @@ \c!depthcorrection=\v!off, #2]% % determine widths - \!!countc\framedtextparameter{cs:#1}\c!n + \!!countc\namedframedtextparameter{cs:#1}\c!n % \!!countd\numexpr(\nofcolumns-\mofcolumns+\plusone)% \!!countd\nofcolumns % n <= n of columns @@ -2474,7 +2474,7 @@ \advance\!!countd -\mofcolumns \advance\!!countd \plusone % n <= n of available columns (alternative a) - \doif{\framedtextparameter{cs:#1}\c!alternative}\v!a + \doif{\namedframedtextparameter{cs:#1}\c!alternative}\v!a {\ifnum\!!countc>\!!countd \!!countc\!!countd \fi}% % here it all starts \setcolumnsetspanhsize\mofcolumns\!!countc % a/b used @@ -2483,7 +2483,7 @@ \dostartframedtext[cs:#1][\v!none]% geen nils placement % spoils spacing : \vskip-\struttotal\par\verticalstrut\par \ifnum\columnsetlevel>\zerocount - \framedtextparameter{cs:#1}\c!before + \namedframedtextparameter{cs:#1}\c!before \fi \unexpanded\def\stopcolumnsetspan{\dostopcolumnsetspan{#1}}} @@ -2493,8 +2493,8 @@ \kern-2\struttotal \verticalstrut \ifnum\columnsetlevel>\zerocount - \doifsomething{\framedtextparameter{cs:#1}\c!after} - {\framedtextparameter{cs:#1}\c!after + \doifsomething{\namedframedtextparameter{cs:#1}\c!after} + {\namedframedtextparameter{cs:#1}\c!after \kern\zeropoint}% otherwise blanks disappear, better be a switch \else \endgraf @@ -2503,8 +2503,7 @@ \egroup \setbox\scratchbox\frozenhbox to \hsize {\dontcomplain - \alignedline{\framedtextparameter{cs:#1}\c!location}\v!middle - {\lower\strutdepth\box\scratchbox}}% + \alignedline{\namedframedtextparameter{cs:#1}\c!location}\v!middle{\lower\strutdepth\box\scratchbox}}% \dp\scratchbox\zeropoint % else wrong snap insidefloat % % to be tested first (strange in grid mode) @@ -2512,7 +2511,7 @@ % \setbox\scratchbox\frozenhbox to \hsize % {\dontcomplain % \alignstrutmode\zerocount -% \alignedline{\framedtextparameter{cs:#1}\c!plaats}\v!midden +% \alignedline{\namedframedtextparameter{cs:#1}\c!plaats}\v!midden % {\box\scratchbox}}% % \ifinsidefloat @@ -2520,24 +2519,24 @@ \else\ifnum\columnsetlevel>\zerocount % we only set \columnsetspacing when asked for, else bottom problems % don't change this any more (test naw) - \columnslotspacing\framedtextparameter{cs:#1}\c!nlines\relax + \columnslotspacing\namedframedtextparameter{cs:#1}\c!nlines\relax % todo: nboven/onder %\OTRSETstoreincolumnslotHERE\scratchbox - \edef\floatmethod{\framedtextparameter{cs:#1}\c!default}% + \edef\floatmethod{\namedframedtextparameter{cs:#1}\c!default}% \@EA\uppercasestring\floatmethod\to\floatmethod % todo : \v!here -> here enzovoorts \OTRSETstoreincolumnslot\floatmethod\scratchbox % watch out: no \dochecknextindentation{tag} - \checknextindentation[\framedtextparameter{cs:#1}\c!indentnext]% + \checknextindentation[\namedframedtextparameter{cs:#1}\c!indentnext]% \else % of course we needed a one-column fall back for tm; brrr, the box has now too % much height (try \ruledvbox); don't change this without testing techniek \scratchdimen\ht\scratchbox \advance\scratchdimen-\strutdp \ht\scratchbox\scratchdimen - \framedtextparameter{cs:#1}\c!before + \namedframedtextparameter{cs:#1}\c!before \snaptogrid\vbox{\box\scratchbox}% - \framedtextparameter{cs:#1}\c!after + \namedframedtextparameter{cs:#1}\c!after \fi\fi \egroup \endgraf} diff --git a/tex/context/base/ppchtex.mkiv b/tex/context/base/ppchtex.mkiv index e6c1f2495..1162c1fcf 100644 --- a/tex/context/base/ppchtex.mkiv +++ b/tex/context/base/ppchtex.mkiv @@ -285,22 +285,10 @@ % regels iets verder uit elkaar gezet. Jammer. Italic fonts % hebben grotere cijfers en vallen min of meer uit de boot. -\newif\ifloweredsubscripts - -% Due to some upward incompatibality of LaTeX to LaTeX2.09 -% and/or LaTeX2e we had to force \@@dochemicalstyle. Otherwise -% some weird \nullfont error comes up. - -\def\beginlatexmathmodehack - {\ifmmode - \let\endlatexmathmodehack=\relax - \else - \def\endlatexmathmodehack{$}$\@@dochemicalstyle\empty - \fi} +\newif\ifloweredsubscripts % this will be redone in the mkiv ways \def\setsubscripts - {\beginlatexmathmodehack - \def\dosetsubscript##1##2##3% + {\def\dosetsubscript##1##2##3% {\dimen0=##3\fontexheight##2% \setxvalue{@@\string##1\string##2}{\the##1##2\relax}% ##1##2=\dimen0\relax}% @@ -311,12 +299,10 @@ %dodosetsubscript\mathsupnormal {?}% \dodosetsubscript\mathsubnormal {.7}% \dodosetsubscript\mathsubcombined{.7}% - \global\loweredsubscriptstrue - \endlatexmathmodehack} + \global\loweredsubscriptstrue} \def\resetsubscripts {\ifloweredsubscripts - \beginlatexmathmodehack \def\doresetsubscript##1##2% {\dimen0=\getvalue{@@\string##1\string##2}\relax ##1##2=\dimen0}% @@ -328,7 +314,6 @@ \dodoresetsubscript\mathsubnormal \dodoresetsubscript\mathsubcombined \global\loweredsubscriptsfalse - \endlatexmathmodehack \fi} \ifx\Umathchar\undefined \else @@ -392,11 +377,8 @@ \def\dowithchemical% {} -\doifdefinedelse{@@iastate} - {\def\localgotochemical#1#2{\naarbox{#2}[#1]}% - \def\localthisischemical#1{\pagereference[#1]}} - {\def\localgotochemical#1{}% - \def\localthisischemical#1{}} +\def\localgotochemical#1#2{\gotobox{#2}[#1]} +\def\localthisischemical#1{\pagereference[#1]} % eind van experiment diff --git a/tex/context/base/s-fnt-10.mkiv b/tex/context/base/s-fnt-10.mkiv index a8ef90c5e..c76574d6d 100644 --- a/tex/context/base/s-fnt-10.mkiv +++ b/tex/context/base/s-fnt-10.mkiv @@ -14,31 +14,25 @@ \startluacode local format, sprint = string.format, tex.sprint -function fonts.otf.show_all() - local tfmdata = fonts.identifiers[font.current()] +local fontdata = fonts.hashes.identifiers + +function fonts.handlers.otf.show_all() + local tfmdata = fontdata[font.current()] if tfmdata and tfmdata.shared then local NC, NR, char = context.NC, context.NR, context.char - local otfdata = tfmdata.shared.otfdata - if otfdata and otfdata.luatex then - local unicodes = otfdata.luatex.unicodes - context.starttabulate { "|l|r|c|" } - for i, name in ipairs(table.sortedkeys(unicodes)) do - local unicode = unicodes[name] - if unicode >= 0 then - NC() context(name) NC() context(unicode) NC() char(unicode) NC() NR() - end - end - context.stoptabulate() + context.starttabulate { "|l|r|c|" } + for unicode, description in fonts.iterators.characters(tfmdata) do + NC() context(description.name) NC() context(unicode) NC() char(unicode) NC() NR() end + context.stoptabulate() end end function fonts.show_all() - local tfmdata = fonts.identifiers[font.current()] + local tfmdata = fontdata[font.current()] if tfmdata then local NC, NR, HL, char, bold, tttf = context.NC, context.NR, context.HL, context.char, context.bold, context.tttf - local chars = tfmdata.characters - local descs = tfmdata.descriptions or { } + local descriptions = tfmdata.descriptions or { } local data = characters.data -- context.setuptabulate { header = "repeat" } context.starttabulatehead() @@ -51,47 +45,44 @@ function fonts.show_all() NC() NR() context.stoptabulatehead() context.starttabulate { "|l|c|l|p|p|p|" } - for k, unicode in ipairs(table.sortedkeys(chars)) do --- for unicode, _ in table.sortedpairs(chars) do - if unicode >= 0 then - local chr, des, dat = chars[unicode], descs[unicode], data[unicode] - local index = chr.index or 0 - local cname = (dat and dat.contextname) or "" - local aname = (dat and dat.adobename) or "" - local gname = (des and des.name) or "" - local mname = dat and dat.mathname - if type(mname) ~= "string" then - mname = "" - end - local mspec = dat and dat.mathspec - if mspec then - for m=1,#mspec do - local n = mspec[m].name - if n then - if mname == "" then - mname = n - else - mname = mname .. " " .. n - end + for unicode, chr in fonts.iterators.characters(tfmdata) do + local des, dat = descriptions[unicode], data[unicode] + local index = chr.index or 0 + local cname = (dat and dat.contextname) or "" + local aname = (dat and dat.adobename) or "" + local gname = (des and des.name) or "" + local mname = dat and dat.mathname + if type(mname) ~= "string" then + mname = "" + end + local mspec = dat and dat.mathspec + if mspec then + for m=1,#mspec do + local n = mspec[m].name + if n then + if mname == "" then + mname = n + else + mname = mname .. " " .. n end end end - if mname ~= "" then - mname = "m: " .. mname - if cname ~= "" then - cname = cname .. " " .. mname - else - cname = mname - end + end + if mname ~= "" then + mname = "m: " .. mname + if cname ~= "" then + cname = cname .. " " .. mname + else + cname = mname end - NC() tttf() context("U+%05X",unicode) - NC() char(unicode) - NC() tttf() context("%05X",index) - NC() tttf() context(gname) - NC() tttf() context(aname) - NC() tttf() context(cname) - NC() NR() end + NC() tttf() context("U+%05X",unicode) + NC() char(unicode) + NC() tttf() context("%05X",index) + NC() tttf() context(gname) + NC() tttf() context(aname) + NC() tttf() context(cname) + NC() NR() end context.stoptabulate() else @@ -100,7 +91,7 @@ function fonts.show_all() end function fonts.show_glyphs() - local tfmdata = fonts.identifiers[font.current()] + local tfmdata = fontdata[font.current()] if tfmdata then for k, v in ipairs(table.sortedkeys(tfmdata.characters)) do if v >=0 then @@ -148,11 +139,11 @@ end \page \egroup} -\doifnotmode{demo} {\endinput} +% \doifnotmode{demo} {\endinput} \starttext -% \ShowCompleteFont{name:dejavusansmono}{10pt}{1} +\ShowCompleteFont{name:dejavusansmono}{10pt}{1} % \ShowCompleteFont{name:dejavuserif}{10pt}{2} % \ShowCompleteFont{name:officinasansbookitcregular}{10pt}{2} % \ShowCompleteFont{name:officinaserifbookitcregular}{10pt}{2} @@ -171,6 +162,6 @@ end % \ShowCompleteFont{name:palatinosansinformalcombold}{20pt}{2} % \ShowCompleteFont{name:palatinonovaregular}{11pt}{2} -\ShowCompleteFont{pirat.ttf}{12pt}{1} +% \ShowCompleteFont{pirat.ttf}{12pt}{1} \stoptext diff --git a/tex/context/base/s-fnt-23.mkiv b/tex/context/base/s-fnt-23.mkiv index 35973d27e..8714d9b50 100644 --- a/tex/context/base/s-fnt-23.mkiv +++ b/tex/context/base/s-fnt-23.mkiv @@ -14,10 +14,15 @@ % last_data was written wrong so it needs checking \startluacode + local fontdata = fonts.hashes.identifiers + local otfhandler = fonts.handlers.otf --- will be moduledata + local last_data = nil + local format = string.format - function fonts.otf.show_shape(n) - local tfmdata = fonts.identifiers[font.current()] + + function otfhandler.show_shape(n) + local tfmdata = fontdata[font.current()] last_data = tfmdata local charnum = tonumber(n) if not charnum then @@ -25,8 +30,9 @@ end local c = tfmdata.characters[charnum] local d = tfmdata.descriptions[charnum] + local parameters = tfmdata.parameters if d then - local factor = (tfmdata.size/tfmdata.units)*((7200/7227)/65536) + 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 @@ -193,22 +199,21 @@ context("no such shape: %s",n) end end - function fonts.otf.show_all_shapes(start,stop) - local tfmdata = fonts.identifiers[font.current()] + function otfhandler.show_all_shapes(start,stop) + local tfmdata = fontdata[font.current()] last_data = tfmdata start, stop = start or "\\startTEXpage\\gobbleoneargument", stop or "\\stopTEXpage" - local unicodes, indices, descriptions = tfmdata.unicodes, tfmdata.indices, tfmdata.descriptions - for _, unicode in next, table.sortedkeys(descriptions) do - local d = descriptions[unicode] - local name = d.name + local unicodes, descriptions = tfmdata.unicodes, tfmdata.descriptions + for unicode, description in fonts.iterators.descriptions(tfmdata) do + local name = description.name context("%s{%s}%%",start,unicode) context("\\writestatus{glyph}{U+%04X -> %s}%%",unicode,name) - fonts.otf.show_shape(unicode) + otfhandler.show_shape(unicode) context(stop) end end - function fonts.otf.show_shape_field(unicode,name) - local tfmdata = last_data or fonts.identifiers[font.current()] + function otfhandler.show_shape_field(unicode,name) + local tfmdata = last_data or fontdata[font.current()] local d = tfmdata.descriptions[unicode] if d then if name == "unicode" then @@ -227,7 +232,7 @@ [state=start] \def\GetGlyphField#1#2% - {\ctxlua{fonts.otf.show_shape_field(#1,"#2")}} + {\ctxlua{fonts.handlers.otf.show_shape_field(#1,"#2")}} \def\StartShowGlyphShape#1% {\startTEXpage @@ -243,14 +248,14 @@ {\begingroup \definedfont[#1 at #2]% \obeyMPboxdepth - \ctxlua{fonts.otf.show_shape("#3")}% + \ctxlua{fonts.handlers.otf.show_shape("#3")}% \endgroup} \def\ShowAllGlyphShapes#1#2% name size {\begingroup \nonknuthmode \definedfont[#1 at #2]% - \ctxlua{fonts.otf.show_all_shapes("\\StartShowGlyphShape","\\StopShowGlyphShape")}% + \ctxlua{fonts.handlers.otf.show_all_shapes("\\StartShowGlyphShape","\\StopShowGlyphShape")}% \endgroup} \setupcolors diff --git a/tex/context/base/s-fnt-25.mkiv b/tex/context/base/s-fnt-25.mkiv index fc78ddfda..36d28bfeb 100644 --- a/tex/context/base/s-fnt-25.mkiv +++ b/tex/context/base/s-fnt-25.mkiv @@ -12,7 +12,7 @@ %C details. \def\enableshowmathfontvirtual - {\ctxlua{fonts.tfm.auto_cleanup=false}} + {\ctxlua{fonts.constructors.autocleanup=false}} \def\showmathfontcharacters {\dodoubleempty\doshowmathfontcharacters} @@ -78,16 +78,19 @@ local concat = table.concat local format, lower = string.format, string.lower +local fontdata = fonts.hashes.identifiers + function document.showmathfont(id,slot) local data = characters.data - local tfmdata = fonts.identifiers[id] + local tfmdata = fontdata[id] local characters = tfmdata.characters local sorted = (slot and { slot }) or table.sortedkeys(characters) - local virtual, names = tfmdata.type == "virtual", { } + local virtual, names = tfmdata.properties.type == "virtual", { } if virtual then for k, v in ipairs(tfmdata.fonts) do - local name = fonts.identifiers[v.id].name - names[k] = (name and file.basename(name)) or v.id + local id = v.properties.id + local name = fontdata[id].properties.name + names[k] = (name and file.basename(name)) or id end end local round = math.round diff --git a/tex/context/base/s-fnt-29.mkiv b/tex/context/base/s-fnt-29.mkiv index 86f40e2f8..0b63635b2 100644 --- a/tex/context/base/s-fnt-29.mkiv +++ b/tex/context/base/s-fnt-29.mkiv @@ -13,11 +13,13 @@ \startluacode + local fontdata = fonts.hashes.identifiers + function fonts.tracers.shapes() -- todo: ranges local NC, NR = context.NC, context.NR local char = context.char - local chrs = fonts.identifiers[font.current()].characters - -- local desc = fonts.identifiers[font.current()].descriptions + local chrs = fontdata[font.current()].characters + -- local desc = fontdata[font.current()].descriptions context.starttabulate { "|l|c|c|c|c|l|" } context.FL() NC() context("unicode") diff --git a/tex/context/base/scrn-bar.mkiv b/tex/context/base/scrn-bar.mkiv deleted file mode 100644 index 75261b42e..000000000 --- a/tex/context/base/scrn-bar.mkiv +++ /dev/null @@ -1,400 +0,0 @@ -%D \module -%D [ file=scrn-bar, % was part of scrn-int -%D version=1995.01.01, -%D title=\CONTEXT\ Core Macros, -%D subtitle=Progress Bars, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{ConTeXt Screen Macros / Progress Bars} - -\unprotect - -%D The code is a bit upgraded to \MKIV\ but the output is mostly the same. -%D In retrospect this shoul dhave been a module. - -% todo: replace blackrule by stupid rules - -% \setupinteraction[state=start] -% \setupsubpagenumber[state=start] -% -% \startsetups bars -% \vbox -% {\hsize 5cm -% \hbox{\interactionbar[a]}\blank -% \hbox{\interactionbar[b]}\blank -% \hbox{\interactionbar[c]}\blank -% \hbox{\interactionbar[d]}\blank -% \hbox{\interactionbar[e]}\blank -% \hbox{\interactionbar[f]}\blank -% \hbox{\interactionbar[g]}\blank -% } -% \stopsetups -% -% \setupheadertexts[\setups{bars}] -% -% \starttext -% \dorecurse{10}{test \page } -% \stoptext - -\presetlocalframed[\??ib] - -%D First the usual definition code. - -\let\currentinteractionbar\empty - -\def\setinteractionbarparameter#1#2#3{\@EA\def\csname\??ib#1#2\endcsname{#3}} -\def\letinteractionbarparameter #1#2{\@EA\let\csname\??ib#1#2\endcsname} - -\def\interactionbarparameter #1{\csname\dointeractionbarparameter{\??ib\currentinteractionbar}#1\endcsname} -\def\namedinteractionbarparameter#1#2{\csname\dointeractionbarparameter{\??ib#1}#2\endcsname} -\def\interactionbarparameterhash #1{\dointeractionbarparameterhash {\??ib\currentinteractionbar}#1} - -\def\dointeractionbarparameter #1#2{\ifcsname#1#2\endcsname#1#2\else\expandafter\dointeractionbarparentparameter \csname#1\s!parent\endcsname#2\fi} -\def\dointeractionbarparameterhash#1#2{\ifcsname#1#2\endcsname #1\else\expandafter\dointeractionbarparentparameterhash\csname#1\s!parent\endcsname#2\fi} - -\def\dointeractionbarparentparameter #1#2{\ifx#1\relax\s!empty\else\dointeractionbarparameter #1#2\fi} -\def\dointeractionbarparentparameterhash#1#2{\ifx#1\relax \else\dointeractionbarparameterhash#1#2\fi} - -\unexpanded\def\defineinteractionbar{\dodoubleargument\dodefineinteractionbar} -\unexpanded\def\setupinteractionbar {\dodoubleempty \dosetupinteractionbar} -\def\interactionbar {\dodoubleempty \dointeractionbar} - -\def\dosetupinteractionbar[#1][#2]% - {\ifsecondargument - \getparameters[\??ib#1][#2]% - \else - \getparameters[\??ib][#1]% - \fi} - -\def\dodefineinteractionbar[#1][#2]% - {\getparameters - [\??ib#1]% - [\s!parent=\??ib,% -% \c!foregroundcolor=\interactionbarparameter\c!color,% -% \c!foregroundstyle=\interactionbarparameter\c!style,% - #2]} - -\def\dointeractionbar[#1][#2]% - {\iflocation - \begingroup - \doifnot{#1}\v!reset % obsolete, no caching any more - {\doifassignmentelse{#1} - {\getparameters[\??ib][#1]% - \edef\currentinteractionbar{\interactionbarparameter\c!alternative}}% - {\edef\currentinteractionbar{#1}% - \ifsecondargument\getparameters[\??ib#1][#2]\fi}% - \doif{\interactionbarparameter\c!state}\v!start - {\interactionbarparameter\c!command}}% - \endgroup - \fi} - -\newdimen\interactionbarwidth -\newdimen\interactionbarheight -\newdimen\interactionbardepth -\newdimen\interactionbardistance - -%D Interaction buttons, in fact a row of tiny buttons, are -%D typically only used for navigational purposed. The next -%D macro builds such a row based on a specification list. -%D -%D \startbuffer -%D \interactionbuttons[width=\hsize][page,PreviousJump,ExitViewer] -%D \stopbuffer -%D -%D \typebuffer -%D -%D gives -%D -%D \getbuffer -%D -%D Apart from individual entries, one can use \type{page} and -%D \type {subpage} as shortcuts to their four associated buttons. -%D The symbols are derived from the symbols linked to the -%D entries. - -\def\interactionbuttons - {\dodoubleempty\dointeractionbuttons} - -\def\dointeractionbuttons[#1][#2]% er is een verdeel macro \horizontalfractions - {\iflocation - \begingroup - % beware, is already set \let\currentinteractionbar\empty - \doif{\interactionbarparameter\c!state}\v!stop\locationfalse - \iflocation - \ifsecondargument - \let\menuparameter\interactionbarparameter - \setupinteractionbar[#1]% - \interactionbarwidth\interactionbarparameter\c!width - \ifdim\interactionbarwidth=\zeropoint - \interactionbarwidth1.5\emwidth - \fi - \doifnothing\@@ibheight{\letinteractionbarparameter\c!height\v!broad}% - \doifnothing\@@ibdepth {\letinteractionbarparameter\c!depth\!!zeropoint}%%% - \setbox2\hbox{\localframed[\??ib\currentinteractionbar][\c!background=]{\symbol[\@@iasymbolset][\v!previouspage]}}% - \!!heighta\ht2 % needed because we default to nothing - \setupinteractionbar[\c!strut=\v!no]% - \setinteractionparameter\c!width\!!zeropoint - \!!counta\zerocount % new, was 1 - \processallactionsinset - [#2] - [ \v!page=>\advance\!!counta 4, - \v!subpage=>\advance\!!counta 4, - \s!unknown=>\advance\!!counta 1]% - \ifdim\interactionbarwidth=\zeropoint - \!!widtha\dimexpr2\emwidth+\interactionbardistance\relax - \!!widthb\dimexpr\!!counta\!!widtha-\interactionbardistance\relax - \else - \!!widtha\interactionbarwidth - \!!widthb\dimexpr\!!counta\interactionbardistance-\interactionbardistance\relax - \advance\!!widtha -\!!widthb - \divide\!!widtha \!!counta - \!!widthb\interactionbarwidth - \fi - \hbox to \!!widthb - {\setnostrut - \processallactionsinset - [#2] - [ \v!page=>\interactionbargotox\v!firstpage \interactionbargotox\v!nextpage \interactionbargotox\v!previouspage \interactionbargotox\v!lastpage, - \v!subpage=>\interactionbargotox\v!firstsubpage\interactionbargotox\v!nextsubpage\interactionbargotox\v!previoussubpage\interactionbargotox\v!lastsubpage, - \s!unknown=>\interactionbargotox\commalistelement]% - \unskip}% - \else - \interactionbuttons[][#1]% - \fi - \fi - \endgroup - \fi} - -\def\interactionbargotox#1% - {\normalexpanded{\noexpand\dodocomplexbutton -% {\??ib\currentinteractionbar}% - {\??ib}% - [\c!height=\the\!!heighta,\c!width=\the\!!widtha]% - {\noexpand\symbol[\@@iasymbolset][#1]}% - [#1]}% - \hss} - -\def\interactionbara - {\iflocation - \interactionbarwidth \interactionbarparameter\c!width - \interactionbardistance\interactionbarparameter\c!distance - \interactionbarheight \interactionbarparameter\c!height - \interactionbardepth \interactionbarparameter\c!depth - \noindent\hbox to \interactionbarwidth \bgroup - \dontcomplain - \setupblackrules[\c!height=\v!max,\c!depth=\v!max]% - \!!widthb\dimexpr\interactionbarwidth-4\emwidth\relax - \processaction - [\interactionbarparameter\c!step] - [ \v!small=>\scratchcounter 20, - \v!medium=>\scratchcounter 10, - \v!big=>\scratchcounter 5, - \s!unknown=>\scratchcounter 10]% - \!!widtha\dimexpr\!!widthb/\scratchcounter\relax - \setupblackrules[\c!width=\!!widtha]% - \setbox\scratchbox\hbox to \interactionbarwidth - {\hskip2\emwidth - \setbox\scratchbox\hbox{\blackrule[\c!color=\interactionbarparameter\c!backgroundcolor]}% - \dorecurse\scratchcounter - {\hss\normalexpanded{\directgotodumbbox{\copy\scratchbox}[page(\the\numexpr\recurselevel*\lastpage/\scratchcounter\relax)]}}% - \hss - \hskip2\emwidth}% - \wd\scratchbox\zeropoint - \box \scratchbox - \setupblackrules[\c!width=\emwidth]% - \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!firstpage]}% - \hskip\emwidth - \ifnum\realpageno>\plusone - \hskip\zeropoint\!!plus\numexpr\realpageno-\plustwo\relax \s!sp\relax % cm gives overflow - \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!previouspage)]}% - \fi - \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[page(\number\realpageno)]}% todo: \v!currentpage - \ifnum\realpageno<\lastpage\relax - \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!nextpage]}% - \hskip\zeropoint\!!plus\numexpr\lastpage-\realpageno-\plusone\relax \s!sp\relax % cm gives overflow - \fi - \hskip\emwidth - \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!lastpage]}% - \egroup - \fi} - -\def\interactionbarb - {\ifnum\lastpage>\firstpage\relax - \interactionbuttons[\v!firstpage,\v!previouspage,\v!nextpage,\v!lastpage]% - \fi} - -\def\interactionbarc - {\iflocation \ifnum\lastpage>\plusone - \interactionbarwidth\interactionbarparameter\c!width - \hbox to \interactionbarwidth - {\setupblackrules[\c!height=\interactionbarparameter\c!height,\c!depth=\interactionbarparameter\c!depth,\c!width=\emwidth]% - \scratchdimen\dimexpr(\interactionbarwidth-4\emwidth)/\numexpr\lastpage+\minusone\relax\relax - \!!widtha\numexpr\realpageno+\minusone\relax\scratchdimen - \!!widthb\numexpr\lastpage-\realpageno\relax\scratchdimen - \directgotospecbox\interactionbarparameter{\blackrule}[\v!firstpage]% - \hss - \directgotospecbox\interactionbarparameter{\blackrule[\c!width=\!!widtha]}[\v!previouspage]% - \blackrule[\c!color=\interactionbarparameter\c!contrastcolor]% - \directgotospecbox\interactionbarparameter{\blackrule[\c!width=\!!widthb]}[\v!nextpage]% - \hss - \directgotospecbox\interactionbarparameter{\blackrule}[\v!lastpage]}% - \fi \fi} - -\unexpanded\def\@@commoninteractionbargotoa#1% - {\symbol[\ifcase#1\v!previous\or\v!somewhere\or\v!next\fi]} - -\unexpanded\def\@@commoninteractionbargotob#1% - {\vrule\!!height\interactionbarheight\!!depth\interactionbardepth\!!width\!!widtha\relax} - -\unexpanded\def\@@commoninteractionbargotoc#1% - {\symbol[\ifcase#1\v!previous\or\v!somewhere\or\v!somewhere\or\v!somewhere\or\v!next\fi} - -\unexpanded\def\@@commoninteractionbargotod#1% - {\vrule \!!width\!!widtha \ifcase#1% - \!!height \interactionbarheight \!!depth \interactionbardepth \or - \!!height.5\interactionbarheight \!!depth.5\interactionbardepth \or - \!!height \interactionbarheight \!!depth \interactionbardepth \or - \!!height.5\interactionbarheight \!!depth.5\interactionbardepth \else - \!!height \interactionbarheight \!!depth \interactionbardepth \fi} - -\newconstant\interactionbarwhatmode - -\unexpanded\def\@@commoninteractionbarx#1% - {\doifelse{\interactionbarparameter\c!symbol}\v!yes - {\setupsymbolset[\@@iasymbolset]% - \let\dogotox\@@commoninteractionbargotoa} - {\let\dogotox\@@commoninteractionbargotob}% - \dorecurse\nofsubpages - {\scratchcounter\numexpr\recurselevel+\firstsubpage+\minusone\relax - \interactionbarwhatmode - \ifnum\scratchcounter<\realpageno \zerocount \else - \ifnum\scratchcounter=\realpageno \plusone \else - \plustwo \fi\fi - \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\dogotox\interactionbarwhatmode}[page(\the\scratchcounter)]}% - #1}% - \unskip} - -\def\interactionbard - {\iflocation \ifnum\nofsubpages>\plusone \doif{\structurecounterparameter\s!subpage\c!state}\v!start{% - \interactionbarwidth \interactionbarparameter\c!width - \interactionbardistance\interactionbarparameter\c!distance - \interactionbarheight \interactionbarparameter\c!height - \interactionbardepth \interactionbarparameter\c!depth - \!!widtha\interactionbarwidth - \noindent\hbox{\@@commoninteractionbarx{\hskip\interactionbardistance}}% - }\fi \fi} - -\def\interactionbare - {\iflocation \ifnum\nofsubpages>\plusone \doif{\structurecounterparameter\s!subpage\c!state}\v!start{% - \begingroup - \interactionbarwidth \interactionbarparameter\c!width - \interactionbardistance\interactionbarparameter\c!distance - \interactionbarheight \interactionbarparameter\c!height - \interactionbardepth \interactionbarparameter\c!depth - \!!widthb\dimexpr\nofsubpages\interactionbardistance-\interactionbardistance\relax % (n-1) - \!!widtha\dimexpr(\interactionbarwidth-\!!widthb)/\nofsubpages\relax - \ifdim\!!widtha<\interactionbardistance - \interactionbarf - \else - \noindent\hbox to \interactionbarwidth{\@@commoninteractionbarx{\hss}\unskip}% - \fi - \endgroup - }\fi\fi} - -\def\interactionbarf - {\iflocation \ifnum\nofsubpages>\plusone \doif{\structurecounterparameter\s!subpage\c!state}\v!start{% - \interactionbarwidth \interactionbarparameter\c!width - \interactionbardistance\interactionbarparameter\c!distance - \interactionbarheight \interactionbarparameter\c!height - \interactionbardepth \interactionbarparameter\c!depth - \noindent \hbox to \interactionbarwidth \bgroup - \doloop - {\!!countc\numexpr(\nofsubpages/\recurselevel)+\plusone\relax % rounding - \!!widthb\interactionbardistance - \multiply\!!widthb \!!countc - \advance\!!widthb -\interactionbardistance - \!!widtha\interactionbarwidth - \advance\!!widtha -\!!widthb - \divide\!!widtha \!!countc - \ifdim\!!widtha<\interactionbardistance\else - \!!countb\recurselevel - \exitloop - \fi}% - \ifnum\!!countc>\plusone - % this is not that well tested - \advance\!!countc \minustwo - \!!widtha-\interactionbardistance - \!!widtha\!!countc\!!widtha - \advance\!!widtha \interactionbarwidth - \advance\!!countc \plusone - \divide\!!widtha \!!countc - \fi - \doifelse{\interactionbarparameter\c!symbol}\v!yes - {\setupsymbolset[\@@iasymbolset]% - \let\dogotox\@@commoninteractionbargotoc}% - {\let\dogotox\@@commoninteractionbargotod}% - \!!countc\numexpr\realpageno-\plustwo\relax - \!!countd\numexpr\realpageno+\plustwo\relax - \ifnum\!!countc<\plusone \!!countc\plusone \fi - \!!countf\zerocount - \dostepwiserecurse\firstsubpage\lastsubpage\plusone - {\!!doneafalse - \advance\!!countf \plusone - \ifnum\recurselevel=\firstsubpage\relax \!!doneatrue \fi - \ifnum\recurselevel=\lastsubpage \relax \!!doneatrue \fi - \interactionbarwhatmode \if!!donea - \ifnum\recurselevel<\realpageno \zerocount \else - \ifnum\recurselevel>\realpageno \plustwo \else - \plusfour \fi\fi - \else \ifnum\!!countf=\!!countb - \ifnum\recurselevel<\realpageno \plusone \else - \ifnum\recurselevel>\realpageno \plusthree \else - \plustwo \fi\fi - \fi \fi - \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\dogotox\interactionbarwhatmode}[page(\recurselevel)]}% - \hss - \!!countf\zerocount}% - \unskip - \egroup - }\fi\fi} - -\def\interactionbarg - {\iflocation \ifnum\lastsubpage>\firstsubpage\relax % no test for state? - \interactionbuttons[\v!firstsubpage,\v!previoussubpage,\v!nextsubpage,\v!lastsubpage]% - \fi \fi} - -\setupinteractionbar - [\c!state=\v!start, - \c!alternative=a, - \c!symbol=\v!no, - \c!width=10\emwidth, - \c!height=.5\emwidth, - \c!depth=\zeropoint, - \c!distance=.5\emwidth, - \c!step=\v!medium, - \c!foregroundcolor=\interactionbarparameter\c!color, - \c!foregroundstyle=\interactionbarparameter\c!style, - \c!color=\@@iacolor, - \c!contrastcolor=\@@iacontrastcolor, - \c!style=, - \c!frame=\v!on, - \c!background=color, - \c!backgroundcolor=gray, - \c!samepage=\v!yes, - \c!unknownreference=\v!yes] - -\defineinteractionbar[a][\c!command=\interactionbara] -\defineinteractionbar[b][\c!command=\interactionbarb,\c!height=\v!broad] -\defineinteractionbar[c][\c!command=\interactionbarc,\c!height=\v!max,\c!depth=\v!max] -\defineinteractionbar[d][\c!command=\interactionbard,\c!width=.5\emwidth] -\defineinteractionbar[e][\c!command=\interactionbare] -\defineinteractionbar[f][\c!command=\interactionbarf] -\defineinteractionbar[g][\c!command=\interactionbarg,\c!height=\v!broad] - -\protect \endinput diff --git a/tex/context/base/scrn-bar.mkvi b/tex/context/base/scrn-bar.mkvi new file mode 100644 index 000000000..4605f1d2e --- /dev/null +++ b/tex/context/base/scrn-bar.mkvi @@ -0,0 +1,405 @@ +%D \module +%D [ file=scrn-bar, % was part of scrn-int +%D version=1995.01.01, +%D title=\CONTEXT\ Core Macros, +%D subtitle=Progress Bars, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Screen Macros / Progress Bars} + +\unprotect + +%D The code is a bit upgraded to \MKIV\ but the output is mostly the +%D same. In retrospect this should have been a module. We can move +%D some definitions to scrn-run-bar.mkiv if needed. We can also make +%D the code a bit more efficient. + +% todo: replace blackrule by stupid rules + +%D \starttyping +%D \setupinteraction +%D [state=start] +%D +%D \setupsubpagenumber +%D [state=start] +%D +%D \setuplayout +%D [middle] +%D +%D \setuppapersize +%D [S4][S4] +%D +%D \startsetups bars +%D \ruledvbox to \textheight \bgroup +%D a \ruledhbox{\interactionbar[a]}\vss +%D b \ruledhbox{\interactionbar[b]}\vss +%D c \ruledhbox{\interactionbar[c]}\vss +%D d \ruledhbox{\interactionbar[d]}\vss +%D e \ruledhbox{\interactionbar[e]}\vss +%D f \ruledhbox{\interactionbar[f]}\vss +%D g \ruledhbox{\interactionbar[g]}\vss +%D \egroup +%D \stopsetups +%D +%D \setuptexttexts[\setups{bars}] +%D +%D \starttext +%D \dorecurse {12} { +%D \startstandardmakeup +%D \stopstandardmakeup +%D } +%D \stoptext +%D \stoptyping + +\installcommandhandler \??ib {interactionbar} \??ib + +\presetlocalframed[\??ib] + +\unexpanded\def\interactionbar + {\dodoubleempty\scrn_bar_direct} + +\def\scrn_bar_direct[#tag][#settings]% somewhat messy + {\iflocation + \begingroup + \doifassignmentelse{#tag} + {\getparameters[\??ib][#tag]% + \edef\currentinteractionbar{\interactionbarparameter\c!alternative}}% + {\edef\currentinteractionbar{#tag}% + \ifsecondargument\getparameters[\??ib#tag][#settings]\fi}% + \doif{\interactionbarparameter\c!state}\v!start + {\interactionbarparameter\c!command}% + \endgroup + \fi} + +\newdimen\scrn_bar_width +\newdimen\scrn_bar_height +\newdimen\scrn_bar_depth +\newdimen\scrn_bar_distance + +%D Interaction buttons, in fact a row of tiny buttons, are +%D typically only used for navigational purposed. The next +%D macro builds such a row based on a specification list. +%D +%D \startbuffer +%D \interactionbuttons[width=\hsize][page,PreviousJump,ExitViewer] +%D \stopbuffer +%D +%D \typebuffer +%D +%D gives +%D +%D \getbuffer +%D +%D Apart from individual entries, one can use \type{page} and +%D \type {subpage} as shortcuts to their four associated buttons. +%D The symbols are derived from the symbols linked to the +%D entries. + +\unexpanded\def\interactionbuttons + {\dodoubleempty\scrn_bar_buttons} + +\def\scrn_bar_buttons + {\iflocation + \expandafter\scrn_bar_buttons_status + \else + \expandafter\scrn_bar_buttons_ignore + \fi} + +\def\scrn_bar_buttons_status[#settings][#list]% + {\doif{\interactionbarparameter\c!state}\v!start + {\ifsecondargument + \scrn_bar_buttons_indeed[#settings][#list]% + \else + \scrn_bar_buttons_indeed[][#settings]% + \fi}} + +\def\scrn_bar_buttons_ignore[#settings][#list]% \gobbletwooptionals + {} + +\def\scrn_bar_buttons_indeed[#settings][#list]% + {\begingroup + %\let\menuparameter\interactionbarparameter + \setupinteractionbar[#settings]% + \scrn_bar_width\interactionbarparameter\c!width + \ifdim\scrn_bar_width=\zeropoint + \scrn_bar_width1.5\emwidth + \fi + \doifnothing{\interactionbarparameter\c!height}{\letinteractionbarparameter\c!height\v!broad}% + \doifnothing{\interactionbarparameter\c!depth }{\letinteractionbarparameter\c!depth\!!zeropoint}%%% + \setbox2\hbox{\localframed[\??ib\currentinteractionbar][\c!background=]{\symbol[\interactionparameter\c!symbolset][\v!previouspage]}}% + \!!heighta\ht2 % needed because we default to nothing + \setupinteractionbar[\c!strut=\v!no]% + \letinteractionparameter\c!width\zeropoint + \!!counta\zerocount % new, was 1 + \processallactionsinset + [#list] + [ \v!page=>\advance\!!counta 4, + \v!subpage=>\advance\!!counta 4, + \s!unknown=>\advance\!!counta 1]% + \ifdim\scrn_bar_width=\zeropoint + \!!widtha\dimexpr2\emwidth+\scrn_bar_distance\relax + \!!widthb\dimexpr\!!counta\!!widtha-\scrn_bar_distance\relax + \else + \!!widtha\scrn_bar_width + \!!widthb\dimexpr\!!counta\scrn_bar_distance-\scrn_bar_distance\relax + \advance\!!widtha -\!!widthb + \divide\!!widtha \!!counta + \!!widthb\scrn_bar_width + \fi + \hbox to \!!widthb + {\setnostrut + \startsymbolset[\interactionparameter\c!symbolset]% + \processallactionsinset + [#list] + [ \v!page=>\scrn_bar_goto\v!firstpage + \scrn_bar_goto\v!nextpage + \scrn_bar_goto\v!previouspage + \scrn_bar_goto\v!lastpage, + \v!subpage=>\scrn_bar_goto\v!firstsubpage + \scrn_bar_goto\v!nextsubpage + \scrn_bar_goto\v!previoussubpage + \scrn_bar_goto\v!lastsubpage, + \s!unknown=>\scrn_bar_goto\commalistelement]% + \unskip + \stopsymbolset}% + \endgroup} + +\def\scrn_bar_goto#action% + {\button + [\c!height=\the\!!heighta,\c!width=\the\!!widtha]% + {\symbol[#action]}% we could expand this one once only + [#action]% + \hss} + +\def\scrn_bar_alternative_a + {\scrn_bar_width \interactionbarparameter\c!width + \scrn_bar_distance\interactionbarparameter\c!distance + \scrn_bar_height \interactionbarparameter\c!height + \scrn_bar_depth \interactionbarparameter\c!depth + \noindent\hbox to \scrn_bar_width \bgroup + \dontcomplain + \setupblackrules[\c!height=\v!max,\c!depth=\v!max]% + \!!widthb\dimexpr\scrn_bar_width-4\emwidth\relax + \processaction + [\interactionbarparameter\c!step] + [ \v!small=>\scratchcounter 20, + \v!medium=>\scratchcounter 10, + \v!big=>\scratchcounter 5, + \s!unknown=>\scratchcounter 10]% + \!!widtha\dimexpr\!!widthb/\scratchcounter\relax + \setupblackrules[\c!width=\!!widtha]% + \setbox\scratchbox\hbox to \scrn_bar_width + {\hskip2\emwidth + \setbox\scratchbox\hbox{\blackrule[\c!color=\interactionbarparameter\c!backgroundcolor]}% + \dorecurse\scratchcounter + {\hss\normalexpanded{\directgotodumbbox{\copy\scratchbox}[page(\the\numexpr\recurselevel*\lastpage/\scratchcounter\relax)]}}% + \hss + \hskip2\emwidth}% + \wd\scratchbox\zeropoint + \box \scratchbox + \setupblackrules[\c!width=\emwidth]% + \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!firstpage]}% + \hskip\emwidth + \ifnum\realpageno>\plusone + \hskip\zeropoint\!!plus\numexpr\realpageno-\plustwo\relax \s!sp\relax % cm gives overflow + \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!previouspage)]}% + \fi + \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[page(\number\realpageno)]}% todo: \v!currentpage + \ifnum\realpageno<\lastpage\relax + \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!nextpage]}% + \hskip\zeropoint\!!plus\numexpr\lastpage-\realpageno-\plusone\relax \s!sp\relax % cm gives overflow + \fi + \hskip\emwidth + \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\blackrule}[\v!lastpage]}% + \egroup} + +\def\scrn_bar_alternative_b + {\ifnum\lastpage>\firstpage\relax + \interactionbuttons[\v!firstpage,\v!previouspage,\v!nextpage,\v!lastpage]% + \fi} + +\def\scrn_bar_alternative_c + {\ifnum\lastpage>\plusone + \scrn_bar_width\interactionbarparameter\c!width + \hbox to \scrn_bar_width + {\setupblackrules[\c!height=\interactionbarparameter\c!height,\c!depth=\interactionbarparameter\c!depth,\c!width=\emwidth]% + \scratchdimen\dimexpr(\scrn_bar_width-4\emwidth)/\numexpr\lastpage+\minusone\relax\relax + \!!widtha\numexpr\realpageno+\minusone\relax\scratchdimen + \!!widthb\numexpr\lastpage-\realpageno\relax\scratchdimen + \directgotospecbox\interactionbarparameter{\blackrule}[\v!firstpage]% + \hss + \directgotospecbox\interactionbarparameter{\blackrule[\c!width=\!!widtha]}[\v!previouspage]% + \blackrule[\c!color=\interactionbarparameter\c!contrastcolor]% + \directgotospecbox\interactionbarparameter{\blackrule[\c!width=\!!widthb]}[\v!nextpage]% + \hss + \directgotospecbox\interactionbarparameter{\blackrule}[\v!lastpage]}% + \fi} + +\unexpanded\def\scrn_bar_goto_a#whereto% + {\symbol[\ifcase#whereto\v!previous\or\v!somewhere\or\v!next\fi]} + +\unexpanded\def\scrn_bar_goto_b#whereto% + {\vrule\!!height\scrn_bar_height\!!depth\scrn_bar_depth\!!width\!!widtha\relax} + +\unexpanded\def\scrn_bar_goto_c#whereto% + {\symbol[\ifcase#whereto\v!previous\or\v!somewhere\or\v!somewhere\or\v!somewhere\or\v!next\fi} + +\unexpanded\def\scrn_bar_goto_d#whereto% + {\vrule \!!width\!!widtha \ifcase#whereto% + \!!height \scrn_bar_height \!!depth \scrn_bar_depth \or + \!!height.5\scrn_bar_height \!!depth.5\scrn_bar_depth \or + \!!height \scrn_bar_height \!!depth \scrn_bar_depth \or + \!!height.5\scrn_bar_height \!!depth.5\scrn_bar_depth \else + \!!height \scrn_bar_height \!!depth \scrn_bar_depth \fi} + +\newconstant\scrn_bar_mode + +\unexpanded\def\scrn_bar_goto_x#command% + {\doifelse{\interactionbarparameter\c!symbol}\v!yes + {\setupsymbolset[\interactionparameter\c!symbolset]% + \let\scrn_bar_goto_indeed\scrn_bar_goto_a} + {\let\scrn_bar_goto_indeed\scrn_bar_goto_b}% + \dorecurse\nofsubpages + {\scratchcounter\numexpr\recurselevel+\firstsubpage+\minusone\relax + \scrn_bar_mode + \ifnum\scratchcounter<\realpageno \zerocount \else + \ifnum\scratchcounter=\realpageno \plusone \else + \plustwo \fi\fi + \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\scrn_bar_goto_indeed\scrn_bar_mode}[page(\the\scratchcounter)]}% + #command}% + \unskip} + +\def\scrn_bar_alternative_d + {\ifnum\nofsubpages>\plusone \doif{\structurecounterparameter\s!subpage\c!state}\v!start{% + \scrn_bar_width \interactionbarparameter\c!width + \scrn_bar_distance\interactionbarparameter\c!distance + \scrn_bar_height \interactionbarparameter\c!height + \scrn_bar_depth \interactionbarparameter\c!depth + \!!widtha\scrn_bar_width + \noindent\hbox{\scrn_bar_goto_x{\hskip\scrn_bar_distance}}% + }\fi} + +\def\scrn_bar_alternative_e + {\ifnum\nofsubpages>\plusone \doif{\structurecounterparameter\s!subpage\c!state}\v!start{% + \scrn_bar_width \interactionbarparameter\c!width + \scrn_bar_distance\interactionbarparameter\c!distance + \scrn_bar_height \interactionbarparameter\c!height + \scrn_bar_depth \interactionbarparameter\c!depth + \!!widthb\dimexpr\nofsubpages\scrn_bar_distance-\scrn_bar_distance\relax % (n-1) + \!!widtha\dimexpr(\scrn_bar_width-\!!widthb)/\nofsubpages\relax + \ifdim\!!widtha<\scrn_bar_distance + \scrn_bar_alternative_f + \else + \noindent\hbox to \scrn_bar_width{\scrn_bar_goto_x{\hss}\unskip}% + \fi + }\fi} + +\def\scrn_bar_alternative_f + {\ifnum\nofsubpages>\plusone \doif{\structurecounterparameter\s!subpage\c!state}\v!start{% + \scrn_bar_width \interactionbarparameter\c!width + \scrn_bar_distance\interactionbarparameter\c!distance + \scrn_bar_height \interactionbarparameter\c!height + \scrn_bar_depth \interactionbarparameter\c!depth + \noindent \hbox to \scrn_bar_width \bgroup + \doloop + {\!!countc\numexpr(\nofsubpages/\recurselevel)+\plusone\relax % rounding + \!!widthb\scrn_bar_distance + \multiply\!!widthb \!!countc + \advance\!!widthb -\scrn_bar_distance + \!!widtha\scrn_bar_width + \advance\!!widtha -\!!widthb + \divide\!!widtha \!!countc + \ifdim\!!widtha<\scrn_bar_distance\else + \!!countb\recurselevel + \exitloop + \fi}% + \ifnum\!!countc>\plusone + % this is not that well tested + \advance\!!countc \minustwo + \!!widtha-\scrn_bar_distance + \!!widtha\!!countc\!!widtha + \advance\!!widtha \scrn_bar_width + \advance\!!countc \plusone + \divide\!!widtha \!!countc + \fi + \doifelse{\interactionbarparameter\c!symbol}\v!yes + {\setupsymbolset[\interactionparameter\c!symbolset]% + \let\scrn_bar_goto_indeed\scrn_bar_goto_c}% + {\let\scrn_bar_goto_indeed\scrn_bar_goto_d}% + \!!countc\numexpr\realpageno-\plustwo\relax + \!!countd\numexpr\realpageno+\plustwo\relax + \ifnum\!!countc<\plusone \!!countc\plusone \fi + \!!countf\zerocount + \dostepwiserecurse\firstsubpage\lastsubpage\plusone + {\!!doneafalse + \advance\!!countf \plusone + \ifnum\recurselevel=\firstsubpage\relax \!!doneatrue \fi + \ifnum\recurselevel=\lastsubpage \relax \!!doneatrue \fi + \scrn_bar_mode + \if!!donea + \ifnum\recurselevel<\realpageno + \zerocount + \else\ifnum\recurselevel>\realpageno + \plustwo + \else + \plusfour + \fi\fi + \else + \ifnum\!!countf=\!!countb + \ifnum\recurselevel<\realpageno + \plusone + \else\ifnum\recurselevel>\realpageno + \plusthree + \else + \plustwo + \fi\fi + \else + \plusthree + \fi + \fi + \normalexpanded{\directgotospecbox\noexpand\interactionbarparameter{\scrn_bar_goto_indeed\scrn_bar_mode}[page(\recurselevel)]}% + \hss + \!!countf\zerocount}% + \unskip + \egroup + }\fi} + +\def\scrn_bar_alternative_g + {\ifnum\lastsubpage>\firstsubpage\relax % no test for state? + \interactionbuttons[\v!firstsubpage,\v!previoussubpage,\v!nextsubpage,\v!lastsubpage]% + \fi} + +\setupinteractionbar + [\c!state=\v!start, + \c!alternative=a, + \c!symbol=\v!no, + \c!width=10\emwidth, + \c!height=.5\emwidth, + \c!depth=\zeropoint, + \c!distance=.5\emwidth, + \c!step=\v!medium, + \c!foregroundcolor=\interactionbarparameter\c!color, + \c!foregroundstyle=\interactionbarparameter\c!style, + \c!color=\interactionparameter\c!color, + \c!contrastcolor=\interactionparameter\c!contrastcolor, + \c!style=, + \c!frame=\v!on, + \c!background=color, + \c!backgroundcolor=gray, + \c!samepage=\v!yes] + +\defineinteractionbar[a][\c!command=\scrn_bar_alternative_a] +\defineinteractionbar[b][\c!command=\scrn_bar_alternative_b,\c!height=\v!broad] +\defineinteractionbar[c][\c!command=\scrn_bar_alternative_c,\c!height=\v!max,\c!depth=\v!max] +\defineinteractionbar[d][\c!command=\scrn_bar_alternative_d,\c!width=.5\emwidth] +\defineinteractionbar[e][\c!command=\scrn_bar_alternative_e] +\defineinteractionbar[f][\c!command=\scrn_bar_alternative_f] +\defineinteractionbar[g][\c!command=\scrn_bar_alternative_g,\c!height=\v!broad] + +\protect \endinput diff --git a/tex/context/base/scrn-but.lua b/tex/context/base/scrn-but.lua new file mode 100644 index 000000000..b5b0a8ae4 --- /dev/null +++ b/tex/context/base/scrn-but.lua @@ -0,0 +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 format = string.format + +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,format("%s:%s",register,order),order) + end +end diff --git a/tex/context/base/scrn-but.mkiv b/tex/context/base/scrn-but.mkiv deleted file mode 100644 index 569909b50..000000000 --- a/tex/context/base/scrn-but.mkiv +++ /dev/null @@ -1,127 +0,0 @@ -%D \module -%D [ file=scrn-but, % moved code -%D version=1995.01.01, -%D title=\CONTEXT\ Core Macros, -%D subtitle=Interaction, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{ConTeXt Screen Macros / Buttons} - -\unprotect - -%D Buttons are just what their names says: things that can be -%D clicked (pushed) on. They are similar to \type{\goto}, -%D except that the text argument is not interpreted. -%D Furthermore one can apply anything to them that can be done -%D with \type{\framed}. -%D -%D \startbuffer -%D \button[width=3cm,height=1.5cm]{Exit}[ExitViewer] -%D \stopbuffer -%D -%D \typebuffer -%D -%D gives -%D -%D \getbuffer -%D -%D This command is formally specified as: -%D -%D \showsetup{button} -%D -%D The characteristics can be set with: -%D -%D \showsetup{setupbuttons} - -\unexpanded\def\setupbuttons - {\dodoubleargument\getparameters[\??bt]} - -\definecomplexorsimpleempty\button - -\def\complexbutton - {\docomplexbutton\??bt} - -\presetlocalframed[\??bt] - -\def\buttonparameter#1{\csname\??bt#1\endcsname} % simple version - -\long\def\docomplexbutton#1[#2]#3#4% get rid of possible space before [#4] - {\dodocomplexbutton#1[#2]{#3}#4} % #4 == [ - -\def\buttonframed{\dodoubleempty\localframed[\??bt]} % goodie - -% #3=none is obsolete, just use empty=yes - -\long\def\dodocomplexbutton#1[#2]#3[#4]% #3 can contain [] -> {#3} later - {\begingroup - \let\menuparameter\buttonparameter - \doif{\buttonparameter\c!state}\v!stop\locationfalse - \iflocation - \setlocationboxyes#1[#2]{#3}[#4]% - \fi - \endgroup} - -%D \macros -%D {overlaybutton} -%D -%D For converience we provide: -%D -%D \starttyping -%D \overlaybutton[reference] -%D \stoptyping -%D -%D This command can be used to define overlays an/or can be -%D used in the whatevertext areas, like: -%D -%D \starttyping -%D \defineoverlay[PrevPage][\overlaybutton{PrevPage}] -%D \setupbackgrounds[page][background=PrevPage] -%D \setuptexttexts[\overlaybutton{NextPage}] -%D \stoptyping -%D -%D For practical reasons, this macro accepts square brackets -%D as well as braces. - -\definecomplexorsimple\overlaybutton - -\def\simpleoverlaybutton#1% - {\complexoverlaybutton[#1]} - -\def\complexoverlaybutton[#1]% - {\iflocation - \gotobox{\overlayfakebox}[#1]% - \fi} - -\def\overlayfakebox - {\hbox - {\setbox\scratchbox\emptyhbox - \wd\scratchbox\overlaywidth - \ht\scratchbox\overlayheight - \box\scratchbox}} - -%D Done. - -\setupbuttons - [\c!state=\v!start, - \c!width=\v!fit, - \c!height=\v!broad, - \c!offset=0.25em, - \c!frame=\v!on, - \c!background=, - \c!backgroundcolor=, - \c!foregroundstyle=\buttonparameter\c!style, - \c!foregroundcolor=\buttonparameter\c!color, - \c!style=\@@iastyle, - \c!color=\@@iacolor, - \c!contrastcolor=\@@iacontrastcolor, - \c!samepage=\v!yes, - \c!unknownreference=\v!yes, - \c!distance=\zeropoint] % for menubuttons - -\protect \endinput diff --git a/tex/context/base/scrn-but.mkvi b/tex/context/base/scrn-but.mkvi new file mode 100644 index 000000000..c2d4900e6 --- /dev/null +++ b/tex/context/base/scrn-but.mkvi @@ -0,0 +1,984 @@ +%D \module +%D [ file=scrn-but, % moved code +%D version=1995.01.01, +%D title=\CONTEXT\ Core Macros, +%D subtitle=Interaction, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% \restorestandardblank +% better namespace for pos + +\writestatus{loading}{ConTeXt Screen Macros / Buttons} + +\registerctxluafile{scrn-but}{1.001} + +\unprotect + +%D Buttons are just what their names says: things that can be +%D clicked (pushed) on. They are similar to \type{\goto}, +%D except that the text argument is not interpreted. +%D Furthermore one can apply anything to them that can be done +%D with \type{\framed}. +%D +%D \startbuffer +%D \button[width=3cm,height=1.5cm]{Exit}[ExitViewer] +%D \stopbuffer +%D +%D \typebuffer +%D +%D gives +%D +%D \getbuffer +%D +%D This command is formally specified as: +%D +%D \showsetup{button} +%D +%D The characteristics can be set with: +%D +%D \showsetup{setupbuttons} + +\installcommandhandler \??bt {button} \??bt + +\let\setupbuttons\setupbutton + +\presetlocalframed[\??bt] + +\appendtoks + \setuevalue\currentbutton{\scrn_button_direct{\currentbutton}}% + \setevalue{\??bt:\currentbutton\s!parent}{\??bt\currentbutton}% framed +\to \everydefinebutton + +\unexpanded\def\scrn_button_direct#tag% + {\begingroup + \edef\currentbutton{#tag}% + \doifelselocation + {\dosingleempty\scrn_button_direct_status}% + {\dosingleempty\scrn_button_direct_ignore}} + +\def\scrn_button_direct_status + {\doifelse{\buttonparameter\c!state}\v!start + \scrn_button_direct_indeed + \scrn_button_direct_ignore} + +%\def\buttonframed{\dodoubleempty\localframed[\??bt]} % goodie + +% empty=yes +% +% \button[settings]{}[action] % normally used at the tex end + +\def\scrn_button_direct_indeed[#settings]#text[#action]% + {\iffirstargument + \setupbuttons[\currentbutton][#settings]% + \fi + \scrn_button_make\??bt\currentbutton\buttonparameter{#text}{#action}% + \endgroup} + +\def\scrn_button_direct_ignore[#settings]#text[#destination]% + {\endgroup} + +\definebutton[button] % english + +\setupbuttons + [\c!state=\v!start, + \c!width=\v!fit, + \c!height=\v!broad, + \c!offset=0.25em, + \c!frame=\v!on, + \c!background=, + \c!backgroundcolor=, + \c!foregroundstyle=\buttonparameter\c!style, + \c!foregroundcolor=\buttonparameter\c!color, + \c!style=\interactionparameter\c!style, + \c!color=\interactionparameter\c!color, + \c!contrastcolor=\interactionparameter\c!contrastcolor, + \c!samepage=\v!yes, + \c!unknownreference=\v!yes, + \c!distance=\zeropoint] % for menubuttons + +%D \macros +%D {overlaybutton} +%D +%D For converience we provide: +%D +%D \starttyping +%D \overlaybutton[reference] +%D \stoptyping +%D +%D This command can be used to define overlays an/or can be +%D used in the whatevertext areas, like: +%D +%D \starttyping +%D \defineoverlay[PrevPage][\overlaybutton{PrevPage}] +%D \setupbackgrounds[page][background=PrevPage] +%D \setuptexttexts[\overlaybutton{NextPage}] +%D \stoptyping +%D +%D For practical reasons, this macro accepts square brackets +%D as well as braces. + +\unexpanded\def\overlaybutton + {\dosingleempty\scrn_button_overlay} + +\def\scrn_button_overlay[#1]% + {\iffirstargument + \scrn_button_overlay_indeed{#1}% + \else + \expandafter\scrn_button_overlay_indeed + \fi} + +\def\scrn_button_overlay_indeed#1% + {\iflocation + \gotobox{\overlayfakebox}[#1]% + \fi} + +%D The renderers: + +\expandafter\let\csname\??bt:\c!location:\v!yes \endcsname\zerocount +\expandafter\let\csname\??bt:\c!location:\v!empty \endcsname\plusone +\expandafter\let\csname\??bt:\c!location:\v!no \endcsname\plustwo +\expandafter\let\csname\??bt:\c!location:\v!none \endcsname\plusthree +\expandafter\let\csname\??bt:\c!location:\v!normal \endcsname\plusone % default +\expandafter\let\csname\??bt:\c!location:\s!default\endcsname\plusone % default +\expandafter\let\csname\??bt:\c!location:\s!empty \endcsname\plusone % default + +\newconditional\scrn_button_skipped + +\def\scrn_button_make#namespace#current#currentparameter#text#action% + {\begingroup + \attribute\referenceattribute\attributeunsetvalue + \global\setfalse\scrn_button_skipped + \chardef\locationboxpagestate\csname\??bt:\c!location:#currentparameter\c!samepage\endcsname + \doifreferencefoundelse{#action}\scrn_button_make_yes\scrn_button_make_nop + {#namespace}{#current}{#currentparameter}{#text}% + \endgroup} + +\def\scrn_button_make_yes + {\analyzecurrentreference % needed as we act on the state + \ifcase\referencepagestate + \expandafter\scrn_button_make_normal % no state : something else than a page reference + \or + \ifcase\locationboxpagestate\relax + \expandafter\expandafter\expandafter\scrn_button_make_contrast % same page: yes: same page or not ... todo + \or + \expandafter\expandafter\expandafter\scrn_button_make_empty % same page: empty but frame: no click + \or + \expandafter\expandafter\expandafter\scrn_button_make_nothing % same page: empty no frame: no + \else + \expandafter\expandafter\expandafter\scrn_button_make_skipped % same page: nothing at all + \fi + \else + \ifcase\locationboxpagestate\relax + \expandafter\expandafter\expandafter\scrn_button_make_normal % other page: yes: same page or not ... todo + \or + \expandafter\expandafter\expandafter\scrn_button_make_normal % other page: empty but frame: no click + \or + \expandafter\expandafter\expandafter\scrn_button_make_normal % other page: empty no frame: no + \else + \expandafter\expandafter\expandafter\scrn_button_make_skipped % other page: nothing at all + \fi% + \fi} + +\def\scrn_button_make_nop + {\ifcase\locationboxpagestate\relax + \expandafter\scrn_button_make_framed + \or + \expandafter\scrn_button_make_empty + \or + \expandafter\scrn_button_make_nothing + \or + \expandafter\scrn_button_make_skipped + \fi} + +\def\scrn_button_make_framed#namespace#current#currentparameter#text% + {\directlocalframed[#namespace:#current]{\ignorespaces#text\removeunwantedspaces}} + +\def\scrn_button_make_skipped#namespace#current#currentparameter#text% + {\global\settrue\scrn_button_skipped} + +\def\scrn_button_make_normal#namespace#current#currentparameter#text% + {\ctxlua{structures.references.injectcurrentset(nil,nil)}% + \hbox attr \referenceattribute \lastreferenceattribute + {\directlocalframed + [#namespace:#current]% + {\ignorespaces#text\removeunwantedspaces}}} + +\def\scrn_button_make_contrast#namespace#current#currentparameter#text% + {\ctxlua{structures.references.injectcurrentset(nil,nil)}% + \hbox attr \referenceattribute \lastreferenceattribute + {\localframedwithsettings + [#namespace:#current]% + [\c!foregroundcolor=#currentparameter\c!contrastcolor]% + {\ignorespaces#text\removeunwantedspaces}}} + +\def\scrn_button_make_empty#namespace#current#currentparameter#text% + {\localframedwithsettings + [#namespace:#current]% + [\c!empty=\v!yes]% + {\ignorespaces#text\removeunwantedspaces}} + +\def\scrn_button_make_nothing#namespace#current#currentparameter#text% + {\localframedwithsettings + [#namespace:#current]% + [\c!empty=\v!yes,\c!frame=,\c!background=]% + {\ignorespaces#text\removeunwantedspaces}} + +%D Menus: +%D +%D \starttyping +%D \setuppapersize +%D [S6][S6] +%D +%D \setuplayout +%D [backspace=6cm, cutspace=6cm, +%D leftedge=3cm, rightedge=3cm, +%D leftmargin=1cm, rightmargin=1cm, +%D margindistance=5mm, edgedistance=5mm, +%D topspace=4cm, bottomspace=4cm, +%D header=0pt, footer=0pt, +%D top=1cm, bottom=1cm, +%D topdistance=5mm, bottomdistance=5mm, +%D width=middle, height=middle] +%D +%D \setupinteraction +%D [state=start, +%D menu=on] +%D +%D \setupinteractionmenu +%D [right] +%D [state=start,background=color,frame=off,backgroundcolor=red,color=white,contrastcolor=blue] +%D \setupinteractionmenu +%D [left] +%D [state=start,background=color,frame=off,backgroundcolor=green,color=white] +%D \setupinteractionmenu +%D [top] +%D [state=start,background=color,frame=off,backgroundcolor=blue,color=white] +%D \setupinteractionmenu +%D [bottom] +%D [state=start,background=color,frame=off,backgroundcolor=yellow,color=white] +%D +%D \setupinteractionmenu +%D [left] +%D [state=local] +%D \setupinteractionmenu +%D [bottom] +%D [state=local] +%D +%D \startinteractionmenu[right] +%D \startbut [page(2)] Page 2 \stopbut +%D \startbut [page(1)] Page 1 \stopbut +%D \includemenu[left] +%D \includemenu[bottom] +%D \stopinteractionmenu +%D +%D \startinteractionmenu[left] +%D \startbut [page(1)] Page 1 \stopbut +%D \startbut [page(2)] Page 2 \stopbut +%D \stopinteractionmenu +%D +%D \startinteractionmenu[top] +%D \startbut [page(1)] Page 1 \stopbut +%D \startbut [page(2)] Page 2 \stopbut +%D \stopinteractionmenu +%D +%D \startinteractionmenu[bottom] +%D \startbut [page(2)] Page 2 \stopbut +%D \startbut [page(1)] Page 1 \stopbut +%D \stopinteractionmenu +%D \stoptyping +%D +%D \starttyping +%D \startinteractionmenu[rechts] +%D \startbut [eerste] eerste \stopbut +%D \starttxt hello world \stoptxt +%D \startbut [tweede] tweede \stopbut +%D \startnop \stopnop +%D \startbut [tweede] tweede \stopbut +%D \startrul whow \stoprul +%D \startbut [tweede] tweede \stopbut +%D \startraw hello world \stopraw +%D \startbut [tweede] tweede \stopbut +%D \startcom \vfill \stopcom +%D \startbut [derde] derde \stopbut +%D \stopinteractionmenu +%D \stoptyping +%D +%D \starttyping +%D \setupinteractionmenu[right][samepage=yes, unknownreference=yes] +%D \setupinteractionmenu[right][samepage=empty,unknownreference=empty] +%D \setupinteractionmenu[right][samepage=no, unknownreference=no] +%D \setupinteractionmenu[right][samepage=none, unknownreference=none] +%D \stoptyping + +\installcommandhandler \??am {interactionmenu} \??am + +\let\setupinteractionmenus\setupinteractionmenu + +\presetlocalframed[\??am] + +\let\scrn_menu_action\relax + +\let\scrn_menu_define_original\defineinteractionmenu + +\unexpanded\def\defineinteractionmenu + {\dotripleempty\scrn_menu_define} + +\def\scrn_menu_define[#tag][#category][#settings]% category reflects location, settings can be parent + {\ifthirdargument + \doifassignmentelse{#settings}% + {\scrn_menu_define_original[#tag][#category][\c!category=#category,#settings]}% child definition + {\scrn_menu_define_original[#tag][#settings][\c!category=#category]}% % child definition + \scrn_menu_register{#tag}{#category}% + \setevalue{\??am:#tag\s!parent}{\??am#tag}% framed + \else\ifsecondargument + \doifassignmentelse{#category}% + {\scrn_menu_define_original[#tag][#category]% % root definition + \setevalue{\??am:#tag\s!parent}{\??am}}% framed + {\scrn_menu_define_original[#tag][#category][\c!category=#category]% % child definition + \scrn_menu_register{#tag}{#category}% + \setevalue{\??am:#tag\s!parent}{\??am#tag}}% framed + \else + \scrn_menu_define_original[#tag]% % root definition + \setevalue{\??am:#tag\s!parent}{\??am}% framed + \fi\fi} + +\def\scrn_menu_register#tag#category% + {\ifcsname\??am:t:#category\endcsname \else + \expandafter\newtoks \csname\??am:t:#category\endcsname + \expandafter\setfalse\csname\??am:c:#category\endcsname + \fi + \normalexpanded{\csname\??am:t:#category\endcsname{\the\csname\??am:t:#category\endcsname\scrn_menu_action{#tag}}}} + +\def\scrn_menu_actions#category% + {\the\csname\??am:t:#category\endcsname} + +%D Fill menus: + +\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\??am:c:\interactionmenuparameter\c!category\endcsname + \setinteractionmenuparameter\c!menu{#content}} + +\def\resetinteractionmenu[#tag]% + {\def\currentinteractionmenu{#tag}% + \resetinteractionmenuparameter\c!menu} + +%D Placement of menus: +%D +%D The offset mechanism is not the same as in in \MKII. There we +%D adapted automatically to offsets in the text backgrounds. Here we +%D have a bit more (but manual) control. +%D +%D \starttyping +%D \setupbackgrounds +%D [text][text] +%D [background=color,backgroundcolor=gray,backgroundoffset=2mm] +%D +%D \setupbackgrounds +%D [text] +%D [rightedge,leftedge] +%D [background=color,backgroundcolor=gray] +%D +%D \setupbackgrounds +%D [top,bottom] +%D [text] +%D [background=color,backgroundcolor=gray] +%D +%D \setupinteractionmenu +%D [right] +%D [topoffset=0mm,bottomoffset=0mm] +%D +%D \setupinteractionmenu +%D [top] +%D [topoffset=2mm,bottomoffset=2mm,rightoffset=2mm,leftoffset=2mm] +%D \stoptyping +%D +%D The no longer hard coded text areas offset compensation makes tuning +%D easier. After all, menus need some setup anyway. + +\newbox \scrn_menu_box + +\newdimen\scrn_menu_next_distance +\newdimen\scrn_menu_final_width +\newdimen\scrn_menu_final_height +\newdimen\scrn_menu_used_width +\newdimen\scrn_menu_used_height +\newdimen\scrn_menu_asked_width +\newdimen\scrn_menu_asked_height +\newdimen\scrn_menu_offset_top +\newdimen\scrn_menu_offset_bottom +\newdimen\scrn_menu_offset_left +\newdimen\scrn_menu_offset_right + +\def\scrn_menu_set_used + {\scrn_menu_offset_left \interactionmenuparameter\c!leftoffset + \scrn_menu_offset_right \interactionmenuparameter\c!rightoffset + \scrn_menu_offset_top \interactionmenuparameter\c!topoffset + \scrn_menu_offset_bottom\interactionmenuparameter\c!bottomoffset + \scrn_menu_asked_width \interactionmenuparameter\c!maxwidth + \scrn_menu_asked_height \interactionmenuparameter\c!maxheight + \scrn_menu_used_width\dimexpr + \scrn_menu_asked_width + \scrn_menu_offset_left + \scrn_menu_offset_right + \relax + \scrn_menu_used_height\dimexpr + \scrn_menu_asked_height + \scrn_menu_offset_top + \scrn_menu_offset_bottom + \relax} + +\def\scrn_menu_set_final + {\scrn_menu_final_width \namedinteractionmenuparameter\askedinteractionmenulocation\c!maxwidth + \scrn_menu_final_height\namedinteractionmenuparameter\askedinteractionmenulocation\c!maxheight} + +\def\scrn_menu_apply_final + {\wd\scrn_menu_box\scrn_menu_final_width + \ht\scrn_menu_box\scrn_menu_final_height + \dp\scrn_menu_box\zeropoint} + +\def\scrn_menu_apply_used + {\ifdim\scrn_menu_offset_left=\zeropoint \else + \setbox\scrn_menu_box\hbox{\hskip-\scrn_menu_offset_left \box\scrn_menu_box}% + \fi + \ifdim\scrn_menu_offset_bottom=\zeropoint \else + \setbox\scrn_menu_box\hbox{\lower \scrn_menu_offset_bottom \box\scrn_menu_box}% + \fi + \wd\scrn_menu_box\scrn_menu_asked_width + \ht\scrn_menu_box\scrn_menu_asked_height + \dp\scrn_menu_box\zeropoint} + +\setvalue{scrn_menu_align_\v!right }{\let\scrn_menu_left_align\raggedright} +\setvalue{scrn_menu_align_\v!left }{\let\scrn_menu_left_align\raggedleft} +\setvalue{scrn_menu_align_\v!flushright}{\let\scrn_menu_left_align\raggedleft} +\setvalue{scrn_menu_align_\v!flushleft }{\let\scrn_menu_left_align\raggedright} +\setvalue{scrn_menu_align_\v!middle }{\let\scrn_menu_left_align\raggedcenter} +\setvalue{scrn_menu_align_\v!low }{\let\scrn_menu_top_align\vss\let\scrn_menu_bottom_align\relax} +\setvalue{scrn_menu_align_\v!high }{\let\scrn_menu_top_align\relax\let\scrn_menu_bottom_align\vss} +\setvalue{scrn_menu_align_\v!lohi }{\let\scrn_menu_top_align\vss\let\scrn_menu_bottom_align\vss} + +\let\scrn_menu_left_align \relax +\let\scrn_menu_right_align \relax +\let\scrn_menu_top_align \relax +\let\scrn_menu_bottom_align\relax + +\def\scrn_menu_set_align + {\csname scrn_menu_align_\interactionmenuparameter\c!itemalign\endcsname} + +%D Hook into the pagebuilder (as less testing as possible): + +\def\scrn_menu_insert + {\iflocation + \expandafter\scrn_menu_insert_checked + \else + \expandafter\gobbleoneargument + \fi} + +\def\scrn_menu_insert_checked#location% + {\ifconditional\csname\??am:c:#location\endcsname + \scrn_menu_insert_indeed{#location}% + \fi} + +\def\scrn_menu_insert_indeed#location% + {\begingroup + \edef\askedinteractionmenulocation{#location}% + \scrn_menu_set_final + \ifcase\scrn_menu_final_width \else \ifcase\scrn_menu_final_height \else + \forgetall + \global\scrn_menu_next_distance\zeropoint + \let\scrn_menu_action\scrn_menu_package_indeed + \the\everysetmenucommands + \csname\??am:\c!menu:\namedinteractionmenuparameter\askedinteractionmenulocation\c!alternative\endcsname + \fi \fi + \endgroup} + +%D This calls: % can be \c!command for vertical/horizontal + +\setvalue{\??am:\c!menu:\v!vertical}% all menus + {\let\scrn_menu_packager\scrn_menu_packager_vertical + \setbox\scrn_menu_box\hbox{\scrn_menu_actions\askedinteractionmenulocation}% + \scrn_menu_apply_final + \box\scrn_menu_box} + +\setvalue{\??am:\c!menu:\v!horizontal}% all menus + {\let\scrn_menu_packager\scrn_menu_packager_horizontal + \setbox\scrn_menu_box\vbox{\scrn_menu_actions\askedinteractionmenulocation}% + \scrn_menu_apply_final + \box\scrn_menu_box} + +% stop : skipped +% start: processed +% local: skipped but can be included +% empty: processed but invisible + +\unexpanded\def\scrn_menu_package_indeed#tag% one menu + {\begingroup + \edef\currentinteractionmenu{#tag}% + \edef\currentinteractionmenustate{\interactionmenuparameter\c!state}% + \ifx\currentinteractionmenustate\v!start + \scrn_menu_packager + \else\ifx\currentinteractionmenustate\v!empty + \scrn_menu_packager + \fi\fi + \endgroup} + +%D With the packager being one of: + +\def\scrn_menu_packager_vertical + {\scrn_menu_set_used + \hskip\scrn_menu_next_distance + \setbox\scrn_menu_box\hbox to \scrn_menu_used_width + {\ifx\currentinteractionmenustate\v!empty \else + \interactionmenuparameter\c!left + \scrn_menu_package_vertical{\interactionmenuparameter\c!menu}% + \interactionmenuparameter\c!right + \fi}% + \edef\currentinteractionmenudistance{\interactionmenuparameter\c!distance}% + \ifx\currentinteractionmenudistance\v!overlay + \global\scrn_menu_next_distance\zeropoint + \wd\scrn_menu_box\zeropoint + \else + \global\scrn_menu_next_distance\currentinteractionmenudistance + \scrn_menu_apply_used + \fi + \box\scrn_menu_box} + +\def\scrn_menu_packager_horizontal + {\scrn_menu_set_used + \vskip\scrn_menu_next_distance + \scrn_menu_set_align + \setbox\scrn_menu_box\vbox to \scrn_menu_used_height + {\ifx\currentinteractionmenustate\v!none \else + \scrn_menu_top_align + \interactionmenuparameter\c!before + \scrn_menu_package_horizontal{\interactionmenuparameter\c!menu}% + \interactionmenuparameter\c!after + \scrn_menu_bottom_align + \fi}% + \edef\currentinteractionmenudistance{\interactionmenuparameter\c!distance}% + \ifx\currentinteractionmenudistance\v!overlay + \global\scrn_menu_next_distance\zeropoint + \offinterlineskip + \dp\scrn_menu_box\zeropoint + \ht\scrn_menu_box\zeropoint + \else + \global\scrn_menu_next_distance\currentinteractionmenudistance + \scrn_menu_apply_used + \fi + \box\scrn_menu_box} + +%D For a right menu, a sequence of calls to \type +%D {right_menu_button} is generated. +%D +%D \starttyping +%D right_menu_button (n, p, s=0/1/2, x, y, w, h, d) ; +%D \stoptyping +%D +%D Here, n is the number of the button, s a status variable, +%D while the rest is positional info. The status variable is +%D 0, 1 or~2: not found, found and found but current page. + +\newcount \scrn_menu_position +\newconstant \scrn_menu_page_mode % 0=notfound 1=found 2=currentpage +\newconditional\scrn_menu_positioning +\newtoks \scrn_menu_mp_data + +\def\scrn_menu_button_mp_template + {\askedinteractionmenulocation_menu_button(% + \number\scrn_menu_position,% + \number\scrn_menu_page_mode,% + \MPpos{\askedinteractionmenulocation:\number\scrn_menu_position}% + );} + +\def\MPmenubuttons#1{\the\scrn_menu_mp_data} + +\appendtoks + \global\scrn_menu_mp_data\emptytoks +\to \everyshipout + +\def\scrn_menu_whole_position % cannot happen in previous due to align + {\setbox\scrn_menu_box\hbox \bgroup + \hpos{menu:\askedinteractionmenulocation:\the\realpageno}{\box\scrn_menu_box}% + \egroup} + +% removed: \restorestandardblank (vspacing) ... should happen elsewhere + +\def\scrn_menu_package_vertical#content% + {\begingroup + \global\scrn_menu_position\zerocount + \def\scrn_menu_between_action_indeed{\interactionmenuparameter\c!inbetween}% + \doifelse{\interactionmenuparameter\c!position}\v!yes\settrue\setfalse\scrn_menu_positioning + \scrn_menu_set_align + \setbox\scrn_menu_box\vbox to \scrn_menu_used_height \bgroup + \hsize\scrn_menu_used_width + \scrn_menu_left_align + \interactionmenuparameter\c!before\relax + \ignorespaces#content\unskip + \interactionmenuparameter\c!after + \scrn_menu_right_align + \egroup + \ifconditional\scrn_menu_positioning + \scrn_menu_whole_position + \fi + \box\scrn_menu_box + \endgroup} + +\def\scrn_menu_package_horizontal#content% + {\begingroup + \global\scrn_menu_position\zerocount + \def\scrn_menu_between_action_indeed{\interactionmenuparameter\c!middle}% + \doifelse{\interactionmenuparameter\c!position}\v!yes\settrue\setfalse\scrn_menu_positioning + \setbox\scrn_menu_box\hbox to \scrn_menu_used_width \bgroup + \interactionmenuparameter\c!left\relax + \ignorespaces#content\unskip + \interactionmenuparameter\c!right + \egroup + \ifconditional\scrn_menu_positioning + \scrn_menu_whole_position + \fi + \box\scrn_menu_box + \endgroup} + +\def\scrn_menu_action_start + {\dontleavehmode + \begingroup} + +\def\scrn_menu_action_stop + {\ifconditional\scrn_button_skipped \else + \scrn_menu_between_action_indeed + \fi + \endgroup + \ignorespaces} + +\unexpanded\def\scrn_menu_raw_start[#action]#text\stopraw + {\scrn_menu_action_start + \gotobox{\ignorespaces#text\unskip}[#action]% + \scrn_menu_action_stop} + +\unexpanded\def\scrn_menu_but_start[#action]#text\stopbut + {\scrn_menu_action_start + \ifconditional\scrn_menu_positioning + \expandafter\scrn_button_make_position + \else + \expandafter\scrn_button_make + \fi\??am\currentinteractionmenu\interactionmenuparameter{#text}{#action}% + \scrn_menu_action_stop} + +\def\scrn_button_make_position#namespace#current#currentparameter#text#action% + {\global\advance\scrn_menu_position\plusone + \doifreferencefoundelse{#action}% 0=not found, 1=same page, >1=elsewhere + {\scrn_menu_page_mode\ifnum\currentreferencerealpage=\realpageno\plusone\else\plustwo\fi}% + {\scrn_menu_page_mode\plustwo}% + \doglobal\appendetoks + \scrn_menu_button_mp_template + \to \scrn_menu_mp_data + \hpos + {\askedinteractionmenulocation:\number\scrn_menu_position}% + {\scrn_button_make{#namespace}{#current}{#currentparameter}{#text}{#action}}} + +\unexpanded\def\scrn_menu_got_start[#action]#text\stopgot + {\scrn_menu_action_start + \setupbuttons[\currentinteractionmenu][\c!frame=\v!off,\c!background=]% + \scrn_button_make\??am\currentinteractionmenu\interactionmenuparameter{#text}{#action}% + \scrn_menu_action_stop} + +\unexpanded\def\scrn_menu_nop_start#text\stopnop + {\scrn_menu_action_start + \localframedwithsettings + [\??am:\currentinteractionmenu]% + [\c!frame=\v!off,\c!background=,\c!empty=\v!yes]% + {\ignorespaces#text\removeunwantedspaces}% + \scrn_menu_action_stop} + +\unexpanded\def\scrn_menu_txt_start#text\stoptxt + {\scrn_menu_action_start + \localframedwithsettings + [\??am:\currentinteractionmenu]% + [\c!frame=\v!off,\c!background=]% + {\ignorespaces#text\removeunwantedspaces}% + \scrn_menu_action_stop} + +\unexpanded\def\scrn_menu_rul_start#text\stoprul + {\scrn_menu_action_start + \directlocalframed + [\??am:\currentinteractionmenu]% + {\ignorespaces#text\removeunwantedspaces}% + \scrn_menu_action_stop} + +\unexpanded\def\scrn_menu_com_start#text\stopcom + {\ignorespaces#text\removeunwantedspaces + \ignorespaces} + +\unexpanded\def\scrn_menu_raw#content\\{\scrn_menu_raw_start#content\stopraw} \let\stopraw\relax +\unexpanded\def\scrn_menu_but#content\\{\scrn_menu_but_start#content\stopbut} \let\stopbut\relax +\unexpanded\def\scrn_menu_got#content\\{\scrn_menu_got_start#content\stopgot} \let\stopgot\relax +\unexpanded\def\scrn_menu_nop#content\\{\scrn_menu_nop_start#content\stopnop} \let\stopnop\relax +\unexpanded\def\scrn_menu_txt#content\\{\scrn_menu_nop_start#content\stoptxt} \let\stoptxt\relax +\unexpanded\def\scrn_menu_rul#content\\{\scrn_menu_rul_start#content\stoprul} \let\stoprul\relax +\unexpanded\def\scrn_menu_com#content\\{\scrn_menu_com_start#content\stopcom} \let\stopcom\relax + +\newtoks\everysetmenucommands % public + +\appendtoks + \let\raw\scrn_menu_raw \let\startraw\scrn_menu_raw_start + \let\but\scrn_menu_but \let\startbut\scrn_menu_but_start + \let\got\scrn_menu_got \let\startgot\scrn_menu_got_start + \let\nop\scrn_menu_nop \let\startnop\scrn_menu_nop_start + \let\txt\scrn_menu_txt \let\starttxt\scrn_menu_txt_start + \let\rul\scrn_menu_rul \let\startrul\scrn_menu_rul_start + \let\com\scrn_menu_com \let\startcom\scrn_menu_com_start +\to \everysetmenucommands + +%D Sometimes handy: +%D +%D \starttyping +%D \setupinteractionmenu +%D [left] +%D [state=local] +%D +%D \startinteractionmenu[right] +%D ... +%D \includemenu[left] +%D ... +%D \stopinteractionmenu +%D \stoptyping + +\unexpanded\def\includemenu[#tag]% + {\begingroup + \edef\currentinteractionmenu{#tag}% + \doif{\interactionmenuparameter\c!state}\v!local + {\letinteractionmenuparameter\c!state\v!start + \setinteractionmenuparameter\s!parent{\??am\askedinteractionmenulocation}% nice hack + \interactionmenuparameter\c!menu}% + \endgroup} + +%D Direct call (todo): + +\unexpanded\def\interactionmenu + {\dodoubleempty\scrn_menu_interaction_menu} + +\def\scrn_menu_interaction_menu[#tag][#settings]% + {\begingroup + \setupinteractionmenu[#tag][#settings]% +% \edef\currentinteractionmenu {#tag}% +% \edef\askedinteractionmenulocation {\interactionmenuparameter\c!category}% +% \edef\askedinteractionmenualternative{\interactionmenuparameter\c!alternative}% +% \def\scrn_menu_actions#dummy{\scrn_menu_package_indeed\currentinteractionmenu}% +% \csname\??am:\c!menu:\askedinteractionmenualternative\endcsname + \scrn_menu_insert{#tag}% + \endgroup} + +%D Plugin handler: + +\unexpanded\def\scrn_menu_insert_content_indeed + {\iflocation % here as we can have a fast turn-off + \expandafter\firstofoneargument + \else + \expandafter\gobbleoneargument + \fi} + +\let\scrn_menu_insert_content_ignore\gobbleoneargument + +\appendtoks + \doifelse{\interactionparameter\c!menu}\v!on + {\let\scrn_menu_insert_content\scrn_menu_insert_content_indeed}% + {\let\scrn_menu_insert_content\scrn_menu_insert_content_ignore}% +\to \everysetupinteraction + +%D Plugs into the page builder: + +\unexpanded\def\scrn_menu_leftedge + {\hbox to \leftedgewidth \bgroup + \hsize\leftedgewidth + %\csname\??tk\v!leftedge\c!left\endcsname + \scrn_menu_insert\v!left + %\csname\??tk\v!leftedge\c!right\endcsname + \egroup + \hskip-\leftedgewidth} + +\unexpanded\def\scrn_menu_rightedge + {\hbox to \rightedgewidth \bgroup + \hsize\rightedgewidth + %\csname\??tk\v!rightedge\c!left\endcsname + \scrn_menu_insert\v!right + %\csname\??tk\v!rightedge\c!right\endcsname + \egroup + \hskip-\rightedgewidth} + +\unexpanded\def\scrn_menu_top % uses \??tk + {\vbox to \topheight \bgroup + \vsize\topheight + %\csname\??tk\v!top\c!before\endcsname + \scrn_menu_insert\v!top + %\csname\??tk\v!top\c!after\endcsname + \kern\zeropoint + \egroup + \vskip-\topheight} + +\unexpanded\def\scrn_menu_bottom % uses \??tk + {\vbox to \bottomheight \bgroup + \vsize\bottomheight + %\csname\??tk\v!bottom\c!before\endcsname + \scrn_menu_insert\v!bottom + %\csname\??tk\v!bottom\c!after\endcsname + \kern\zeropoint + \egroup + \vskip-\bottomheight} + +\appendtoks \scrn_menu_insert_content\scrn_menu_leftedge \to \leftedgetextcontent +\appendtoks \scrn_menu_insert_content\scrn_menu_rightedge \to \rightedgetextcontent +\appendtoks \scrn_menu_insert_content\scrn_menu_top \to \toptextcontent +\appendtoks \scrn_menu_insert_content\scrn_menu_bottom \to \bottomtextcontent + +%D Initialization (root definitions, main builders): + +\defineinteractionmenu [\v!vertical] [\c!alternative=\v!vertical] +\defineinteractionmenu [\v!horizontal] [\c!alternative=\v!horizontal] + +%D Initialization (parent definitions, 4 area builders): + +\defineinteractionmenu [\v!right ] [\v!right ] [\v!vertical ] +\defineinteractionmenu [\v!left ] [\v!left ] [\v!vertical ] +\defineinteractionmenu [\v!top ] [\v!top ] [\v!horizontal] +\defineinteractionmenu [\v!bottom] [\v!bottom] [\v!horizontal] + +\setupinteraction + [\c!menu=\v!off] + +\setupinteractionmenu + [\c!offset=.25em, + \c!position=\v!no, + \c!frame=\v!on, + \c!maxwidth=\hsize, + \c!maxheight=\vsize, + \c!background=, + \c!backgroundcolor=, + \c!foregroundstyle=\interactionmenuparameter\c!style, + \c!foregroundcolor=\interactionmenuparameter\c!color, + \c!style=\interactionparameter\c!style, + \c!color=\interactionparameter\c!color, + \c!contrastcolor=\interactionparameter\c!contrastcolor, + \c!state=\v!start, + \c!samepage=\v!yes, + \c!unknownreference=\v!empty, + \c!distance=\bodyfontsize, + \c!topoffset=\zeropoint, + \c!bottomoffset=\zeropoint, + \c!leftoffset=\zeropoint, + \c!rightoffset=\zeropoint] + +\setupinteractionmenu + [\v!vertical] % not really a menu + [\c!inbetween=\blank, + \c!before=, + \c!after=\vfil, + %\c!width=\v!fit, + \c!height=\v!broad] + +\setupinteractionmenu + [\v!horizontal] % not really a menu + [\c!middle=\hfil, + %\c!left=\hss, + %\c!right=\hss, + \c!width=\v!fit, + \c!height=\v!broad] + +\setupinteractionmenu + [\v!left] + [\c!itemalign=\v!flushright, + \c!maxwidth=\leftedgewidth, + \c!maxheight=\makeupheight] + +\setupinteractionmenu + [\v!right] + [\c!itemalign=\v!flushleft, + \c!maxwidth=\rightedgewidth, + \c!maxheight=\makeupheight] + +\setupinteractionmenu + [\v!top] + [\c!itemalign=\v!high, + \c!maxwidth=\makeupwidth, + \c!maxheight=\topheight] + +\setupinteractionmenu + [\v!bottom] + [\c!itemalign=\v!low, + \c!maxwidth=\makeupwidth, + \c!maxheight=\bottomheight] + +%D Lists: + +\setvalue{\@@dodolistelement\v!left }{\def\dosomelistelement{\scrn_menu_list_element\v!left }} +\setvalue{\@@dodolistelement\v!right }{\def\dosomelistelement{\scrn_menu_list_element\v!right }} +\setvalue{\@@dodolistelement\v!top }{\def\dosomelistelement{\scrn_menu_list_element\v!top }} +\setvalue{\@@dodolistelement\v!bottom}{\def\dosomelistelement{\scrn_menu_list_element\v!bottom}} + +\def\scrn_menu_list_element#1#2#3#4#5#6#7% + {\startbut[internal(#3)] + \limitatetext{#5}{\namedlistparameter{#2}\c!maxwidth}\unknown% + \stopbut} + +%D Sometimes handy: + +\unexpanded\def\menubutton % tag settings text action + {\dodoubleempty\scrn_menu_menu_button} + +\def\scrn_button_direct_status + {\doifelse{\buttonparameter\c!state}\v!start + {\dosingleempty\scrn_button_direct_indeed}% + {\dosingleempty\scrn_button_direct_ignore}} + +\def\scrn_menu_menu_button + {\iflocation + \expandafter\scrn_menu_menu_button_indeed + \else + \expandafter\scrn_menu_menu_button_ignore + \fi} + +\def\scrn_menu_menu_button_indeed[#menutag][#settings]#text[#action]% + {\ifsecondargument + \scrn_menu_menu_button_a + {#menutag}{#settings}{#text}{#action}% + \else + \doifassignmentelse{#menutag}\scrn_menu_menu_button_b\scrn_menu_menu_button_c{#menutag}{#text}{#action}% + \fi} + +\def\scrn_menu_menu_button_ignore[#menutag][#settings]#text[#action]% + {} + +\def\scrn_menu_menu_button_a#tag#settings#text#action% + {\doif{\interactionmenuparameter\c!state}\v!start + {\dontleavehmode \begingroup + \setupinteractionmenu[#tag][#settings]% + \scrn_button_make\??am{#tag}\menuparameter{#text}{#action}% + \endgroup}} + +\def\scrn_menu_menu_button_b#settings#text#action% + {\doif{\buttonparameter\c!state}\v!start + {\dontleavehmode \begingroup + \setupbuttons[#settings]% + \scrn_button_make\??bt\empty\buttonparameter{#text}{#action}% + \endgroup}} + +\def\scrn_menu_menu_button_c#tag#text#action% + {\doif{\interactionmenuparameter\c!state}\v!start + {\dontleavehmode \begingroup + \scrn_button_make\??am{#tag}\menuparameter{#text}{#action}% + \endgroup}} + +%D Untested: + +\unexpanded\def\registermenubuttons + {\dodoubleempty\scrn_menu_register_menu_buttons} + +\def\scrn_menu_register_menu_buttons[#menu][#register]% + {\ifsecondargument + \ctxcommand{registerbuttons("menu","#register","\currentlanguage")} + \else + \ctxcommand{registerbuttons("","#menu","\currentlanguage")} + \fi} + +\protect \endinput diff --git a/tex/context/base/scrn-fld.lua b/tex/context/base/scrn-fld.lua new file mode 100644 index 000000000..439de13b3 --- /dev/null +++ b/tex/context/base/scrn-fld.lua @@ -0,0 +1,76 @@ +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 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["scrn_field_box_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.setformsmethod(method) + codeinjections.setformsmethod(method) +end + +function commands.doiffieldcategoryelse(name) + commands.testcase(codeinjections.validfieldcategory(name)) +end + +function commands.doiffieldsetelse(tag) + commands.testcase(codeinjections.validfieldset(name)) +end + +function commands.doiffieldelse(name) + commands.testcase(codeinjections.validfield(name)) +end diff --git a/tex/context/base/scrn-fld.mkiv b/tex/context/base/scrn-fld.mkiv deleted file mode 100644 index 7fc88a39f..000000000 --- a/tex/context/base/scrn-fld.mkiv +++ /dev/null @@ -1,687 +0,0 @@ -%D \module -%D [ file=scrn-fld, -%D version=1997.05.18, -%D title=\CONTEXT\ Screen Macros, -%D subtitle=Fields, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{ConTeXt Screen Macros / Fields} - -\unprotect - -%D First we hook fields into the (viewer based) layering mechanism -%D (implemented as properties). - -\appendtoks - \doif\@@iafieldlayer\v!auto{\def\@@iafieldlayer{\currentviewerlayer}}% -\to \everysetupinteraction - -\setupinteraction - [\c!fieldlayer=\v!auto] % auto by default - -%D \starttyping -%D \definefield [name] [type] [group] [values] [default] -%D -%D \definefield [WWWW] [text] [textsetup] [default text] -%D \definefield [XXXX] [push] [pushsetup] [yes,no] [yes] -%D \definefield [XXXX] [check] [checksetup] [yes,no] [yes] -%D \definefield [YYYY] [combo] [combosetup] [a,b,c,d] [b] -%D \definefield [ZZZZ] [radio] [radiosetup] [W,X,Y,Z] [Y] -%D -%D \definesubfield [W] [subsetup] [p,q] -%D \definesubfield [X,Y] [subsetup] [p,r] -%D \definesubfield [Z] [subsetup] [y,z] -%D -%D evt \definemainfield ... wanneer geplaatst voor subs gegeven -%D -%D \clonefield [XXXX] [XX,YY] [mysetup] [on,off] -%D \clonefield [Z] [AA,BB] [somesetup] [true,false] -%D \clonefield [Z] [CC,DD] [anothersetup] -%D -%D \copyfield [XXXX] [PP,QQ,RR] -%D -%D \field[XXXX] -%D \fitfield[XXXX] -%D \stoptyping - -%D Internal command, linked to \type{\definesymbol}. - -\def\dogetfieldsymbol#1% - {\getobject{SYM}{#1}} - -\def\dopresetfieldsymbol#1% - {\doifobjectfoundelse{SYM}{#1} - {} - {\settightobject{SYM}{#1}\hbox{\symbol[#1]}% - \flushatshipout - {\setbox0\hbox{\hskip-\maxdimen\getobject{SYM}{#1}}% - \smashbox0\box0}}} - -\def\presetfieldsymbols[#1]% slow - {\def\dopresetfieldsymbols##1{\processcommalist[##1]\dopresetfieldsymbol}% - \@EA\processcommalist\@EA[#1]\dopresetfieldsymbols} - -\unexpanded\def\definedefaultsymbols - {\definesymbol[defaultyes][\mathematics{\times}]% - \definesymbol[defaultno ][\mathematics{\cdot }]} - -\def\resetfieldsymbol[#1]% for experimental usage only - {\resetobject{SYM}{#1}} - -%D The interface to the specials. DEFAULT NOG ANDERS - -\def\typesetfield - {\bgroup - \dosetfontattribute\??fd\c!style - \ctxlua{backends.codeinjections.typesetfield("\currentfieldname", { - title = "\currentfieldname", - width = \number\dimexpr\@@fdwidth\relax, - height = \number\dimexpr\@@fdheight\relax, - align = "\@@fdalign", - length = tonumber("\@@fdn") or 0, - style = "\@@fdstyle", - fontstyle = "\fontstyle", - fontalternative = "\fontalternative", - fontsize = "\fontbody", - color = "\@@fdcolor", - colorvalue = \thecolorattribute\@@fdcolor, - backgroundcolor = "\@@fdfieldbackgroundcolor", - framecolor = "\@@fdfieldframecolor", - layer = "\@@fdfieldlayer", - options = "\@@fdoption", - align = "\@@fdalign", - clickin = "\@@fdclickin", - clickout = "\@@fdclickout", - regionin = "\@@fdregionin", - regionout = "\@@fdregionout", - afterkey = "\@@fdafterkey", - format = "\@@fdformat", - validate = "\@@fdvalidate", - calculate = "\@@fdcalculate", - focusin = "\@@fdfocusin", - focusout = "\@@fdfocusout", - }) }% - \egroup} - -\unexpanded\def\definefieldset {\dodoubleargument\dodefinefieldset} -\unexpanded\def\definefield {\doquintupleempty\dodefinefield} -\unexpanded\def\definemainfield{\doquintupleempty\dodefinefield} % redundant -\unexpanded\def\definesubfield {\dotripleempty \dodefinesubfield} -\unexpanded\def\clonefield {\doquadrupleempty\doclonefield} -\unexpanded\def\copyfield {\dodoubleempty \docopyfield} -\unexpanded\def\field {\dodoubleempty \donormalfield} -\unexpanded\def\fitfield {\dodoubleempty \dofitfield} -\unexpanded\def\setupfield {\doquintupleempty\dosetupfield} -\unexpanded\def\setupfields {\doquadrupleempty\dosetupfields} - -% misc - -% \appendtoks\ctxlua{backends.codeinjections.finishfields()}\to\everylastshipout - -% testing - -\def\doiffieldelse #1{\ctxlua{backends.codeinjections.doiffieldelse("#1")}} -\def\doiffieldgroupelse#1{\ctxlua{backends.codeinjections.doiffieldgroupelse("#1")}} - -% definition - -\def\dodefinefield[#1][#2][#3][#4][#5]% [name] [kind] [group] [values] [default] | [name] [kind] [group] [default] - {\ctxlua{backends.codeinjections.definefield{ variant="normal", name="#1", kind="#2", group="#3", values=\!!bs#4\!!es, default=\!!bs#5\!!es }}} - -\def\dodefinesubfield[#1][#2][#3]% [name] [group] [values] - {\ctxlua{backends.codeinjections.definefield{ variant="normal", name="#1", kind="sub", group="#2", values=\!!bs#3\!!es }}} - -\def\doclonefield[#1][#2][#3][#4]% [parent] [children] [group] [values] - {\ctxlua{backends.codeinjections.clonefield{ variant="clone", parent="#1", children="#2", group="#3", values=\!!bs#4\!!es }}} - -\def\docopyfield[#1][#2]% [parent] [children] - {\ctxlua{backends.codeinjections.clonefield{ variant="copy", parent="#1", children="#2" }}} - -\def\dodefinefieldset[#1][#2]% - {\ctxlua{backends.codeinjections.definefieldset("#1","#2")}} - -% usage -% -% \iftrialtypesetting -% -% just a default setup - -\def\loadfieldscripts{\useJSscripts[fld]\globallet\loadfieldscripts\relax} - -\def\donormalfield{\doprocessfield\dohandlefield} -\def\dofitfield {\doprocessfield\dohandlefitfield} - -\def\dosetupfieldindeed#1#2[#3]% [#4][#5][#6][#7]% - {#1[#2]} % [#4][#5][#6][#7]} - -\def\doprocessfield#1[#2][#3]% \method [name] [label] - {\dontleavehmode - \begingroup - \loadfieldscripts - \edef\currentfieldname {#2}% - \edef\currentfieldlabel{#3}% - \edef\currentfieldgroup{\ctxlua{backends.codeinjections.getfieldgroup("#2")}}% - \ifx\currentfieldlabel\empty - \let\currentfieldlabel\currentfieldname - \fi - \ifx\currentfieldgroup\empty - #1[#2][\v!label,\v!frame,\v!horizontal][][][]% - \else - \def\dosetupfield{\dosetupfieldindeed{#1}{#2}}% - \getvalue{\??fd::\currentfieldgroup}% - \fi - \endgroup} - -% setups - -\def\dosetupfield[#1][#2][#3][#4][#5]% - {\iffifthargument - \def\docommand##1{\dodosetupfield[##1][#2][#3][#4][#5]}% - \processcommalist[#1]\docommand - \else\ifthirdargument - \def\docommand##1{\dodosetupfield[##1][#2][][][#3]}% - \processcommalist[#1]\docommand - \else\ifsecondargument - \doifelse{#2}\v!reset - {\def\docommand##1{\donosetupfield[#1][][][][]}} - {\def\docommand##1{\dodosetupfield[##1][][][][#2]}}% - \processcommalist[#1]\docommand - \else\iffirstargument - \def\docommand##1{\dodosetupfield[##1][][][][]}% - \processcommalist[#1]\docommand - \else - \writestatus\m!fields{provide either 1, 2, 3 or 5 arguments}% - \fi\fi\fi\fi} - -\def\normaldodosetupfield[#1][#2][#3][#4][#5]% - {\ifcsname\??fd::#1\endcsname - \pushmacro\dosetupfield - \def\dosetupfield[##1][##2][##3][##4][##5]{\setvalue{\??fd::#1}{\dosetupfield[#1][##2,#2][##3,#3][##4,#4][##5,#5]}}% - \getvalue{\??fd::#1}% - \popmacro\dosetupfield - \else - \setvalue{\??fd::#1}{\dosetupfield[#1][#2][#3][#4][#5]}% - \fi} - -\let\dodosetupfield\normaldodosetupfield - -\def\donosetupfield[#1][#2][#3][#4][#5]% - {\setvalue{\??fd::#1}{\dosetupfield[#1][#2][#3][#4][#5]}} - -\def\dosetupfields[#1][#2][#3][#4]% - {\ifsecondargument - \def\dodosetupfield[##1][##2][##3][##4][##5]% - {\ifcsname\??fd::##1\endcsname - \def\dosetupfield[####1][####2][####3][####4][####5]% - {\setvalue{\??fd::##1}{\dosetupfield[##1][#1,####2,##2][#2,####3,##3][#3,####4,##4][#4,####5,##5]}}% - \getvalue{\??fd::##1}% - \else - \setvalue{\??fd::##1}{\dosetupfield[##1][#1,##2][#2,##3][#3,##4][#4,##5]}% - \fi}% - \else\iffirstargument - \doifelse{#1}\v!reset - {\resetfields} - {\setupfields[][][][#1]}% checken - \else - \writestatus\m!fields{provide either 1 or 4 arguments}% - \fi\fi} - -\def\resetfields - {\let\dodosetupfield\normaldodosetupfield} - -% \setupfields[\v!reset] - -% opties: veld, label, kader, vertikaal/horizontaal - -\newif\ifShowFieldLabel -\newif\ifShowFieldFrame -\newif\ifVerticalField -\newif\ifHorizontalField - -% way to slow/complicated, we need some simple alternative -% as well - -\def\dohandlefield[#1][#2][#3][#4][#5]% - {\presetlocalframed[\??fd]% - \processallactionsinset - [#2] - [ \v!reset=>\ShowFieldLabelfalse\ShowFieldFramefalse\HorizontalFieldfalse\VerticalFieldfalse, - \v!label=>\ShowFieldLabeltrue, - \v!frame=>\ShowFieldFrametrue, - \v!horizontal=>\HorizontalFieldtrue, - \v!vertical=>\VerticalFieldtrue]% - \ifVerticalField - \getparameters[\??fd][\c!distance=\zeropoint,\c!inbetween=\vskip\@@localoffset,\c!align=\v!right,\c!width=20em]% - \else\ifHorizontalField - \getparameters[\??fd][\c!distance=\@@localoffset,\c!inbetween=,\c!align=\c!left,\c!height=10ex]% - \else - \getparameters[\??fd][\c!distance=\zeropoint,\c!inbetween=,\c!align=\c!left]% - \fi\fi - \getparameters[\??fd][\c!n=,\c!before=,\c!after=\vss,\c!style=,\c!color=,#3]% - \ifShowFieldFrame - \localframed[\??fd][\c!strut=\v!no,\c!align=]\bgroup - \else - \vbox\bgroup - \fi - \dontcomplain - \ifShowFieldLabel - \setbox0\hbox - {\reshapeframeboxtrue % else wrong dimensions - \framed - [\c!style=,\c!color=,\c!align=\c!right,#4] - {\currentfieldlabel}}% - \fi - \setbox2\hbox - {\reshapeframeboxtrue % else wrong dimensions - \ifVerticalField - \setupframed[\c!height=6ex,\c!width=\hsize]% - \else\ifHorizontalField - \setupframed[\c!height=\vsize,\c!width=20em]% - \else - \setupframed[\c!height=2cm,\c!width=2cm]% - \fi\fi - \framed - [\c!align=\v!right,\c!strut=\v!no,#5] - {\getparameters - [\??fd] - [\c!color=,\c!style=,\c!align=\v!right,\c!option=, - \c!clickin=,\c!clickout=,\c!regionin=,\c!regionout=, - \c!afterkey=,\c!format=,\c!validate=,\c!calculate=, - \c!focusin=,\c!focusout=, - \c!fieldoffset=-\framedoffset,\c!fieldbackgroundcolor=, - \c!fieldframecolor=,\c!fieldlayer=\@@iafieldlayer,#5]% - \scratchdimen\framedwidth \edef\@@fdwidth {\the\scratchdimen}% - \scratchdimen\framedheight\edef\@@fdheight{\the\scratchdimen}% - \vfill - \hbox{\lower\@@fdfieldoffset\hbox{\typesetfield}} - \vss}}% - \ifShowFieldLabel - \ifVerticalField - \vbox - {\copy0 - \@@fdinbetween - \copy2}% - \else - \hbox - {\vbox \ifdim\ht2>\ht0 to \ht2 \fi - {\@@fdbefore - \copy0 - \@@fdafter}% - \hskip\@@fddistance - \vbox \ifdim\ht0>\ht2 to \ht0 \fi - {\@@fdbefore - \box2 - \@@fdafter}}% - \fi - \else - \box2 - \fi - \egroup} - -\setnewconstant\fitfieldmode\plusone % 3 = best - -\def\dohandlefitfield[#1][#2][#3][#4][#5]% alleen check - {\presetlocalframed[\??fd]% - \localframed - [\??fd] - [\c!n=1024, % beware: weblink plug in truncates - \c!strut=\v!no,\c!color=,\c!style=,\c!option=, - \c!clickin=,\c!clickout=,\c!regionin=,\c!regionout=, - \c!focusin=,\c!focusout=, - \c!afterkey=,\c!format=,\c!validate=,\c!calculate=, - \c!fieldoffset=\zeropoint,\c!fieldbackgroundcolor=, - \c!fieldframecolor=,\c!fieldlayer=\@@iafieldlayer,#5,\c!align=] - {\edef\defaultfield{\ctxlua{backends.codeinjections.getdefaultfieldvalue("#1")}}% - % \dopresetsymbol\defaultfield - \setbox\scratchbox\hbox{\symbol[\defaultfield]}% - \edef\@@fdwidth {\the\wd\scratchbox}% - \ifcase\fitfieldmode - \edef\@@fdheight{\the\ht\scratchbox}% - \typesetfield - \or % 1 = ignore depth (original, assumed no depth, actually a bug) - \edef\@@fdheight{\the\ht\scratchbox}% - \vbox to \ht\scratchbox{\vfill\hbox to \wd\scratchbox{\typesetfield\hfill}\vss}% - \or % 2 = add depth to height, but no depth in result - \edef\@@fdheight{\the\htdp\scratchbox}% - \vbox to \ht\scratchbox{\vfill\hbox to \wd\scratchbox{\typesetfield\hfill}\vss}% - \or % 3 = add depth to height, and apply depth to result - \edef\@@fdheight{\the\htdp\scratchbox}% - \hbox to \wd\scratchbox{\lower\dp\scratchbox\hbox{\typesetfield}\hfill}% - \fi}} - -%D Common stuff - -\newcount\nofsystemfields - -\def\nextsystemfield - {\global\advance\nofsystemfields\plusone - \def\currentsystemfield{sys::\number\nofsystemfields}} - -%D An example: - -\def\fillinfield - {\dosingleempty\dofillinfield} - -\def\dofillinfield[#1]#2% - {\dontleavehmode - \hbox - {\forgetall - \setupfields[\v!reset]% - \nextsystemfield - \useJSscripts[ans]% - \doifelsenothing{#1} - {\def\therightanswer{#2}} - {\def\therightanswer{#1}}% - \setbox0\hbox{#2}% - \setbox2\hbox{\therightanswer}% - \dimen0=\ifdim\wd0>\wd2 \wd0 \else \wd2 \fi - \advance\dimen0 .2em - \definefield - [\currentsystemfield][line][systemfield]% - \setupfield - [systemfield] - [\c!n=1024, % beware: weblink plugin truncates - \c!location=\v!low,\c!strut=\v!yes,\c!fieldoffset=\zeropoint, - \c!height=1.2\openlineheight,\c!width=\dimen0,\c!offset=\v!overlay, - \c!style=,\c!align=\v!middle,\c!frame=\v!off, - \c!color=red,\c!fieldbackgroundcolor=\s!white,\c!fieldframecolor=blue, - \c!validate=JS(Check_Answer{\currentsystemfield,\therightanswer})]% - \switchtobodyfont - [\c!small]% - \hbox to \wd0 - {\copy0\hskip-\wd0\hss\field[\currentsystemfield]\hss}}} - -%D and another one: - -\def\tooltip - {\dosingleempty\dotooltip} - -\def\dotooltip[#1]#2#3% - {\dontleavehmode - \begingroup - \setupfields[\v!reset]% - \useJSscripts[fld]% - \setbox0\hbox - {\dontcomplain - \nextsystemfield - \setbox0\hbox{#2}% - \definesymbol - [\currentsystemfield:txt] - [{\inframed[\c!frame=\v!off,\c!background=\v!screen]{#3}}]% - \setbox2\hbox{\symbol[\currentsystemfield:txt]}% - \definefield - [\currentsystemfield:txt][check] - [dummy][\currentsystemfield:txt][\currentsystemfield:txt]% - \setupfield - [dummy] - [\c!frame=\v!off, - \c!regionout=JS(Hide_Field{\currentsystemfield:txt}), - \c!option=\v!hidden]% - \hbox to \zeropoint - {\dimen0\wd2\advance\dimen0 -\wd0 - \doifelse{#1}\v!left - {\hskip-\dimen0} - {\doif{#1}\v!middle - {\hskip-.5\dimen0}}% - \lower\openlineheight\hbox to \zeropoint - {\fitfield[\currentsystemfield:txt]}}% - \dimen0=\ifdim\wd0=\zeropoint 3em\else\wd0\fi - \definesymbol - [\currentsystemfield:but] - [{\framed[\c!height=2ex,\c!width=\dimen0,\c!frame=\v!off]{}}]% - \definefield - [\currentsystemfield:but][push] - [dummy][\currentsystemfield:but][\currentsystemfield:but]% - \setupfield - [dummy] - [\c!frame=\v!off, - \c!option=, - \c!regionin=JS(Vide_Field{\currentsystemfield:txt}), - \c!regionout=JS(Hide_Field{\currentsystemfield:txt}), - \c!fieldlayer=\@@iafieldlayer]% - \lower2ex\hbox to \zeropoint - {\fitfield[\currentsystemfield:but]}% - #2}% - \ht0\strutht\dp0\strutdp\box0 - \endgroup} - -%D And one more: - -\unexpanded\def\definefieldstack - {\dotripleargument\dodefinefieldstack} - -\def\dodefinefieldstack[#1][#2][#3]% name, symbols, settings - {\ifcsname fieldstack:#1\endcsname \else - \setgvalue{fieldstack:#1}{\dodofieldstack[#1][#2][#3]}% - \fi} - -\def\fieldstack - {\dotripleempty\dofieldstack} - -\def\dofieldstack[#1][#2][#3]% - {\ifsecondargument - \dodefinefieldstack[#1][#2][#3]\fieldstack[#1]% - \else - \getvalue {fieldstack:#1}% - \setgvalue{fieldstack:#1}{[#1]}% - \fi} - -\def\dodofieldstack[#1][#2][#3]% start=n, 0 == leeg - {\dontleavehmode - \begingroup - \getparameters[\??fd][\c!start=1,#3]% - \setupfields[\v!reset]% - % \definesymbol[\v!empty][]% - \useJSscripts[fld][FieldStack]% - \newcounter\stackedfieldnumber - \def\dododofieldstack##1% - {\increment\stackedfieldnumber - \ifnum\stackedfieldnumber=\@@fdstart\relax - \definefield[#1:\stackedfieldnumber][check][#1][##1,\empty][##1]% \v!empty fails - \else - \definefield[#1:\stackedfieldnumber][check][#1][##1,\empty][\empty]% \v!empty fails - \fi}% - \processcommalist[#2]\dododofieldstack - \setupfield[#1][\v!reset]% added - \setupfield[#1][\c!option=\v!readonly,#3]% #3 swapped - \newcounter\stackedfieldnumber - \def\dododofieldstack##1% - {\doglobal\increment\stackedfieldnumber - \fitfield[#1:\stackedfieldnumber]\egroup\bgroup}% - \startoverlay - \bgroup - \globalprocesscommalist[#2]\dododofieldstack - \egroup - \stopoverlay - \endgroup} - -%D When submitting a form, we need to tell the driver module -%D that we want \FDF\ or \HTML. - -\newtoks\everysetupforms - -\unexpanded\def\setupforms{\dosingleempty\dosetupforms} - -\def\dosetupforms[#1] - {\getparameters[\??fr][#1]% - \the\everysetupforms} - -\appendtoks - \ctxlua{backends.codeinjections.setformsmethod("@@frmethod")}% -\to \everysetupforms - -\setupforms - [\c!method=XML] % no need for everyjob initialization as this is the default - -%D Goodie: (unchecked in \MKIV) - -\unexpanded\def\definepushbutton % name optional setup - {\dodoubleempty\dodefinepushbutton} - -\def\dodefinepushbutton[#1][#2]% name setup - {\dododefinepushbutton{#1}{n}{push}% - \dododefinepushbutton{#1}{r}{\symbol[psym:#1:n]}% - \dododefinepushbutton{#1}{d}{\symbol[psym:#1:r]}% - \setvalue{pushbutton:#1}{\dohandlepushbutton{#1}{#2}}} - -\def\dododefinepushbutton#1#2#3% - {\doifsymboldefinedelse{psym:#1:#2}% - \donothing{\definesymbol[psym:#1:#2][{#3}]}} - -\unexpanded\def\definepushsymbol - {\dotripleargument\dodefinepushsymbol} - -\def\dodefinepushsymbol[#1][#2]% [#3] - {\definesymbol[psym:#1:#2]} - -\def\dopushbutton[#1][#2]% - {\executeifdefined{pushbutton:#1}\gobbleoneargument{#2}} - -\def\pushbutton - {\dodoubleargument\dopushbutton} - -\def\dohandlepushbutton#1#2#3% identifier setup script - {\bgroup - \nextsystemfield - \setupfield - [pushbutton] - [\c!frame=\v!overlay, - \c!offset=\v!overlay, - \c!clickout=#3,#2]% - \definefield - [\currentsystemfield] - [push] - [pushbutton] - [psym:#1:n,psym:#1:r,psym:#1:d]% - \fitfield - [\currentsystemfield]% - \egroup} - -% \def\menu@psh -% {\dodoubleargument\domenu@psh} -% -% \def\domenu@psh[#1][#2]#3\\% -% {\txt\pushbutton[#1][#2]\\}% -% -%\appendtoks \let\psh\do@@ampsh \to \everysetmenucommands - -\unexpanded\def\menu@psh{\txt\pushbutton} - -\appendtoks \let\psh\menu@psh \to \everysetmenucommands - -% \definepushbutton [reset] -% -% \definepushsymbol [reset] [n] [\uniqueMPgraphic{whatever}{color=green}] -% \definepushsymbol [reset] [r] [\uniqueMPgraphic{whatever}{color=white}] -% -% \startinteractionmenu[bottom] -% \psh [reset] [JS(reset_something)] \\ -% \stopinteractionmenu - -%D Another goodie: (unchecked in \MKIV) - -% \definecolor[rollover:n][red] -% \definecolor[rollover:r][green] -% \definecolor[rollover:d][blue] - -\definepalet - [rollover] - [n=red, - r=green, - d=blue] - -\newcounter\nofrollovers -\newcounter\nofrollbuttons - -\def\dorollbutton[#1][#2]#3[#4]% - {\dontleavehmode - \bgroup - \doglobal\increment\nofrollovers - \doglobal\increment\nofrollbuttons - \unexpanded\def\dosetlocationbox[##1]##2[##3]% - {\getparameters[##1][##3]% - \definecolor[rollover][rollover:##2]% - \doifelse{##2}{n}{\doifelsevalue{##1\c!alternative}\v!hidden\phantom\hbox}\hbox - {\localframed[##1] - [\c!framecolor=rollover,\c!backgroundcolor=rollover,\c!color=rollover]% - {\dolocationattributes{##1}\c!style\c!color{#3}}}}% - \iffirstargument - \ifsecondargument - \def\setlocationbox##1{\dosetlocationbox[\??am#1]{##1}[#2]}% - \else - \doifassignmentelse{#1} - {\def\setlocationbox##1{\dosetlocationbox[\??bt]{##1}[#1]}} - {\def\setlocationbox##1{\dosetlocationbox[\??am#1]{##1}[]}}% - \fi - \else - \def\setlocationbox##1{\dosetlocationbox[\??bt]{##1}[]}% - \fi - % todo: share symbols, tricky since different dimensions - \definesymbol[rsym:\nofrollovers:n][\setlocationbox n]% - \definesymbol[rsym:\nofrollovers:r][\setlocationbox r]% - \definesymbol[rsym:\nofrollovers:d][\setlocationbox d]% - \setupfield - [rollbutton] - [\c!frame=\v!off, - \c!offset=\v!overlay, - \c!clickout={#4}]% - \definefield - [roll:\nofrollbuttons][push][rollbutton] - [rsym:\nofrollovers:n,% - rsym:\nofrollovers:r,% - rsym:\nofrollovers:d]% - \fitfield[roll:\nofrollbuttons]% - \egroup} - -\unexpanded\def\rollbutton - {\dodoubleempty\dorollbutton} - -\def\menu@rob[#1]#2\\% - {\txt\rollbutton[\currentmenu]{\ignorespaces#2\unskip}[#1]\\}% - -\appendtoks \let\rob\menu@rob \to \everysetmenucommands - -% calls: -% {..} [JS..] -% [left] {..} [JS..] -% [a=b] {..} [JS..] -% [left] [a=b] {..} [JS..] -% -% \setupbuttons[offset=0pt,frame=off] % alternative=hidden -% -% \rollbutton {Manuals} [JS(Goto_File{show-man.pdf})] -% \rollbutton {Articles} [JS(Goto_File{show-art.pdf})] -% \rollbutton {Papers} [JS(Goto_File{show-pap.pdf})] -% \rollbutton {Presentations} [JS(Goto_File{show-pre.pdf})] -% \rollbutton {Resources} [JS(Goto_File{show-res.pdf})] -% -% \rob [JS(...)] bla bla \\ - -\unexpanded\def\overlayrollbutton - {\dodoubleargument\dooverlayrollbutton} - -\def\dooverlayrollbutton[#1][#2]% - {\bgroup - \nextsystemfield - \setupfield - [overlayrollbutton] - [\c!frame=\v!off,\c!offset=\v!overlay,\c!regionin={#1},\c!regionout={#2}]% - \definesymbol - [\currentsystemfield] - [{\framed[\c!frame=\v!off,\c!width=\overlaywidth,\c!height=\overlayheight]{}}]% - \definefield - [\currentsystemfield][push][overlayrollbutton][\currentsystemfield][\currentsystemfield]% - \fitfield[\currentsystemfield]% - \egroup} - -% \defineoverlay -% [ShowMenu] -% [{\overlayrollbutton[VideLayer{navigation}][HideLayer{navigation}]}] - -\protect \endinput diff --git a/tex/context/base/scrn-fld.mkvi b/tex/context/base/scrn-fld.mkvi new file mode 100644 index 000000000..ae2e5541f --- /dev/null +++ b/tex/context/base/scrn-fld.mkvi @@ -0,0 +1,965 @@ +%D \module +%D [ file=scrn-fld, +%D version=1997.05.18, +%D title=\CONTEXT\ Screen Macros, +%D subtitle=Fields, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% There is still some leftover code from mkii, where we need to +% be sparse with hash entries and so have a somewhat complex +% setup mechanism. + +% interaction checking + +\writestatus{loading}{ConTeXt Screen Macros / Fields} + +\unprotect + +\registerctxluafile{scrn-fld}{1.001} + +%D In \MKII\ we had to cheat a bit with setups in order not to run +%D out of memory with thousands of fields, which we happen to need at +%D that time. In \MKIV\ we can store some data at the \LUA\ end and +%D use a somewhat slower but quite okay inheritance mechanism. For +%D this reason we now have split definitions, although the old method +%D is still somewhat supported. The clone and copy commands are +%D somewhat obsolete for several reasons: we can now use inheritance +%D and autocloning has been supported for a while. In most cases +%D cloning (especially with check boxes) the acrobat browser is not +%D stable enough with respect to appearance handling. +%D +%D A fieldcategory is nothing more than a collection of settings. +%D +%D \starttyping +%D \definefieldcategory +%D [all-email] +%D [height=1cm, +%D width=14cm, +%D style=sstf] +%D \stoptyping +%D +%D A definition can refer to this category: +%D +%D \starttyping +%D \definefieldbody [email] [type=line,category=all-email,default=pragma@wxs.nl] +%D \stoptyping +%D +%D A copy of a field is made as follows: +%D +%D \starttyping +%D \definefieldbody [xmail] [email] +%D \stoptyping +%D +%D You can also be more specific: +%D +%D \starttyping +%D \definefieldbody[buttona][type=check,values={one,two}] +%D \definefieldbody[buttonb][type=check,values={three,four}] +%D \definefieldbody[buttonc][buttona][values={three,four}] +%D \stoptyping +%D +%D Actually typesetting a field happens this way: +%D +%D \starttyping +%D \fieldbody [Email] +%D \fieldbody [Email] [width=6cm] +%D \fieldbody [eMAIL] +%D \fieldbody [eMAIL] [width=7cm] +%D +%D \fieldbody [buttona] +%D \fieldbody [buttona] +%D \fieldbody [buttonb] +%D \fieldbody [buttonb] +%D \fieldbody [buttonc] +%D \fieldbody [buttonc] +%D \stoptyping +%D +%D So, you can call up a field many times and quite some parameters +%D can be set. +%D +%D Because there are persistent problems with acrobat rendering +%D associated appearance streams (some messy /MK interferende) we +%D also support native (built-in dingbat) symbols: check, circle, +%D cross, diamond, square and star. +%D +%D \starttyping +%D \definefield[test1][check] +%D \definefield[test2][check] +%D +%D \fieldbody[test1][width=1em,height=\strutht,depth=\strutdp,symbol=check] +%D \fieldbody[test1][width=1em,height=\strutht,depth=\strutdp,symbol=circle] +%D \fieldbody[test2][width=1em,height=\strutht,depth=\strutdp,symbol=square] +%D \stoptyping +%D +%D When submitting a form, we need to tell the driver module +%D that we want \FDF\ or \HTML. + +\newtoks\everysetupforms + +\unexpanded\def\setupforms + {\dosingleempty\scrn_forms_setup} + +\def\scrn_forms_setup[#settings] + {\getparameters[\??fr][#settings]% + \the\everysetupforms} + +\appendtoks + \ctxcommand{setformsmethod("@@frmethod")}% +\to \everysetupforms + +\setupforms + [\c!method=XML] % no need for everyjob initialization as this is the default + +%D We need to initialize symbols in a special way so that they +%D can be used as rendering for a widget. + +\unexpanded\def\presetfieldsymbols[#list]% slow + {\processcommacommand[#list]\scrn_symbols_preset} + +\def\scrn_symbols_preset#set% + {\processcommalist[#set]\scrn_symbols_preset_indeed}% + +\def\scrn_symbols_preset_indeed#tag% + {\doifobjectfoundelse{SYM}{#tag} + {} + {\settightobject{SYM}{#tag}\hbox{\symbol[#tag]}% + \flushatshipout + {\setbox0\hbox{\hskip-\maxdimen\getobject{SYM}{#tag}}% + \smashbox0\box0}}} + +\let\dosetfieldsymbol\scrn_symbols_preset_indeed + +\def\dogetfieldsymbol#tag% + {\getobject{SYM}{#tag}} + +\unexpanded\def\definedefaultsymbols % used ? + {\definesymbol[defaultyes][\mathematics{\times}]% + \definesymbol[defaultno ][\mathematics{\cdot }]} + +% \def\resetfieldsymbol[#tag]% for experimental usage only +% {\resetobject{SYM}{#tag}} + +%D Now comes the real code: + +\installcommandhandler \??fd {fieldcategory} \??fd +\installcommandhandler \??fb {fieldbody} \??fb + +\newbox\scrn_field_box_body + +\setupfieldcategory + [\c!alternative=\v!normal, % normal clone copy + \c!type=\v!line, % line text ... + \c!width=5em, + \c!height=1em, + \c!depth=\zeropoint, + \c!align=\v!flushleft, + \c!option=\v!printable, % maybe we need a globaloptions and marge them + \c!n=1024] + +\def\scrn_field_check_category + {\edef\currentfieldbodycategory{\fieldbodyparameter\c!category}% + \ifx\currentfieldbodycategory\empty + \setevalue{\??fb\currentfieldbody\s!parent}{\??fd}% + \else + \setevalue{\??fb\currentfieldbody\s!parent}{\??fd\currentfieldbodycategory}% + \fi} + +\appendtoks % we cannot use parent .. maybe s!parent has to change + \ifx\currentfieldbodyparent\empty + \scrn_field_check_category + \ctxcommand{definefield{ + name = "\currentfieldbody", + alternative = "normal", + type = "\fieldbodyparameter\c!type", + category = "\fieldbodyparameter\c!category", + values = \!!bs\fieldbodyparameter\c!values\!!es, + default = \!!bs\fieldbodyparameter\c!default\!!es + }}% + \else + \ctxcommand{clonefield{ + children = "\currentfieldbody", + alternative = "clone", + parent = "\currentfieldbodyparent", + category = "\fieldbodyparameter\c!category", + values = \!!bs\fieldbodyparameter\c!values\!!es, + default = \!!bs\fieldbodyparameter\c!default\!!es + }}% + \fi +\to \everydefinefieldbody + +\unexpanded\def\fieldbody + {\dodoubleempty\scrn_field_body} + +\def\scrn_field_body[#tag][#settings]% + {\iflocation + \hbox\bgroup + \edef\currentfieldbody{#tag}% + \ifsecondargument + \setupfieldbody[#tag][#settings]% + \fi + \scrn_field_body_typeset + \box\scrn_field_box_body + \egroup + \fi} + +\def\scrn_field_body_typeset % todo: fieldsymbol (checkfields /MK mess) + {\edef\currentfieldframecolor{\fieldbodyparameter\c!fieldframecolor}% + \ifx\currentfieldframecolor\empty\else + \getcolorattributevalue\currentfieldframecolor\currentfieldframecolorvalue + \fi + \edef\currentfieldbackgroundcolor{\fieldbodyparameter\c!fieldbackgroundcolor}% + \ifx\currentfieldbackgroundcolor\empty\else + \getcolorattributevalue\currentfieldbackgroundcolor\currentfieldbackgroundcolorvalue + \fi + \dosetfieldbodyattributes\c!style\c!color + \ctxcommand{insertfield("\currentfieldbody", { + title = "\currentfieldbody", + width = \number\dimexpr\fieldbodyparameter\c!width \relax, + height = \number\dimexpr\fieldbodyparameter\c!height\relax, + depth = \number\dimexpr\fieldbodyparameter\c!depth \relax, + align = "\fieldbodyparameter\c!align", + length = "\fieldbodyparameter\c!n", + style = "\fieldbodyparameter\c!style", + fontstyle = "\fontstyle", + fontalternative = "\fontalternative", + fontsize = "\fontbody", + fontsymbol = "\fieldbodyparameter\c!symbol", + color = "\fieldbodyparameter\c!color", + colorvalue = \number\attribute\colorattribute, + \ifx\currentfieldbackgroundcolor\empty \else + backgroundcolor = "\currentfieldbackgroundcolor", + backgroundcolorvalue = "\currentfieldbackgroundcolorvalue", + \fi + \ifx\currentfieldframecolor\empty \else + framecolor = "\currentfieldframecolor", + framecolorvalue = "\currentfieldframecolorvalue", + \fi + layer = "\fieldbodyparameter\c!fieldlayer", + options = "\fieldbodyparameter\c!option", + align = "\fieldbodyparameter\c!align", + clickin = "\fieldbodyparameter\c!clickin", + clickout = "\fieldbodyparameter\c!clickout", + regionin = "\fieldbodyparameter\c!regionin", + regionout = "\fieldbodyparameter\c!regionout", + afterkey = "\fieldbodyparameter\c!afterkey", + format = "\fieldbodyparameter\c!format", + validate = "\fieldbodyparameter\c!validate", + calculate = "\fieldbodyparameter\c!calculate", + focusin = "\fieldbodyparameter\c!focusin", + focusout = "\fieldbodyparameter\c!focusout", + openpage = "\fieldbodyparameter\c!openpage", + closepage = "\fieldbodyparameter\c!closepage", + })}} + +%D The sets are used in grouped calculations. +%D +%D [name] [set] + +\unexpanded\def\definefieldbodyset + {\dodoubleempty\scrn_field_define_set} + +\def\scrn_field_define_set[#tag][#list]% + {\ctxcommand{definefieldset("#tag","#list")}} + +\let\dodefinefieldset\definefieldbodyset % compatibility + +%D A few testing macros: + +\def\doiffieldbodyelse #tag{\ctxcommand{doiffieldelse("#tag")}} +\def\doiffieldcategoryelse#tag{\ctxcommand{doiffieldcategoryelse("#tag")}} + +\let\doiffieldelse\doiffieldbodyelse % compatibility + +%D We still support the traditional method of defining fields: +%D +%D \starttyping +%D \definefield [name] [type] [category] [values] [default] +%D +%D \definefield [WWWW] [text] [textsetup] [default text] +%D \definefield [XXXX] [push] [pushsetup] [yes,no] [yes] +%D \definefield [XXXX] [check] [checksetup] [yes,no] [yes] +%D \definefield [YYYY] [combo] [combosetup] [a,b,c,d] [b] +%D \definefield [ZZZZ] [radio] [radiosetup] [W,X,Y,Z] [Y] +%D +%D \definesubfield [W] [subsetup] [p,q] +%D \definesubfield [X,Y] [subsetup] [p,r] +%D \definesubfield [Z] [subsetup] [y,z] +%D +%D evt \definemainfield ... wanneer geplaatst voor subs gegeven +%D +%D \clonefield [XXXX] [XX,YY] [mysetup] [on,off] +%D \clonefield [Z] [AA,BB] [somesetup] [true,false] +%D \clonefield [Z] [CC,DD] [anothersetup] +%D +%D \copyfield [XXXX] [PP,QQ,RR] +%D \stoptyping +%D +%D Keep in mind that you can also use \type {\definefieldbody} to +%D achieve the same. + +\unexpanded\def\definefield {\doquintupleempty\scrn_field_define_field} +\unexpanded\def\definesubfield{\dotripleempty \scrn_field_define_subfield} +\unexpanded\def\clonefield {\doquadrupleempty\scrn_field_clone_field} +\unexpanded\def\copyfield {\dodoubleempty \scrn_field_copy_field} + +\let\definemainfield\definefield % obsolete ! + +\def\scrn_field_define_field[#tag][#type][#category][#values][#default]% + {\definefieldbody[#tag][\c!type=#type,\c!category=#category,\c!values={#values},\c!default={#default}]} + +\def\scrn_field_define_subfield[#tag][#category][#values]% + {\definefieldbody[#tag][\c!type=sub,\c!category=#category,\c!values={#values}]} + +\def\scrn_field_clone_field[#parent][#tag][#category][#values]% + {\definefieldbody[#tag][#parent][\c!category=#category,\c!values={#values}]} + +\def\scrn_field_copy_field[#parent][#tag]% + {\definefieldbody[#tag][#parent]} + +%D We hook fields into the (viewer based) layering mechanism +%D (implemented as properties). + +\appendtoks + \doifelse{\interactionparameter\c!fieldlayer}\v!auto + {\setupfieldcategory[\c!fieldlayer=\currentviewerlayer]}% + {\setupfieldcategory[\c!fieldlayer=]}% +\to \everysetupinteraction + +\setupinteraction + [\c!fieldlayer=\v!auto] % auto by default + +%D The \type {\fieldbody} is the more bare one. One step further goes +%D \type {\fitfield}, in fact it (now) uses a dedicated instance of +%D framed: \type {fitfieldframed}. +%D +%D \starttyping +%D \ruledhbox{\fieldbody[Email][height=\strutht,depth=\strutdp,style=normal]} +%D \ruledhbox{\fitfield[Email][height=\strutht,depth=\strutdp,style=normal]} +%D \ruledhbox{\fitfield[buttona]} +%D \stoptyping + +\newbox\scrn_field_box_fit_symbol + +\defineframed + [fitfieldframed] + [\c!strut=\v!no, + \c!frame=off, + \c!offset=\v!overlay, + \c!align=] + +\unexpanded\def\fitfield + {\dodoubleempty\scrn_field_fit} + +\def\scrn_field_fit[#tag][#settings]% + {\iflocation + \begingroup + \edef\currentdefaultfieldvalue{\ctxcommand{getdefaultfieldvalue("#tag")}}% + \setbox\scrn_field_box_fit_symbol\hbox{\symbol[\currentdefaultfieldvalue]}% + \fitfieldframed[#tag] + {\fieldbody[#tag] + [\c!width=\wd\scrn_field_box_fit_symbol, + \c!height=\ht\scrn_field_box_fit_symbol, + \c!depth=\dp\scrn_field_box_fit_symbol, + #settings]}% + \endgroup + \fi} + +%D The traditional field command does some labeling and +%D boxing: + +\installparameterhandler \??wl {fieldlabelframed} +\installparameterhandler \??wc {fieldcontentframed} +\installparameterhandler \??wt {fieldtotalframed} + +\installsetuphandler \??wl {fieldlabelframed} +\installsetuphandler \??wc {fieldcontentframed} +\installsetuphandler \??wt {fieldtotalframed} + +\unexpanded\def\setupfield {\doquintupleempty\scrn_field_setup_field} +\unexpanded\def\setupfields{\doquadrupleempty\scrn_field_setup_fields} + +% \presetlocalframed[\??wl] +% \presetlocalframed[\??wc] +% \presetlocalframed[\??wt] + +\setupfieldcontentframed + [\c!align=\v!flushleft, + \c!strut=\v!no, + \s!parent=\??ol] + +\setupfieldcontentframed % independent + [\c!alternative=\v!normal, + \c!type=\v!line, + \c!width=5em, + \c!height=1em, + \c!depth=\zeropoint, + \c!align=\v!flushleft, + \c!option=\v!printable, + \c!n=1024] + +\setupfieldlabelframed + [\c!style=, + \c!color=, + \c!align=\v!flushleft, + \s!parent=\??ol] + +\setupfieldtotalframed + [%\c!alternative={\v!label,\v!frame,\v!horizontal}, + \c!strut=\v!no, + \c!align=, + \s!parent=\??ol] + +\def\scrn_field_setup_field[#tag][#variant][#totalsettings][#labelsettings][#fieldsettings]% + {\iffifthargument + \definefieldcategory[#tag][\s!parent=\??wc#tag,#fieldsettings] + \setupfieldtotalframed [#tag][\s!parent=\??wt,\c!alternative={#variant},#totalsettings]% + \setupfieldlabelframed [#tag][\s!parent=\??wl,#labelsettings]% + \setupfieldcontentframed[#tag][\s!parent=\??wc,#fieldsettings]% + \else\iffourthargument + \definefieldcategory[#tag][\s!parent=\??wc#tag,#labelsettings] + \setupfieldtotalframed [#tag][\s!parent=\??wt,\c!alternative={#variant},#totalsettings]% + \setupfieldlabelframed [#tag][\s!parent=\??wl]% + \setupfieldcontentframed[#tag][\s!parent=\??wc,#labelsettings]% + \else\ifthirdargument + \definefieldcategory[#tag][\s!parent=\??wc#tag,#totalsettings] + \setupfieldtotalframed [#tag][\s!parent=\??wt,\c!alternative={#variant}]% + \setupfieldlabelframed [#tag][\s!parent=\??wl]% + \setupfieldcontentframed[#tag][\s!parent=\??wc,#totalsettings]% + \else\ifsecondargument + \definefieldcategory[#tag][\s!parent=\??wc#tag,#variant] + \setupfieldtotalframed [#tag][\s!parent=\??wt]% + \setupfieldlabelframed [#tag][\s!parent=\??wl]% + \setupfieldcontentframed[#tag][\s!parent=\??wc,#variant]% + \fi\fi\fi\fi} + +\def\scrn_field_setup_fields[#tag][#totalsettings][#labelsettings][#fieldsettings] + {\iffourthargument + \definefieldcategory[#tag][\s!parent=\??wc#tag,#fieldsettings] + \setupfieldtotalframed [#tag][\s!parent=\??wt,#totalsettings]% + \setupfieldlabelframed [#tag][\s!parent=\??wl,#labelsettings]% + \setupfieldcontentframed[#tag][\s!parent=\??wc,#fieldsettings]% + \else\ifthirdargument + \definefieldcategory[#tag][\s!parent=\??wc#tag,#fieldsettings] + \setupfieldtotalframed [#tag][\s!parent=\??wt,#totalsettings]% + \setupfieldlabelframed [#tag][\s!parent=\??wl]% + \setupfieldcontentframed[#tag][\s!parent=\??wc,#labelsettings]% + \else\ifsecondargument + \definefieldcategory[#tag][\s!parent=\??wc#tag,#fieldsettings] + \setupfieldtotalframed [#tag][\s!parent=\??wt]% + \setupfieldlabelframed [#tag][\s!parent=\??wl]% + \setupfieldcontentframed[#tag][\s!parent=\??wc,#totalsettings]% + \fi\fi\fi} + +\let\resetfields\relax % no longer supported + +\def\scrn_field_load_scripts{\useJSscripts[fld]\globallet\scrn_field_load_scripts\relax} + +\newconditional\fieldlabelshown +\newconditional\fieldframeshown +\newconditional\fieldisvertical +\newconditional\fieldishorizontal + +\unexpanded\def\field + {\dodoubleempty\scrn_field_direct} + +\def\scrn_field_direct[#tag][#label]% + {\iflocation + \dontleavehmode + \begingroup + \scrn_field_load_scripts + \edef\currentfieldbody {#tag}% + \edef\currentfieldlabel {#label}% + \edef\currentfieldcategory{\ctxcommand{getfieldcategory("#tag")}}% + \ifx\currentfieldlabel\empty + \let\currentfieldlabel\currentfieldbody + \fi + \ifx\currentfieldcategory\empty + \setupfieldtotalframed [\currentfieldbody][\s!parent=\??wt]% + \setupfieldlabelframed [\currentfieldbody][\s!parent=\??wl]% + \setupfieldcontentframed[\currentfieldbody][\s!parent=\??wc]% + \definefieldcategory [\currentfieldbody]% + \setupfieldbody [\currentfieldbody][\c!category=\currentfieldbody]% + \let\currentfieldcategory\currentfieldbody + \fi + \let\currentfieldtotalframed \currentfieldcategory + \let\currentfieldlabelframed \currentfieldcategory + \let\currentfieldcontentframed\currentfieldcategory + \scrn_field_analyze_setups + \ifconditional\fieldframeshown + \localframed[\??wt\currentfieldcategory]\bgroup + \else + \vbox\bgroup + \fi + \dontcomplain + \ifconditional\fieldlabelshown + \scrn_field_set_label_box + \fi + \scrn_field_set_content_box + \ifconditional\fieldlabelshown + \ifconditional\fieldisvertical + \scrn_field_flush_vertical + \else + \scrn_field_flush_horizontal + \fi + \else + \scrn_field_flush_content + \fi + \egroup + \endgroup + \fi} + +% opties: veld, label, kader, vertikaal/horizontaal + +\newbox\scrn_field_box_label +\newbox\scrn_field_box_content + +% lower framedoffset + +\def\scrn_field_set_label_box + {\setbox\scrn_field_box_label\hbox + {\reshapeframeboxtrue % else wrong dimensions % still needed? + \localframed + [\??wl\currentfieldcategory] + {\currentfieldlabel}}} + +% \c!fieldoffset=-\framedoffset,\c!fieldbackgroundcolor=, +% \hbox{\lower\@@fdfieldoffset\hbox{\typesetfield}} + +\def\scrn_field_set_content_box + {\setbox\scrn_field_box_content\hbox + {\reshapeframeboxtrue % else wrong dimensions (to be checked) + \ifconditional\fieldisvertical + \setupfieldcontentframed[\c!height=6ex,\c!width=\hsize]% + \else\ifconditional\fieldishorizontal + \setupfieldcontentframed[\c!height=\vsize,\c!width=20em]% + \else + \setupfieldcontentframed[\c!height=2cm,\c!width=2cm]% + \fi\fi + \localframed % lower framedoffset + [\??wc\currentfieldcategory] + {\fieldbody + [\currentfieldbody] + [\c!width=\framedwidth,\c!height=\framedheight]}}} + +\def\scrn_field_flush_vertical + {\vbox + {\copy\scrn_field_box_label + \fieldtotalframedparameter\c!inbetween + \copy\scrn_field_box_content}} + +\def\scrn_field_flush_horizontal + {\hbox + {\vbox \ifdim\ht\scrn_field_box_content>\ht\scrn_field_box_label to \ht\scrn_field_box_content \fi + {\fieldtotalframedparameter\c!before + \copy\scrn_field_box_label + \fieldtotalframedparameter\c!after}% + \hskip\fieldtotalframedparameter\c!distance + \vbox \ifdim\ht\scrn_field_box_label>\ht\scrn_field_box_content to \ht\scrn_field_box_label \fi + {\fieldtotalframedparameter\c!before + \box\scrn_field_box_content + \fieldtotalframedparameter\c!after}}} + +\def\scrn_field_flush_content + {\box\scrn_field_box_content} + +\def\scrn_field_analyze_setups + {\setfalse\fieldlabelshown + \setfalse\fieldframeshown + \setfalse\fieldishorizontal + \setfalse\fieldisvertical + \normalexpanded{\processallactionsinset[\fieldtotalframedparameter\c!alternative]} + [ \v!reset=>\setfalse\fieldlabelshown + \setfalse\fieldframeshown + \setfalse\fieldishorizontal + \setfalse\fieldisvertical, + \v!label=>\settrue\fieldlabelshown, + \v!frame=>\settrue\fieldframeshown, + \v!horizontal=>\settrue\fieldishorizontal, + \v!vertical=>\settrue\fieldisvertical]% + \ifconditional\fieldisvertical + \setupfieldtotalframed[\c!distance=\zeropoint,\c!inbetween=\vskip\@@localoffset,\c!align=\v!right,\c!width=20em]% + \else\ifconditional\fieldishorizontal + \setupfieldtotalframed[\c!distance=\@@localoffset,\c!inbetween=,\c!align=\c!left,\c!height=10ex]% + \else + \setupfieldtotalframed[\c!distance=\zeropoint,\c!inbetween=,\c!align=\c!left]% + \fi\fi + \setupfieldtotalframed[\c!n=,\c!before=,\c!after=\vss,\c!style=,\c!color=]} + +%D Common stuff (obsolete) + +\newcount\scrn_field_system_n + +\def\nextsystemfield + {\global\advance\scrn_field_system_n\plusone + \def\currentsystemfield{sys::\number\scrn_field_system_n}} + +%D \CONTEXT\ had tooltips right from the moment that it +%D supported fields. Due to the at that moment somewhat +%D limited \PDF\ specification, they were implemented +%D using \JAVASCRIPT, but nowadays more kind of actions +%D are supported, so we can do without. The \MKIV\ version +%D also supports definition of tooltips and configuration. +%D +%D \starttyping +%D before \tooltip[right]{inbetween}{a very nice tip} after\par +%D before \tooltip[align=normal]{inbetween}{a very\\nice tip} after\par +%D before \tooltip[middle]{inbetween}{a very nice tip} after\par +%D before \tooltip[left]{inbetween}{a very nice tip} after\par +%D \stoptyping + +\newbox \scrn_tooltip_box_anchor +\newbox \scrn_tooltip_box_text +\newcount\scrn_tooltip_n + +\installcommandhandler \??wh {tooltip} \??wh + +\setuptooltip + [\c!location=\v!right, + \c!frame=\v!off, + \c!offset=.1ex, + \c!background=\v!color, + \c!backgroundcolor=gray] + +\presetlocalframed[\??wh] + +\appendtoks + \setuevalue\currenttooltip{\scrn_tooltip_direct{\currenttooltip}}% +\to \everydefinetooltip + +\unexpanded\def\scrn_tooltip_direct#tag% + {\def\currenttooltip{#tag}% + \doifelselocation + {\dosingleempty\scrn_tooltip_indeed} + {\dosingleempty\scrn_tooltip_ignore}} + +\def\scrn_tooltip_ignore[#settings]#anchortext#tiptext% + {#anchortext} + +\def\scrn_tooltip_indeed[#settings]#anchortext#tiptext% a more modern aproach (push buttons) + {\dontleavehmode \hbox \bgroup + \dontcomplain + \global\advance\scrn_tooltip_n\plusone + \edef\currenttooltipname{tooltip:\number\scrn_tooltip_n}% + \setbox\scrn_tooltip_box_anchor\hbox + {\strut#anchortext}% + \doifassignmentelse{#settings} + {\setuptooltip[\currenttooltip][#settings]}% + {\setuptooltip[\currenttooltip][\c!location=#settings]}% + \setbox\scrn_tooltip_box_text\hbox + {\localframed[\??wh\currenttooltip][\c!location=]{#tiptext}}% + \definesymbol + [\currenttooltipname:txt] + [\copy\scrn_tooltip_box_text]% + \definefieldbody + [\currenttooltipname:txt] + [\c!type=push, + \c!width=\wd\scrn_tooltip_box_text, + \c!height=\ht\scrn_tooltip_box_text, + \c!depth=\dp\scrn_tooltip_box_text, + \c!option=\v!hidden, + \c!values=\currenttooltipname:txt]% + \setbox\scrn_tooltip_box_text\hbox + {\fieldbody[\currenttooltipname:txt]}% + \setbox\scrn_tooltip_box_text\hbox + {\strut\lower\dimexpr.25ex+\ht\scrn_tooltip_box_text\relax\box\scrn_tooltip_box_text}% + \edef\currenttooltiplocation{\tooltipparameter\c!location}% + \ifx\currenttooltiplocation\v!left + \hsmashed{\hskip\wd\scrn_tooltip_box_anchor\llap{\box\scrn_tooltip_box_text}}% + \else\ifx\currenttooltiplocation\v!middle + \hsmashed to \wd\scrn_tooltip_box_anchor{\hss\box\scrn_tooltip_box_text\hss}% + \else + \hsmashed{\box\scrn_tooltip_box_text}% + \fi\fi + \definesymbol + [\currenttooltipname:but] + [\hphantom{\copy\scrn_tooltip_box_anchor}]% + \definefieldbody + [\currenttooltipname:but] + [\c!type=push, + \c!regionin=action(show{\currenttooltipname:txt}), + \c!regionout=action(hide{\currenttooltipname:txt}), + \c!width=\wd\scrn_tooltip_box_anchor, + \c!height=\ht\scrn_tooltip_box_anchor, + \c!depth=\dp\scrn_tooltip_box_anchor]% + \hsmashed{\fieldbody[\currenttooltipname:but]}% + \egroup + #anchortext}% when hyphenated the text wil stick out ... such are fields and we cannot use a link here + +\definetooltip[tooltip] + +%D From messages on the mailing list we can conclude that +%D fieldstacks are used so we keep them in the core: +%D +%D \starttyping +%D \definesymbol[one] [one] +%D \definesymbol[two] [two] +%D \definesymbol[three][three] +%D +%D \definefieldstack[mine][one,two,three] +%D \fieldstack[mine] +%D \fieldstack[mine] +%D +%D \goto{walk field}[Walk{mine}] +%D \stoptyping + +\unexpanded\def\definefieldstack + {\dotripleargument\scrn_fieldstack_define} + +\def\scrn_fieldstack_define[#tag][#symbols][#settings]% + {\ifcsname scrn_fieldstack:#tag\endcsname \else + \setgvalue{scrn_fieldstack:#tag}{\scrn_fieldstack_construct[#tag][#symbols][#settings]}% + \fi} + +\unexpanded\def\fieldstack + {\dotripleempty\scrn_fieldstack_direct} + +\def\scrn_fieldstack_direct[#tag][#symbols][#settings]% + {\ifsecondargument + \scrn_fieldstack_define[#tag][#symbols][#settings]% + \fi + \getvalue{scrn_fieldstack:#tag}} + +\newbox\scrn_fieldstack_box + +\def\scrn_fieldstack_add#tag#settings#symbol% + {\advance\scratchcounter\plusone + \edef\currentfieldstackname{#tag:\number\scratchcounter}% + \ifnum\scratchcounter=\@@fdstart\relax + \definefieldbody[\currentfieldstackname][\c!type=check,\c!values={#symbol,\empty},\c!default={#symbol}]% + \else + \definefieldbody[\currentfieldstackname][\c!type=check,\c!values={#symbol,\empty},\c!default=]% + \fi + \setbox\scrn_fieldstack_box\hbox{\symbol[#symbol]}% + \setcollector + [fieldstack] + {\fieldbody + [\currentfieldstackname] + [\c!option=\v!readonly, + \c!width=\wd\scrn_fieldstack_box, + \c!height=\ht\scrn_fieldstack_box, + \c!depth=\dp\scrn_fieldstack_box, + #settings]}} + +\def\scrn_fieldstack_construct[#tag][#symbols][#settings]% start=n, 0 == leeg + {\iflocation + \dontleavehmode + \begingroup + \getparameters[\??fd][\c!start=1,#settings]% + \scrn_field_load_scripts + \definecollector + [fieldstack]% + [\c!corner=\v!middle, + \c!location=\v!middle]% + \scratchcounter\zerocount + \processcommalist[#symbols]{\scrn_fieldstack_add{#tag}{#settings}}% + \flushcollector[fieldstack]% + \endgroup + \fi} + +%D Another goodie. Two actions can be hookes into an overlay. +%D +%D \starttyping +%D \defineviewerlayer[test] +%D +%D \startviewerlayer[test]Hide Me\stopviewerlayer +%D +%D \defineoverlay +%D [WithTest] +%D [{\overlayrollbutton[HideLayer{test}][VideLayer{test}]}] +%D +%D \framed[background=WithTest]{toggle} +%D \stoptyping + +\newcount\scrn_rollbutton_n + +\unexpanded\def\overlayrollbutton + {\dodoubleargument\scrn_rollbutton_overlay} + +\def\scrn_rollbutton_overlay[#regionin][#regionout]% + {\iflocation + \bgroup + \global\advance\scrn_rollbutton_n\plusone + \definesymbol + [rollbutton:\number\scrn_rollbutton_n] + [{\framed[\c!frame=\v!off,\c!width=\overlaywidth,\c!height=\overlayheight]{}}]% + \definefieldbody + [rollbutton:\number\scrn_rollbutton_n] + [\c!type=push, + \c!regionin={#regionin}, + \c!regionout={#regionout}, + \c!values=\currentsystemfield, + \c!default=\currentsystemfield]% + \fitfield[\currentsystemfield]% + \egroup + \fi} + +\protect \endinput + +%D I will redo these when I need them. + +% \definepushbutton [reset] +% +% \definepushsymbol [reset] [n] [\uniqueMPgraphic{whatever}{color=green}] +% \definepushsymbol [reset] [r] [\uniqueMPgraphic{whatever}{color=white}] +% +% \startinteractionmenu[bottom] +% \psh [reset] [JS(reset_something)] \\ +% \stopinteractionmenu + +\newcount\scrn_pushbutton_n + +\unexpanded\def\definepushbutton % name optional setup + {\dodoubleempty\scrn_pushbutton_define} + +\def\scrn_pushbutton_define[#tag][#settings]% + {\scrn_pushbutton_define_variant{#tag}{n}{push}% + \scrn_pushbutton_define_variant{#tag}{r}{\symbol[pushsymbol:#tag:n]}% + \scrn_pushbutton_define_variant{#tag}{d}{\symbol[pushsymbol:#tag:r]}% + \setvalue{pushbutton:#tag}{\scrn_pushbutton_handle{#tag}{#settings}}} + +\def\scrn_pushbutton_define_variant#tag#variant#content% + {\doifsymboldefinedelse{pushsymbol:#tag:#variant} + \donothing + {\definesymbol[pushsymbol:#tag:#variant][{#content}]}} + +\def\scrn_pushbutton_handle#tag#settings#reference% + {\bgroup + \global\advance\scrn_pushbutton_n\plusone + \setupfield + [pushbutton] + [\c!frame=\v!overlay, + \c!offset=\v!overlay, + \c!clickout={#reference}, + #settings]% + \definefield + [pushbutton:\number\scrn_pushbutton_n]% + [push] + [pushbutton] + [pushsymbol:#tag:n,pushsymbol:#tag:r,pushsymbol:#tag:d]% + \fitfield + [pushbutton:\number\scrn_pushbutton_n]% + \egroup} + +\unexpanded\def\definepushsymbol + {\dotripleargument\scrn_pushsymbol_define} + +\def\scrn_pushsymbol_define[#tag][#variant]% [#reference] + {\definesymbol[pushsymbol:#tag:#variant]} + +\def\pushbutton + {\dodoubleargument\scrn_pushbutton_direct} + +\def\scrn_pushbutton_direct[#tag][#variant]% + {\executeifdefined{pushbutton:#tag}\gobbleoneargument{#variant}} + +%D We plug into the menu system + +\unexpanded\def\scrn_menu_psh_start[#reference]#text\stoppsh + {\starttxt\pushbutton[\currentmenu][#reference]\stoptxt} + +\unexpanded\def\scrn_menu_psh_direct[#reference]#text\\ + {\scrn_menu_psh_start[#reference]\stoprob} + +\appendtoks + \let\startpsh\scrn_menu_psh_start + \let\stoppsh \relax + \let\psh \scrn_menu_psh_direct +\everysetmenucommands + +%D Another goodie: (unchecked in \MKIV) + +% calls: +% {..} [JS..] +% [left] {..} [JS..] +% [a=b] {..} [JS..] +% [left] [a=b] {..} [JS..] +% +% \setupbuttons[offset=0pt,frame=off] % alternative=hidden +% +% \rollbutton {Manuals} [JS(Goto_File{show-man.pdf})] +% \rollbutton {Articles} [JS(Goto_File{show-art.pdf})] +% \rollbutton {Papers} [JS(Goto_File{show-pap.pdf})] +% \rollbutton {Presentations} [JS(Goto_File{show-pre.pdf})] +% \rollbutton {Resources} [JS(Goto_File{show-res.pdf})] +% +% \rob [JS(...)] bla bla \\ + +% \definecolor[rollover:n][red] +% \definecolor[rollover:r][green] +% \definecolor[rollover:d][blue] + +\definepalet + [rollover] + [n=red, + r=green, + d=blue] + +\newcount\scrn_rollbutton_n_button +\newcount\scrn_rollbutton_n_symbol + +\unexpanded\def\rollbutton + {\dodoubleempty\scrn_rollbutton} + +\def\scrn_rollbutton[#tag][#settings]#text[#reference]% + {\dontleavehmode + \bgroup + \doglobal\advance\scrn_rollbutton_n_button + \doglobal\advance\scrn_rollbutton_n_symbol + \iffirstargument + \ifsecondargument + \getparameters[\??am#tag][#settings]% + \def\scrn_rollbutton_symbol{\scrn_rollbutton_symbol_indeed{\??am#tag}{#text}}% + \else + \doifassignmentelse{#tag} + {\getparameters[\??bt][#tag]% + \def\scrn_rollbutton_symbol{\scrn_rollbutton_symbol_indeed{\??bt}{#text}}} + {\def\scrn_rollbutton_symbol{\scrn_rollbutton_symbol_indeed{\??am#tag}{#text}}}% + \fi + \else + \def\scrn_rollbutton_symbol{\set_location_box_indeed_indeed{\??bt}{#text}}% + \fi + % todo: share symbols, tricky since different dimensions + \definesymbol[rollsymbol:\number\scrn_rollbutton_n_symbol:n][\scrn_rollbutton_symbol{n}]% + \definesymbol[rollsymbol:\number\scrn_rollbutton_n_symbol:r][\scrn_rollbutton_symbol{r}]% + \definesymbol[rollsymbol:\number\scrn_rollbutton_n_symbol:d][\scrn_rollbutton_symbol{d}]% + \setupfield + [rollbutton] + [\c!frame=\v!off, + \c!offset=\v!overlay, + \c!clickout={#reference}]% + \definefield + [rollbutton:\number\scrn_rollbutton_n_button][push][rollbutton] + [rollsymbol:\number\scrn_rollbutton_n_symbol:n,% + rollsymbol:\number\scrn_rollbutton_n_symbol:r,% + rollsymbol:\number\scrn_rollbutton_n_symbol:d]% + \fitfield[rollbutton:\number\scrn_rollbutton_n_button]% + \egroup} + +\unexpanded\def\scrn_rollbutton_symbol_indeed#namespace#text#what% + {\definecolor[rollover][rollover:#what]% + \doifelse{#what}{n}{\doifelsevalue{#namespace\c!alternative}\v!hidden\phantom\hbox}\hbox + {\localframed[#namespace] + [\c!framecolor=rollover,\c!backgroundcolor=rollover,\c!color=rollover]% + {\dolocationattributes{#namespace}\c!style\c!color{#text}}}}% + +%D We plug into the menu system + +\unexpanded\def\scrn_menu_rob_start[#reference]#text\stoprob + {\starttxt\rollbutton[\currentmenu]{\ignorespaces#text\unskip}[#reference]\stoptxt} + +\unexpanded\def\scrn_menu_rob_direct[#reference]#text\\ + {\scrn_menu_rob_start[#reference]#text\stoprob} + +\appendtoks + \let\startrob\scrn_menu_rob_start + \let\stoprob \relax + \let\rob \scrn_menu_rob_direct +\everysetmenucommands + +\protect \endinput diff --git a/tex/context/base/scrn-hlp.lua b/tex/context/base/scrn-hlp.lua new file mode 100644 index 000000000..81d68840b --- /dev/null +++ b/tex/context/base/scrn-hlp.lua @@ -0,0 +1,120 @@ +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 has_attribute = node.has_attribute +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 = has_attribute(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-hlp.mkiv b/tex/context/base/scrn-hlp.mkiv deleted file mode 100644 index 4eaa340ca..000000000 --- a/tex/context/base/scrn-hlp.mkiv +++ /dev/null @@ -1,179 +0,0 @@ -%D \module -%D [ file=scrn-hlp, -%D version=1998.10.10, -%D title=\CONTEXT\ Screen Macros, -%D subtitle=Help (Experimental), -%D author={Hans Hagen \& Ton Otten}, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -% todo : dedicated vide/hide voor helps - -\writestatus{loading}{ConTeXt Screen Macros / Help popups} - -%D This is an experimental and private module, so the interface -%D and functionality can change. Pieces of code will be moved -%D to other modules. More features are possible but will be -%D interfaces later. See m-chart for an application as well -%D as the second tno-tpd manual (graphic in margin, click -%D on it, pop up big one, use menu with hides, as well as -%D background, etc. etc. - -\unprotect - -\defineframedtext - [\v!helptext] - -\setupframedtexts - [\v!helptext] - [\c!width=.75\textwidth, - \c!align=\v!normal, - \c!frame=\v!off, - \c!background=\v!screen] - -\newcounter \nofhelpdataentries -\newconditional \somehelpdatadefined - -\let\getpagehelpdata \relax -\let\synchronizepagehelpdata\relax - -\appendtoks \getpagehelpdata \to \beforeeverypage -\appendtoks \synchronizepagehelpdata \to \aftereverypage - -% will be proper state variable - -\let\pagehelpdata\empty - -\def\dontresetpagedata - {\let\synchronizepagehelpdataindeed\relax} - -\def\resetpagehelpdata - {\iflocation - \let\synchronizepagehelpdataindeed\resetpagehelpdata - \global\let\pagehelpdata\empty - \resetreference[HideHelp]% - \fi} - -\let\synchronizepagehelpdataindeed\resetpagehelpdata - -\resetreference[HideHelp] - -\def\getpagehelpdataindeed - {\iflocation\ifcase\nofhelpdataentries\else - \dogetpagehelpdataindeed - \fi\fi} - -\def\dogetpagehelpdataindeed - {\let\pagehelpdata\empty - \ifconditional\somehelpdatadefined - \definetwopasslist{hlp:\realfolio}% - \doloop - {\gettwopassdata{hlp:\realfolio}% - \iftwopassdatafound - \addtocommalist\twopassdata\pagehelpdata - \else - \exitloop - \fi}% - \fi - \ifx\pagehelpdata\empty \else - \useJSscripts[fld]% - \definereference[HideHelp][JS(Hide_Fields)]% for the moment - \fi} - -\def\setpagehelpdata[#1]% - {\iflocation\expanded{\dosetpagehelpdata{#1}}\fi} - -\def\dosetpagehelpdata#1% - {\doglobal\increment\nofhelpdataentries - \global\let\getpagehelpdata\getpagehelpdataindeed - \global\let\synchronizepagehelpdata\synchronizepagehelpdataindeed - \savetwopassdata{hlp:\realfolio}{\nofhelpdataentries}{#1}} - -\setvalue{\e!start\v!helptext}[#1]% - {\iflocation - \global\settrue\somehelpdatadefined - \setvalue{\e!stop\v!helptext}% - %{\definesymbol[helpinfo:#1][{\doframedtext[\v!helptext]{\getbuffer[\v!helptext]}}]% - % \dopresetfieldsymbol{helpinfo:#1}}% - {\definesymbol[\v!helptext:#1][{\doframedtext[\v!helptext]{\getbuffer[\v!helptext]}}]% - \dopresetfieldsymbol{\v!helptext:#1}}% - \else - \letvalue{\e!stop\v!helptext}\relax - \fi - \dostartbuffer[\v!helptext][\e!start\v!helptext][\e!stop\v!helptext]} - -\long\def\helptext[#1]#2% - {\iflocation - \global\settrue\somehelpdatadefined - %\definesymbol[helpinfo:#1][{\doframedtext[\v!helptext]{#2}}]% - %\dopresetfieldsymbol{helpinfo:#1}% - \definesymbol[\v!helptext:#1][{\doframedtext[\v!helptext]{#2}}]% - \dopresetfieldsymbol{\v!helptext:#1}% - \fi} - -\let\definehelptext\helptext % for backward compabilities sake - -\def\dohelpdata#1% - {\setbox\scratchbox\hbox - {\startoverlay - {\box\scratchbox} - %{\definemainfield[help:#1][check][helpsetup][helpinfo:#1][helpinfo:#1]% - {\definemainfield[help:#1][check][helpsetup][\v!helptext:#1][\v!helptext:#1]% - \fitfield[help:#1]} - \stopoverlay}} - -\def\helpdata - {\iflocation - \bgroup - %\getpagehelpdata - \ifx\pagehelpdata\empty \else - \setupfields[\v!reset]% - \setupfield - [helpsetup] - [\c!width=\v!fit, - \c!height=\v!fit, - \c!frame=\v!off, - \c!clickin=JS(Hide_Fields), - \c!option={\v!readonly,\v!hidden}]% - \setbox\scratchbox\emptybox - \processcommacommand[\pagehelpdata]\dohelpdata - \box\scratchbox - \fi - \egroup - \fi} - -\def\helpbutton % also gobble spaces between [][] - {\dodoubleempty\dohelpbutton} - -\def\dohelpbutton - {\ifsecondargument - \expandafter\donohelpbutton - \else - \expandafter\dodohelpbutton - \fi} - -\def\dodohelpbutton[#1][#2]#3[#4]% #2 is space gobbling dummy - {\iflocation - \setpagehelpdata[#4]% - \useJSscripts[fld]% - \button[#1]{#3}[JS(Vide_Hide_Fields{help:#4})]% - \fi} - -\def\donohelpbutton[#1][#2]% - {\dodohelpbutton[#1][]{}[#2]} - -\def\doifhelpinfo#1#2% - {\iflocation - \doifsymboldefinedelse{helpinfo:#1}{#2}\donothing - \fi} - -\def\doifelsehelpinfo#1#2#3% - {\iflocation - \doifsymboldefinedelse{helpinfo:#1}{#2}{#3}% - \fi} - -\protect \endinput diff --git a/tex/context/base/scrn-hlp.mkvi b/tex/context/base/scrn-hlp.mkvi new file mode 100644 index 000000000..d97824300 --- /dev/null +++ b/tex/context/base/scrn-hlp.mkvi @@ -0,0 +1,162 @@ +%D \module +%D [ file=scrn-hlp, +%D version=1998.10.10, +%D title=\CONTEXT\ Screen Macros, +%D subtitle=Help (Experimental), +%D author={Hans Hagen \& Ton Otten}, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Screen Macros / Help popups} + +%D As this functionality was in the core and as I don't know +%D how often it is used, we'll keep it around. However, it is +%D upgraded and usage has changed a bit. We also use some +%D \LUA\ magic in order to avoid multiple passes. + +\registerctxluafile{scrn-hlp}{1.001} + +%D Using help boils down to plugging the placement macro +%D someplace visible, like: +%D +%D \starttyping +%D \setuptexttexts[\centerbox{\placehelp}] +%D \stoptyping +%D +%D When this is done, the following should work out okay: +%D +%D \starttyping +%D test \helptext{word}{tip top 1 is somewhat longer} test +%D test \helptext{word}{tip top 2} test +%D +%D \starthelptext[oeps] +%D \input tufte +%D \stophelptext +%D +%D test test \showhelp{some help}[oeps] test +%D test test \button[location=depth]{\helpsignal{oeps}OEPS}[oeps] test +%D +%D test test \button[location=depth]{next}[page(2)] test +%D +%D \page +%D +%D test \helptext{word}{tip top one} test +%D test \helptext{word}{tip top two} test +%D \stoptyping +%D +%D Currently you need to use the signal in custom macros but +%D that might change at some point. + +\unprotect + +% also status + +\newbox \scrn_help_box +\newcount\scrn_help_n + +\definesystemattribute[help][public] + +\installcommandhandler \??wp {help} \??wp + +\setuphelp + [\c!frame=\v!off, + \c!align=\v!normal, + \c!background=\v!color, + \c!backgroundcolor=gray] + +\presetlocalframed[\??wp] + +\appendtoks + \setuevalue \currenthelp {\scrn_help_argument{\currenthelp}}% + \setuevalue{\e!start\currenthelp}{\scrn_help_start {\currenthelp}}% + \setuevalue{\e!stop \currenthelp}{\scrn_help_stop }% +\to \everydefinehelp + +\unexpanded\def\scrn_help_argument#category% + {\def\currenthelp{#category}% + \global\advance\scrn_help_n\plusone + \edef\currenthelpname{help:\number\scrn_help_n}% + \doifelselocation + {\dosingleempty\scrn_help_argument_indeed} + {\dosingleempty\scrn_help_argument_ignore}} + +\def\scrn_help_argument_indeed[#reference]#text#target% + {\edef\currenthelpreference{#reference}% + \dontleavehmode \hbox \bgroup + \dontcomplain + \setbox\scrn_help_box\hbox{\strut#text}% + \doregisterhelp{#target}% + \egroup % can be usernode instead + \goto + {\helpsignal{\number\scrn_help_n}#target}% + [\helpaction{\number\scrn_help_n}]} + +\def\scrn_help_argument_ignore[#reference]#text#target% + {#target} + +\unexpanded\def\scrn_help_start#category% + {\def\currenthelp{#category}% + \global\advance\scrn_help_n\plusone + \edef\currenthelpname{help:\number\scrn_help_n}% + \dosingleempty\scrn_help_start_indeed} + +\def\scrn_help_start_indeed[#reference]% + {\edef\currenthelpreference{#reference}% + \dostartbuffer[\currenthelp][\e!start\currenthelp][\e!stop\currenthelp]} + +\unexpanded\def\scrn_help_stop + {\iflocation + \scrn_help_register{\getbuffer[\currenthelp]}% + \fi} + +\def\scrn_help_register#text% + {\setbox\scrn_help_box\hbox + {\localframed[\??wp\currenthelp]{#text}}% + \definesymbol + [\currenthelpname] + [\copy\scrn_help_box]% + \definefieldbody + [\currenthelpname] + [\c!type=push, + \c!width=\wd\scrn_help_box, + \c!height=\ht\scrn_help_box, + \c!depth=\dp\scrn_help_box, + \c!option=\v!hidden, + \c!clickin=action(hide{\currenthelpname}), + \c!closepage=action(hide{\currenthelpname}), + \c!values=\currenthelpname]% + \setbox\scrn_help_box\hbox + {\fieldbody[\currenthelpname]}% + \ctxcommand{registerhelp(\number\scrn_help_n,"\currenthelpreference",\number\scrn_help_box)}} + +\def\doifelsehelp + {\ifcase\scrn_help_n + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\def\placehelp % was \helpdata + {\ifinpagebody\ifcase\scrn_help_n\else + \ctxcommand{collecthelp(255)}% rather hard coded ... bad + \fi\fi} + +\def\helpreference#category% + {\ctxcommand{helpreference("#category")}} + +\def\helpaction#category% + {\ctxcommand{helpaction("#category")}} + +\unexpanded\def\helpsignal#category% + {\hbox attr \helpattribute \helpreference{#category}{}} + +\unexpanded\def\showhelp#target[#category]% + {\goto{\helpsignal{#category}#target}[#category]} + +\definehelp[\v!helptext] + +\protect \endinput diff --git a/tex/context/base/scrn-ini.lua b/tex/context/base/scrn-ini.lua new file mode 100644 index 000000000..76696eed0 --- /dev/null +++ b/tex/context/base/scrn-ini.lua @@ -0,0 +1,21 @@ +if not modules then modules = { } end modules ['scrn-int'] = { + 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.general = interactions.general or { } +local general = interactions.general + +local codeinjections = backends.codeinjections + +local function setupidentity(specification) + codeinjections.setupidentity(specification) +end + +general.setupidentity = setupidentity + +commands.setupidentity = setupidentity diff --git a/tex/context/base/scrn-ini.mkvi b/tex/context/base/scrn-ini.mkvi new file mode 100644 index 000000000..860c696c0 --- /dev/null +++ b/tex/context/base/scrn-ini.mkvi @@ -0,0 +1,178 @@ +%D \module +%D [ file=scrn-ini, +%D version=2011.02.27, +%D title=\CONTEXT\ Interaction Macros, +%D subtitle=Initialization, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Interaction Macros / Initialization} + +\unprotect + +\registerctxluafile{scrn-ini}{1.001} + +%D There is no interaction at all unless enabled by saying: +%D +%D \starttyping +%D \setupinteraction[state=start] +%D \stoptyping +%D +%D The other settings are: +%D +%D \showsetup{setupinteraction} + +\installcommandhandler\??ia{interaction}\??ia + +\let\currentinteraction\empty + +\appendtoks + \doifelse{\interactionparameter\c!state}\v!start + {\locationtrue \setsystemmode \v!interaction}% + {\locationfalse \resetsystemmode\v!interaction}% +\to \everysetupinteraction + +\def\doifelselocation + {\iflocation + \expandafter\firstoftwoarguments + \else + \expandafter\secondoftwoarguments + \fi} + +\setupinteraction + [\c!state=\v!stop] + +\appendtoks + \setupinteraction % todo: remember info at the lua end (already possible) +\to \everyjob + +% it makes no sense to create an environment as we will seldom have structured +% interactionso a general start-stop will do +% +% \appendtoks +% \setuevalue \currentinteraction {\scrn_interaction_direct{\currentinteraction}}% +% \setuevalue{\e!start\currentinteraction}{\scrn_interaction_start {\currentinteraction}}% +% \setuevalue{\e!stop \currentinteraction}{\scrn_interaction_stop }% +% \to \everydefineinteraction +% +% \unexpanded\def\scrn_interaction_direct#1% +% {\edef\currentinteraction{#1}} +% +% \unexpanded\def\scrn_interaction_start#1% +% {\pushmacro\currentinteraction +% \edef\currentinteraction{#1}} +% +% \unexpanded\def\scrn_interaction_stop +% {\popmacro\currentinteraction} +% +% \unexpanded\def\setinteraction[#1]% +% {\def\currentinteraction{#1}} +% +% \defineinteraction[\v!interaction] + +\unexpanded\def\startinteraction[#1]% + {\pushmacro\currentinteraction + \edef\currentinteraction{#1}} + +\unexpanded\def\stopinteraction + {\popmacro\currentinteraction} + +\unexpanded\def\setinteraction[#1]% + {\def\currentinteraction{#1}} + +%D As long as there a natural feeling of what can be considered +%D hyper active or not, we have to tell users where they can +%D possibly click. We've already seen a few macros that deal +%D with this visualization, something we definitely do not let +%D up to the viewer. One way of telling is using a distinctive +%D typeface, another way is using color. +%D +%D There are two colors involved: one for normal hyperlinks, +%D and one for those that point to the currentpage, the +%D contrast color. + +\definecolor [interactioncolor] [r=0, g=.6, b=0] +\definecolor [interactioncontrastcolor] [r=.8, g=0, b=0] + +%D The next few macros are responsible for highlighting hyper +%D links. The first one, \type{\showlocation}, is used in those +%D situations where the typeface is handled by the calling +%D macro. + +%D When we're dealing with pure page references, contrast +%D colors are used when we are already at the page mentioned. + +\def\setlocationcolor#1% not grouped ! + {\ifnum\referencepagestate=\plusone + \edef\askedcontrastcolor{\csname#1\c!contrastcolor\endcsname}% + \ifx\askedcontrastcolor\empty + \dosetcolorattribute{#1}\c!color + \else + \dosetcolorattribute{#1}\c!contrastcolor + \fi + \else % we could just set and if > 0 set again + \dosetcolorattribute{#1}\c!color + \fi} + +\def\setlocationfont#1% + {\dosetfontattribute{#1}\c!style} + +\def\setlocationattributes#1% + {\ifnum\referencepagestate=\plusone + \edef\askedcontrastcolor{\csname#1\c!contrastcolor\endcsname}% + \ifx\askedcontrastcolor\empty + \dosetcolorattribute{#1}\c!color + \else + \dosetcolorattribute{#1}\c!contrastcolor + \fi + \else % we could just set and if > 0 set again + \dosetcolorattribute{#1}\c!color + \fi + \dosetfontattribute{#1}\c!style} + +\def\setlocationcolorspec#1% \resolver + {\ifnum\referencepagestate=\plusone + \edef\askedcontrastcolor{#1\c!contrastcolor}% + \ifx\askedcontrastcolor\empty + \doactivatecolor{#1\c!color}% + \else + \doactivatecolor\askedcontrastcolor + \fi + \else + \doactivatecolor{#1\c!color}% + \fi} + +\setupinteraction + [\c!style=\v!bold, + \c!color=interactioncolor, + \c!contrastcolor=interactioncontrastcolor] + +%D Identity + +\def\scrn_identity_synchronize + {\ctxcommand{setupidentity{ + title = \!!bs\interactionparameter\c!title\!!es, + subtitle = \!!bs\interactionparameter\c!subtitle\!!es, + author = \!!bs\interactionparameter\c!author\!!es, + creator = \!!bs ConTeXt - \contextversion\!!es, + date = \!!bs\interactionparameter\c!date\!!es, + keywords = \!!bs\interactionparameter\c!keyword\!!es, + }}} + +\appendtoks + \scrn_identity_synchronize +\to \everysetupinteraction + +\setupinteraction + [\c!title=, + \c!subtitle=, + \c!author=, + \c!keyword=, + \c!date=] + +\protect \endinput diff --git a/tex/context/base/scrn-int.lua b/tex/context/base/scrn-int.lua deleted file mode 100644 index 7bb1a7a66..000000000 --- a/tex/context/base/scrn-int.lua +++ /dev/null @@ -1,114 +0,0 @@ -if not modules then modules = { } end modules ['scrn-int'] = { - 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 { } -local interactions = interactions - -interactions.attachments = interactions.attachments or { } -interactions.soundclips = interactions.soundclips or { } -interactions.renderings = interactions.renderings or { } -interactions.linkedlists = interactions.linkedlists or { } - -local attachments = interactions.attachments -local soundclips = interactions.soundclips -local renderings = interactions.renderings -local linkedlists = interactions.linkedlists - -local jobpasses = job.passes - -function attachments.register(specification) - if specification.label then - specification.filename = specification.filename or specification.label - specification.newname = specification.newname or specification.filename - specification.title = specification.title or specification.filename - specification.newname = file.addsuffix(specification.newname,file.extname(specification.filename)) - attachments[specification.label] = specification - return specification - end -end - -function attachments.attachment(label) - local at = attachments[label] - if not at then - interfaces.showmessage("interactions",6,label) - return attachments.register { label = label } - else - return at - end -end - -function attachments.var(label,key) - local at = attachments[label] - context(at and at[key] or "") -end - -function soundclips.register(specification) - if specification.label then - specification.filename = specification.filename or specification.label - soundclips[specification.label] = specification - return specification - end -end - -function soundclips.soundclip(label) - local sc = soundclips[label] - if not sc then - -- todo: message - return soundclips.register { label = label } - else - return sc - end -end - -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 - -function renderings.var(label,key) - local rn = renderings[label] - context(rn and rn[key] or "") -end - --- linked lists - -function linkedlists.define(name) - -- no need -end - -function linkedlists.add(name) - local tobesaved = jobpasses.gettobesaved(name) - local collected = jobpasses.getcollected(name) 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.setlinkproperties(currentlink,noflinks,f,p,n,l) -end - -function linkedlists.enhance(name,n) - local ll = jobpasses.gettobesaved(name) - if ll then - ll[n] = texcount.realpageno - end -end diff --git a/tex/context/base/scrn-int.mkiv b/tex/context/base/scrn-int.mkiv deleted file mode 100644 index cc25f48b2..000000000 --- a/tex/context/base/scrn-int.mkiv +++ /dev/null @@ -1,613 +0,0 @@ -%D \module -%D [ file=scrn-int, -%D version=1995.01.01, -%D title=\CONTEXT\ Core Macros, -%D subtitle=Interaction, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{ConTeXt Screen Macros / Interaction} - -\registerctxluafile{scrn-int}{1.001} - -\unprotect - -%D This is an update of \MKII\ code. In the process profiles and versions -%D were removed as I never used them (although they were kind of cool at -%D that time). - -% a bit complex due to papercomment (see imposition code) - -\newtoks\everysetupinteractionscreen - -\unexpanded\def\setupinteractionscreen - {\dosingleempty\dosetupinteractionscreen} - -\def\dosetupinteractionscreen[#1]% - {\getparameters[\??sc][#1]% - \the\everysetupinteractionscreen} - -\def\synchronizepaperdimensionssimple % simple version - {\bgroup - \ifx\@@ppleft \empty - \ifx\@@ppright \empty - \ifx\@@pptop \empty - \ifx\@@ppbottom \empty - \ifx\@@pcstate\v!start - \locationfalse\fi\else - \locationfalse\fi\else - \locationfalse\fi\else - \locationfalse\fi\else - \locationfalse\fi - \iflocation % without screen settings - \ctxlua{backends.codeinjections.setupcanvas { - paperwidth = \number\paperwidth, - paperheight = \number\paperheight - }}% - \else - \ctxlua{backends.codeinjections.setupcanvas { - paperwidth = \number\printpaperwidth, - paperheight = \number\printpaperheight - }}% - \fi - \egroup} - -\def\synchronizepaperdimensionscomplex % complex version - {\bgroup - \edef\@@scwidth {\@@scwidth}% - \edef\@@scheight{\@@scheight}% - \ifx\@@scwidth\v!fit - \!!widtha\leftcombitotal - \ifdim\backspace>\!!widtha\ifdim\backspace>\zeropoint\relax - \advance\backspace -\!!widtha - \fi\fi - \advance\!!widtha\dimexpr\rightcombitotal+2\dimexpr\@@scbackspace+\@@schoroffset\relax\relax - \else\ifx\@@scwidth\v!max - \!!widtha\printpaperwidth - \else - \!!widtha\@@scwidth - \fi\fi - \ifx\@@scheight\v!fit - \!!heighta\dimexpr\topheight+\topdistance\relax - \ifdim\topspace>\!!heighta\ifdim\topspace>\zeropoint\relax - \advance\topspace -\!!heighta - \fi\fi - \advance\!!heighta\dimexpr\makeupheight+\bottomdistance+\bottomheight+2\dimexpr\@@sctopspace+\@@scveroffset\relax\relax - \else\ifx\@@scheight\v!max - \!!heighta\printpaperheight - \else - \!!heighta\@@scheight - \fi\fi - \doif\@@scdelay\v!none{\let\@@scdelay\zerocountervalue}% - \ifdim\!!widtha>\paperwidth\ifdim\!!widtha>\zeropoint - \paperwidth\!!widtha - \fi\fi - \ifdim\!!heighta>\paperheight\ifdim\!!heighta>\zeropoint - \paperheight\!!heighta - \fi\fi - \ctxlua{backends.codeinjections.setupcanvas { - mode = "\@@scoption", - % doublesided = \ifsinglesided false\else\ifdoublesided true\else false\fi\fi, - singlesided = \ifsinglesided true\else false\fi, - doublesided = \ifdoublesided true\else false\fi, - leftoffset = \number\dimexpr\backoffset\relax, - topoffset = \number\dimexpr\topoffset \relax, - width = \number\dimexpr\!!widtha \relax, - height = \number\dimexpr\!!heighta \relax, - paperwidth = \number\paperwidth, - paperheight = \number\paperheight - }}% - \egroup} - -\let\synchronizepaperdimensions \synchronizepaperdimensionscomplex - -\appendtoks - \ifproductionrun - \doifelse\@@pcstate\v!start - {\let\synchronizepaperdimensions\synchronizepaperdimensionssimple} - {\let\synchronizepaperdimensions\synchronizepaperdimensionscomplex}% - \fi -\to \everysetupinteractionscreen - -\appendtoks \synchronizepaperdimensions \to \everyshipout - -%D The next mechanism, linked lists, is quite old and -%D is \MKIV'd for completeness. I will finish the -%D confuguration part when I need it. - -% todo: a kind of button that gets a tag passed (\??tk) - -% \starttext -% \setupinteraction[state=start] -% \definelinkedlist[demo] -% \dorecurse{10}{\linkedlistelement[demo]{link \recurselevel} \page} -% \stoptext - -\def\linkedlistparameter #1{\csname\dolinkedlistparameter{\??lk\currentlinkedlist}#1\endcsname} -\def\dolinkedlistparameter #1#2{\ifcsname#1#2\endcsname#1#2\else\expandafter\dolinkedlistparentparameter\csname#1\s!parent\endcsname#2\fi} -\def\dolinkedlistparentparameter#1#2{\ifx#1\relax\s!empty\else\dolinkedlistparameter#1#2\fi} - -\unexpanded\def\definelinkedlist{\dodoubleargument\dodefinelinkedlist} -\unexpanded\def\setuplinkedlist {\dodoubleargument\dosetuplinkedlist } -\unexpanded\def\setuplinkedlists{\dosingleargument\dosetuplinkedlists} - -\def\dodefinelinkedlist[#1][#2]% - {\ctxlua{interactions.linkedlists.define("#1")}% - \getparameters[\??lk#1][\s!parent=\??lk,#2]} - -\def\dosetuplinkedlist[#1][#2]% - {\getparameters[\??lk#1][#2]} - -\def\dosetuplinkedlists[#1]% - {\getparameters[\??lk][#1]} - -\def\setlinkproperties#1#2#3#4#5#6% - {\def\currentlink {#1}% - \def\noflinks {#2}% - \def\firstlink {#3}% - \def\previouslink{#4}% - \def\nextlink {#5}% - \def\lastlink {#6}} - -\def\linkedlistelement[#1]#2% currently no view support - {\dontleavehmode\hbox\bgroup - #2% - \iflocation - \edef\currentlinkedlist{#1}% - \ifcsname\??lk\currentlinkedlist\s!parent\endcsname - \hskip\linkedlistparameter\c!distance - \ctxlua{interactions.linklists.add("\currentlinkedlist")}% - \expanded{\ctxlatelua{interactions.linklists.enhance("\currentlinkedlist",\currentlink)}}% - \dogotosomepage {\??lk\currentlinkedlist}\gotobegincharacter \firstlink - \ifnum\noflinks>\plustwo - \dogotosomepage{\??lk\currentlinkedlist}\gobackwardcharacter\previouslink - \dogotosomepage{\??lk\currentlinkedlist}\goforwardcharacter \nextlink - \fi - \dogotosomepage {\??lk\currentlinkedlist}\gotoendcharacter \lastlink - \else - \writestatus\m!interactions{no such linked list: \currentlinkedlist}% - \fi - \fi - \egroup} - -\setuplinkedlists - [\c!distance=.25em, - \c!width=\v!fit, - \c!location=\v!low, - \c!color=\@@iacolor, - \c!frame=\v!off, - \c!background=, - \c!backgroundcolor=] - -\def\koppeling {\linkedlistelement} -\def\stelkoppelingenin {\setuplinkedlists} -\def\definieerkoppeling{\definelinkedlist} - -%D Conditional page breaks: - -\def\screen - {\dosingleempty\doscreen} - -\def\doscreen[#1]% - {\iflocation\page[#1]\fi} - -%D Page transitions: - -\let\askedpagetransitions\empty - -\unexpanded\def\setuppagetransitions - {\dosingleempty\dosetuppagetransitions} - -\def\dosetuppagetransitions[#1]% - {\edef\askedpagetransitions{#1}} - -\def\setpagetransition - {\iflocation \ifx\askedpagetransitions\empty \else - \ctxlua{backends.codeinjections.setpagetransition{ n = "\askedpagetransitions", delay = "\@@scdelay" }}% - \fi \fi} - -\prependtoks \setpagetransition \to \everyshipout - -\setuppagetransitions - [\v!reset] - -%D Comments: - -\newbox\commentcollection -\newbox\commentbox -\newbox\commentboxone -\newbox\commentboxtwo - -\def\raisedcommentanchors#1#2{#1{\hbox{\raise\strutht#2}}} - -\setvalue{\??cc:\c!location:\v!inmargin }{\raisedcommentanchors\inmargin } -\setvalue{\??cc:\c!location:\v!leftedge }{\raisedcommentanchors\inleftedge } -\setvalue{\??cc:\c!location:\v!rightedge }{\raisedcommentanchors\inrightedge } -\setvalue{\??cc:\c!location:\v!leftmargin }{\raisedcommentanchors\inleftmargin } -\setvalue{\??cc:\c!location:\v!rightmargin}{\raisedcommentanchors\inrightmargin} - -\let\flushcommentanchors\relax - -\def\doflushcommentanchors - {\global\let\flushcommentanchors\relax - \ifvoid\commentbox\else\dodoflushcommentanchors\fi} % in everypar so indirect - -\def\dodoflushcommentanchors - {\executeifdefined{\??cc:\c!location:\@@cclocation}\hbox{\box\commentbox}} - -\unexpanded\def\setupcomment - {\dodoubleargument\getparameters[\??cc]} - -\unexpanded\def\placecomments{\box\commentcollection} % when option=buffer - -\def\doinsertcomment#1% - {\begingroup - \doifelse\@@ccoption\v!max{\let\@@ccopen\s!true}{\let\@@ccopen\s!false}% - \ctxlua{backends.codeinjections.presetsymbollist("\@@ccsymbol")}% - % in between predefined symbols are dealt with - \ctxlua{backends.codeinjections.registercomment { - title = "\@@cctitle", - width = \number\dimexpr\@@ccwidth \relax, - height = \number\dimexpr\@@ccheight\relax, - colormodel = \number\currentcolormodel, - colorvalue = \thecolorattribute{\@@cccolor}, - open = \@@ccopen, - symbol = "\@@ccsymbol", - buffer = "#1", - layer = "\@@cctextlayer" - }}% - \box\commentboxone - \doif\@@ccoption\v!buffer - {\setbox\scratchbox\vbox to \@@ccheight{\forgetall\vss\box\commentboxtwo}% - \wd\scratchbox\@@ccwidth - \global\setbox\commentcollection\vbox - {\startoverlay{\box\commentcollection}{\box\scratchbox}\stopoverlay}}% - \endgroup} - -\setvalue{\e!start\v!comment}{\dodoubleempty\dostartcomment} - -\def\dostartcomment[#1][#2]% - {\bgroup - \doifassignmentelse{#1}{\getparameters[\??cc][#1]}{\getparameters[\??cc][\c!title=#1,#2]}% - \dostartbuffer[\v!comment\v!buffer][\v!comment\v!buffer][\e!start\v!comment][\e!stop\v!comment]} - -\unexpanded\def\stopcomment - {\doif\@@ccstate\v!start - {\global\let\flushcommentanchors\doflushcommentanchors - \global\setbox\commentbox\frozenhbox - {\hbox to \zeropoint{\struttedbox{\tbox{\doinsertcomment{\v!comment\v!buffer}}}\hss}% - \hskip\ifvoid\commentbox\@@ccmargin\else\@@ccdistance\fi - \box\commentbox}}% - \egroup} - -\def\comment - {\dodoubleempty\docomment} - -\def\docomment[#1][#2]#3% - {\doif\@@ccstate\v!start - {\hbox to \zeropoint - {\doifassignmentelse{#1}{\getparameters[\??cc][#1]}{\getparameters[\??cc][\c!title=#1,#2]}% - \hskip-\@@ccmargin - \ctxlua{buffers.assign("\v!comment\v!buffer", \!!bs\detokenize{#3}\!!es)}% - \struttedbox{\tbox{\doinsertcomment{\v!comment\v!buffer}}\hss}}}% - \ignorespaces} - -% test -% -% \startcomment -% hello beautiful\\world -% \stopcomment -% -% test -% -% \startcomment[hello] -% hello << eerste >> -% beautiful -% world -% \stopcomment -% -% test -% -% \startcomment[hello][color=green,width=10cm,height=3cm] -% hello \leftguillemot\ \'e\'erste \rightguillemot\ -% beautiful -% world -% \stopcomment -% -% test -% -% \startcomment[hello][color=red,width=4cm,height=3cm] -% hello \leftguillemot\ \'e\'erste \rightguillemot\ test -% -% beautiful -% -% world -% \stopcomment -% -% test -% -% \startcomment[symbol=Balloon] -% Do we want this kind of rubish? And, why isn't this and -% some more features related to text annotations so poorly -% (actually not) documented? Anyhow, by providing this -% functionality we demonstrate that \pdfTeX\ can do it. By -% the way, it's funny that when in Acrobat we scale up the -% text, the symbols scale down. -% \stopcomment -% -% test -% -% \definesymbol [comment-normal][{\externalfigure[cow.pdf]}] -% \definesymbol [comment-down] [{\externalfigure[cow.pdf]}] -% -% \def\CowSymbol#1#2% -% {\scale -% [\c!height=#1] -% {\startMPcode -% loadfigure "koe.mp" number 1 ; -% refill currentpicture withcolor #2 ; -% \stopMPcode}} -% -% \definesymbol [comment-normal] -% [\CowSymbol{4ex}{red}] -% -% \definesymbol [comment-down] -% [\CowSymbol{4ex}{green}] -% -% \setupcomment -% [\c!symbol={comment-normal,comment-down}, -% \c!option=\v!buffer] -% -% \startcomment[hello] -% oeps -% \stopcomment -% -% test -% -% \setupcomment -% [\c!symbol=normal, -% \c!option=max,width=10cm] -% -% \startcomment[hello] -% oeps -% \stopcomment -% -% test - -\setupcomment - [\c!state=\v!start, - \c!margin=2.5em, - \c!distance=1em, - \c!width=.3\textwidth, - \c!height=.2\textheight, - \c!color=\@@iacolor, - \c!title=, - \c!space=\v!no, - \c!symbol=\v!normal, - \c!location=\v!inmargin, - \c!option=, - \c!textlayer=] - -%D Attachments: - -% \setupinteraction[state=start] -% -% \useattachment[test.tex] -% \useattachment[whatever][test.tex] -% \useattachment[whatever][newname][test.tex] -% \useattachment[whatever][title][newname][test.tex] -% -% % \setupattachments[\c!symbol={symbol-normal,symbol-down}] -% -% \starttext \attachment[whatever] \stoptext - -\def\useattachment - {\doquadrupleempty\douseattachment} - -\def\douseattachment[#1][#2][#3][#4]% tag title newname filename - {\iffourthargument - \dodouseattachment{#1}{#2}{#3}{#4}% - \else\ifthirdargument - \dodouseattachment{#1}{#2}{#2}{#3}% - \else\ifsecondargument - \dodouseattachment{#1}{#2}{#2}{#2}% - \else - \dodouseattachment{#1}{#1}{#1}{#1}% - \fi\fi\fi} - -\def\dodouseattachment#1#2#3#4% tag title newname filename - {\ctxlua{interactions.attachments.register{label="#1",title="#2",newname="#3",filename="#4"}}} - -\def\attachment - {\dodoubleempty\doattachment} - -\def\doattachment[#1][#2]% [tag] [settings] - {\iflocation - \doif\@@atstate\v!start - {\bgroup - \setupattachments[#2]% - \ctxlua{backends.codeinjections.presetsymbollist("\@@atsymbol")}% - % we cannot yet ask for the wd/ht/dp of an xform else we could use those - \setbox\scratchbox\hbox{\symbol[\lastpredefinedsymbol]}% - \doif\@@atwidth \v!fit{\edef\@@atwidth {\the\wd\scratchbox}}% - \doif\@@atheight\v!fit{\edef\@@atheight{\the\ht\scratchbox}}% - \doif\@@atdepth \v!fit{\edef\@@atdepth {\the\dp\scratchbox}}% - % - \setbox\scratchbox\hbox - {\getvalue{\??at:\@@atalternative}{\ctxlua{backends.codeinjections.attachfile{ - label = "#1", - width = \number\dimexpr\@@atwidth \relax, - height = \number\dimexpr\@@atheight\relax, - depth = \number\dimexpr\@@atdepth \relax, - color = "\@@atcolor", - symbol = "\@@atsymbol", - layer = "\@@attextlayer", - }}}}% - \wd\scratchbox\@@atwidth - \ht\scratchbox\@@atheight - \dp\scratchbox\@@atdepth - \box\scratchbox - \egroup}% - \fi} - -\setvalue{\??at:\v!high}#1{\struttedbox{\tbox{#1}}} - -\unexpanded\def\setupattachments - {\dodoubleempty\getparameters[\??at]} - -\setupattachments - [\c!state=\v!start, - \c!color=\@@iacolor, - \c!textlayer=, - \c!width=\v!fit, - \c!height=\v!fit, - \c!depth=\v!fit, - \c!alternative=\v!high, - \c!symbol=] - -%D Defining sound tracks: -%D -%D \starttyping -%D \useexternalsoundtrack[label][file] -%D \stoptyping -%D -%D associated actions: StartSound StopSound PauseSound ResumeSound -%D -%D Todo: like external figures, also search on path, -%D although, they need to be present ar viewing time, so ... - -\def\useexternalsoundtrack - {\dodoubleargument\douseexternalsoundtrack} - -\def\douseexternalsoundtrack[#1][#2]% - {\ctxlua{interactions.soundclips.register{ label="#1", filename="#2" }}} - -\def\checksoundtrack#1% yet untested in mkiv (also move management to lua) - {\iflocation - \ctxlua{backends.nodeinjections.insertsound{ - label = "#1", - repeat = "\@@sdoption", % not entirely ok but works - }}% - \fi} - -\unexpanded\def\setupexternalsoundtracks - {\dodoubleargument\getparameters[\??sd]} - -\setupexternalsoundtracks - [\c!option=] - -%D Multi Media: - -% todo: multiple instances, dus indirect - -\let\currentrendering\empty - -\definereference[StartCurrentRendering] [\v!StartRendering {\currentrendering}] -\definereference[StopCurrentRendering] [\v!StopRendering {\currentrendering}] -\definereference[PauseCurrentRendering] [\v!PauseRendering {\currentrendering}] -\definereference[ResumeCurrentRendering][\v!ResumeRendering{\currentrendering}] - -\newcounter\nofexternalrenderings - -\def\useexternalrendering{\doquadrupleempty\douseexternalrendering} -\def\setinternalrendering{\dodoubleempty \dosetinternalrendering} - -\def\douseexternalrendering[#1][#2][#3][#4]% tag mime file options - {\ctxlua{interactions.renderings.register { - kind = "external", - label = "#1", - mime = "#2", - filename = "#3", - options = "#4", - }}} - -\def\dosetinternalrendering[#1][#2]% tag options {content} - {\bgroup - \dowithnextbox - {\ctxlua{interactions.renderings.register { - kind = "internal", - label = "#1", - mime = "IRO", - filename = "#1", - options = "#2", - }}% - \let\objectoffset\zeropoint - \setobject{IRO}{#1}\hbox{\box\nextbox}% - \egroup}% - \hbox} - -\def\renderingtype #1{\ctxlua{interactions.renderings.var("#1","kind")}} -\def\renderingoptions#1{\ctxlua{interactions.renderings.var("#1","options")}} - -\def\renderingwidth {8cm} -\def\renderingheight {6cm} - -\unexpanded\def\definerenderingwindow - {\dodoubleempty\dodefinerenderingwindow} - -\def\dodefinerenderingwindow[#1][#2]% - {\presetlocalframed[\??rw#1]% - \getparameters - [\??rw#1]% - [\c!openpageaction=,\c!closepageaction=,% - \c!width=\renderingwidth,\c!height=\renderingheight,% - #2]} - -\unexpanded\def\setuprenderingwindow - {\dodoubleargument\dosetuprenderingwindow} - -\def\dosetuprenderingwindow[#1]% - {\getparameters[\??rw#1]} - -\unexpanded\def\placerenderingwindow - {\dodoubleempty\doplacerenderingwindow} - -\def\doplacerenderingwindow[#1][#2]% - {\bgroup - \edef\currentrendering{\ifsecondargument#2\else#1\fi}% - \doifelse{\renderingtype\currentrendering}{internal} % an object - {\getobjectdimensions{IRO}\currentrendering - \edef\renderingheight{\the\dimexpr\objectheight+\objectdepth\relax}% - \edef\renderingwidth{\objectwidth}% - \dogetobjectreferencepage{IRO}\currentrendering\renderingpage}% - {\def\renderingheight{\vsize}% - \def\renderingwidth{\hsize}% - \def\renderingpage{\realpageno}}% - % create fall back if needed - \ifcsname\??rw#1\c!width\endcsname - \def\currentrenderingwindow{#1}% - \else - \let\currentrenderingwindow\s!default - \definerenderingwindow[\currentrenderingwindow]% - \fi -% todo -% \handlereferenceactions{\getvalue{\??rw\currentrenderingwindow\c!openpageaction }}\dosetuprenderingopenpageaction -% \handlereferenceactions{\getvalue{\??rw\currentrenderingwindow\c!closepageaction}}\dosetuprenderingclosepageaction - \localframed - [\??rw\currentrenderingwindow][\c!offset=\v!overlay]% - {\vfill - \ctxlua{backends.codeinjections.insertrenderingwindow { - label = "\currentrendering", - width = \number\dimexpr\renderingwidth\relax, - height = \number\dimexpr\renderingheight\relax, - options = "\renderingoptions\currentrendering", - page = \number\renderingpage, - }}\hfill}% - \egroup} - -\setupinteractionscreen - [\c!width=\printpaperwidth, - \c!height=\printpaperheight, - \c!horoffset=\!!zeropoint, - \c!veroffset=\!!zeropoint, - \c!backspace=\backspace, - \c!topspace=\topspace, - \c!option=\v!auto, - \c!delay=\v!none] - -\protect \endinput diff --git a/tex/context/base/scrn-men.mkiv b/tex/context/base/scrn-men.mkiv deleted file mode 100644 index 1e987f098..000000000 --- a/tex/context/base/scrn-men.mkiv +++ /dev/null @@ -1,629 +0,0 @@ -%D \module -%D [ file=scrn-bar, % was part of scrn-int -%D version=1995.01.01, -%D title=\CONTEXT\ Core Macros, -%D subtitle=Menus, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{ConTeXt Screen Macros / Menus} - -\unprotect - -% \startinteractionmenu[rechts] -% \but [eerste] eerste \\ -% \txt hello world \\ -% \but [tweede] tweede \\ -% \nop \\ -% \but [tweede] tweede \\ -% \rul whow \\ -% \but [tweede] tweede \\ -% \raw hello world \\ -% \but [tweede] tweede \\ -% \com \vfill \\ -% \but [derde] derde \\ -% \stopinteractionmenu - -% \setupinteraction[menu=on,state=start] -% -% \defineinteractionmenuclass[test] [vertical] -% \defineinteractionmenuclass[another][horizontal] -% -% \defineinteractionmenu[test] [left][state=start,width=4cm] -% \defineinteractionmenu[another][top] [state=start,height=1cm] -% -% \startinteractionmenu[test] -% \but [firstpage] test-a \\ -% \but [nextpage] test-b \\ -% \stopinteractionmenu -% -% \startinteractionmenu[another] -% \but [firstpage] test-a \\ -% \but [nextpage] test-b \\ -% \stopinteractionmenu -% -% \setupheadertexts[{\interactionmenu[another]}] -% -% \starttext -% -% test \interactionmenu[test] \page -% test \interactionmenu[test] \page -% -% \stoptext - -% ja : kader/achtergrond met tekst -% leeg : kader/achtergrond maar geen tekst -% nee : alleen ruimte reserveren -% geen : helemaal weglaten -% -% \setupinteractionmenu[right][samepage=yes, unknownreference=yes] -% \setupinteractionmenu[right][samepage=empty,unknownreference=empty] -% \setupinteractionmenu[right][samepage=no, unknownreference=no] -% \setupinteractionmenu[right][samepage=none, unknownreference=none] -% -% \startinteractionmenu[right] -% \but [firstpage] first \\ -% \but [lastpage] last \\ -% \but [somepage] crap \\ -% \stopinteractionmenu - -%D Define menus: - -\def\setmenuparameter#1#2#3{\@EA\def\csname\??am#1:#2\endcsname{#3}} -\def\letmenuparameter #1#2{\@EA\let\csname\??am#1:#2\endcsname} - -\def\menuparameter #1{\csname\domenuparameter{\??am\currentmenu:}#1\endcsname} -\def\namedmenuparameter#1#2{\csname\domenuparameter{\??am #1:}#2\endcsname} -\def\menuparameterhash #1{\domenuparameterhash {\??am\currentmenu:}#1} - -\def\domenuparameter #1#2{\ifcsname#1#2\endcsname#1#2\else\expandafter\domenuparentparameter \csname#1\s!parent\endcsname#2\fi} -\def\domenuparameterhash#1#2{\ifcsname#1#2\endcsname #1\else\expandafter\domenuparentparameterhash\csname#1\s!parent\endcsname#2\fi} - -\def\domenuparentparameter #1#2{\ifx#1\relax\s!empty\else\domenuparameter #1#2\fi} -\def\domenuparentparameterhash#1#2{\ifx#1\relax \else\domenuparameterhash#1#2\fi} - -\unexpanded\def\defineinteractionmenu - {\dotripleempty\dodefineinteractionmenu} - -\def\dodefineinteractionmenu[#1][#2][#3]% [name] [location] [settings|parent] % right right vertical - {\ifsecondargument - \ifcsname\??am:\c!list:#2\endcsname \else - \letvalueempty{\??am:\c!list:#2}% - \fi - \normalexpanded{\noexpand\addtocommalist{#1}\@EA\noexpand\csname\??am:\c!list:#2\endcsname}% - \setvalue{\@@dodolistelement#1}{\def\dosomelistelement{\dodomenulistelement{#1}}}% - \ifthirdargument - \presetlocalframed[\??am#1:]% - \doifassignmentelse{#3} - {\doifelse{#1}{#2} - {\getparameters[\??am#1:][\c!location=#2,\c!menu=,\s!parent=\??am,#3]} - {\getparameters[\??am#1:][\c!location=#2,\c!menu=,\s!parent=\??am#2:,#3]}}% - {\doifelsenothing{#3} - {\getparameters[\??am#1:][\c!location=#2,\c!menu=,\s!parent=\??am]} - {\getparameters[\??am#1:][\c!location=#2,\c!menu=,\s!parent=\??am#3:]}}% - \else - \getparameters[\??am#1:][\c!location=#2,\c!menu=,\s!parent=\??am#2:]% - \fi - \else - \getparameters[\??am#1:][\s!parent=\??am]% simple cloning, like vertical - \fi} - -\def\currentmenulist{\ifcsname\??am:\c!list:\currentmenu\endcsname\csname\??am:\c!list:\currentmenu\endcsname\fi} - -%D Setup menus: - -\unexpanded\def\setupinteractionmenu - {\dodoubleargument\dosetupinteractionmenu} - -\def\dosetupinteractionmenu[#1][#2]% - {\def\docommand##1{\getparameters[\??am##1:][#2]}% - \processcommalist[#1]\docommand} - -\unexpanded\def\setupinteractionmenus[#1]% - {\getparameters[\??am][#1]} - -%D Fill menus: - -\normalexpanded{\long\def\expandafter\noexpand\csname\e!start\v!interactionmenu\endcsname[#1]#2\expandafter\noexpand\csname\e!stop\v!interactionmenu\endcsname}% - {\long\setmenuparameter{#1}\c!menu{\dointeractionmenu{#1}{#2}}} - -\def\resetinteractionmenu[#1]% - {\letmenuparameter{#1}\c!menu\empty} - -%D Check if menus permitted: - -\newif\iflocationmenupermitted - -\def\testinteractionmenu - {\iflocation - \doifelse\@@iamenu\v!on - {\doifelse{\menuparameter\c!state}\v!start - {\global\locationmenupermittedtrue} - {\global\locationmenupermittedfalse}} - {\global\locationmenupermittedfalse}% - \else - \global\locationmenupermittedfalse - \fi} - -%D Placement of menus: - -\def\interactionmenus[#1]% location - {\iflocation - \csname\??am:\c!menu:#1\endcsname - \fi} - -\setvalue{\??am:\c!menu :\v!left }{\horizontalinteractionmenu\v!left \leftedgewidth } -\setvalue{\??am:\c!menu :\v!right }{\horizontalinteractionmenu\v!right \rightedgewidth} -\setvalue{\??am:\c!menu :\v!top }{\verticalinteractionmenu \v!top \topheight } -\setvalue{\??am:\c!menu :\v!bottom}{\verticalinteractionmenu \v!bottom\bottomheight } - -\setvalue{\??am:\c!command:\v!right }{\@@amvbox{}\rightedgewidth} -\setvalue{\??am:\c!command:\v!left }{\@@amvbox{}\leftedgewidth } -\setvalue{\??am:\c!command:\v!top }{\@@amhbox{}\topheight } -\setvalue{\??am:\c!command:\v!bottom}{\@@amhbox{}\bottomheight } - -\def\dointeractionmenu#1#2% - {\edef\currentmenu{#1}% - \getvalue{\??am:\c!command:\menuparameter\c!location}\currentmenu{#2}} - -\unexpanded\def\interactionmenu[#1]% - {\def\currentmenu{#1}% - \menuparameter\c!menu} - -\newdimen \intermenudistance -\newdimen \finalmenuwidth -\newdimen \finalmenuheight - -\newcounter\currentamposition % better \currentmenuposition -\newtoks \everysetmenucommands - -\def\horizontalinteractionmenu#1#2% location vhsize before/after - {\ifdim#2>\zeropoint - \edef\currentmenu{#1}% - \finalmenuwidth#2\relax - \horizontalinteractionmenuindeed - \fi} - -\def\verticalinteractionmenu#1#2% - {\ifdim#2>\zeropoint - \edef\currentmenu{#1}% - \finalmenuheight#2\relax - \verticalinteractionmenuindeed - \fi} - -\def\horizontalinteractionmenuindeed - {\global\intermenudistance\zeropoint - \setbox\scratchbox\hbox - {\processcommacommand[\currentmenulist]\somehorizontalinteractionmenu}% - \wd\scratchbox\finalmenuwidth\relax - \box\scratchbox} - -\def\verticalinteractionmenuindeed - {\global\intermenudistance\zeropoint - \setbox\scratchbox\vbox - {\processcommacommand[\currentmenulist]\someverticalinteractionmenu}% - \ht\scratchbox\finalmenuheight - \dp\scratchbox\zeropoint - \box\scratchbox} - -\def\somehorizontalinteractionmenu#1% - {\begingroup - \edef\currentmenu{#1}% - \doifnot{\menuparameter\c!state}\v!none - {\hskip\intermenudistance - \setbox\scratchbox\hbox to \finalmenuwidth - {\menuparameter\c!left - \interactionmenu[#1]% - \menuparameter\c!right}% - \doifelse{\menuparameter\c!distance}\v!overlay - {\global\intermenudistance\zeropoint - \wd\scratchbox\zeropoint}% - {\global\intermenudistance\menuparameter\c!distance}% - \box\scratchbox}% - \endgroup} - -\def\someverticalinteractionmenu#1% - {\begingroup - \edef\currentmenu{#1}% - \doifnot{\menuparameter\c!state}\v!none - {\vskip\intermenudistance - \setbox\scratchbox\vbox to \finalmenuheight - {\menuparameter\c!before - \interactionmenu[#1]% - \menuparameter\c!after}% - \doifelse{\menuparameter\c!distance}\v!overlay - {\global\intermenudistance\zeropoint - \offinterlineskip - \dp\scratchbox\zeropoint - \ht\scratchbox\zeropoint}% - {\global\intermenudistance\menuparameter\c!distance}% - \box\scratchbox}% - \endgroup} - -% don't change skipping, this one works! \showcomposition removed - -\def\@@amhbox#1#2#3#4% #1 obsolete, #3 is redundant - {\edef\currentmenu{#3}% - \testinteractionmenu - \iflocationmenupermitted - \begingroup - \forgetall - \scratchdimen\dimexpr\makeupwidth+\pagebackgroundhoffset*2-\menuparameter\c!leftoffset-\menuparameter\c!rightoffset\relax - \setbox\scratchbox\hbox to \scratchdimen - {\executeamboxcommands{#3}{#4}\c!left\c!middle\c!right}% - \setbox\scratchbox\hbox{\dowholemenuposition{#3}{\box\scratchbox}}% cannot happen in previous due to align - \wd\scratchbox\makeupwidth % geen \ht=#2 setting (yet) - \hskip\dimexpr-\pagebackgroundhoffset+\menuparameter\c!leftoffset\relax - \box\scratchbox - \endgroup - \fi} - -\def\@@amvbox#1#2#3#4% #1 obsolete, #3 is redundant - {\edef\currentmenu{#3}% - \testinteractionmenu - \iflocationmenupermitted - \bgroup - \forgetall - \scratchdimen\dimexpr\textheight+\pagebackgroundvoffset*2+\pagebackgrounddepth-\menuparameter\c!topoffset-\menuparameter\c!bottomoffset\relax - \setbox\scratchbox\vbox to \scratchdimen - {\restorestandardblank % todo: vspacing - \hsize#2\relax - \executeamboxcommands{#3}{#4}\c!before\c!inbetween\c!after}% - % strange: when we mnake this a hbox the content disappears - \setbox\scratchbox\vbox{\dowholemenuposition{#3}{\box\scratchbox}}% cannot happen in previous due to align - \setbox\scratchbox\vbox - {\ht\scratchbox\zeropoint - \vskip\dimexpr-\pagebackgroundvoffset+\menuparameter\c!topoffset\relax - \box\scratchbox - \vskip\pagebackgroundvoffset}% overbodig - \ht\scratchbox\textheight - \wd\scratchbox#2\relax - \box\scratchbox - \egroup - \fi} - -\def\executeamboxcommands#1#2#3#4#5% - {\begingroup - \edef\currentmenu{#1}% - \menuparameter#3\relax - \setamboxcommands{#1}{#4}% - \ignorespaces#2\unskip - \menuparameter#5\relax - \endgroup} - -\def\setamboxcommands#1#2% - {\edef\currentmenu{#1}% - \edef\betweenmenu{#2}% - \doglobal\newcounter\currentamposition - \the\everysetmenucommands} - -\def\addsomemenuitem#1% - {\dontleavehmode - \begingroup - \ignorespaces#1\unskip\relax - \ifconditional\skippedmenuitem \else - \menuparameter\betweenmenu - \fi - \endgroup - \ignorespaces} - -%D This can save complicated menu macros when one want to -%D keep control over parts of a menu (i.e.\ turn them on and -%D off). We could have achieved something similar with modes. - -\def\local@@ambox#1#2#3#4% don't change skipping, this one works! - {\begingroup - \edef\currentmenu{#3}% - \iflocationmenupermitted - \executeamboxcommands{#3}{#4}\c!before\c!inbetween\c!after - \fi - \endgroup} - -\def\includemenu[#1]% - {\begingroup - \edef\currentmenu{#1}% - \doif{\menuparameter\c!state}\v!local - {\letmenuparameter\currentmenu\c!state\v!start - \let\@@amvbox\local@@ambox - \let\@@amhbox\local@@ambox - \menuparameter\c!menu}% - \endgroup} - -%D The menu commands: - -% to be redone, using parent inheritance instead - -% ja : kader/achtergrond met tekst -% leeg : kader/achtergrond maar geen tekst -% nee : alleen ruimte reserveren -% geen : helemaal weglaten - -\newconditional\skippedmenuitem -\newconditional\usemenuclick - -\def\dosetlocationboxcontent#1[#2]#3[#4]% to be checked - {\global\setfalse\skippedmenuitem - \setbox\locationbox\hbox{\localframed[#1][#2]{#3}}% - \ifconditional\usemenuclick - \gotobox{\box\locationbox}[#4]% - \else - \box\locationbox - \fi} - -\def\dosetlocationboxempty#1[% - {\dosetlocationboxcontent{#1}[\c!empty=\v!yes,} - -\def\dosetlocationboxno#1[% - {\dosetlocationboxcontent{#1}[\c!empty=\v!yes,\c!frame=,\c!background=,} - -\def\dosetlocationboxnone#1[#2]#3[#4]% - {\global\settrue\skippedmenuitem} - -% make two sub macros - -% \dosetfontattribute {#1}{#2}% -% \dosetcolorattribute{#1}{#3}% - -\def\locboxyesinject - {\ctxlua{structures.references.injectcurrentset(nil,nil)}} - -\def\locboxyesnormal#1#2#3% - {\hbox attr \referenceattribute \lastreferenceattribute {\localframed[#1][#2]{#3}}} - -\def\locboxyescontrast#1#2#3% - {\hbox attr \referenceattribute \lastreferenceattribute {\localframed[#1][#2,\c!color=\menuparameter\c!contrastcolor]{#3}}} - -\def\locboxyesempty#1#2#3% - {\localframed[#1][\c!empty=\v!yes,#2]{#3}} - -\def\locboxyesnothing#1#2#3% - {\localframed[#1][\c!empty=\v!yes,\c!frame=,\c!background=,#2]{#1}} - -\def\setlocationboxyes#1[#2]#3[#4]% needs to be split as the attr is not applicable to the box - {\begingroup - \settrue\usemenuclick - \global\setfalse\skippedmenuitem - \attribute\referenceattribute\attributeunsetvalue - \doifreferencefoundelse{#4} - {\analyzecurrentreference % we need to act on the state - \ifcase\referencepagestate - % something else than a page reference - \locboxyesinject - \locboxyesnormal{#1}{#2}{#3}% - \else - \ifcase\csname\??am:\c!location:\menuparameter\c!samepage\endcsname\relax - % yes: same page or not ... todo - \locboxyesinject - \ifnum\referencepagestate=\plusone % same page - \locboxyescontrast{#1}{#2}{#3}% - \else % elsewhere - \locboxyesnormal{#1}{#2}{#3}% - \fi - \or - % empty but frame: no click - \ifnum\referencepagestate=\plusone % same page - \locboxyesempty{#1}{#2}{#3} - \else % elsewhere - \locboxyesinject - \locboxyesnormal{#1}{#2}{#3}% - \fi - \or - % empty no frame: no - \ifnum\referencepagestate=\plusone % same page - \locboxyesnothing{#1}{#2}{#3}% - \else % elsewhere - \locboxyesinject - \locboxyesnormal{#1}{#2}{#3}% - \fi - \or - % nothing at all - \global\settrue\skippedmenuitem - \fi - \fi}% - {\ifcase\csname\??am:\c!location:\menuparameter\c!unknownreference\endcsname\relax - \localframed[#1][#2]{#3}% - \or - \locboxyesempty{#1}{#2}{#3} - \or - \locboxyesnothing{#1}{#2}{#3}% - \or - \global\skippedmenuitemtrue - \fi}% - \endgroup} - -\def\setlocationboxraw#1[#2]#3[#4]% - {\localframed[#1][#2]{#3}} - -\def\setlocationnop#1[#2]#3% - {\localframed[#1][#2]{#3}} - -\def\menu@raw[#1]#2\\% - {\addsomemenuitem{\gotobox{\ignorespaces#2\unskip}[#1]}} - -\def\menu@but[#1]#2\\% - {\addsomemenuitem{\domenuitemposition\currentmenu{#1}{\setlocationboxyes{\??am\currentmenu:}[]{\ignorespaces#2\unskip}[#1]}}} - -\def\menu@got[#1]#2\\% - {\addsomemenuitem{\setlocationboxyes{\??am\currentmenu:}[\c!frame=\v!off,\c!background=]{\ignorespaces#2\unskip}[#1]}} - -\def\menu@nop#1\\% - {\addsomemenuitem{\setlocationboxraw{\??am\currentmenu:}[\c!frame=\v!off,\c!background=,\c!empty=\v!yes]{\ignorespaces#1\unskip}[]}} - -\def\menu@txt#1\\% - {\addsomemenuitem{\localframed[\??am\currentmenu:][\c!frame=\v!off,\c!background=]{\ignorespaces#1\unskip}}} - -\def\menu@rul#1\\% - {\addsomemenuitem{\localframed[\??am\currentmenu:][]{\ignorespaces#1\unskip}}} - -\def\menu@com#1\\% - {\ignorespaces#1\unskip\ignorespaces} - -\appendtoks - \let\raw\menu@raw \let\but\menu@but \let\got\menu@got \let\nop\menu@nop - \let\txt\menu@txt \let\rul\menu@rul \let\com\menu@com -\to \everysetmenucommands - -\ifdefined\domenuitemposition \else \let\domenuitemposition \gobbletwoarguments \fi -\ifdefined\dowholemenuposition \else \let\dowholemenuposition\gobbleoneargument \fi - -%D We also need an explicit position control some day. I'll -%D do that when I need it. [The stacking order.] - -% [name] [location] -% [name] [location] [pars] - -\expandafter\let\csname\??am:\c!location:\v!yes \endcsname\zerocount -\expandafter\let\csname\??am:\c!location:\v!empty \endcsname\plusone -\expandafter\let\csname\??am:\c!location:\v!no \endcsname\plustwo -\expandafter\let\csname\??am:\c!location:\v!none \endcsname\plusthree -\expandafter\let\csname\??am:\c!location:\v!normal \endcsname\plusone % default -\expandafter\let\csname\??am:\c!location:\s!default\endcsname\plusone % default -\expandafter\let\csname\??am:\c!location:\s!empty \endcsname\plusone % default - -\def\dodomenulistelement#1#2#3#4#5#6#7% - {\addsomemenuitem{\domenuitemposition\currentmenu{internal(#3)}% - {\setlocationboxyes{\??am\currentmenu:}[]{\limitatetext{#5}{\namedlistparameter{#2}\c!maxwidth}{\unknown}}[internal(#3)]}}} - -\unexpanded\def\menubutton - {\dodoubleempty\domenubutton} - -\def\domenubutton[#1]% - {\iffirstargument - \ifsecondargument - \@EAEAEA\domenubuttonB - \else - \doifassignmentelse{#1} - {\@EAEAEA\domenubuttonC} - {\@EAEAEA\domenubuttonD}% - \fi - \else - \@EA\domenubuttonA - \fi[#1]} - -\def\domenubuttonA[#1][#2]#3[#4]{\setlocationboxyes \??bt[]{#3}[#4]} % normal button, no parameters -\def\domenubuttonB[#1][#2]#3[#4]{\setlocationboxyes{\??am#1:}[#2]{#3}[#4]} % menu button, with parameters -\def\domenubuttonC[#1][#2]#3[#4]{\setlocationboxyes \??bt[#1]{#3}[#4]} % normal button, with parameters -\def\domenubuttonD[#1][#2]#3[#4]{\setlocationboxyes {\??am#1:}[]{#3}[#4]} % menu button, no parameters - -\def\menubox - {\dodoubleempty\domenubox} - -\def\domenubox[#1][#2]#3% - {\bgroup - \let\setlocationboxyes\setlocationboxraw - \domenubutton[#1][#2]#3[]% - \egroup} - -% jammer, tussen/midden had erin gemoeten; \c!commando toevoegen - -\def\registermenucommand#1% - {{\textonly\noindent#1\space}} % no math switching - -\def\doregistermenubuttons[#1][#2]% [menu id] [register] - {\bgroup - \ifsecondargument - \setupinteractionmenu[#1][\c!unknownreference=\v!yes,\c!samepage=\v!yes]% - \def\docommand##1{\registermenucommand{\menubutton[#1]{##1}[#2:##1]}}% - \else - \def\docommand##1{\registermenucommand{\button[\c!unknownreference=\v!yes,\c!samepage=\v!yes]{##1}[#1:##1]}}% - \fi - \handletokens abcdefghijklmnopqrstuvwxyz\with\docommand % moet anders - \egroup} - -\def\registermenubuttons - {\dodoubleempty\doregistermenubuttons} - -\defineinteractionmenu [\v!vertical] % we happen to know that this works out ok (just a setup set) -\defineinteractionmenu [\v!horizontal] % we happen to know that this works out ok (just a setup set) - -\defineinteractionmenu [\v!right ] [\v!right ] [\v!vertical ] % we share a setup set -\defineinteractionmenu [\v!left ] [\v!left ] [\v!vertical ] % we share a setup set -\defineinteractionmenu [\v!top ] [\v!top ] [\v!horizontal] % we share a setup set -\defineinteractionmenu [\v!bottom] [\v!bottom] [\v!horizontal] % we share a setup set - -\setupinteractionmenus - [\c!offset=.25em, - \c!position=\v!no, - \c!frame=\v!on, - \c!background=, - \c!backgroundcolor=, - \c!foregroundstyle=\menuparameter\c!style, - \c!foregroundcolor=\menuparameter\c!color, - \c!style=\@@iastyle, - \c!color=\@@iacolor, - \c!contrastcolor=\@@iacontrastcolor, - \c!state=\v!start, - \c!samepage=\v!yes, - \c!unknownreference=\v!empty, - \c!distance=\bodyfontsize, % 12pt - \c!topoffset=\zeropoint, - \c!bottomoffset=\zeropoint, - \c!leftoffset=\zeropoint, - \c!rightoffset=\zeropoint] - -\setupinteractionmenu - [\v!vertical] % not really a menu - [\c!before=, - \c!after=\vfil, - \c!inbetween=\blank, - \c!left=\hss, - \c!right=\hss, - \c!height=\v!broad] - -\setupinteractionmenu - [\v!horizontal] % not really a menu - [\c!before=\vss, - \c!after=\vss, - \c!middle=\hfil, - \c!width=\v!fit, - \c!height=\v!broad] - -\setupinteractionmenu[\v!left ][\c!width=\leftedgewidth ] -\setupinteractionmenu[\v!right ][\c!width=\rightedgewidth] -% \setupinteractionmenu[\v!top ] [\c!height=\topheight ] -% \setupinteractionmenu[\v!bottom] [\c!height=\bottomheight ] - -\unexpanded\def\placeleftedgetextblock % Is \hss/\hsize really needed here? (check outer level and settings) - {\hbox to \leftedgewidth{\hsize\leftedgewidth\hss\interactionmenus[\v!left]}} - -\unexpanded\def\placerightedgetextblock % Is \hss/\hsize really needed here? (check outer level and settings) - {\hbox to \rightedgewidth{\hsize\rightedgewidth\interactionmenus[\v!right]\hss}} - -\unexpanded\def\placetoptextblock - {\vbox to \topheight - {\vsize\topheight - \csname\??tk\v!top\c!before\endcsname - \interactionmenus[\v!top]% - \csname\??tk\v!top\c!after\endcsname - \kern\zeropoint}} - -\unexpanded\def\placebottomtextblock - {\vbox to \bottomheight - {\vsize\bottomheight - \csname\??tk\v!bottom\c!before\endcsname - \interactionmenus[\v!bottom]% - \csname\??tk\v!bottom\c!after\endcsname - \kern\zeropoint}} - -\ifdefined\leftedgetextcontent - - \appendtoks \iflocation\placeleftedgetextblock \hskip-\leftedgewidth \fi\to \leftedgetextcontent - \appendtoks \iflocation\placerightedgetextblock \hskip-\rightedgewidth \fi\to \rightedgetextcontent - \appendtoks \iflocation\placetoptextblock \vskip-\topheight \fi\to \toptextcontent - \appendtoks \iflocation\placebottomtextblock \vskip-\bottomheight \fi\to \bottomtextcontent - -\fi - -%D Enable and disable menus \unknown\ obsolete: - -\def\gobbletwoparameters[#1][#2]{} - -\def\disableinteractionmenu{\dodoubleempty\gobbletwoparameters} -\def\enableinteractionmenu {\dodoubleempty\gobbletwoparameters} - -\protect \endinput diff --git a/tex/context/base/scrn-nav.mkiv b/tex/context/base/scrn-nav.mkiv deleted file mode 100644 index 7b8fbdfa7..000000000 --- a/tex/context/base/scrn-nav.mkiv +++ /dev/null @@ -1,258 +0,0 @@ -%D \module -%D [ file=scrn-nav, -%D version=1998.01.15, -%D title=\CONTEXT\ Screen Macros, -%D subtitle=Navigation, -%D author=Hans Hagen, -%D date=\currentdate, -%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] -%C -%C This module is part of the \CONTEXT\ macro||package and is -%C therefore copyrighted by \PRAGMA. See mreadme.pdf for -%C details. - -\writestatus{loading}{ConTeXt Screen Macros / Navigation} - -\unprotect - -%D Support for interactive document is very present in -%D \CONTEXT\ and interwoven in many modules. This means that in -%D this module, where we deal with some common navigational -%D features, there will be quite some forward references. -%D -%D The current support in \MKIV\ is mostly the same as in -%D \MKII\ and the old files have some more detailed -%D (sometimes historic) information. - -%D There is no interaction at all unless enabled by saying: -%D -%D \starttyping -%D \setupinteraction[state=start] -%D \stoptyping -%D -%D The other settings are: -%D -%D \showsetup{setupinteraction} - -% use with care, no checking done - -\def\setinteractionparameter#1#2% - {\expandafter\def\csname\??ia#1\endcsname{#2}} - -\def\resetinteractionparameter#1% - {\expandafter\let\csname\??ia#1\endcsname\empty} - -\newtoks\everysetupinteraction - -\unexpanded\def\setupinteraction - {\dosingleargument\dodosetupinteraction} - -\def\dodosetupinteraction[#1]% % \dosetupinteraction == special - {\getparameters[\??ia][#1]% - \the\everysetupinteraction} - -% todo, move partial append to where the action happens - -\appendtoks - \doifelse\@@iastate\v!start - {\iflocation\else - \showmessage\m!interactions2{(page mode: \@@iapage)}% - \global\locationtrue - \fi}% - {\iflocation - \showmessage\m!interactions3{(page mode: \@@iapage)}% - \global\locationfalse - \fi}% - \iflocation - \setsystemmode \v!interaction - \else - \resetsystemmode\v!interaction - \fi - \doifsomething\@@iacalculate - {\doregistercalculationset\@@iacalculate}% - \doifelse\@@iastrut \v!yes \settrue \setfalse \uselocationstrut - \doifelse\@@iaclick \v!yes \settrue \setfalse \highlighthyperlinks - \doifelse\@@iadisplay\v!new \settrue \setfalse \gotonewwindow - \doifnot \@@iapage \v!no \dosetpagedestinations -\to \everysetupinteraction - -\def\dosetpagedestinations - {\ctxlua{structures.references.setinnermethod("\@@iapage")}} - -\def\synchronizebackendidentity - {\ctxlua{backends.codeinjections.setupidentity{ - title = \!!bs\@@iatitle\!!es, - subject = \!!bs\@@iasubtitle\!!es, - author = \!!bs\@@iaauthor\!!es, - creator = \!!bs ConTeXt - \contextversion\!!es, - date = \!!bs\@@iadate\!!es, - keywords = \!!bs\@@iakeyword\!!es, - }}} - -\appendtoks - \synchronizebackendidentity -\to \everyfirstshipout - -%D We have to make sure of some settings: - -\def\dolocationstartup - {\iflocation - \donefalse - \ifx\@@iaopenaction\empty \else \donetrue - \ctxlua{structures.references.checkopendocumentactions("\@@iaopenaction")}% - \ctxlua{structures.references.expandcurrent()}% - \fi - \ifx\@@iacloseaction\empty \else \donetrue - \ctxlua{structures.references.checkclosedocumentactions("\@@iacloseaction")}% - \ctxlua{structures.references.expandcurrent()}% - \fi - \ifdone - \ctxlua{structures.references.flushdocumentactions()}% - \fi - \global\let\dolocationstartup\relax - \fi} - -\def\dolocationpagecheck - {\iflocation - \donefalse - \ifx\@@iaopenpageaction\empty \else \donetrue - \ctxlua{structures.references.checkopenpageactions("\@@iaopenpageaction")}% - \ctxlua{structures.references.expandcurrent()}% - \fi - \ifx\@@iaclosepageaction\empty \else \donetrue - \ctxlua{structures.references.checkclosepageactions("\@@iaclosepageaction")}% - \ctxlua{structures.references.expandcurrent()}% - \fi - \ifdone - \ctxlua{structures.references.flushpageactions()}% - \fi - \fi} - -\appendtoks \dolocationstartup \to \everyshipout -\appendtoks \dolocationpagecheck \to \everyshipout - -%D As long as there a natural feeling of what can be considered -%D hyper active or not, we have to tell users where they can -%D possibly click. We've already seen a few macros that deal -%D with this visualization, something we definitely do not let -%D up to the viewer. One way of telling is using a distinctive -%D typeface, another way is using color. -%D -%D There are two colors involved: one for normal hyperlinks, -%D and one for those that point to the currentpage, the -%D contrast color. - -\definecolor [interactioncolor] [r=0, g=.6, b=0] -\definecolor [interactioncontrastcolor] [r=.8, g=0, b=0] - -\definecolor [interactiekleur] [interactioncolor] -\definecolor [interactiecontrastkleur] [interactioncontrastcolor] - -%D The next few macros are responsible for highlighting hyper -%D links. The first one, \type{\showlocation}, is used in those -%D situations where the typeface is handled by the calling -%D macro. - -%D When we're dealing with pure page references, contrast -%D colors are used when we are already at the page mentioned. - -\def\setlocationcolor#1% not grouped ! - {\ifnum\referencepagestate=\plusone - \edef\askedcontrastcolor{\csname#1\c!contrastcolor\endcsname}% - \ifx\askedcontrastcolor\empty - \dosetcolorattribute{#1}\c!color - \else - \dosetcolorattribute{#1}\c!contrastcolor - \fi - \else % we could just set and if > 0 set again - \dosetcolorattribute{#1}\c!color - \fi} - -\def\setlocationfont#1% - {\dosetfontattribute{#1}\c!style} - -\def\setlocationattributes#1% - {\ifnum\referencepagestate=\plusone - \edef\askedcontrastcolor{\csname#1\c!contrastcolor\endcsname}% - \ifx\askedcontrastcolor\empty - \dosetcolorattribute{#1}\c!color - \else - \dosetcolorattribute{#1}\c!contrastcolor - \fi - \else % we could just set and if > 0 set again - \dosetcolorattribute{#1}\c!color - \fi - \dosetfontattribute{#1}\c!style} - -\def\setlocationcolorspec#1% \resolver - {\ifnum\referencepagestate=\plusone - \edef\askedcontrastcolor{#1\c!contrastcolor}% - \ifx\askedcontrastcolor\empty - \doactivatecolor{#1\c!color}% - \else - \doactivatecolor\askedcontrastcolor - \fi - \else - \doactivatecolor{#1\c!color}% - \fi} - -%D delayed ... - -\def\enableinteractivereferences - {\ifproductionrun - \ctxlua{structures.references.enableinteraction()}% - \globallet\enableinteractivereferences\relax - \fi} - -\appendtoks - \enableinteractivereferences -\to \everysetupinteraction - -%D More tokens are spend when we want both typeface and color -%D highlighting. - -\def\@@iatimestamp - {\the\normalyear - \ifnum\normalmonth<10 0\fi\the\normalmonth - \ifnum\normalday <10 0\fi\the\normalday} - -\setupinteraction % start fit page and reset form - [\c!state=\v!stop, - \c!page=\v!no, - \c!click=\v!yes, - \c!openaction=, - \c!closeaction=, - \c!openpageaction=, - \c!closepageaction=, - \c!display=\v!normal, - \c!focus=\v!fit, - \c!menu=\v!off, - \c!style=\v!bold, - \c!calculate=, - \c!strut=\v!yes, - \c!split=\v!yes, - \c!color=interactioncolor, - \c!contrastcolor=interactioncontrastcolor, - \c!symbolset=, - \c!width=1em, - \c!height=\!!zeropoint, - \c!depth=\!!zeropoint, - \c!title=\jobname, % needed for fdf/x - \c!subtitle=, - \c!author=, - \c!keyword=, - \c!date=\@@iatimestamp] - -%D XMP support: - -\setupinteraction - [xmpfile=] - -\appendtoks - % this will move as it is a backend issue - \doifsomething\@@iaxmpfile - {\ctxlua{if lpdf then lpdf.setxmpfile("\@@iaxmpfile") end}% - \globallet\@@iaxmpfile\empty}% -\to \everysetupinteraction - -\protect \endinput diff --git a/tex/context/base/scrn-pag.lua b/tex/context/base/scrn-pag.lua new file mode 100644 index 000000000..7003d0285 --- /dev/null +++ b/tex/context/base/scrn-pag.lua @@ -0,0 +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 diff --git a/tex/context/base/scrn-pag.mkvi b/tex/context/base/scrn-pag.mkvi new file mode 100644 index 000000000..a5b4d0dfd --- /dev/null +++ b/tex/context/base/scrn-pag.mkvi @@ -0,0 +1,180 @@ +%D \module +%D [ file=scrn-pag, +%D version=1998.01.15, +%D title=\CONTEXT\ Screen Macros, +%D subtitle=Pages, % moved code +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% pagecomments will be done differently + +\writestatus{loading}{ConTeXt Screen Macros / Pages} + +\registerctxluafile{scrn-pag}{1.001} + +\unprotect + +\installparameterhandler \??sc {interactionscreen} +\installsetuphandler \??sc {interactionscreen} + +\def\scrn_canvas_synchronize_simple % this will be done differently (or disappear) + {\begingroup + \ifx\@@ppleft \empty + \ifx\@@ppright \empty + \ifx\@@pptop \empty + \ifx\@@ppbottom \empty + \ifx\@@pcstate\v!start + \locationfalse\fi\else + \locationfalse\fi\else + \locationfalse\fi\else + \locationfalse\fi\else + \locationfalse\fi + \iflocation % without screen settings + \ctxcommand{setupcanvas{ + paperwidth = \number\paperwidth, + paperheight = \number\paperheight + }}% + \else + \ctxcommand{setupcanvas{ + paperwidth = \number\printpaperwidth, + paperheight = \number\printpaperheight + }}% + \fi + \endgroup} + +\def\scrn_canvas_synchronize_complex + {\begingroup + \edef\currentinteractionscreenwidth {\interactionscreenparameter\c!width }% + \edef\currentinteractionscreenheight{\interactionscreenparameter\c!height}% + \ifx\currentinteractionscreenwidth\v!fit + \!!widtha\leftcombitotal + \ifdim\backspace>\!!widtha + \ifdim\backspace>\zeropoint\relax + \advance\backspace -\!!widtha + \fi + \fi + \advance\!!widtha\dimexpr + \rightcombitotal + + 2\dimexpr + \interactionscreenparameter\c!backspace + + \interactionscreenparameter\c!horoffset + \relax + \relax + \else\ifx\currentinteractionscreenwidth\v!max + \!!widtha\printpaperwidth + \else + \!!widtha\currentinteractionscreenwidth + \fi\fi + \ifdim\!!widtha>\paperwidth\ifdim\!!widtha>\zeropoint + \global\paperwidth\!!widtha + \fi\fi + \ifx\currentinteractionscreenheight\v!fit + \!!heighta\dimexpr\topheight+\topdistance\relax + \ifdim\topspace>\!!heighta + \ifdim\topspace>\zeropoint\relax + \advance\topspace -\!!heighta + \fi + \fi + \advance\!!heighta\dimexpr + \makeupheight + + \bottomdistance + + \bottomheight + + 2\dimexpr + \interactionscreenparameter\c!topspace + + \interactionscreenparameter\c!veroffset + \relax + \relax + \else\ifx\currentinteractionscreenheight\v!max + \!!heighta\printpaperheight + \else + \!!heighta\currentinteractionscreenheight + \fi\fi + \ifdim\!!heighta>\paperheight\ifdim\!!heighta>\zeropoint + \global\paperheight\!!heighta + \fi\fi + \ctxcommand{setupcanvas{ + mode = "\interactionscreenparameter\c!option", + singlesided = \ifsinglesided true\else false\fi, + doublesided = \ifdoublesided true\else false\fi, + leftoffset = \number\dimexpr\backoffset\relax, + topoffset = \number\dimexpr\topoffset \relax, + width = \number\dimexpr\!!widtha \relax, + height = \number\dimexpr\!!heighta \relax, + paperwidth = \number\paperwidth, + paperheight = \number\paperheight + }}% + \endgroup} + +\let\scrn_canvas_synchronize\scrn_canvas_synchronize_complex + +\appendtoks + \ifproductionrun + \doifelse\@@pcstate\v!start + {\let\scrn_canvas_synchronize\scrn_canvas_synchronize_simple } + {\let\scrn_canvas_synchronize\scrn_canvas_synchronize_complex}% + \fi +\to \everysetupinteractionscreen + +\appendtoks + \scrn_canvas_synchronize +\to \everyshipout + +\setupinteractionscreen + [\c!width=\printpaperwidth, + \c!height=\printpaperheight, + \c!horoffset=\zeropoint, + \c!veroffset=\zeropoint, + \c!backspace=\backspace, + \c!topspace=\topspace, + \c!option=\v!auto] + +%D Conditional page breaks: + +\unexpanded\def\screen + {\dosingleempty\scrn_screen} + +\def\scrn_screen[#list]% + {\iflocation + \page[#list]% + \fi} + +%D Page transitions: + +\let\scrn_transitions_list\empty + +\unexpanded\def\setuppagetransitions + {\dosingleempty\scrn_transitions_setup} + +\def\scrn_transitions_setup[#list]% + {\edef\scrn_transitions_list{#list}} + +\def\scrn_transitions_set + {\iflocation \ifx\scrn_transitions_list\empty \else + \scrn_transitions_set_indeed + \fi \fi} + +\def\scrn_transitions_set_indeed + {\begingroup + \edef\currentinteractionscreendelay{\interactionscreenparameter\c!delay}% + \ctxcommand{setpagetransition{ + n = "\scrn_transitions_list", + delay = "\ifx\currentinteractionscreendelay\v!none 0\else\currentinteractionscreendelay\v!none\fi" + }}% + \endgroup} + +\prependtoks + \scrn_transitions_set +\to \everyshipout + +\setupinteractionscreen + [\c!delay=\v!none] + +\setuppagetransitions + [\v!reset] + +\protect \endinput diff --git a/tex/context/base/scrn-ref.lua b/tex/context/base/scrn-ref.lua new file mode 100644 index 000000000..9609e8aa2 --- /dev/null +++ b/tex/context/base/scrn-ref.lua @@ -0,0 +1,65 @@ +if not modules then modules = { } end modules ['scrn-int'] = { + 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(openpage) + 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-ref.mkvi b/tex/context/base/scrn-ref.mkvi new file mode 100644 index 000000000..8c3f4fb4a --- /dev/null +++ b/tex/context/base/scrn-ref.mkvi @@ -0,0 +1,90 @@ +%D \module +%D [ file=scrn-ref, +%D version=1998.01.15, +%D title=\CONTEXT\ Screen Macros, +%D subtitle=References, % moved code +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Screen Macros / References} + +\registerctxluafile{scrn-ref}{1.001} + +\unprotect + +\appendtoks + \doifsomething{\interactionparameter\c!calculate}{\doregistercalculationset{\interactionparameter\c!calculate}}% + \doifelse{\interactionparameter\c!click }\v!yes \settrue \setfalse \highlighthyperlinks + \doifelse{\interactionparameter\c!display}\v!new \settrue \setfalse \gotonewwindow + \doifnot {\interactionparameter\c!page }\v!no \scrn_reference_enable_page_destinations +\to \everysetupinteraction + +\def\scrn_reference_enable_page_destinations % no reset + {\ctxlua{structures.references.setinnermethod("\interactionparameter\c!page")}} + +\setupinteraction % start fit page and reset form + [\c!page=\v!no, + \c!click=\v!yes, + \c!display=\v!normal, + \c!focus=\v!fit, + \c!calculate=, + % rendering: + \c!width=1em, + \c!height=\zeropoint, + \c!depth=\zeropoint, + \c!symbolset=] + +%D We have to make sure of some settings: + +\def\scrn_reference_set_text_actions + {\iflocation + \edef\currentinteractionopenaction {\interactionparameter\c!openaction }% + \edef\currentinteractioncloseaction{\interactionparameter\c!closeaction}% + \ifx\currentinteractionopenaction\empty \else + \ctxcommand{setopendocumentaction("\currentinteractionopenaction")}% + \fi + \ifx\currentinteractioncloseaction\empty \else + \ctxcommand{setclosedocumentaction("\currentinteractioncloseaction")}% + \fi + \glet\scrn_reference_set_text_actions\relax + \fi} + +\def\scrn_reference_set_page_actions + {\iflocation + \edef\currentinteractionopenpageaction {\interactionparameter\c!openpageaction }% + \edef\currentinteractionclosepageaction{\interactionparameter\c!closepageaction}% + \ifx\currentinteractionopenpageaction\empty \else + \ctxcommand{setopenpageaction("\currentinteractionopenpageaction")}% + \fi + \ifx\currentinteractionclosepageaction\empty \else + \ctxcommand{setclosepageaction("\currentinteractionclosepageaction")}% + \fi + \fi} + +\appendtoks \scrn_reference_set_text_actions \to \everyshipout +\appendtoks \scrn_reference_set_page_actions \to \everyshipout + +%D delayed ... + +\def\scrn_reference_enable_references + {\ifproductionrun + \ctxlua{structures.references.enableinteraction()}% + \glet\scrn_reference_enable_references\relax + \fi} + +\appendtoks + \scrn_reference_enable_references +\to \everysetupinteraction + +\setupinteraction % start fit page and reset form + [\c!openaction=, + \c!closeaction=, + \c!openpageaction=, + \c!closepageaction=] + +\protect \endinput diff --git a/tex/context/base/scrn-wid.lua b/tex/context/base/scrn-wid.lua new file mode 100644 index 000000000..7b1dd940a --- /dev/null +++ b/tex/context/base/scrn-wid.lua @@ -0,0 +1,194 @@ +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 + +-- 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 "" + end +end + +function attachments.register(specification) + checkregistered(specification) + checkbuffer(specification) + attachments[lastregistered] = specification + return specification +end + +function attachments.insert(specification) + local registered = checkregistered(specification) + local r = attachments[registered] + if r then + for k, v in next, r do + local s = specification[k] + if s == "" then + specification[k] = v + end + end + end + checkbuffer(specification) + return nodeinjections.attachfile(specification) +end + +commands.registerattachment = attachments.register + +function commands.insertattachment(specification) + tex.box["scrn_attachment_box_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["scrn_comment_box_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 + +function renderings.var(label,key) + local rn = renderings[label] + context(rn and rn[key] or "") +end + +-- 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/scrn-wid.mkvi b/tex/context/base/scrn-wid.mkvi new file mode 100644 index 000000000..4674dea28 --- /dev/null +++ b/tex/context/base/scrn-wid.mkvi @@ -0,0 +1,700 @@ +%D \module +%D [ file=scrn-int, +%D version=2011.02.27, % moved from scrn-int +%D title=\CONTEXT\ Core Macros, +%D subtitle=Widgets, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\writestatus{loading}{ConTeXt Interaction Macros / Widgets} + +\registerctxluafile{scrn-wid}{1.001} + +\unprotect + +%D Attachments (mkiv upgraded): +%D +%D As usual in \CONTEXT\ we separate the general definition (frontend) +%D and the rendering (backend). + +% old but stil valid method: +% +% \useattachment[test.tex] +% \useattachment[whatever][test.tex] +% \useattachment[whatever][newname][test.tex] +% \useattachment[whatever][title][newname][test.tex] +% +% new method: +% +% \registerattachment[sometag][specification] % name file author title subtitle +% +% \attachment[sometag][extra specs] +% \attachment[test.tex] +% \attachment[file=test.tex] +% \attachment[file=test.tex,method=hidden] +% \attachment[name=newname,file=test.tex] +% \attachment[title=mytitle,name=newname,file=test.tex] +% +% indirect +% +% \defineattachment[whatever5][file=test.tex] \attachment[whatever5][method=hidden] +% \defineattachment[whatever5][file=test.tex,method=hidden] \attachment[whatever5] +% +% direct (no definitions) +% +% \attachment[test][file=oeps.tex,title=Oeps,author=Hans,subtitle=TeX File,method=hidden] +% \attachment[label=test,file=oeps.tex,title=Oeps,author=Hans,subtitle=TeX File,method=hidden] +% +% autolabel: +% +% \attachment[file=oeps.tex,title=Oeps,author=Hans,subtitle=TeX File,method=hidden] +% +% % \setupattachments[\c!symbol={symbol-normal,symbol-down}] + +% startattachment -> temp file + +\newbox\scrn_attachment_box_collect +\newbox\scrn_attachment_box_link +\newbox\scrn_attachment_box_symbol + +\installcommandhandler\??at{attachment}\??at + +\let\setupattachments\setupattachment % convenience and compatibility + +\setupattachment + [\c!state=\v!start, + \c!color=\interactionparameter\c!color, + %\c!textlayer=, + %\c!symbol=, + %\c!title=, + %\c!subtitle=, + %\c!file=, % input filename + %\c!name=, % new filename + %\c!author=, + %\c!method=, % \v!hidden = not in menu + %\c!buffer= + \c!symbol=, + \c!distance=1em, + \c!width=\v!fit, + \c!height=\v!fit, + \c!depth=\v!fit, + \c!location=\v!high] + +\unexpanded\def\registerattachment + {\dodoubleempty\scrn_attachment_register} + +\def\scrn_attachment_register[#tag][#settings]% we save (globally) at the lua end + {\ifsecondargument + \begingroup + \def\currentattachment{_}% + \setupattachment[_][#settings,\s!parent=\??at]% + \ctxcommand{registerattachment{ + tag = "#tag", + title = "\attachmentparameter\c!title", + subtitle = "\attachmentparameter\c!subtitle", + author = "\attachmentparameter\c!author", + file = "\attachmentparameter\c!file", + name = "\attachmentparameter\c!name", + buffer = "\attachmentparameter\c!buffer", + }}% + \endgroup + \fi} + +\appendtoks + \setuevalue \currentattachment {\scrn_attachment_direct{\currentattachment}}% + \setuevalue{\e!start\currentattachment}{\scrn_attachment_start {\currentattachment}}% + \setuevalue{\e!stop \currentattachment}{\scrn_attachment_stop }% +\to \everydefineattachment + +\unexpanded\def\scrn_attachment_direct#tag% + {\edef\currentattachment{#tag}% + \doifelselocation + {\dodoubleempty\scrn_attachment_direct_status} + {\dodoubleempty\scrn_attachment_direct_ignore}} + +\def\scrn_attachment_direct_status + {\doifelse{\attachmentparameter\c!state}\v!start + \scrn_attachment_direct_indeed + \scrn_attachment_direct_ignore} + +\def\scrn_attachment_direct_indeed[#registered][#settings]% + {\bgroup + \doifelsenothing{#registered} + {\scrn_attachment_inject[\v!auto][]} + {\doifassignmentelse{#registered} + {\scrn_attachment_inject[\v!auto][#registered]} + {\scrn_attachment_inject[#registered][#settings]}}% + \egroup} + +\def\scrn_attachment_direct_ignore[#tag][#settings]% + {} + +\unexpanded\def\scrn_attachment_start#tag% + {\edef\currentattachment{#tag}% + \doifelselocation + {\dodoubleempty\scrn_attachment_start_indeed} + {\dodoubleempty\scrn_attachment_start_ignore}} + +\unexpanded\def\scrn_attachment_stop + {} + +\def\scrn_attachment_start_indeed + {\doif{\attachmentparameter\c!state}\v!start + {\scrn_attachment_start_indeed} + {\scrn_attachment_start_ignore}} + +\def\scrn_attachment_start_indeed[#registered][#settings]% + {\bgroup + \doifelsenothing{#registered} + {\def\scrn_attachment_stop{\scrn_attachment_inject[\v!auto][\c!buffer=\v!attachment]\egroup}}% + {\doifassignmentelse{#registered} + {\def\scrn_attachment_stop{\scrn_attachment_inject[\v!auto][\c!buffer=\v!attachment,#registered]\egroup}}% + {\def\scrn_attachment_stop{\scrn_attachment_inject[#registered][\c!buffer=\v!attachment,#settings]\egroup}}}% + \dostartbuffer[\v!attachment][\e!start\currentattachment][\e!stop\currentattachment]} + +\def\scrn_attachment_start_ignore + {\expandafter\gobbleuntil\csname\e!stop\currentattachment\endcsname} + +\def\scrn_attachment_inject[#registered][#settings]% + {\edef\currentattachmentregistered{#registered}% + \setupattachment[\currentattachment][#settings]% + \expandcheckedcsname{scrn_attachment_method_}{\attachmentparameter\c!method}\v!normal} + +\setvalue{scrn_attachment_method_\v!normal}% + {\edef\currentattachmentsymbol{\attachmentparameter\c!symbol}% + \edef\currentattachmentwidth {\attachmentparameter\c!width }% + \edef\currentattachmentheight{\attachmentparameter\c!height}% + \edef\currentattachmentdepth {\attachmentparameter\c!depth }% + \ifx\currentattachmentsymbol\empty + \ifx\currentattachmentwidth \v!fit\edef\currentattachmentwidth {.5em}\fi + \ifx\currentattachmentheight\v!fit\edef\currentattachmentheight{.5em}\fi + \ifx\currentattachmentdepth \v!fit\let \currentattachmentdepth \zeropoint\fi + \else + \ctxcommand{presetsymbollist("\attachmentparameter\c!symbol")}% + % we cannot yet ask for the wd/ht/dp of an xform else we could use those + \setbox\scrn_attachment_box_symbol\hbox{\symbol[\lastpredefinedsymbol]}% + \ifx\currentattachmentwidth \v!fit\edef\currentattachmentwidth {\wd\scrn_attachment_box_symbol}\fi + \ifx\currentattachmentheight\v!fit\edef\currentattachmentheight{\ht\scrn_attachment_box_symbol}\fi + \ifx\currentattachmentdepth \v!fit\edef\currentattachmentdepth {\dp\scrn_attachment_box_symbol}\fi + \fi + \ctxcommand{insertattachment{ + tag = "\currentattachment", + registered = "\currentattachmentregistered", + width = \number\dimexpr\currentattachmentwidth \relax, + height = \number\dimexpr\currentattachmentheight\relax, + depth = \number\dimexpr\currentattachmentdepth \relax, + color = "\attachmentparameter\c!color", + colormodel = \number\currentcolormodel, + colorvalue = \thecolorattribute{\attachmentparameter\c!color}, + transparencyvalue = \thetransparencyattribute{\attachmentparameter\c!color}, + symbol = "\currentattachmentsymbol", + layer = "\attachmentparameter\c!textlayer", + % these will be overloaded by registered when available + title = "\attachmentparameter\c!title", + subtitle = "\attachmentparameter\c!subtitle", + author = "\attachmentparameter\c!author", + file = "\attachmentparameter\c!file", + name = "\attachmentparameter\c!name", + buffer = "\attachmentparameter\c!buffer", + }}% + \setbox\scrn_attachment_box_link\hbox{\scrn_attachment_place}% + \wd\scrn_attachment_box_link\currentattachmentwidth + \ht\scrn_attachment_box_link\currentattachmentheight + \dp\scrn_attachment_box_link\currentattachmentdepth + \box\scrn_attachment_box_link} + +\setvalue{scrn_attachment_method_\v!hidden}% + {\ctxcommand{insertattachment{ + tag = "\currentattachment", + registered = "\currentattachmentregistered", + method = "\v!hidden" + }}} + +\def\scrn_attachment_place + {\executeifdefined + {\??at:\c!location:\attachmentparameter\c!location}\hbox + {\box\scrn_attachment_box_link}} + +\setvalue{\??at:\c!location:\v!inmargin }{\inmargin } +\setvalue{\??at:\c!location:\v!leftedge }{\inleftedge } +\setvalue{\??at:\c!location:\v!rightedge }{\inrightedge } +\setvalue{\??at:\c!location:\v!leftmargin }{\inleftmargin } +\setvalue{\??at:\c!location:\v!rightmargin}{\inrightmargin} +\setvalue{\??at:\c!location:\v!high }{\high} +\setvalue{\??at:\c!location:\v!none }{\scrn_attachment_collect} + +\def\scrn_attachment_collect#content% + {\global\setbox\scrn_attachment_box_collect\hbox\bgroup + \ifvoid\scrn_attachment_box_collect\else + \box\scrn_attachment_box_collect + \hskip\attachmentparameter\c!distance + \fi + #content% + \egroup} + +\unexpanded\def\placeattachments + {\ifvoid\scrn_attachment_box_collect\else + \box\scrn_attachment_box_collect + \fi} + +\defineattachment[attachment] + +% \ifx\currentinterface\defaultinterface \else +% \defineattachment[\v!attachment] +% \fi + +% backward compatible: + +\unexpanded\def\useattachment + {\doquadrupleempty\scrn_attachment_use} + +\def\scrn_attachmen_use[#tag][#title][#name][#file]% + {\iffourthargument + \registerattachment[#tag][title=#title,name=#name,file=#file]% + \else\ifthirdargument + \registerattachment[#tag][title=#title,name=#title,file=#name]% + \else\ifsecondargument + \registerattachment[#tag][title=#title,name=#title,file=#title]% + \else + \registerattachment[#tag][title=#title,name=#tag,file=#tag]% + \fi\fi\fi} + +%D Comments: + +% test +% +% \startcomment +% hello beautiful\\world +% \stopcomment +% +% test +% +% \startcomment[hello] +% hello << eerste >> +% beautiful +% world +% \stopcomment +% +% test +% +% \startcomment[hello][color=green,width=10cm,height=3cm] +% hello +% beautiful +% world +% \stopcomment +% +% test +% +% \startcomment[hello][color=red,width=4cm,height=3cm] +% hello +% +% beautiful +% +% world +% \stopcomment +% +% test +% +% \startcomment[symbol=Balloon] +% Do we want this kind of rubish? +% \stopcomment +% +% test +% +% \definesymbol [comment-normal][{\externalfigure[cow.pdf]}] +% \definesymbol [comment-down] [{\externalfigure[cow.pdf]}] +% +% \def\CowSymbol#1#2% +% {\scale +% [\c!height=#1] +% {\startMPcode +% loadfigure "koe.mp" number 1 ; +% refill currentpicture withcolor #2 ; +% \stopMPcode}} +% +% \definesymbol [comment-normal] +% [\CowSymbol{4ex}{red}] +% +% \definesymbol [comment-down] +% [\CowSymbol{4ex}{green}] +% +% \setupcomment +% [\c!symbol={comment-normal,comment-down}, +% \c!option=\v!buffer] +% +% \startcomment[hello] +% oeps +% \stopcomment +% +% test +% +% \setupcomment +% [\c!symbol=normal, +% \c!option=max,width=10cm] +% +% \startcomment[hello] +% oeps +% \stopcomment +% +% test + +\installcommandhandler\??cc{comment}\??cc + +\newbox\scrn_comment_box_collect +\newbox\scrn_comment_box_rendering +\newbox\scrn_comment_box_link +\newbox\scrn_comment_box_symbol + +\setupcomment + [\c!state=\v!start, + \c!distance=1em, + \c!color=\interactionparameter\c!color, + \c!space=\v!no, + \c!symbol=, + %\c!title=, + %\c!option=, + %\c!textlayer=, + \c!width=\v!fit, + \c!height=\v!fit, + \c!depth=\v!fit, + \c!nx=40, + \c!ny=10, + \c!location=\v!high] + +\presetlocalframed[\??cc] + +\appendtoks + \setuevalue \currentcomment {\scrn_comment_argument{\currentcomment}}% + \setuevalue{\e!start\currentcomment}{\scrn_comment_start {\currentcomment}}% + \setuevalue{\e!stop \currentcomment}{\scrn_comment_stop }% +\to \everydefinecomment + +\unexpanded\def\scrn_comment_argument#category% + {\def\currentcomment{#category}% + \doifelselocation + {\dodoubleempty\scrn_comment_argument_status} + {\dodoubleempty\scrn_comment_argument_ignore}} + +\def\scrn_comment_argument_status + {\doifelse{\commentparameter\c!state}\v!start + \scrn_comment_argument_indeed + \scrn_comment_argument_ignore} + +\def\scrn_comment_argument_indeed[#title][#settings]#text% + {\doifassignmentelse{#title} + {\setupcomment[\currentcomment][#title]} + {\setupcomment[\currentcomment][\c!title=#title,#settings]}% + \ctxlua{buffers.assign("\v!comment",\!!bs\detokenize{#text}\!!es)}% + \scrn_comment_inject + \ignorespaces} + +\def\scrn_comment_argument_ignore[#title][#settings]#text% + {\ignorespaces} + +\unexpanded\def\scrn_comment_start#category% + {\def\currentcomment{#category}% + \doifelselocation + {\dodoubleempty\scrn_comment_start_indeed} + {\dodoubleempty\scrn_comment_start_ignore}} + +\def\scrn_comment_start_indeed + {\doifelse{\commentparameter\c!state}\v!start + {\scrn_comment_start_indeed} + {\scrn_comment_start_ignore}} + +\def\scrn_comment_start_indeed[#title][#settings]% + {\bgroup + \doifassignmentelse{#title} + {\setupcomment[\currentcomment][#title]} + {\setupcomment[\currentcomment][\c!title=#title,#settings]}% + \def\scrn_comment_stop{\scrn_comment_inject\egroup}% + \dostartbuffer[\v!comment][\e!start\currentcomment][\e!stop\currentcomment]} + +\def\scrn_comment_start_ignore + {\expandafter\gobbleuntil\csname\e!stop\currentcomment\endcsname} + +\unexpanded\def\scrn_comment_stop + {} + +\def\scrn_comment_inject + {\expandcheckedcsname{scrn_comment_method_}{\commentparameter\c!method}\v!normal} + +%D Beware: comments symbols don't scale in acrobat (cf. spec but somewhat +%D weird, esp because for instance attachment symbols do scale). + +\setvalue{scrn_comment_method_\v!normal}% + {\edef\currentcommentsymbol{\commentparameter\c!symbol}% + \edef\currentcommentwidth {\commentparameter\c!width }% + \edef\currentcommentheight{\commentparameter\c!height}% + \edef\currentcommentdepth {\commentparameter\c!depth }% + \ifx\currentcommentsymbol\empty + \ifx\currentcommentwidth \v!fit\edef\currentcommentwidth {.5em}\fi + \ifx\currentcommentheight\v!fit\edef\currentcommentheight{.5em}\fi + \ifx\currentcommentdepth \v!fit\let \currentcommentdepth \zeropoint\fi + \else + \ctxcommand{presetsymbollist("\commentparameter\c!symbol")}% + % we cannot yet ask for the wd/ht/dp of an xform else we could use those + \setbox\scrn_comment_box_symbol\hbox{\symbol[\lastpredefinedsymbol]}% + \ifx\currentcommentwidth \v!fit\edef\currentcommentwidth {\wd\scrn_comment_box_symbol}\fi + \ifx\currentcommentheight\v!fit\edef\currentcommentheight{\ht\scrn_comment_box_symbol}\fi + \ifx\currentcommentdepth \v!fit\edef\currentcommentdepth {\dp\scrn_comment_box_symbol}\fi + \fi + \ctxcommand{insertcomment{ + tag = "\currentcomment", + title = "\commentparameter\c!title", + subtitle = "\commentparameter\c!subtitle", + author = "\commentparameter\c!author", + width = \number\dimexpr\currentcommentwidth, + height = \number\dimexpr\currentcommentheight, + depth = \number\dimexpr\currentcommentdepth, + nx = \commentparameter\c!nx, + ny = \commentparameter\c!ny, + colormodel = \number\currentcolormodel, + colorvalue = \thecolorattribute{\commentparameter\c!color}, + transparencyvalue = \thetransparencyattribute{\commentparameter\c!color}, + option = "\commentparameter\c!option", % todo + symbol = "\commentparameter\c!symbol", + buffer = "\v!comment", + layer = "\commentparameter\c!textlayer" + }}% + \wd\scrn_comment_box_link\currentcommentwidth + \ht\scrn_comment_box_link\currentcommentheight + \dp\scrn_comment_box_link\currentcommentdepth + \scrn_comment_place} + +\setvalue{scrn_comment_method_\v!hidden}% + {} + +% todo: dedicated margin classes + +\def\scrn_comment_place + {\executeifdefined + {\??cc:\c!location:\commentparameter\c!location}\hbox + {\hbox{\box\scrn_comment_box_link}}} + +\setvalue{\??cc:\c!location:\v!inmargin }{\inmargin } +\setvalue{\??cc:\c!location:\v!leftedge }{\inleftedge } +\setvalue{\??cc:\c!location:\v!rightedge }{\inrightedge } +\setvalue{\??cc:\c!location:\v!leftmargin }{\inleftmargin } +\setvalue{\??cc:\c!location:\v!rightmargin}{\inrightmargin} +\setvalue{\??cc:\c!location:\v!high }{\high} +\setvalue{\??cc:\c!location:\v!none }{\scrn_comment_collect} + +\def\scrn_comment_collect#content% + {\global\setbox\scrn_comment_box_collect\hbox\bgroup + \ifvoid\scrn_comment_box_collect\else + \box\scrn_comment_box_collect + \hskip\commentparameter\c!distance + \fi + #content% + \egroup} + +\unexpanded\def\placecomments + {\ifvoid\scrn_comment_box_collect\else + \box\scrn_comment_box_collect + \fi} + +\definecomment[comment] + +% \ifx\currentinterface\defaultinterface \else +% \definecomment[\v!comment] +% \fi + +%D Soundclips: +%D +%D Defining sound tracks: +%D +%D \starttyping +%D \useexternalsoundtrack[label][file] +%D \stoptyping +%D +%D associated actions: StartSound StopSound PauseSound ResumeSound +%D +%D Todo: like external figures, also search on path, +%D although, they need to be present ar viewing time, so ... + +\unexpanded\def\useexternalsoundtrack + {\dodoubleargument\scrn_soundtrack_indeed} + +\def\scrn_soundtrack_indeed[#tag][#filename]% + {\ctxcommand{registersoundclip{ + tag = "#tag", + file = "#filename" + }}} + +\def\checksoundtrack#tag% yet untested in mkiv (also move management to lua) + {\iflocation + \ctxcommand{insertsoundclip{ + tag = "#tag", + repeat = "\@@sdoption", % not entirely ok but works + }}% + \fi} + +\unexpanded\def\setupexternalsoundtracks + {\dodoubleargument\getparameters[\??sd]} + +\setupexternalsoundtracks + [\c!option=] + +%D Renderings (not yet tested in mkvi): + +% Todo: multiple instances and inheritance .. will be done when +% needed i.e. when I see usage. + +\let\currentrendering\empty + +\definereference[StartCurrentRendering] [\v!StartRendering {\currentrendering}] +\definereference[StopCurrentRendering] [\v!StopRendering {\currentrendering}] +\definereference[PauseCurrentRendering] [\v!PauseRendering {\currentrendering}] +\definereference[ResumeCurrentRendering][\v!ResumeRendering{\currentrendering}] + +\def\useexternalrendering{\doquadrupleempty\scrn_rendering_use} +\def\setinternalrendering{\dodoubleempty \scrn_rendering_set} + +\def\scrn_rendering_use[#tag][#mime][#file][#options]% + {\ctxlua{interactions.renderings.register { + type = "external", + label = "#tag", + mime = "#mime", + filename = "#file", + options = "#options", + }}} + +\def\scrn_rendering_set[#tag][#options]% {content} + {\bgroup + \dowithnextbox + {\ctxlua{interactions.renderings.register { + type = "internal", + label = "#tag", + mime = "IRO", % brrr + filename = "#tag", + options = "#options", + }}% + \let\objectoffset\zeropoint + \setobject{IRO}{#tag}\hbox{\box\nextbox}% + \egroup}% + \hbox} + +\def\renderingtype #tag{\ctxlua{interactions.renderings.var("#tag","type")}} +\def\renderingoptions#tag{\ctxlua{interactions.renderings.var("#tag","options")}} + +\def\renderingwidth {8cm} % will become private +\def\renderingheight {6cm} % will become private + +\unexpanded\def\definerenderingwindow + {\dodoubleempty\scrn_rendering_define_window} + +\def\scrn_rendering_define_window[#tag][#settings]% + {\presetlocalframed[\??rw#tag]% + \getparameters + [\??rw#tag]% + [\c!openpageaction=,\c!closepageaction=,% + \c!width=\renderingwidth,\c!height=\renderingheight,% + #settings]} + +\unexpanded\def\setuprenderingwindow + {\dodoubleargument\scrn_rendering_setup_window} + +\def\scrn_rendering_setup_window[#tag]% + {\getparameters[\??rw#tag]} + +\unexpanded\def\placerenderingwindow + {\dodoubleempty\scrn_rendering_place_window} + +\def\scrn_rendering_place_window[#window][#rendering]% + {\bgroup + \edef\currentrendering{\ifsecondargument#rendering\else#window\fi}% + \doifelse{\renderingtype\currentrendering}{internal} % an object + {\getobjectdimensions{IRO}\currentrendering + \edef\renderingheight{\the\dimexpr\objectheight+\objectdepth\relax}% + \edef\renderingwidth{\objectwidth}% + \dogetobjectreferencepage{IRO}\currentrendering\renderingpage}% + {\def\renderingheight{\vsize}% + \def\renderingwidth{\hsize}% + \def\renderingpage{\realpageno}}% + % create fall back if needed + \ifcsname\??rw#window\c!width\endcsname + \def\currentrenderingwindow{#window}% + \else + \let\currentrenderingwindow\s!default + \scrn_rendering_define_window[\currentrenderingwindow]% + \fi +% todo +% \handlereferenceactions{\getvalue{\??rw\currentrenderingwindow\c!openpageaction }}\dosetuprenderingopenpageaction +% \handlereferenceactions{\getvalue{\??rw\currentrenderingwindow\c!closepageaction}}\dosetuprenderingclosepageaction + \localframed + [\??rw\currentrenderingwindow][\c!offset=\v!overlay]% + {\vfill + \ctxcommand{insertrenderingwindow { + label = "\currentrendering", + width = \number\dimexpr\renderingwidth\relax, + height = \number\dimexpr\renderingheight\relax, + options = "\renderingoptions\currentrendering", + page = \number\renderingpage, + }}\hfill}% + \egroup} + +%D Linkedlists (not tested in mkvi): + +% %D The next mechanism, linked lists, is quite old and +% %D is \MKIV'd for completeness. I will finish the +% %D configuration part when I need it. +% %D +% %D \starttyping +% %D \setupinteraction[state=start] +% %D \definelinkedlist[demo] +% %D \dorecurse{10}{\linkedlistelement[demo]{link \recurselevel} \page} +% %D \stoptyping +% +% \installcommandhandler\??lk{linkedlist}\??lk +% +% \let\setupbutton\setuplinkedlists\setuplinkedlist +% +% \appendtoks +% \ctxcommand{definelinkedlist("\currentlinkedlist")}% +% \to \everydefinelinkedlist +% +% \def\setlinkedlistproperties#1#2#3#4#5#6% +% {\def\currentlink {#1}% +% \def\noflinks {#2}% +% \def\firstlink {#3}% +% \def\previouslink{#4}% +% \def\nextlink {#5}% +% \def\lastlink {#6}} +% +% \def\linkedlistelement[#1]#2% currently no view support +% {\dontleavehmode\hbox\bgroup +% #2% +% \iflocation +% \edef\currentlinkedlist{#1}% +% \ifcsname\??lk\currentlinkedlist\s!parent\endcsname +% \hskip\linkedlistparameter\c!distance +% \ctxcommand{addlinklistelement("\currentlinkedlist")}% +% \expanded{\ctxlatelua{commands.enhancelinkedlist("\currentlinkedlist",\currentlink)}}% can also be done at the lua end +% \dogotosomepage {\??lk\currentlinkedlist}\gotobegincharacter \firstlink +% \ifnum\noflinks>\plustwo +% \dogotosomepage{\??lk\currentlinkedlist}\gobackwardcharacter\previouslink +% \dogotosomepage{\??lk\currentlinkedlist}\goforwardcharacter \nextlink +% \fi +% \dogotosomepage {\??lk\currentlinkedlist}\gotoendcharacter \lastlink +% \else +% \writestatus\m!interactions{no such linked list: \currentlinkedlist}% +% \fi +% \fi +% \egroup} +% +% \setuplinkedlists +% [\c!distance=.25em, +% \c!width=\v!fit, +% \c!location=\v!low, +% \c!color=\interactionparameter\c!color, +% \c!frame=\v!off, +% \c!background=, +% \c!backgroundcolor=] + +\protect \endinput diff --git a/tex/context/base/scrp-cjk.lua b/tex/context/base/scrp-cjk.lua index 7c9833bb3..5570532c8 100644 --- a/tex/context/base/scrp-cjk.lua +++ b/tex/context/base/scrp-cjk.lua @@ -28,14 +28,15 @@ local a_preproc = attributes.private('preproc') scripts.cjk = scripts.cjk or { } -local kindtonumber = scripts.kindtonumber -local numbertokind = scripts.numbertokind -local hash = scripts.hash -local cjk = scripts.cjk -local numbertodataset = scripts.numbertodataset +local categorytonumber = scripts.categorytonumber +local numbertocategory = scripts.numbertocategory +local hash = scripts.hash +local cjk = scripts.cjk +local numbertodataset = scripts.numbertodataset -local fontdata = fonts.identifiers -local quaddata = fonts.quads +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local quaddata = fonthashes.quads -- 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 @@ -328,12 +329,12 @@ local injectors = { -- [previous] [current] local function process(head,first,last) if first ~= last then - local lastfont, previous, originals, last = nil, "start", nil, nil + local lastfont, previous, last = nil, "start", nil while true do local upcoming, id = first.next, first.id if id == glyph_code then local a = has_attribute(first,a_prestat) - local current = numbertokind[a] + local current = numbertocategory[a] local action = injectors[previous] if action then action = action[current] @@ -353,7 +354,7 @@ local function process(head,first,last) local pid, nid = p.id, n.id if pid == glyph_code and nid == glyph_code then local pa, na = has_attribute(p,a_prestat), has_attribute(n,a_prestat) - local pcjk, ncjk = pa and numbertokind[pa], na and numbertokind[na] + 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" @@ -532,12 +533,12 @@ local injectors = { -- [previous] [current] local function process(head,first,last) if first ~= last then - local lastfont, previous, originals, last = nil, "start", nil, nil + local lastfont, previous, last = nil, "start", nil while true do local upcoming, id = first.next, first.id if id == glyph_code then local a = has_attribute(first,a_prestat) - local current = numbertokind[a] + local current = numbertocategory[a] local action = injectors[previous] if action then action = action[current] @@ -557,7 +558,7 @@ local function process(head,first,last) local pid, nid = p.id, n.id if pid == glyph_code and nid == glyph_code then local pa, na = has_attribute(p,a_prestat), has_attribute(n,a_prestat) - local pcjk, ncjk = pa and numbertokind[pa], na and numbertokind[na] + 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" diff --git a/tex/context/base/scrp-ini.lua b/tex/context/base/scrp-ini.lua index 846d74d87..543211c36 100644 --- a/tex/context/base/scrp-ini.lua +++ b/tex/context/base/scrp-ini.lua @@ -35,10 +35,9 @@ local glue_code = nodecodes.glue local a_preproc = attributes.private('preproc') local a_prestat = attributes.private('prestat') -local fontdata = fonts.identifiers +local fontdata = fonts.hashes.identifiers -local fcs = fonts.colors.set -- move this to tracers -local fcr = fonts.colors.reset +local setnodecolor = nodes.tracers.colors.set scripts = scripts or { } local scripts = scripts @@ -278,7 +277,7 @@ local scriptcolors = allocate { -- todo: just named colors scripts.colors = scriptcolors -local numbertokind = allocate { -- rather bound to cjk ... will be generalized +local numbertocategory = allocate { -- rather bound to cjk ... will be generalized "korean", "chinese", "full_width_open", @@ -292,20 +291,18 @@ local numbertokind = allocate { -- rather bound to cjk ... will be generalized "jamo_final", } -local kindtonumber = allocate(table.swapped(numbertokind)) -- could be one table +local categorytonumber = allocate(table.swapped(numbertocategory)) -- could be one table -scripts.kindtonumber = kindtonumber -scripts.numbertokind = numbertokind - --- some time i will make a fonts.originals[id] +scripts.categorytonumber = categorytonumber +scripts.numbertocategory = numbertocategory local function colorize(start,stop) for n in traverse_id(glyph_code,start) do - local kind = numbertokind[has_attribute(n,a_prestat)] + local kind = numbertocategory[has_attribute(n,a_prestat)] if kind then local ac = scriptcolors[kind] if ac then - fcs(n,ac) + setnodecolor(n,ac) end end if n == stop then @@ -364,14 +361,14 @@ function scripts.preprocess(head) if normal_process then local f = start.font if f ~= lastfont then - originals = fontdata[f].originals + originals = fontdata[f].resources.originals lastfont = f end local c = start.char if originals then c = originals[c] or c end local h = hash[c] if h then - set_attribute(start,a_prestat,kindtonumber[h]) + set_attribute(start,a_prestat,categorytonumber[h]) if not first then first, last = start, start else diff --git a/tex/context/base/sort-ini.lua b/tex/context/base/sort-ini.lua index e8580653a..3ff6f1d96 100644 --- a/tex/context/base/sort-ini.lua +++ b/tex/context/base/sort-ini.lua @@ -38,8 +38,12 @@ relatively easy to do.

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.

+ +

In the future index entries will become more clever, i.e. they will +have language etc properties that then can be used.

]]-- + local utf = unicode.utf8 local gsub, rep, sub, sort, concat = string.gsub, string.rep, string.sub, table.sort, table.concat local utfbyte, utfchar = utf.byte, utf.char diff --git a/tex/context/base/spac-ali.mkiv b/tex/context/base/spac-ali.mkiv index e93b20c63..297302808 100644 --- a/tex/context/base/spac-ali.mkiv +++ b/tex/context/base/spac-ali.mkiv @@ -83,17 +83,6 @@ % To be redone: -% \def\iobox#1#2#3#% here #3# is not really needed -% {\vbox\bgroup % we want to return a vbox like the others -% \hbox\bgroup% we need to pack the signal with the box -% \signalrightpage -% \dowithnextboxcontent -% {\let\\=\endgraf\forgetall\doifrightpageelse#1#2} -% {\box\nextbox\egroup\egroup} -% \vbox#3} -% \def\obox{\iobox\raggedleft \raggedright} % outerbox -% \def\ibox{\iobox\raggedright\raggedleft} % innerbox - \def\ibox#1#2#3% {\vbox\bgroup \forgetall @@ -110,33 +99,6 @@ \doifrightpageelse\raggedleft\raggedright \let\next} -% \def\dosetraggedvbox#1% can be more keys -% {\let\raggedbox\vbox -% \processfirstactioninset -% [#1] -% [ \v!left=>\let\raggedbox\lbox, -% \v!right=>\let\raggedbox\rbox, -% \v!middle=>\let\raggedbox\cbox, -% \v!inner=>\let\raggedbox\ibox, -% \v!outer=>\let\raggedbox\obox, -% \v!flushleft=>\let\raggedbox\rbox, -% \v!flushright=>\let\raggedbox\lbox, -% \v!center=>\let\raggedbox\cbox, -% \v!no=>\def\raggedbox{\vbox\bgroup\raggedright\let\next=}]} - -% \def\dosetraggedhbox#1% can be more keys -% {\let\raggedbox\hbox -% \processaction % slow -% [#1] -% [ \v!left=>\def\raggedbox{\doalignedline\v!left }, -% \v!right=>\def\raggedbox{\doalignedline\v!right }, -% \v!middle=>\def\raggedbox{\doalignedline\v!middle}, -% \v!inner=>\def\raggedbox{\doalignedline\v!inner }, -% \v!outer=>\def\raggedbox{\doalignedline\v!outer }, -% \v!flushleft=>\def\raggedbox{\doalignedline\v!right }, -% \v!flushright=>\def\raggedbox{\doalignedline\v!left }, -% \v!center=>\def\raggedbox{\doalignedline\v!middle}]} - \def\@@ragged@@command{@@ragged@@c} \def\@@ragged@@hbox {@@ragged@@h} \def\@@ragged@@vbox {@@ragged@@v} @@ -651,7 +613,7 @@ \let\centeraligned\midaligned -\def\regelbegrensd#1{\limitatetext{#1}{\hsize}{\unknown}} % to be translated +% \def\regelbegrensd#1{\limitatetext{#1}{\hsize}{\unknown}} % to be translated % indirecte commando's @@ -686,12 +648,6 @@ \endgroup} \hbox} -% \def\doxcheckline % no vbox so no -% {\doifrightpageelse\donetrue\donefalse -% \ifdoublesided -% \ifdone\signalinnerrealign\else\signalouterrealign\fi -% \fi} - \def\doxcheckline % used for floats so multipass anyway {\signalrightpage\doifrightpageelse\donetrue\donefalse} @@ -720,9 +676,6 @@ \def\alignedline#1#2% setting default {\csname\s!do\v!line\ifcsname\s!do\v!line#1\endcsname#1\else#2\fi\endcsname} -% \def\woordrechts -% {\groupedcommand{\hfill\hbox}{\parfillskip\zeropoint}} - % beware: \wordright{whatever\kern-\rightskip} should work! % so, no funny boxing here @@ -756,12 +709,35 @@ % \simplealignedbox{2cm}{right}{x} -\setvalue{\s!simple\c!align\v!right }#1#2{\hbox to #1{#2\hss}} -\setvalue{\s!simple\c!align\v!left }#1#2{\hbox to #1{\hss#2}} -\setvalue{\s!simple\c!align\v!flushright }#1#2{\hbox to #1{\hss#2}} -\setvalue{\s!simple\c!align\v!flushleft }#1#2{\hbox to #1{#2\hss}} -\setvalue{\s!simple\c!align\v!middle }#1#2{\hbox to #1{\hss#2\hss}} - -\unexpanded\def\simplealignedbox#1{\executeifdefined{\s!simple\c!align#1}{\getvalue{\s!simple\c!align\v!right}}} +% \setvalue{\s!simple\c!align\v!right }#1#2{\hbox to #1{#2\hss}} +% \setvalue{\s!simple\c!align\v!left }#1#2{\hbox to #1{\hss#2}} +% \setvalue{\s!simple\c!align\v!flushright }#1#2{\hbox to #1{\hss#2}} +% \setvalue{\s!simple\c!align\v!flushleft }#1#2{\hbox to #1{#2\hss}} +% \setvalue{\s!simple\c!align\v!middle }#1#2{\hbox to #1{\hss#2\hss}} + +% \unexpanded\def\simplealignedbox#1% +% {\csname\s!simple\c!align\ifcsname\s!simple\c!align#1\endcsname#1\else\v!right\fi\endcsname} + +\setvalue{\s!simple:\c!align:\v!right }#1{{#1\hss}} +\setvalue{\s!simple:\c!align:\v!left }#1{{\hss#1}} +\setvalue{\s!simple:\c!align:\v!flushright }#1{{\hss#1}} +\setvalue{\s!simple:\c!align:\v!flushleft }#1{{#1\hss}} +\setvalue{\s!simple:\c!align:\v!middle }#1{{\hss#1\hss}} + +\unexpanded\def\simplealignedbox#1#2% + {\hbox to #1\csname\s!simple:\c!align:\ifcsname\s!simple:\c!align:#2\endcsname#2\else\v!right\fi\endcsname} + +% \setvalue{spac_align_set_ss_\v!right }#1#2{\let#1\relax\let#2\hss } +% \setvalue{spac_align_set_ss_\v!left }#1#2{\let#1\hss \let#2\relax} +% \setvalue{spac_align_set_ss_\v!flushright}#1#2{\let#1\hss \let#2\relax} +% \setvalue{spac_align_set_ss_\v!flushleft }#1#2{\let#1\relax\let#2\hss } +% \setvalue{spac_align_set_ss_\v!middle }#1#2{\let#1\hss \let#2\hss } +% \setvalue{spac_align_set_ss_\v!low }#1#2{\let#1\vss \let#2\relax} +% \setvalue{spac_align_set_ss_\v!high }#1#2{\let#1\relax\let#2\vss } +% \setvalue{spac_align_set_ss_\v!lohi }#1#2{\let#1\vss \let#2\vss } +% \setvalue{spac_align_set_ss_\s!unknown }#1#2{\let#1\relax\let#2\relax} + +% \unexpanded\def\spac_align_set_ss#1% +% {\csname spac_align_set_ss_\ifcsname spac_align_set_ss_#1\endcsname#1\else\s!unknown\fi\endcsname} \protect \endinput diff --git a/tex/context/base/spac-grd.mkiv b/tex/context/base/spac-grd.mkiv index 243e20817..979f56056 100644 --- a/tex/context/base/spac-grd.mkiv +++ b/tex/context/base/spac-grd.mkiv @@ -228,7 +228,7 @@ {\ifvmode \bgroup \setbaselinecorrections - \whitespace + \whitespace % no longer ok \nointerlineskip \dotopbaselinecorrection \egroup @@ -246,6 +246,23 @@ \let\forcedbotbaselinecorrection\botbaselinecorrection +% experiment, todo: proper mkiv mechanism + +\def\dotopbaselinecorrection {\blank[\thetopbaselinecorrection ]} +\def\dobotbaselinecorrection {\blank[\thebotbaselinecorrection ]} +\def\donegtopbaselinecorrection{\blank[\thenegtopbaselinecorrection]} +\def\donegbotbaselinecorrection{\blank[\thenegbotbaselinecorrection]} + +\def\forcedtopbaselinecorrection + {\ifvmode + \bgroup + \setbaselinecorrections + % \vspacing[white] + \nointerlineskip + \dotopbaselinecorrection + \egroup +\fi} + \let\normalstartbaselinecorrection\startbaselinecorrection \unexpanded\def\startbaselinecorrection diff --git a/tex/context/base/spac-hor.mkiv b/tex/context/base/spac-hor.mkiv index fd4febabb..39edaa2ff 100644 --- a/tex/context/base/spac-hor.mkiv +++ b/tex/context/base/spac-hor.mkiv @@ -260,7 +260,7 @@ %D \macros %D {frenchspacing,nonfrenchspacing} %D -%D Smehow \type{\frenchspacing} can lead to hyphenation between +%D Somehow \type{\frenchspacing} can lead to hyphenation between %D dashes so we now have \type {\newfrenchspacing} (moved from %D \type {syst-chr}). @@ -888,7 +888,7 @@ \normalspaces % to be sure \to \everybeforeoutput -%D A more robust variant ofthe \MKII\ one: +%D A more robust variant of the \MKII\ one: %D %D \startbuffer %D bla \TEX\autoinsertnextspace bla diff --git a/tex/context/base/spac-ver.mkiv b/tex/context/base/spac-ver.mkiv index fb84e186f..220e1c15c 100644 --- a/tex/context/base/spac-ver.mkiv +++ b/tex/context/base/spac-ver.mkiv @@ -78,39 +78,6 @@ \fi -% \def\presetnormallineheight -% {\edef\normallineheight{\@@itline}% -% %done elsewhere : \spacing\!!plusone % new per 10/08/2004, else problems in otr / !! needed -% \iflocalinterlinespace \else -% \doifdefined\bodyfontinterlinespecs -% {\doifsomething\bodyfontinterlinespace -% {\edef\normallineheight{\bodyfontinterlinespace}}}% -% \fi} - -% \unexpanded\def\setupspecifiedinterlinespace[#1]% -% {\getparameters[\??it][#1]% -% \scratchdimen0\@@itheight\points -% \advance\scratchdimen 0\@@itdepth\points -% \ifdim\scratchdimen>\onepoint -% \showmessage\m!layouts{10}{\@@itheight,\@@itdepth}% -% \let\@@itheight\strutheightfactor -% \let\@@itdepth \strutdepthfactor -% \else -% \let\strutheightfactor\@@itheight -% \let\strutdepthfactor \@@itdepth -% \fi -% \let\minimumstrutheight \@@itminheight -% \let\minimumstrutdepth \@@itmindepth -% \let\minimumlinedistance\@@itdistance -% \let\normallineheight \@@itline % let ! ! ! ! ! ivm ex -% \doifelse\@@ittop\v!height % new, topskip does more bad than good -% {\let\topskipfactor \@@itheight} -% {\let\topskipfactor \@@ittop }% -% \let\maxdepthfactor \@@itbottom -% \let\baselinegluefactor \@@itstretch -% \setfontparameters % redundant, can be \setstrut, test first -% \updateraggedskips} % yes indeed - \def\presetnormallineheight {\edef\normallineheight{\interlinespaceparameter\c!line}% %done elsewhere : \spacing\!!plusone % new per 10/08/2004, else problems in otr / !! needed @@ -150,16 +117,6 @@ \setfontparameters} \setvalue{\??it::\v!auto }{\let\setrelativeinterlinespace\dosetrelativeinterlinespace} -% \def\dosetspecifiedrelativeinterlinespace#1% -% {\assignvalue{#1}\currentrelativeinterlinespace{1.00}{1.25}{1.50}% -% \spacing\currentrelativeinterlinespace} - -% \def\dosetspecifiedrelativeinterlinespace#1% fragile? -% {\doifdimensionelse{#1} -% {\setupspecifiedinterlinespace[\c!line=#1]} -% {\assignvalue{#1}\currentrelativeinterlinespace{1.00}{1.25}{1.50}% -% \spacing\currentrelativeinterlinespace}} - \def\dosetspecifiedrelativeinterlinespace#1% fragile? {\doifdimenstringelse{#1} {\setupspecifiedinterlinespace[\c!line=#1]} @@ -183,15 +140,9 @@ \let\setrelativeinterlinespace\relax -% \appendtoks \setrelativeinterlinespace \to \everybodyfont - \newtoks \everysetupglobalinterlinespace \newtoks \everysetuplocalinterlinespace -% \def\complexsetupinterlinespace[#1]% \commalistelement ipv #1 -% {\doifassignmentelse{#1}\setupspecifiedinterlinespace\setuprelativeinterlinespace[#1]% -% \the\iflocalinterlinespace\everysetuplocalinterlinespace\else\everysetupglobalinterlinespace\fi} - \def\interlinespaceparameter #1{\csname\dointerlinespaceparameter{\??it\currentinterlinespace}#1\endcsname} \def\dointerlinespaceparameter #1#2{\ifcsname#1#2\endcsname#1#2\else\expandafter\dointerlinespaceparentparameter\csname#1\s!parent\endcsname#2\fi} \def\dointerlinespaceparentparameter#1#2{\ifx#1\relax\s!empty\else\dointerlinespaceparameter#1#2\fi} @@ -236,23 +187,6 @@ \the\everysetuplocalinterlinespace \localinterlinespacefalse} -% \def\dosetupcheckedinterlinespace#1% often a chain -% {\edef\askedinterlinespace{#1}% -% \ifx\askedinterlinespace\empty -% \simplesetupinterlinespace -% \else -% \normalexpanded{\noexpand\doifassignmentelse{\askedinterlinespace}% -% \noexpand\setupspecifiedinterlinespace -% \noexpand\setuprelativeinterlinespace[\askedinterlinespace]}% -% \iflocalinterlinespace -% \the\everysetuplocalinterlinespace -% \else -% \localinterlinespacetrue -% \the\everysetuplocalinterlinespace -% \localinterlinespacefalse -% \fi -% \fi} - \def\dosetupcheckedinterlinespace#1% often a chain {\edef\askedinterlinespace{#1}% \ifx\askedinterlinespace\empty @@ -282,29 +216,6 @@ \fi \fi\fi} -% \unexpanded\def\setuplocalinterlinespace[#1]% -% {\localinterlinespacetrue -% \let\@@saveditheight \@@itheight -% \let\@@saveditdepth \@@itdepth -% \let\@@saveditline \@@itline -% \let\@@saveditminheight\@@itminheight -% \let\@@saveditmindepth \@@itmindepth -% \let\@@saveditdistance \@@itdistance -% \let\@@savedittop \@@ittop -% \let\@@saveditbottom \@@itbottom -% \let\@@saveditstretch \@@itstretch -% \setupinterlinespace[#1]% -% \let\@@itheight \@@saveditheight -% \let\@@itdepth \@@saveditdepth -% \let\@@itline \@@saveditline -% \let\@@itminheight\@@saveditminheight -% \let\@@itmindepth \@@saveditmindepth -% \let\@@itdistance \@@saveditdistance -% \let\@@ittop \@@savedittop -% \let\@@itbottom \@@saveditbottom -% \let\@@itstretch \@@saveditstretch -% \localinterlinespacefalse} - \unexpanded\def\setuplocalinterlinespace[#1]% {\localinterlinespacetrue \pushmacro\currentinterlinespace @@ -537,38 +448,10 @@ \def\dowhitespacemethod#1% {\ifcsname\??ws\??ws#1\endcsname\csname\??ws\??ws#1\endcsname\else\ctxparskip#1\fi\relax} - -% \def\nowhitespace -% {\ifdim\parskip>\zeropoint\relax -% \ifdim\lastskip=-\parskip -% \else -% \vskip-\parskip -% \fi -% \fi} -% -% \def\nowhitespaceunlessskip -% {\ifdim\lastskip>\zeropoint \else -% \nowhitespace -% \fi} -% -% \def\whitespace -% {\par -% \ifdim\parskip>\zeropoint\relax -% %\ifdim\lastskip>\parskip \else -% % \removelastskip interferes with blanko blokkeer en klein -% \vskip\parskip -% %\fi -% \fi} \def\nowhitespace{\vspacing[\v!nowhite]} \def\whitespace {\vspacing[\v!white]} -% obsolete: -% -% \def\savedcurrentwhitespace{\currentwhitespace} -% \def\savecurrentwhitespace {\edef\savedcurrentwhitespace{\currentwhitespace}} -% \def\restorecurrentwhitespace{\edef\currentwhitespace{\savedcurrentwhitespace}} - % De onderstaande macro handelt ook de situatie dat er geen % tekst tussen \start ... \stop is geplaatst. Daartoe wordt de % laatste skip over de lege tekst heen gehaald. Dit komt goed @@ -979,7 +862,6 @@ \def\strut{\relax\dontleavehmode\copy\strutbox} % still callbacks for \hbox{\strut} - \let\normalstrut\strut %D Sometimes a capstrut comes in handy @@ -1121,13 +1003,6 @@ %D why|>| this assignment gives troubles in for instance the %D visual debugger. -%D The plain ones: - -% \def\offinterlineskip -% {\baselineskip-\thousandpoint -% \lineskip\zeropoint -% \lineskiplimit\maxdimen} - \def\offinterlineskip {\baselineskip-\thousandpoint \lineskip\zeropoint @@ -1363,11 +1238,6 @@ \attribute \snapvboxattribute \attribute\snapmethodattribute}% \fi} - -% \appendtoks -% \dosetupgridsnapping -% \to \everysetupbodyfont - \def\installsnapvalues#1#2% todo: a proper define {\edef\currentsnapper{#1:#2}% \ifcsname\currentsnapper\endcsname \else @@ -1454,11 +1324,6 @@ \global\globalbodyfontstrutheight\strutheight \global\globalbodyfontstrutdepth \strutdepth} -% \appendtoks -% \synchronizegloballinespecs -% \synchronizelocallinespecs -% \to \everysetupgridsnapping - \appendtoks \synchronizegloballinespecs \synchronizelocallinespecs @@ -1468,10 +1333,6 @@ \synchronizelocallinespecs \to \everysetuplocalinterlinespace -% \appendtoks -% \resetsnapvalues -% \to \everyforgetall - %D Snapping. \newif\ifgridsnapping @@ -1557,10 +1418,6 @@ \fi \gridboxvbox % calculated size {\getrawnoflines{#3}% \getnoflines{#3}% -% \ifgridsnapping \else -% \vskip\topskip -% \vskip-\strutht -% \fi \scratchdimen#2\advance\scratchdimen \lineheight \dorecurse\noflines {\strut @@ -1586,10 +1443,6 @@ \def\fuzzysnappedbox#1#2% \box \unvbox {#1#2} -% \def\moveboxontogrid#1#2#3% will become obsolete -% {\doif{#2}\v!top {\setbox#1\hbox{\snaptogrid[\v!first]\box#1}}% -% \doif{#2}\v!bottom{\setbox#1\hbox{\snaptogrid[\v!last ]\box#1}}} - \def\moveboxontogrid#1#2#3% will become obsolete {} @@ -1730,9 +1583,6 @@ \unexpanded\def\definevspacingamount {\dotripleempty\dodefinevspacingamount} -% \def\dodefinevspacingamount[#1][#2][#3]% -% {\ctxlua{builders.vspacing.setskip("#1",\!!bs\detokenize{#2}\!!es,\!!bs\detokenize{#3}\!!es)}} - \def\dodefinevspacingamount[#1][#2][#3]% can be combined {\setvalue{\??vs:#1}{\ifgridsnapping#3\else#2\fi}% \ctxlua{builders.vspacing.setskip("#1")}} @@ -1801,16 +1651,6 @@ % category:4 is default -% \definevspacingamount[\v!big] [\bigskipamount] [\openlineheight] -% \definevspacingamount[\v!medium] [\medskipamount] [0.50\openlineheight] -% \definevspacingamount[\v!small] [\smallskipamount] [0.25\openlineheight] -% \definevspacingamount[\v!line] [\openlineheight] [\openlineheight] -% \definevspacingamount[\v!halfline][0.50\openlineheight][0.50\openlineheight] -% \definevspacingamount[\v!formula] [\medskipamount] [0.50\openlineheight] -% \definevspacingamount[\v!white] [\parskip] [\openlineheight] -% \definevspacingamount[\v!height] [\strutheight] [\strutheight] -% \definevspacingamount[\v!depth] [\strutdepth] [\strutdepth] - \definevspacingamount[\v!none] [\zeropoint] [\zeropoint] \definevspacingamount[\v!big] [\bigskipamount] [\bodyfontlineheight] \definevspacingamount[\v!medium] [\medskipamount] [0.50\bodyfontlineheight] @@ -1849,26 +1689,6 @@ \dorecurse{10} % todo: other values < 4000 {\expanded{\definevspacing[\v!samepage-\recurselevel][penalty:\the\numexpr4000+250*\recurselevel\relax]}} -% \setfalse\vspacingenabled -% -% \newtoks\everyenablevspacing -% \newtoks\everydisablevspacing -% -% \def\enablevspacing {\the\everyenablevspacing} -% \def\disablevspacing{\the\everydisablevspacing} -% -% \appendtoks -% \writestatus\m!system{! ! enabling vspacing ! !}% -% \settrue\vspacingenabled -% \ctxlua{builders.vspacing.enable()}% -% \to \everyenablevspacing -% -% \appendtoks -% \writestatus\m!system{! ! disabling vspacing ! !}% -% \setfalse\vspacingenabled -% \ctxlua{builders.vspacing.disable()}% -% \to \everydisablevspacing - \let\blank \vspacing \let\defineblank \definevspacing \let\defineblankmethod\definevspacingamount @@ -1937,48 +1757,6 @@ %D \stoplines %D \stoptyping -% \unexpanded\def\setuplines -% {\dodoubleargument\getparameters[\??rg]} -% -% \unexpanded\def\startlines -% {\@@rgbefore -% \pushmacro\checkindentation -% \whitespace -% %\page[\v!preference]} gaat mis na koppen, nieuw: later \nobreak -% \begingroup -% \setupindenting[\@@rgindenting]% -% \typesettinglinestrue -% \setupwhitespace[\v!none]% -% \obeylines -% \ignorespaces -% \gdef\afterfirstobeyedline % tzt two pass, net als opsomming -% {\gdef\afterfirstobeyedline -% {\nobreak -% \doifnot\@@rgoption\v!packed{\global\let\afterfirstobeyedline\relax}}}% -% \def\obeyedline -% {\par -% \futurelet\next\dobetweenthelines}% -% \activatespacehandler\@@rgspace -% \GotoPar} -% -% \unexpanded\def\stoplines -% {\endgroup -% \popmacro\checkindentation -% \@@rgafter} -% -% \def\dobetweenthelines -% {\doifmeaningelse\next\obeyedline -% {\@@rginbetween} -% {\afterfirstobeyedline}} -% -% \setuplines -% [\c!option=, -% \c!before=\blank, -% \c!after=\blank, -% \c!inbetween=\blank, -% \c!indenting=\v!no, -% \c!space=\v!default] - %D Contrary to \MKII\ we can now define classes of lines (generalized by %D Wolfgang). I will probably rewrite bits in \LUA. @@ -2026,30 +1804,6 @@ {\def\docommand##1{\getparameters[\??rg##1][#2]}% \processcommacommand[#1]\docommand}} -% \def\dostartlines[#1]% -% {\bgroup -% \edef\currentlines{#1}% -% \linesparameter\c!before -% \pushmacro\checkindentation -% \whitespace -% \begingroup -% \dosetlinesattributes\c!style\c!color -% \setupindenting[\linesparameter\c!indenting]% -% \setupalign[\linesparameter\c!align]% -% \typesettinglinestrue -% \setupwhitespace[\v!none]% -% \obeylines -% \ignorespaces -% \gdef\afterfirstobeyedline % tzt two pass, net als opsomming -% {\gdef\afterfirstobeyedline -% {\nobreak -% \doifnot{\linesparameter\c!option}\v!packed{\global\let\afterfirstobeyedline\relax}}}% -% \def\obeyedline -% {\par -% \futurelet\next\dobetweenthelines}% -% \activatespacehandler{\linesparameter\c!space}% -% \GotoPar} - \def\dostartlines[#1]% {\bgroup \edef\currentlines{#1}% diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf index bb84d7da8..9601ad93f 100644 Binary files a/tex/context/base/status-files.pdf and b/tex/context/base/status-files.pdf differ diff --git a/tex/context/base/strc-blk.lua b/tex/context/base/strc-blk.lua index 5040c34e5..0c94af5bb 100644 --- a/tex/context/base/strc-blk.lua +++ b/tex/context/base/strc-blk.lua @@ -35,7 +35,8 @@ end job.register('structures.blocks.collected', tobesaved, initializer) -local printer = (lpeg.patterns.textline/tex.print)^0 -- can be shared +local printer = (lpeg.patterns.textline/tex.print)^0 -- can be shared +local listitem = utilities.parsers.listitem function blocks.print(name,data,hide) if hide then @@ -58,7 +59,7 @@ end function blocks.setstate(state,name,tag) local all = tag == "" local tags = not all and settings_to_array(tag) - for n in gmatch(name,"%s*([^,]+)") do + for n in listitem(name) do local sn = states[n] if not sn then -- error diff --git a/tex/context/base/strc-des.mkiv b/tex/context/base/strc-des.mkiv index 9cca5e8d4..54eba1eaa 100644 --- a/tex/context/base/strc-des.mkiv +++ b/tex/context/base/strc-des.mkiv @@ -902,7 +902,7 @@ userdata = structures.helpers.touserdata(\!!bs\detokenize{#2}\!!es) } }}% - \xdef\currentdescriptionattribute {\ctxlua {tex.write(structures.references.setinternalreference("\referenceprefix","\currentdescriptionreference",\nextinternalreference,"\@@iafocus"))}}% + \xdef\currentdescriptionattribute {\ctxlua {tex.write(structures.references.setinternalreference("\referenceprefix","\currentdescriptionreference",\nextinternalreference,"\interactionparameter\c!focus"))}}% \xdef\currentdescriptionsynchronize{\ctxlatelua{structures.lists.enhance(\currentdescriptionnumberentry)}}% \fi \endgroup} diff --git a/tex/context/base/strc-doc.mkiv b/tex/context/base/strc-doc.mkiv index 0942de4c3..683d65d2f 100644 --- a/tex/context/base/strc-doc.mkiv +++ b/tex/context/base/strc-doc.mkiv @@ -188,7 +188,7 @@ \let\previousstructurecounter\!!zerocount \def\setstructuresynchronization#1% - {\xdef\currentstructureattribute {\ctxlua {tex.write(structures.references.setinternalreference("\currentstructurereferenceprefix","\currentstructurereference",\nextinternalreference,"\@@iafocus"))}}% + {\xdef\currentstructureattribute {\ctxlua {tex.write(structures.references.setinternalreference("\currentstructurereferenceprefix","\currentstructurereference",\nextinternalreference,"\interactionparameter\c!focus"))}}% \xdef\currentstructuresynchronize{\ctxlatelua{structures.lists.enhance(#1)}}} \def\reportcurrentstructure{\ctxlua{structures.sections.reportstructure()}} diff --git a/tex/context/base/strc-itm.mkiv b/tex/context/base/strc-itm.mkiv index d0cf8817f..24e343132 100644 --- a/tex/context/base/strc-itm.mkiv +++ b/tex/context/base/strc-itm.mkiv @@ -46,7 +46,7 @@ \newcount\noflistelements \newcount\itemcolumndepth \newcount\itemdepth -\newcount\maxitemdepth \maxitemdepth=6 +% \newcount\maxitemdepth \maxitemdepth=6 \newdimen\itemgrouplistwidth \newdimen\itemgroupaskedwidth @@ -202,10 +202,11 @@ % so we have: \def\initializeitemgrouplevel#1% - {\ifcsname\??op\currentitemgroup#1\s!parent\endcsname + {\ifcsname\??op\currentitemgroup\number#1\s!parent\endcsname % ok \else - \setevalue{\??op\currentitemgroup#1\s!parent}{\??op\currentitemgroup}% + \setxvalue{\??op\currentitemgroup \c!levels}{\number#1}% + \setxvalue{\??op\currentitemgroup\number#1\s!parent}{\??op\currentitemgroup}% \fi} \unexpanded\def\defineitemgroup @@ -215,15 +216,15 @@ {\doifsomething{#1} {\pushmacro\currentitemgroup \def\currentitemgroup{#1}% - \setvalue{\e!start#1}{\startitemgroup[#1]}% - \setvalue{\e!stop#1}{\stopitemgroup}% - \setvalue{\e!setup#1\e!endsetup}{\setupitemgroup[#1]}% for old times sake + \setuvalue{\e!start#1}{\startitemgroup[#1]}% + \setuvalue{\e!stop#1}{\stopitemgroup}% + \setuvalue{\e!setup#1\e!endsetup}{\setupitemgroup[#1]}% for old times sake \doifelsenothing{#2} {\getparameters[\??op#1][\s!parent=\??oo,#3]}% {\doifassignmentelse{#2} {\getparameters[\??op#1][\s!parent=\??oo,#2]}% {\getparameters[\??op#1][\s!parent=\??op#2,#3]}}% - \dorecurse\maxitemdepth{\initializeitemgrouplevel\recurselevel}% + \dorecurse{\itemparameter\empty\c!levels}{\initializeitemgrouplevel\recurselevel}% \definestructurecounter[itemgroup:#1]% \popmacro\currentitemgroup}} @@ -359,7 +360,7 @@ \getitemparameter\currentitemlevel\c!afterhead \fi\fi} -\def\dododododosetupitemgroup[#1][#2]% +\unexpanded\def\dododododosetupitemgroup[#1][#2]% prevent expansion below {\doifassignmentelse{#2}% {\dosetupitemgroupvariable[#1][#2]}% {\setitemparameter{#1}\c!option{#2}}}% @@ -367,19 +368,8 @@ \def\dodododosetupitemgroup[#1][#2]% {\doifsomething{#2} {\doifelse{#1}\v!each - {\dorecurse\maxitemdepth{\normalexpanded{\noexpand\dododododosetupitemgroup[\recurselevel]}[#2]}} - {\normalexpanded{\noexpand\dododododosetupitemgroup[#1]}[#2]}}} - -% \def\dododosetupitemgroup[#1][#2]% -% {\doifelsenothing{#2} -% {\doifelsenothing{#1} -% {\dodododosetupitemgroup[\currentitemlevel][#2]} -% {\dodododosetupitemgroup[#1][#2]}} -% {\ifcase\currentitemlevel\relax -% \dodododosetupitemgroup[\v!each][#1]% -% \else -% \dodododosetupitemgroup[\currentitemlevel][#1]% -% \fi}} + {\dorecurse{\itemparameter\empty\c!levels}{\normalexpanded{\dododododosetupitemgroup[\recurselevel]}[#2]}} + {\normalexpanded{\dododododosetupitemgroup[#1]}[#2]}}} \def\dododosetupitemgroup[#1][#2]% {\doifelsenothing{#2} @@ -538,13 +528,8 @@ \def\dodostartitemgroup[#1]% [#2]% {\relax % prevents lookahead - \ifnum\currentitemlevel=\maxitemdepth\relax - \showmessage\m!layouts9{\number\maxitemdepth}% - \let\itemincrement\zerocount - \else - \let\itemincrement\plusone - \fi - \global\advance\itemdepth\itemincrement + \global\advance\itemdepth\plusone + \initializeitemgrouplevel\itemdepth \xdef\currentitemlevel{\number\itemdepth}% \edef\itemgroupoptions{\getitemparameter\currentitemlevel\c!option}% \ifx\itemgroupoptions\empty @@ -818,12 +803,12 @@ \fi \ifconditional\textlistitem % else forgotten \endgroup - \global\advance\itemdepth-\itemincrement + \global\advance\itemdepth-\plusone \xdef\currentitemlevel{\number\itemdepth}% \egroup \else \endgroup - \global\advance\itemdepth-\itemincrement + \global\advance\itemdepth-\plusone \xdef\currentitemlevel{\number\itemdepth}% \egroup \par @@ -1385,6 +1370,7 @@ %\c!inner=, \c!n=2, \c!items=4, + \c!levels=10, \c!lefttext=(, \c!righttext=), \c!start=1, diff --git a/tex/context/base/strc-lst.mkiv b/tex/context/base/strc-lst.mkiv index 50458c4d5..b14297e26 100644 --- a/tex/context/base/strc-lst.mkiv +++ b/tex/context/base/strc-lst.mkiv @@ -635,13 +635,7 @@ {\edef\currentlist{#1}% \edef\currentlistnumber{#3}% \docurrentlistalternative - \let\@@iawidth\!!zeropoint % todo: constant or so -% \begingroup -% \edef\listelements{\listparameter\c!pageboundaries}% -% \normalexpanded{\noexpand\doifinset{#3}{\listelements}} -% {\showmessage\m!system{14}{#3}% -% \page}% -% \endgroup + \letinteractionparameter\c!width\zeropoint \dontcomplain \dosomelistelement{#1}{#2}{#3}{#4}{#5}{#6}} diff --git a/tex/context/base/strc-num.lua b/tex/context/base/strc-num.lua index 943e9fd4f..b8a62d152 100644 --- a/tex/context/base/strc-num.lua +++ b/tex/context/base/strc-num.lua @@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['strc-num'] = { license = "see context related readme files" } +-- this will be reimplemented + local format = string.format local next, type = next, type local min, max = math.min, math.max @@ -117,7 +119,7 @@ local function enhance() enhance = nil end -local function allocate(name,i) +local function allocate(name,i) -- can be metatable local cd = counterdata[name] if not cd then cd = { diff --git a/tex/context/base/strc-ref.lua b/tex/context/base/strc-ref.lua index d97de03ed..eeca38bb6 100644 --- a/tex/context/base/strc-ref.lua +++ b/tex/context/base/strc-ref.lua @@ -137,20 +137,6 @@ function references.referredpage(n) return referred[n] or referred[n] or texcount.realpageno end -function references.checkedpage(n,page) - local r, p = referred[n] or texcount.realpageno, tonumber(page) - if not p then - -- sorry - elseif p > r then - texcount.referencepagestate = 3 - elseif p < r then - texcount.referencepagestate = 2 - else - texcount.referencepagestate = 1 - end - return p -end - function references.registerpage(n) if not tobereferred[n] then if n > maxreferred then @@ -319,7 +305,7 @@ local function register_from_lists(collected,derived) if kind and realpage then local d = derived[prefix] if not d then d = { } derived[prefix] = d end local t = { kind, i } - for s in gmatch(reference,"%s*([^,]+)") do + for s in gmatch(reference,"[^, ]+") do if trace_referencing then report_references("list entry %s provides %s reference '%s' on realpage %s",i,kind,s,realpage) end @@ -784,39 +770,67 @@ end references.currentset = nil -local b, e = "\\ctxlua{local jc = structures.references.currentset;", "}" -local o, a = 'jc[%s].operation=[[%s]];', 'jc[%s].arguments=[[%s]];' +--~ local b, e = "\\ctxlua{local jc = structures.references.currentset;", "}" +--~ local o, a = 'jc[%s].operation=[[%s]];', 'jc[%s].arguments=[[%s]];' +--~ +--~ function references.expandcurrent() -- todo: two booleans: o_has_tex& a_has_tex +--~ local currentset = references.currentset +--~ if currentset and currentset.has_tex then +--~ local done = false +--~ for i=1,#currentset do +--~ local ci = currentset[i] +--~ local operation = ci.operation +--~ if operation then +--~ if find(operation,"\\") then -- if o_has_tex then +--~ if not done then +--~ context(b) +--~ done = true +--~ end +--~ context(o,i,operation) +--~ end +--~ end +--~ local arguments = ci.arguments +--~ if arguments then +--~ if find(arguments,"\\") then -- if a_has_tex then +--~ if not done then +--~ context(b) +--~ done = true +--~ end +--~ context(a,i,arguments) +--~ end +--~ end +--~ end +--~ if done then +--~ context(e) +--~ end +--~ end +--~ end + +function commands.setreferenceoperation(k,v) + references.currentset.operation[k] = v +end + +function commands.setreferencearguments(k,v) + references.currentset.arguments[k] = 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 - local done = false for i=1,#currentset do local ci = currentset[i] local operation = ci.operation - if operation then - if find(operation,"\\") then -- if o_has_tex then - if not done then - context(b) - done = true - end - context(o,i,operation) - end + if operation and find(operation,"\\") then -- if o_has_tex then + expandreferenceoperation(i,operation) end local arguments = ci.arguments - if arguments then - if find(arguments,"\\") then -- if a_has_tex then - if not done then - context(b) - done = true - end - context(a,i,arguments) - end + if arguments and find(arguments,"\\") then -- if a_has_tex then + expandreferencearguments(i,arguments) end end - if done then - context(e) - end end end @@ -1377,17 +1391,31 @@ references.testspecials = references.testspecials or { } local runners = references.testrunners local specials = references.testspecials +local function checkedpagestate(n,page) + local r, p = referred[n] or texcount.realpageno, tonumber(page) + if not p then + return 0 + elseif p > r then + return 3 + elseif p < r then + return 2 + else + return 1 + end +end + function references.analyze(actions) actions = actions or references.currentset if not actions then actions = { realpage = 0 } + texcount.referencepagestate = 0 elseif actions.realpage then -- already analyzed + texcount.referencepagestate = checkedpagestate(actions.n,actions.realpage) else -- 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 - texcount.referencepagestate = 0 local nofactions = #actions if nofactions > 0 then for i=1,nofactions do @@ -1397,7 +1425,9 @@ function references.analyze(actions) what = what(a,actions) end end - references.checkedpage(actions.n,actions.realpage) + texcount.referencepagestate = checkedpagestate(actions.n,actions.realpage) + else + texcount.referencepagestate = 0 end end return actions @@ -1425,7 +1455,7 @@ references.realpageofpage = realpageofpage -- -references.pages = allocate { +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, @@ -1440,6 +1470,8 @@ references.pages = allocate { [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) @@ -1458,6 +1490,8 @@ end runners["special operation"] = runners["special"] runners["special operation with arguments"] = runners["special"] +-- weird, why is this code here and in lpdf-ano + function specials.internal(var,actions) local v = references.internals[tonumber(var.operation)] local r = v and v.references.realpage @@ -1468,25 +1502,32 @@ end specials.i = specials.internal --- weird, why is this code here and in lpdf-ano - -local pages = references.pages - -function specials.page(var,actions) -- is this ok? - local p = pages[var.operation] +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 - actions.realpage = p + var.r = p + actions.realpage = actions.realpage or p -- first wins end end -function specials.realpage(var,actions) -- is this ok? - actions.realpage = tonumber(var.operation) +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) -- is this ok? - actions.realpage = tonumber(realpageofpage(var.operation)) +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 diff --git a/tex/context/base/strc-ref.mkiv b/tex/context/base/strc-ref.mkiv index ecda0387d..60a02c171 100644 --- a/tex/context/base/strc-ref.mkiv +++ b/tex/context/base/strc-ref.mkiv @@ -173,7 +173,7 @@ \ifx\currentreferenceuserdata\empty\else userdata = structures.helpers.touserdata(\!!bs\detokenize{#3}\!!es) \fi - },"\@@iafocus") + },"\interactionparameter\c!focus") }% % todo: optional \xdef\currentdestinationattribute{\number\lastdestinationattribute}% @@ -197,7 +197,7 @@ metadata = { % we could assume page to have no metadata kind = "\s!page", }, - },"\@@iafocus") + },"\interactionparameter\c!focus") }% \xdef\currentdestinationattribute{\number\lastdestinationattribute}% \else @@ -454,6 +454,9 @@ \def\expandtexincurrentreference % will happen in lua some time {\ifcase\referencehastexstate\else\ctxlua{structures.references.expandcurrent()}\fi} +\def\expandreferenceoperation#1#2{\ctxcommand{setreferenceoperation(#1,\!!bs#2\!!es)}} +\def\expandreferencearguments#1#2{\ctxcommand{setreferencearguments(#1,\!!bs#2\!!es)}} + \def\doifreferencefoundelse#1#2#3% {\ctxlua{structures.references.doifelse("\referenceprefix","#1",\luaconditional\highlighthyperlinks,\luaconditional\gotonewwindow)}% {\expandtexincurrentreference#2}% @@ -891,7 +894,7 @@ \unexpanded\def\referencesymbol {\hbox{\strut\high - {\setupsymbolset[\@@iasymbolset]% + {\setupsymbolset[\interactionparameter\c!symbolset]% \symbol [\ifcase\referencepagestate \v!somewhere @@ -1066,11 +1069,11 @@ % but at least aditya can play with it now \doifsomething{\referenceformatparameter\c!style} {\dosetfontattribute{\??rf\currentreferenceformat}\c!style - \let\@@iastyle\empty}% + \resetinteractionparameter\c!style}% \doifsomething{\referenceformatparameter\c!color} {\dosetcolorattribute{\??rf\currentreferenceformat}\c!color - \let\@@iacontrastcolor\empty - \let\@@iacolor \empty}% + \resetinteractionparameter\c!contrastcolor + \resetinteractionparameter\c!color}% % \edef\currentreferenceformatlabel{\referenceformatparameter\c!label}% \ifx\currentreferenceformatlabel\autoreferencelabeltextflag @@ -1174,7 +1177,7 @@ \attribute\referenceattribute\attributeunsetvalue \iflocation \dostarttagged\t!link\empty - \ctxlua{structures.references.inject("\referenceprefix","#2",\number\dimexpr\@@iaheight\relax,\number\dimexpr\@@iadepth\relax,\extrareferencearguments)}% + \ctxlua{structures.references.inject("\referenceprefix","#2",\number\dimexpr\interactionparameter\c!height\relax,\number\dimexpr\interactionparameter\c!depth\relax,\extrareferencearguments)}% \dostoptagged \setlocationattributes\??ia \attribute\referenceattribute\lastreferenceattribute @@ -1214,7 +1217,7 @@ \ctxlua{structures.references.doifelse("\referenceprefix","#3",\extrareferencearguments)}% {\expandtexincurrentreference \dostarttagged\t!link\empty - \ctxlua{structures.references.injectcurrentset(\number\dimexpr\@@iaheight\relax,\number\dimexpr\@@iadepth\relax)}% + \ctxlua{structures.references.injectcurrentset(\number\dimexpr\interactionparameter\c!height\relax,\number\dimexpr\interactionparameter\c!depth\relax)}% \dostoptagged \setlocationattributes\??ia \global\lastsavedreferenceattribute\lastreferenceattribute @@ -1592,184 +1595,6 @@ % this will be done differently (when it's needed) \fi} -%D Buttons are just what their names says: things that can be -%D clicked (pushed) on. They are similar to \type{\goto}, -%D except that the text argument is not interpreted. -%D Furthermore one can apply anything to them that can be done -%D with \type{\framed}. -%D -%D \startbuffer -%D \button[width=3cm,height=1.5cm]{Exit}[ExitViewer] -%D \stopbuffer -%D -%D \typebuffer -%D -%D gives -%D -%D \getbuffer -%D -%D This command is formally specified as: -%D -%D \showsetup{button} -%D -%D The characteristics can be set with: -%D -%D \showsetup{setupbuttons} - -\unexpanded\def\setupbuttons - {\dodoubleargument\getparameters[\??bt]} - -\definecomplexorsimpleempty\button - -\def\complexbutton - {\docomplexbutton\??bt} - -\presetlocalframed[\??bt] - -\long\def\docomplexbutton#1[#2]#3#4% get rid of possible space before [#4] - {\dodocomplexbutton#1[#2]{#3}#4} % #4 == [ - -\def\buttonframed{\dodoubleempty\localframed[\??bt]} % goodie - -\long\def\dodocomplexbutton#1[#2]#3[#4]% #3 can contain [] -> {#3} later - {\begingroup - \doifvalue{#1\c!state}\v!stop\locationfalse - \iflocation - \ConvertConstantAfter\doifelse{#3}\v!none\hphantom\hbox - {\doifelsenothing{#4} - {\setlocationboxnop#1[#2]{#3}[#4]} - {\doifreferencefoundelse{#4} % INEFFICIENT - {\setlocationboxyes#1[#2]{#3}[#4]} - {\setlocationboxnop#1[#2]{#3}[#4]}}}% - \fi - \endgroup} - -\setupbuttons - [\c!state=\v!start] - -%D Interaction buttons, in fact a row of tiny buttons, are -%D typically only used for navigational purposed. The next -%D macro builds such a row based on a specification list. -%D -%D \startbuffer -%D \interactionbuttons -%D [width=\hsize][page,PreviousJump,ExitViewer] -%D \stopbuffer -%D -%D \typebuffer -%D -%D gives -%D -%D \getbuffer -%D -%D Apart from individual entries, one can use \type{page} and -%D \type {subpage} as shortcuts to their four associated buttons. -%D The symbols are derived from the symbols linked to the -%D entries. - -% does not work well with for instance SomeRef{whatever} - -\def\interactionbuttons - {\dodoubleempty\dointeractionbuttons} - -\def\dointeractionbuttons[#1][#2]% er is een verdeel macro \horizontalfractions - {\iflocation - % BUG: fails when frame=off; best is to rewrite this macro - \bgroup - \doif\@@ibstate\v!stop\locationfalse - \iflocation - \ifsecondargument - \setupinteractionbar[#1]% - \checkinteractionbar{1.5em}\v!broad\!!zeropoint % brrrrr - \setbox2\hbox{\localframed[\??ib][\c!background=]{\symbol[\@@iasymbolset][\v!previouspage]}}% - \!!heighta\ht2 % needed because we default to nothing - \setupinteractionbar[\c!strut=\v!no]% - \setinteractionparameter\c!width\!!zeropoint - \!!counta\zerocount % new, was 1 - \processallactionsinset - [#2] - [ \v!page=>\advance\!!counta 4, - \v!subpage=>\advance\!!counta 4, - \s!unknown=>\advance\!!counta 1]% - \ifdim\@@ibwidth=\zeropoint - \!!widtha2em - \advance\!!widtha \@@ibdistance % new - \!!widthb\!!counta\!!widtha - \advance\!!widthb -\@@ibdistance % new - \else - \!!widtha\@@ibwidth - \!!widthb\@@ibdistance % new - \multiply\!!widthb \!!counta % new - \advance\!!widthb -\@@ibdistance % new - \advance\!!widtha -\!!widthb % new - \divide\!!widtha \!!counta - \!!widthb\@@ibwidth - \fi - \def\xgoto##1% clash ? - {\setnostrut - \edef\localreference{##1}% - \normalexpanded{\noexpand\dodocomplexbutton\??ib[\c!height=\the\!!heighta,\c!width=\the\!!widtha]}% - {\dontleavehmode\symbol[\@@iasymbolset][\localreference]}% - [\localreference]% - \hss}% - \hbox to \!!widthb - {\processallactionsinset - [#2] - [ \v!page=>\xgoto\v!firstpage - \xgoto\v!nextpage - \xgoto\v!previouspage - \xgoto\v!lastpage, - \v!subpage=>\xgoto\v!firstsubpage - \xgoto\v!nextsubpage - \xgoto\v!previoussubpage - \xgoto\v!lastsubpage, - \s!unknown=>\xgoto\commalistelement]% - \unskip}% - \else - \interactionbuttons[][#1]% - \fi - \fi - \egroup - \fi} - -%D \macros -%D {overlaybutton} -%D -%D For converience we provide: -%D -%D \starttyping -%D \overlaybutton[reference] -%D \stoptyping -%D -%D This command can be used to define overlays an/or can be -%D used in the whatevertext areas, like: -%D -%D \starttyping -%D \defineoverlay[PrevPage][\overlaybutton{PrevPage}] -%D \setupbackgrounds[page][background=PrevPage] -%D \setuptexttexts[\overlaybutton{NextPage}] -%D \stoptyping -%D -%D For practical reasons, this macro accepts square brackets -%D as well as braces. - -\definecomplexorsimple\overlaybutton - -\def\simpleoverlaybutton#1% - {\complexoverlaybutton[#1]} - -\def\complexoverlaybutton[#1]% - {\iflocation - \gotobox{\overlayfakebox}[#1]% - \fi} - -\def\overlayfakebox - {\hbox - {\setbox\scratchbox\emptyhbox - \wd\scratchbox\overlaywidth - \ht\scratchbox\overlayheight - \box\scratchbox}} - %D \macros %D {dotextprefix} %D diff --git a/tex/context/base/strc-sec.mkiv b/tex/context/base/strc-sec.mkiv index ee5c0ec65..350d971a2 100644 --- a/tex/context/base/strc-sec.mkiv +++ b/tex/context/base/strc-sec.mkiv @@ -295,13 +295,15 @@ {\setfalse\currentstructureown %\globalpushmacro\currentstructurehead \xdef\currentstructurehead{#1}% + \structureheadparameter\c!beforesection \the\everybeforestructurehead - \dohandlestructurehead{#1}{#2}{#3}} % name -- -- -- userdata + \dohandlestructurehead{#1}{#2}{#3}} % name -- -- -- userdata (we might move the tagged to here) \unexpanded\def\dostopstructurehead[#1]% - {%\globalpopmacro\currentstructurehead + {\dostoptagged\dostoptagged + %\globalpopmacro\currentstructurehead \xdef\currentstructurehead{#1}% recover - \dostoptagged\dostoptagged + \structureheadparameter\c!aftersection \the\everyafterstructurehead} \unexpanded\def\donextstructurehead[#1][#2][#3]% diff --git a/tex/context/base/strc-syn.mkiv b/tex/context/base/strc-syn.mkiv index ea551625b..1cb1bb1f6 100644 --- a/tex/context/base/strc-syn.mkiv +++ b/tex/context/base/strc-syn.mkiv @@ -343,9 +343,9 @@ % maybe just 'commandset' and then combine \unexpanded\def\placelistofsorts - {\dodoubleempty\placelistofsorts} + {\dodoubleempty\doplacelistofsorts} -\unexpanded\def\placelistofsorts[#1][#2]% NOG EEN RUWE VERSIE MAKEN ZONDER WITRUIMTE ETC ETC +\def\doplacelistofsorts[#1][#2]% NOG EEN RUWE VERSIE MAKEN ZONDER WITRUIMTE ETC ETC {\begingroup \def\currentsorting{#1}% \getparameters[\??so#1][#2]% @@ -358,7 +358,7 @@ \stoppacked \endgroup} -\def\completelistofsorts +\unexpanded\def\completelistofsorts {\dodoubleempty\docompletelistofsorts} \def\docompletelistofsorts[#1][#2]% diff --git a/tex/context/base/supp-fil.lua b/tex/context/base/supp-fil.lua index 810e45d9f..91fa446b9 100644 --- a/tex/context/base/supp-fil.lua +++ b/tex/context/base/supp-fil.lua @@ -45,7 +45,7 @@ end local testcase = commands.testcase function commands.splitfilename(fullname) - local path, name, base, suffix, kind = '', fullname, fullname, '', 0 + local path, name, base, suffix = '', fullname, fullname, '' local p, n = match(fullname,"^(.+)/(.-)$") if p and n then path, name, base = p, n, n diff --git a/tex/context/base/symb-ini.lua b/tex/context/base/symb-ini.lua index eb4315c62..1bc8ee2aa 100644 --- a/tex/context/base/symb-ini.lua +++ b/tex/context/base/symb-ini.lua @@ -6,6 +6,7 @@ if not modules then modules = { } end modules ['symb-ini'] = { license = "see context related readme files" } + local variables = interfaces.variables fonts.symbols = fonts.symbols or { } @@ -15,19 +16,22 @@ 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 function symbols.uselibrary(name) if name ~= variables.reset then - commands.uselibrary(name,patterns,function(name,foundname) - -- context.startnointerference() - context.startreadingfile() - context.input(foundname) - status_symbols("loaded: library '%s'",name) - context.stopreadingfile() - -- context.stopnointerference() - end, function(name) - report_symbols("unknown: library '%s'",name) - end) + for name in listitem(name) do + commands.uselibrary(name,patterns,function(name,foundname) + -- context.startnointerference() + context.startreadingfile() + context.input(foundname) + status_symbols("loaded: library '%s'",name) + context.stopreadingfile() + -- context.stopnointerference() + end, function(name) + report_symbols("unknown: library '%s'",name) + end) + end end end diff --git a/tex/context/base/syst-aux.mkiv b/tex/context/base/syst-aux.mkiv index f9c339dff..0fd453e2d 100644 --- a/tex/context/base/syst-aux.mkiv +++ b/tex/context/base/syst-aux.mkiv @@ -195,16 +195,22 @@ %D The next set of macros just do nothing, except that they %D get rid of a number of arguments. -\long\def\gobbleoneargument #1{} -\long\def\gobbletwoarguments #1#2{} -\long\def\gobblethreearguments #1#2#3{} -\long\def\gobblefourarguments #1#2#3#4{} -\long\def\gobblefivearguments #1#2#3#4#5{} -\long\def\gobblesixarguments #1#2#3#4#5#6{} -\long\def\gobblesevenarguments #1#2#3#4#5#6#7{} -\long\def\gobbleeightarguments #1#2#3#4#5#6#7#8{} -\long\def\gobbleninearguments #1#2#3#4#5#6#7#8#9{} -\long\def\gobbletenarguments #1{\gobbleninearguments} +\long\def\gobbleoneargument #1{} +\long\def\gobbletwoarguments #1#2{} +\long\def\gobblethreearguments#1#2#3{} +\long\def\gobblefourarguments #1#2#3#4{} +\long\def\gobblefivearguments #1#2#3#4#5{} +\long\def\gobblesixarguments #1#2#3#4#5#6{} +\long\def\gobblesevenarguments#1#2#3#4#5#6#7{} +\long\def\gobbleeightarguments#1#2#3#4#5#6#7#8{} +\long\def\gobbleninearguments #1#2#3#4#5#6#7#8#9{} +\long\def\gobbletenarguments #1{\gobbleninearguments} + +\def\gobbleoneoptional [#1]{} +\def\gobbletwooptionals [#1][#2]{} +\def\gobblethreeoptionals[#1][#2][#3]{} +\def\gobblefouroptionals [#1][#2][#3][#4]{} +\def\gobblefiveoptionals [#1][#2][#3][#4][#5]{} %D \macros %D {doifnextcharelse} @@ -1130,7 +1136,7 @@ \fi \fi} -\def\processfirstactioninset[#1]% +\unexpanded\def\processfirstactioninset[#1]% {\expandedaction\!!stringa{#1}% \ifx\!!stringa\empty \expandafter\processaction @@ -1139,7 +1145,7 @@ \fi [#1]} -\def\processfirstactionsinsetindeed[#1]#2[#3]% +\unexpanded\def\processfirstactionsinsetindeed[#1]#2[#3]% {\def\p!doprocessaction##1% {\def\p!dodoprocessaction####1% {\p!compareprocessactionC[####1][##1]}% @@ -1165,7 +1171,7 @@ \def\doprocessallactionsinset {\csname\s!do\the\processlevel\endcsname} -\def\processallactionsinset[#1]% +\unexpanded\def\processallactionsinset[#1]% {\expandedaction\!!stringa{#1}% \ifx\!!stringa\empty \expandafter\processaction @@ -1174,7 +1180,7 @@ \fi [#1]} -\def\processallactionsinsetindeed[#1]#2[#3]% +\unexpanded\def\processallactionsinsetindeed[#1]#2[#3]% {\advance\processlevel \plusone \expandafter\def\csname\s!do\the\processlevel\endcsname##1% {\def\p!dodoprocessaction####1% @@ -7282,7 +7288,14 @@ %D A variant for \type {\executeifdefined}: -\def\expandcheckedcsname#1#2#3% +% \def\expandcheckedcsname#1#2#3% +% {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname} + +\def\expandcheckedcsname#1#2% #2 is often a \xxxparameter so let's expand it once + {\normalexpanded{\noexpand\doexpandcheckedcsname{#1}{#2}}} + +\def\doexpandcheckedcsname#1#2#3% {\csname#1\ifcsname#1#2\endcsname#2\else#3\fi\endcsname} + \protect \endinput diff --git a/tex/context/base/tabl-ntb.mkiv b/tex/context/base/tabl-ntb.mkiv index dfdf46510..96b1aabed 100644 --- a/tex/context/base/tabl-ntb.mkiv +++ b/tex/context/base/tabl-ntb.mkiv @@ -547,6 +547,7 @@ \lettbltag\maximumrow\currentcol\tblcell \settblcol\maximumrow\currentcol{\number\tblnx}% \settblrow\maximumrow\currentcol{\number\tblny}% + % the action key will change! \settblref\maximumrow\currentcol{\ifcsname\@@tbl\c!action\endcsname\csname\@@tbl\c!action\endcsname\fi}% % save text \edef\celltag{{\number\maximumrow}{\number\currentcol}}% diff --git a/tex/context/base/trac-tex.lua b/tex/context/base/trac-tex.lua index ab9e73e6e..dcfbf965d 100644 --- a/tex/context/base/trac-tex.lua +++ b/tex/context/base/trac-tex.lua @@ -8,6 +8,8 @@ if not modules then modules = { } end modules ['trac-hsh'] = { -- moved from trac-deb.lua +local format = string.format + local texhashtokens = tex.hashtokens local trackers = trackers @@ -23,12 +25,12 @@ function trackers.dumphashtofile(filename,delta) 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 kind = command_name(token) - local dk = list[kind] + 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[kind] = dk + list[category] = dk end dk.names[name] = { token[2], token[3] } dk.found = dk.found + 1 @@ -49,3 +51,24 @@ 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 "?", + files = table.sortedkeys(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/type-ini.mkiv b/tex/context/base/type-ini.mkiv index f4b2b13d9..46b46976d 100644 --- a/tex/context/base/type-ini.mkiv +++ b/tex/context/base/type-ini.mkiv @@ -312,9 +312,9 @@ \def\loadmapfile{\dosingleempty\doloadmapfile} \def\loadmapline{\dodoubleempty\doloadmapline} -\def\doloadmapfile [#1]{\ctxlua{fonts.map.loadfile("#1")}} -\def\doloadmapline [#1][#2]{\ctxlua{fonts.map.loadline("#1","#2")}} -\def\forgetmapfiles {\ctxlua{fonts.map.reset()}} +\def\doloadmapfile [#1]{\ctxlua{fonts.mappings.loadfile("#1")}} +\def\doloadmapline [#1][#2]{\ctxlua{fonts.mappings.loadline("#1","#2")}} +\def\forgetmapfiles {\ctxlua{fonts.mappings.reset()}} % \appendtoks % \pdfmapfile{}% somehow does not work at the lua end diff --git a/tex/context/base/type-otf.mkiv b/tex/context/base/type-otf.mkiv index 8d6cb1aad..fcfd134f9 100644 --- a/tex/context/base/type-otf.mkiv +++ b/tex/context/base/type-otf.mkiv @@ -1460,7 +1460,7 @@ \starttypescript [math] [asana] [name] \loadfontgoodies[asana-math] - \definefontsynonym [MathRoman] [AsanaMath] [\s!features=\s!math\mathsizesuffix] + \definefontsynonym [MathRoman] [AsanaMath] [\s!features=\s!math\mathsizesuffix,\s!goodies=asana-math] \stoptypescript \starttypescript[asana] diff --git a/tex/context/base/typo-brk.lua b/tex/context/base/typo-brk.lua index d26f62a07..fe0258bcc 100644 --- a/tex/context/base/typo-brk.lua +++ b/tex/context/base/typo-brk.lua @@ -78,13 +78,13 @@ function breakpoints.setreplacement(id,char,language,settings) end local left, right, middle = settings.left, settings.right, settings.middle cmap[language or ""] = { - kind = tonumber(settings.kind) or 1, + 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 { kind or 1, before or 1, after or 1 } + } -- was { type or 1, before or 1, after or 1 } end local function insert_break(head,start,before,after) @@ -188,7 +188,7 @@ local function process(namespace,attribute,head) if map[next.char] then break elseif m == 1 then - local method = methods[smap.kind] + local method = methods[smap.type] if method then head, start = method(head,start,smap) done = true diff --git a/tex/context/base/typo-brk.mkiv b/tex/context/base/typo-brk.mkiv index 8be00fd8a..4c21093ec 100644 --- a/tex/context/base/typo-brk.mkiv +++ b/tex/context/base/typo-brk.mkiv @@ -50,7 +50,7 @@ \begingroup \getparameters[\??bp][\c!type=1,\c!nleft=3,\c!nright=3,\s!language=,\c!left=,\c!right=,\c!middle=,#3]% \ctxlua{typesetters.breakpoints.setreplacement(\csname\??bp:#1\endcsname, "#2", "\reallanguagetag\@@bplanguage", { - kind = \@@bptype, + type = \@@bptype, nleft = "\@@bpnleft", nright = "\@@bpnright", right = "\@@bpright", diff --git a/tex/context/base/typo-cap.lua b/tex/context/base/typo-cap.lua index a8e714a9b..b762589ce 100644 --- a/tex/context/base/typo-cap.lua +++ b/tex/context/base/typo-cap.lua @@ -36,8 +36,10 @@ local userskip_code = skipcodes.userskip local tasks = nodes.tasks -local fontdata = fonts.identifiers -local fontchar = fonts.characters +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local fontchar = fonthashes.characters + local chardata = characters.data typesetters = typesetters or { } diff --git a/tex/context/base/typo-dig.lua b/tex/context/base/typo-dig.lua index fbbad96f8..1e4a02fc6 100644 --- a/tex/context/base/typo-dig.lua +++ b/tex/context/base/typo-dig.lua @@ -37,11 +37,13 @@ local tasks = nodes.tasks local new_glue = nodepool.glue -local fontdata = fonts.identifiers -local chardata = fonts.characters -local quaddata = fonts.quads +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local chardata = fonthashes.characters +local quaddata = fonthashes.quads + local charbase = characters.data -local getdigitwidth = fonts.getdigitwidth +local getdigitwidth = fonts.helpers.getdigitwidth typesetters = typesetters or { } local typesetters = typesetters diff --git a/tex/context/base/typo-dir.lua b/tex/context/base/typo-dir.lua index 49d58239d..54c2e6e98 100644 --- a/tex/context/base/typo-dir.lua +++ b/tex/context/base/typo-dir.lua @@ -51,8 +51,10 @@ local new_textdir = nodepool.textdir local beginmath_code = mathcodes.beginmath local endmath_code = mathcodes.endmath -local fontdata = fonts.identifiers -local fontchar = fonts.characters +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local chardata = fonthashes.characters + local chardata = characters.data local chardirs = characters.directions -- maybe make a special mirror table diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua index 597950c6a..a9428439c 100644 --- a/tex/context/base/typo-krn.lua +++ b/tex/context/base/typo-krn.lua @@ -46,9 +46,10 @@ local kerning_code = kerncodes.kerning local userkern_code = kerncodes.userkern local userskip_code = skipcodes.userskip -local fontdata = fonts.identifiers -local chardata = fonts.characters -local quaddata = fonts.quads +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local chardata = fonthashes.characters +local quaddata = fonthashes.quads typesetters = typesetters or { } local typesetters = typesetters diff --git a/tex/context/base/typo-mar.lua b/tex/context/base/typo-mar.lua index 47734b3ee..6bc985868 100644 --- a/tex/context/base/typo-mar.lua +++ b/tex/context/base/typo-mar.lua @@ -6,6 +6,13 @@ if not modules then modules = { } end modules ['typo-mar'] = { 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 + local format = string.format local insert, remove = table.insert, table.remove local setmetatable, next = setmetatable, next @@ -372,7 +379,7 @@ local function inject(parent,head,candidate) if line ~= 0 then local delta = line * candidate.lineheight shift = shift + delta -offset = offset + delta + offset = offset + delta end box.shift = shift box.width = 0 @@ -408,7 +415,8 @@ local function flushinline(parent,head,done) end end elseif id == hlist_code or id == vlist_code then - -- optional + -- optional (but sometimes needed) + current.list, done = flushinline(current,current.list,done) end current = current.next end @@ -471,6 +479,9 @@ local function handler(scope,head,group) end current = current.next end + -- if done then + -- resetstacked() + -- end return head, done else return head, false diff --git a/tex/context/base/typo-mar.mkiv b/tex/context/base/typo-mar.mkiv index 4d122204e..aba3ef95a 100644 --- a/tex/context/base/typo-mar.mkiv +++ b/tex/context/base/typo-mar.mkiv @@ -286,7 +286,7 @@ \definemargindata [inmargin] [\v!left] [\c!margin=\c!margin,\c!width=\leftmarginwidth, \c!align=\v!flushright] \definemargindata [inother] [\v!right] [\c!margin=\c!margin,\c!width=\rightmarginwidth,\c!align=\v!flushleft] -\definemargindata [margintext] [\v!left] % keep it a bit separated from inleft and inmargin +\definemargindata [margintext] [\v!left] [\c!margin=\c!margin,\c!width=\leftmarginwidth, \c!align=\v!flushright] \setupmarginframed [\v!left ] [\c!method=\v!first,\c!align=\v!flushright,\s!parent=\??mf] % we could autoparent when no define yet \setupmarginframed [\v!right] [\c!method=\v!first,\c!align=\v!flushleft, \s!parent=\??mf] @@ -328,9 +328,9 @@ % \def\dodefineinmargin[#name][#location][#align][#settings]% not completely compatible % {\definemargindata[#name][\c!location=#location,\c!align=#align,#settings]% % \definemarginframed[#name][#location][\c!align=#align,#settings]} -% -% \let\setupinmargin\setupmargindata -% + +\let\setupinmargin\setupmargindata + % The following is too dangerous: % % \unexpanded\def\setupinmargin diff --git a/tex/context/base/typo-rep.lua b/tex/context/base/typo-rep.lua index 78243229a..5ecff5586 100644 --- a/tex/context/base/typo-rep.lua +++ b/tex/context/base/typo-rep.lua @@ -25,7 +25,7 @@ local has_attribute = node.has_attribute local chardata = characters.data local collected = false local attribute = attributes.private("stripping") -local fontdata = fonts.identifiers +local fontdata = fonts.hashes.identifiers local tasks = nodes.tasks local nodecodes = nodes.nodecodes @@ -102,41 +102,3 @@ function nodes.stripping.enable() tasks.enableaction("processors","nodes.handlers.stripping") function nodes.stripping.enable() end end - --- bonus: - -local initializers, methods = fonts.initializers, fonts.methods - -local function processformatters(head,font) - local how = fontdata[font].shared.features.formatters -- slow - if how == nil or how == "strip" then -- nil when forced - 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 - 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 - end - return head, done - else - return head, false - end -end - -function initializers.common.formatters(tfmdata,value) - if initialize then initialize() end -end - -initializers.base.otf.formatters = initializers.common.formatters -initializers.node.otf.formatters = initializers.common.formatters - -methods.node.otf.formatters = processformatters -methods.base.otf.formatters = processformatters diff --git a/tex/context/base/typo-rep.mkiv b/tex/context/base/typo-rep.mkiv index 89999c3c7..75902d7be 100644 --- a/tex/context/base/typo-rep.mkiv +++ b/tex/context/base/typo-rep.mkiv @@ -48,6 +48,6 @@ % maybe .. this might disappear, but is handy for testing \def\forcecharacterstripping % secret command - {\ctxlua{fonts.otf.features.register("formatters",true)}} + {\ctxlua{fonts.handlers.otf.features.register("formatters",true)}} \protect \endinput diff --git a/tex/context/base/typo-spa.lua b/tex/context/base/typo-spa.lua index 310384aa8..e81a59bda 100644 --- a/tex/context/base/typo-spa.lua +++ b/tex/context/base/typo-spa.lua @@ -25,8 +25,9 @@ local insert_node_before = node.insert_before local insert_node_after = node.insert_after local remove_node = nodes.remove -local fontdata = fonts.identifiers -local quaddata = fonts.quads +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers +local quaddata = fonthashes.quads local texattribute = tex.attribute diff --git a/tex/context/base/util-mrg.lua b/tex/context/base/util-mrg.lua index 3d8e79d93..acf04fead 100644 --- a/tex/context/base/util-mrg.lua +++ b/tex/context/base/util-mrg.lua @@ -12,13 +12,13 @@ local gsub, format = string.gsub, string.format local concat = table.concat local type, next = type, next -utilities = utilities or {} -utilities.merger = utilities.merger or { } -- maybe mergers -utilities.report = logs and logs.reporter("system") or print +utilities = utilities or {} +utilities.merger = utilities.merger or { } -- maybe mergers +utilities.report = logs and logs.reporter("system") or print -local merger = utilities.merger +local merger = utilities.merger -merger.strip_comment = true +merger.strip_comment = true local m_begin_merge = "begin library merge" local m_end_merge = "end library merge" diff --git a/tex/context/base/util-prs.lua b/tex/context/base/util-prs.lua index a369552dc..617dc0c78 100644 --- a/tex/context/base/util-prs.lua +++ b/tex/context/base/util-prs.lua @@ -178,7 +178,8 @@ 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,"%s*([^, ]+)") do -- space added + for s in gmatch(str,"[^, ]+") do -- space added t[s] = true end return t @@ -220,3 +221,7 @@ function parsers.getparameters(self,class,parentclass,settings) end parsers.settings_to_hash(settings,sc) end + +function parsers.listitem(str) + return gmatch(str,"[^, ]+") +end diff --git a/tex/context/base/util-tab.lua b/tex/context/base/util-tab.lua index ea440e736..f8422c626 100644 --- a/tex/context/base/util-tab.lua +++ b/tex/context/base/util-tab.lua @@ -12,7 +12,7 @@ local tables = utilities.tables local format, gmatch = string.format, string.gmatch local concat, insert, remove = table.concat, table.insert, table.remove -local setmetatable = setmetatable +local setmetatable, tonumber, tostring = setmetatable, tonumber, tostring function tables.definetable(target) -- defines undefined tables local composed, t, n = nil, { }, 0 @@ -82,3 +82,25 @@ local _empty_table_ = { __index = function(t,k) return "" end } function table.setemptymetatable(t) setmetatable(t,_empty_table_) end + +-- experimental + +local function toxml(t,d,result) + for k, v in table.sortedpairs(t) do + if type(v) == "table" then + result[#result+1] = format("%s<%s>",d,k) + toxml(v,d.." ",result) + result[#result+1] = format("%s",d,k) + elseif tonumber(k) then + result[#result+1] = format("%s%s",d,k,v,k) + else + result[#result+1] = format("%s<%s>%s",d,k,tostring(v),k) + end + end +end + +function table.toxml(t,name) + local result = { "" } + toxml( { [name or "root"] = t }, "", result) + return concat(result,"\n") +end diff --git a/tex/context/base/x-cals.lua b/tex/context/base/x-cals.lua index 0ccbaab54..904dc39c9 100644 --- a/tex/context/base/x-cals.lua +++ b/tex/context/base/x-cals.lua @@ -13,9 +13,9 @@ local n_todimen, s_todimen = number.todimen, string.todimen -- there is room for speedups as well as cleanup (using context functions) -local cals = { } -local moduledata = moduledata or { } -moduledata.cals = cals +local cals = { } +moduledata.cals = cals +lxml.mathml = cals -- for the moment cals.ignore_widths = false cals.shrink_widths = false diff --git a/tex/context/base/x-css.lua b/tex/context/base/x-css.lua index da21fd21b..1bf559d66 100644 --- a/tex/context/base/x-css.lua +++ b/tex/context/base/x-css.lua @@ -73,14 +73,16 @@ css.padding = padding -- print(padding("10pt 20pt 30pt",pixel,hsize,exheight,emwidth)) -- print(padding("10pt 20pt 30pt 40pt",pixel,hsize,exheight,emwidth)) -local fontidentifiers = fonts.identifiers local currentfont = font.current -local texdimen = tex.dimen +local texdimen = tex.dimen +local hashes = fonts.hashes +local quads = hashes.quads +local xheights = hashes.xheights local function padding(str) - local fnt = fontidentifiers[currentfont()] - local exheight = fnt.ex_height - local emwidth = fnt.quad + 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) diff --git a/tex/context/base/x-mathml.lua b/tex/context/base/x-mathml.lua index 654925f7a..434aefc55 100644 --- a/tex/context/base/x-mathml.lua +++ b/tex/context/base/x-mathml.lua @@ -17,8 +17,8 @@ local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues local lpegmatch = lpeg.match local mathml = { } -local moduledata = moduledata = { } 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 diff --git a/tex/context/fonts/asana-math.lfg b/tex/context/fonts/asana-math.lfg index 160b310f5..f845ca4de 100644 --- a/tex/context/fonts/asana-math.lfg +++ b/tex/context/fonts/asana-math.lfg @@ -1,7 +1,7 @@ -- This patch code is moved from font-pat.lua to this goodies --- files as it does not belomg in the core code. +-- files as it does not belong in the core code. -local patches = fonts.otf.enhancers.patches +local patches = fonts.handlers.otf.enhancers.patches local function patch(data,filename,threshold) local m = data.metadata.math @@ -16,10 +16,37 @@ end patches.register("after","check math parameters","asana",function(data,filename) patch(data,filename,1350) end) +local function less(value,target,original) return 0.25 * value end + return { - name = "lm-asana", + name = "asana-math", version = "1.00", comment = "Goodies that complement asana.", author = "Hans Hagen", copyright = "ConTeXt development team", + mathematics = { + parameters = { + -- StackBottomDisplayStyleShiftDown = 0, + -- StackBottomShiftDown = 0, + -- StackDisplayStyleGapMin = 0, + -- StackGapMin = 0, + -- StackTopDisplayStyleShiftUp = 0, + -- StackTopShiftUp = 0, + -- StretchStackBottomShiftDown = 0, + -- StretchStackGapAboveMin = 0, + -- StretchStackGapBelowMin = 0, + -- StretchStackTopShiftUp = 0, + StackBottomDisplayStyleShiftDown = less, + StackBottomShiftDown = less, + StackDisplayStyleGapMin = less, + StackGapMin = less, + StackTopDisplayStyleShiftUp = less, + StackTopShiftUp = less, + StretchStackBottomShiftDown = less, + StretchStackGapAboveMin = less, + StretchStackGapBelowMin = less, + StretchStackTopShiftUp = less, + } + } } + diff --git a/tex/context/fonts/cambria-math.lfg b/tex/context/fonts/cambria-math.lfg index 26972c9e4..3fd15d8a0 100644 --- a/tex/context/fonts/cambria-math.lfg +++ b/tex/context/fonts/cambria-math.lfg @@ -1,7 +1,7 @@ -- This patch code is moved from font-pat.lua to this goodies --- files as it does not belomg in the core code. +-- files as it does not belong in the core code. -local patches = fonts.otf.enhancers.patches +local patches = fonts.handlers.otf.enhancers.patches local function patch(data,filename,threshold) local m = data.metadata.math @@ -18,7 +18,7 @@ patches.register("after","check math parameters","cambria", function(data,filena patches.register("after","check math parameters","cambmath",function(data,filename) patch(data,filename,2800) end) return { - name = "lm-cambria", + name = "cambria-math", version = "1.00", comment = "Goodies that complement cambria.", author = "Hans Hagen", diff --git a/tex/context/fonts/lm-math.lfg b/tex/context/fonts/lm-math.lfg index 042f3026e..43de0c51e 100644 --- a/tex/context/fonts/lm-math.lfg +++ b/tex/context/fonts/lm-math.lfg @@ -1,7 +1,7 @@ -- This patch code is moved from font-pat.lua to this goodies -- files as it does not belomg in the core code. -local patches = fonts.otf.enhancers.patches +local patches = fonts.handlers.otf.enhancers.patches local function patch(data,filename) local uni_to_ind = data.map.map @@ -268,6 +268,12 @@ return { }, variables = { joinrelfactor = 3, -- default anyway - } + }, + parameters = { -- test values + -- FactorA = 123.456, + -- FactorB = false, + -- FactorC = function(value,target,original) return 7.89 * target.factor end, + -- FactorD = "Hi There!", + }, } } diff --git a/tex/context/fonts/lucida-math.lfg b/tex/context/fonts/lucida-math.lfg index 746d281e0..3341bea95 100644 --- a/tex/context/fonts/lucida-math.lfg +++ b/tex/context/fonts/lucida-math.lfg @@ -1,4 +1,4 @@ -local mathencodings = fonts.enc.math +local mathencodings = fonts.encodings.math mathencodings["lbr-ma"] = { [0x025CB] = 0x00, -- circle @@ -305,7 +305,7 @@ mathencodings["lbr-sy"] = table.merged(mathencodings["tex-sy"],mathencodings["lb mathencodings["lbr-fraktur"] = { } -fonts.vf.math.setletters(mathencodings, "lbr-fraktur", 0x1D504, 0x1D51E) +fonts.handlers.vf.math.setletters(mathencodings, "lbr-fraktur", 0x1D504, 0x1D51E) return { name = "lucida-math", diff --git a/tex/context/fonts/xits-math.lfg b/tex/context/fonts/xits-math.lfg index 84e569e5f..4151e18a0 100644 --- a/tex/context/fonts/xits-math.lfg +++ b/tex/context/fonts/xits-math.lfg @@ -1,7 +1,13 @@ +-- \setupbodyfont[xits] +-- +-- \starttext +-- $ABC$ $\cal ABC$ $\mathalternate{cal}\cal ABC$ +-- \stoptext + return { name = "xits-math", version = "1.00", - comment = "Goodies that complement xits.", + comment = "Goodies that complement xits (by Khaled Hosny).", author = "Hans Hagen", copyright = "ConTeXt development team", mathematics = { diff --git a/tex/context/interface/cont-cs.xml b/tex/context/interface/cont-cs.xml index f71e12fdc..a7b246655 100644 --- a/tex/context/interface/cont-cs.xml +++ b/tex/context/interface/cont-cs.xml @@ -9634,7 +9634,7 @@ - + @@ -9648,7 +9648,7 @@ - + diff --git a/tex/context/interface/cont-de.xml b/tex/context/interface/cont-de.xml index 17b2480c4..61441dd9c 100644 --- a/tex/context/interface/cont-de.xml +++ b/tex/context/interface/cont-de.xml @@ -9634,7 +9634,7 @@ - + @@ -9648,7 +9648,7 @@ - + diff --git a/tex/context/interface/cont-fr.xml b/tex/context/interface/cont-fr.xml index da74f6013..615eda784 100644 --- a/tex/context/interface/cont-fr.xml +++ b/tex/context/interface/cont-fr.xml @@ -9634,7 +9634,7 @@ - + @@ -9648,7 +9648,7 @@ - + diff --git a/tex/context/interface/cont-it.xml b/tex/context/interface/cont-it.xml index 292915a06..5038f13ac 100644 --- a/tex/context/interface/cont-it.xml +++ b/tex/context/interface/cont-it.xml @@ -9634,7 +9634,7 @@ - + @@ -9648,7 +9648,7 @@ - + diff --git a/tex/context/interface/cont-nl.xml b/tex/context/interface/cont-nl.xml index ad038db16..2d53444fb 100644 --- a/tex/context/interface/cont-nl.xml +++ b/tex/context/interface/cont-nl.xml @@ -9634,7 +9634,7 @@ - + @@ -9648,7 +9648,7 @@ - + diff --git a/tex/context/interface/cont-pe.xml b/tex/context/interface/cont-pe.xml index 929f3c4b5..12a54339d 100644 --- a/tex/context/interface/cont-pe.xml +++ b/tex/context/interface/cont-pe.xml @@ -9634,7 +9634,7 @@ - + @@ -9648,7 +9648,7 @@ - + diff --git a/tex/context/interface/cont-ro.xml b/tex/context/interface/cont-ro.xml index 14abb248b..b7fa62d59 100644 --- a/tex/context/interface/cont-ro.xml +++ b/tex/context/interface/cont-ro.xml @@ -9634,7 +9634,7 @@ - + @@ -9648,7 +9648,7 @@ - + diff --git a/tex/context/interface/keys-cs.xml b/tex/context/interface/keys-cs.xml index b03a922fc..16d08fd12 100644 --- a/tex/context/interface/keys-cs.xml +++ b/tex/context/interface/keys-cs.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/context/interface/keys-de.xml b/tex/context/interface/keys-de.xml index cbec2130b..b981d2091 100644 --- a/tex/context/interface/keys-de.xml +++ b/tex/context/interface/keys-de.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/context/interface/keys-en.xml b/tex/context/interface/keys-en.xml index d5c3f4b8f..43f3048f1 100644 --- a/tex/context/interface/keys-en.xml +++ b/tex/context/interface/keys-en.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/context/interface/keys-fr.xml b/tex/context/interface/keys-fr.xml index 6b0c13b43..d41c6e396 100644 --- a/tex/context/interface/keys-fr.xml +++ b/tex/context/interface/keys-fr.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/context/interface/keys-it.xml b/tex/context/interface/keys-it.xml index 77d96aa2d..cc2039fc8 100644 --- a/tex/context/interface/keys-it.xml +++ b/tex/context/interface/keys-it.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/context/interface/keys-nl.xml b/tex/context/interface/keys-nl.xml index 9d18c63f8..e1f05f8b1 100644 --- a/tex/context/interface/keys-nl.xml +++ b/tex/context/interface/keys-nl.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/context/interface/keys-pe.xml b/tex/context/interface/keys-pe.xml index bf81c052a..219a53059 100644 --- a/tex/context/interface/keys-pe.xml +++ b/tex/context/interface/keys-pe.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/context/interface/keys-ro.xml b/tex/context/interface/keys-ro.xml index d44c195f1..47c3e6349 100644 --- a/tex/context/interface/keys-ro.xml +++ b/tex/context/interface/keys-ro.xml @@ -78,6 +78,7 @@ + @@ -531,6 +532,7 @@ + @@ -569,6 +571,7 @@ + @@ -583,6 +586,7 @@ + @@ -593,6 +597,7 @@ + @@ -817,6 +822,7 @@ + @@ -1012,6 +1018,7 @@ + @@ -1110,7 +1117,6 @@ - diff --git a/tex/generic/context/luatex-basics-gen.lua b/tex/generic/context/luatex-basics-gen.lua new file mode 100644 index 000000000..df5e7e6c4 --- /dev/null +++ b/tex/generic/context/luatex-basics-gen.lua @@ -0,0 +1,220 @@ +if not modules then modules = { } end modules ['luat-basics-gen'] = { + version = 1.100, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local dummyfunction = function() end +local dummyreporter = function(c) return function(...) texio.write(c .. " : " .. string.format(...)) end end + +statistics = { + register = dummyfunction, + starttiming = dummyfunction, + stoptiming = dummyfunction, + elapsedtime = nil, +} + +directives = { + register = dummyfunction, + enable = dummyfunction, + disable = dummyfunction, +} + +trackers = { + register = dummyfunction, + enable = dummyfunction, + disable = dummyfunction, +} + +experiments = { + register = dummyfunction, + enable = dummyfunction, + disable = dummyfunction, +} + +storage = { -- probably no longer needed + register = dummyfunction, + shared = { }, +} + +logs = { + new = dummyreporter, + reporter = dummyreporter, + messenger = dummyreporter, + report = dummyfunction, +} + +callbacks = { + register = function(n,f) return callback.register(n,f) end, + +} + +utilities = { + storage = { + allocate = function(t) return t or { } end, + mark = function(t) return t or { } end, + }, +} + +characters = characters or { + data = { } +} + +-- we need to cheat a bit here + +texconfig.kpse_init = true + +resolvers = resolvers or { } -- no fancy file helpers used + +local remapper = { + otf = "opentype fonts", + ttf = "truetype fonts", + ttc = "truetype fonts", + dfont = "truetype fonts", -- "truetype dictionary", + cid = "cid maps", + fea = "font feature files", +} + +function resolvers.findfile(name,fileformat) + name = string.gsub(name,"\\","\/") + fileformat = fileformat and string.lower(fileformat) + local found = kpse.find_file(name,(fileformat and fileformat ~= "" and (remapper[fileformat] or fileformat)) or file.extname(name,"tex")) + if not found or found == "" then + found = kpse.find_file(name,"other text files") + end + return found +end + +function resolvers.findbinfile(name,fileformat) + if not fileformat or fileformat == "" then + fileformat = file.extname(name) -- string.match(name,"%.([^%.]-)$") + end + return resolvers.findfile(name,(fileformat and remapper[fileformat]) or fileformat) +end + +function resolvers.resolve(s) + return s +end + +function resolvers.unresolve(s) + return s +end + +-- Caches ... I will make a real stupid version some day when I'm in the +-- mood. After all, the generic code does not need the more advanced +-- ConTeXt features. Cached data is not shared between ConTeXt and other +-- usage as I don't want any dependency at all. Also, ConTeXt might have +-- different needs and tricks added. + +--~ containers.usecache = true + +caches = { } + +local writable, readables = nil, { } + +if not caches.namespace or caches.namespace == "" or caches.namespace == "context" then + caches.namespace = 'generic' +end + +do + + local cachepaths = kpse.expand_path('$TEXMFCACHE') or "" + + if cachepaths == "" then + cachepaths = kpse.expand_path('$TEXMFVAR') + end + + if cachepaths == "" then + cachepaths = kpse.expand_path('$VARTEXMF') + end + + if cachepaths == "" then + cachepaths = "." + end + + cachepaths = string.split(cachepaths,os.type == "windows" and ";" or ":") + + for i=1,#cachepaths do + if file.is_writable(cachepaths[i]) then + writable = file.join(cachepaths[i],"luatex-cache") + lfs.mkdir(writable) + writable = file.join(writable,caches.namespace) + lfs.mkdir(writable) + break + end + end + + for i=1,#cachepaths do + if file.is_readable(cachepaths[i]) then + readables[#readables+1] = file.join(cachepaths[i],"luatex-cache",caches.namespace) + end + end + + if not writable then + texio.write_nl("quiting: fix your writable cache path") + os.exit() + elseif #readables == 0 then + texio.write_nl("quiting: fix your readable cache path") + os.exit() + elseif #readables == 1 and readables[1] == writable then + texio.write(string.format("(using cache: %s)",writable)) + else + texio.write(string.format("(using write cache: %s)",writable)) + texio.write(string.format("(using read cache: %s)",table.concat(readables, " "))) + end + +end + +function caches.getwritablepath(category,subcategory) + local path = file.join(writable,category) + lfs.mkdir(path) + path = file.join(path,subcategory) + lfs.mkdir(path) + return path +end + +function caches.getreadablepaths(category,subcategory) + local t = { } + for i=1,#readables do + t[i] = file.join(readables[i],category,subcategory) + end + return t +end + +local function makefullname(path,name) + if path and path ~= "" then + name = "temp-" .. name -- clash prevention + return file.addsuffix(file.join(path,name),"lua") + end +end + +function caches.is_writable(path,name) + local fullname = makefullname(path,name) + return fullname and file.is_writable(fullname) +end + +function caches.loaddata(paths,name) + for i=1,#paths do + local fullname = makefullname(paths[i],name) + if fullname then + texio.write(string.format("(load: %s)",fullname)) + local data = loadfile(fullname) + return data and data() + end + end +end + +function caches.savedata(path,name,data) + local fullname = makefullname(path,name) + if fullname then + texio.write(string.format("(save: %s)",fullname)) + table.tofile(fullname,data,'return',false,true,false) + end +end diff --git a/tex/generic/context/luatex-basics-nod.lua b/tex/generic/context/luatex-basics-nod.lua new file mode 100644 index 000000000..151d98a8f --- /dev/null +++ b/tex/generic/context/luatex-basics-nod.lua @@ -0,0 +1,95 @@ +if not modules then modules = { } end modules ['luatex-fonts-nod'] = { + version = 1.001, + comment = "companion to luatex-fonts.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +-- Don't depend on code here as it is only needed to complement the +-- font handler code. + +-- Attributes: + +if tex.attribute[0] ~= 0 then + + texio.write_nl("log","!") + texio.write_nl("log","! Attribute 0 is reserved for ConTeXt's font feature management and has to be") + texio.write_nl("log","! set to zero. Also, some attributes in the range 1-255 are used for special") + texio.write_nl("log","! purposes so setting them at the TeX end might break the font handler.") + texio.write_nl("log","!") + + tex.attribute[0] = 0 -- else no features + +end + +attributes = { } +attributes.unsetvalue = -0x7FFFFFFF + +local numbers, last = { }, 127 + +function attributes.private(name) + local number = numbers[name] + if not number then + if last < 255 then + last = last + 1 + end + number = last + numbers[name] = number + end + return number +end + +-- Nodes: + +nodes = { } +nodes.pool = { } +nodes.handlers = { } + +local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end +local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end +local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } + +nodes.nodecodes = nodecodes +nodes.whatcodes = whatcodes +nodes.whatsitcodes = whatcodes +nodes.glyphcodes = glyphcodes + +local free_node = node.free +local remove_node = node.remove +local new_node = node.new + +nodes.handlers.protectglyphs = node.protect_glyphs +nodes.handlers.unprotectglyphs = node.unprotect_glyphs + +function nodes.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, t.prev = nil, nil + end + end + return head, current, t +end + +function nodes.delete(head,current) + return nodes.remove(head,current,true) +end + +nodes.before = node.insert_before +nodes.after = node.insert_after + +function nodes.pool.kern(k) + local n = new_node("kern",1) + n.kern = k + return n +end diff --git a/tex/generic/context/luatex-fonts-cbk.lua b/tex/generic/context/luatex-fonts-cbk.lua new file mode 100644 index 000000000..9db94f65e --- /dev/null +++ b/tex/generic/context/luatex-fonts-cbk.lua @@ -0,0 +1,68 @@ +if not modules then modules = { } end modules ['luatex-fonts-cbk'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local nodes = nodes + +-- Fonts: (might move to node-gef.lua) + +local traverse_id = node.traverse_id +local glyph_code = nodes.nodecodes.glyph + +function nodes.handlers.characters(head) + local fontdata = fonts.hashes.identifiers + if fontdata then + local usedfonts, done, prevfont = { }, false, nil + for n in traverse_id(glyph_code,head) do + local font = n.font + if font ~= prevfont then + prevfont = font + local used = usedfonts[font] + if not used then + local tfmdata = fontdata[font] -- + if tfmdata then + local shared = tfmdata.shared -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + done = true + end + end + end + end + end + end + if done then + for font, processors in next, usedfonts do + for i=1,#processors do + local h, d = processors[i](head,font,0) + head, done = h or head, done or d + end + end + end + return head, true + else + return head, false + end +end + +function nodes.simple_font_handler(head) +-- lang.hyphenate(head) + head = nodes.handlers.characters(head) + nodes.injections.handler(head) + nodes.handlers.protectglyphs(head) + head = node.ligaturing(head) + head = node.kerning(head) + return head +end diff --git a/tex/generic/context/luatex-fonts-def.lua b/tex/generic/context/luatex-fonts-def.lua new file mode 100644 index 000000000..1d71bf5d5 --- /dev/null +++ b/tex/generic/context/luatex-fonts-def.lua @@ -0,0 +1,97 @@ +if not modules then modules = { } end modules ['luatex-font-def'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts + +-- A bit of tuning for definitions. + +fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload + +-- tricky: we sort of bypass the parser and directly feed all into +-- the sub parser + +function fonts.definers.getspecification(str) + return "", str, "", ":", str +end + +-- the generic name parser (different from context!) + +local list = { } + +local function issome () list.lookup = 'name' end -- xetex mode prefers name (not in context!) +local function isfile () list.lookup = 'file' end +local function isname () list.lookup = 'name' end +local function thename(s) list.name = s end +local function issub (v) list.sub = v end +local function iscrap (s) list.crap = string.lower(s) end +local function iskey (k,v) list[k] = v end +local function istrue (s) list[s] = true end +local function isfalse(s) list[s] = false end + +local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C + +local spaces = P(" ")^0 +local namespec = (1-S("/:("))^0 -- was: (1-S("/: ("))^0 +local crapspec = spaces * P("/") * (((1-P(":"))^0)/iscrap) * spaces +local filename_1 = P("file:")/isfile * (namespec/thename) +local filename_2 = P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]") +local fontname_1 = P("name:")/isname * (namespec/thename) +local fontname_2 = P(true)/issome * (namespec/thename) +local sometext = (R("az","AZ","09") + S("+-."))^1 +local truevalue = P("+") * spaces * (sometext/istrue) +local falsevalue = P("-") * spaces * (sometext/isfalse) +local keyvalue = (C(sometext) * spaces * P("=") * spaces * C(sometext))/iskey +local somevalue = sometext/istrue +local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim +local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces +local options = P(":") * spaces * (P(";")^0 * option)^0 + +local pattern = (filename_1 + filename_2 + fontname_1 + fontname_2) * subvalue^0 * crapspec^0 * options^0 + +local function colonized(specification) -- xetex mode + list = { } + lpeg.match(pattern,specification.specification) + list.crap = nil -- style not supported, maybe some day + if list.name then + specification.name = list.name + list.name = nil + end + if list.lookup then + specification.lookup = list.lookup + list.lookup = nil + end + if list.sub then + specification.sub = list.sub + list.sub = nil + end + specification.features.normal = fonts.handlers.otf.features.normalize(list) + return specification +end + +fonts.definers.registersplit(":",colonized,"cryptic") +fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names] + +function definers.applypostprocessors(tfmdata) + local postprocessors = tfmdata.postprocessors + if postprocessors then + 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 = string.gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end diff --git a/tex/generic/context/luatex-fonts-demo-vf-1.lua b/tex/generic/context/luatex-fonts-demo-vf-1.lua index 31ac4b87b..b9f2a2c76 100644 --- a/tex/generic/context/luatex-fonts-demo-vf-1.lua +++ b/tex/generic/context/luatex-fonts-demo-vf-1.lua @@ -1,7 +1,9 @@ +local identifiers = fonts.hashes.identifiers + return function(specification) - local f1, id1 = fonts.tfm.readanddefine('lmroman10-regular', specification.size) - local f2, id2 = fonts.tfm.readanddefine('lmsans10-regular', specification.size) - local f3, id3 = fonts.tfm.readanddefine('lmtypewriter10-regular',specification.size) + local f1, id1 = fonts.constructors.readanddefine('lmroman10-regular', specification.size) + local f2, id2 = fonts.constructors.readanddefine('lmsans10-regular', specification.size) + local f3, id3 = fonts.constructors.readanddefine('lmtypewriter10-regular',specification.size) if f1 and f2 and f3 then f1.name = specification.name f1.virtualized = true @@ -17,9 +19,9 @@ return function(specification) { "special", "pdf:0 0 1 rg" }, } local chars = { - fonts.identifiers[id1].characters, - fonts.identifiers[id2].characters, - fonts.identifiers[id3].characters, + identifiers[id1].characters, + identifiers[id2].characters, + identifiers[id3].characters, } for u, v in next, f1.characters do local n = math.floor(math.random(1,3)+0.5) diff --git a/tex/generic/context/luatex-fonts-enc.lua b/tex/generic/context/luatex-fonts-enc.lua new file mode 100644 index 000000000..ac736f2a6 --- /dev/null +++ b/tex/generic/context/luatex-fonts-enc.lua @@ -0,0 +1,28 @@ +if not modules then modules = { } end modules ['luatex-font-enc'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +fonts.encodings = { } +fonts.encodings.agl = { } + +setmetatable(fonts.encodings.agl, { __index = function(t,k) + if k == "unicodes" then + texio.write(" ") + local unicodes = dofile(resolvers.findfile("font-agl.lua")) + fonts.encodings.agl = { unicodes = unicodes } + return unicodes + else + return nil + end +end }) + diff --git a/tex/generic/context/luatex-fonts-ext.lua b/tex/generic/context/luatex-fonts-ext.lua new file mode 100644 index 000000000..951afcc7e --- /dev/null +++ b/tex/generic/context/luatex-fonts-ext.lua @@ -0,0 +1,276 @@ +if not modules then modules = { } end modules ['luatex-fonts-ext'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local otffeatures = fonts.constructors.newfeatures("otf") + +-- A few generic extensions. + +local function initializeitlc(tfmdata,value) + if value then + -- the magic 40 and it formula come from Dohyun Kim + local parameters = tfmdata.parameters + local italicangle = parameters.italicangle + if italicangle and italicangle ~= 0 then + local uwidth = (parameters.uwidth or 40)/2 + for unicode, d in next, tfmdata.descriptions do + local it = d.boundingbox[3] - d.width + uwidth + if it ~= 0 then + d.italic = it + end + end + tfmdata.properties.italic_correction = true + end + end +end + +otffeatures.register { + name = "itlc", + description = "italic correction", + initializers = { + base = initializeitlc, + node = initializeitlc, + } +} + +-- slant and extend + +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.slant_factor = value +end + +otffeatures.register { + 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.extend_factor = value +end + +otffeatures.register { + name = "extend", + description = "scale glyphs horizontally", + initializers = { + base = initializeextend, + node = initializeextend, + } +} + +-- expansion and protrusion + +fonts.protrusions = fonts.protrusions or { } +fonts.protrusions.setups = fonts.protrusions.setups or { } + +local setups = fonts.protrusions.setups + +local function initializeprotrusion(tfmdata,value) + if value then + local setup = setups[value] + if setup then + local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 + local emwidth = tfmdata.parameters.quad + tfmdata.parameters.protrusion = { + auto = true, + } + for i, chr in next, tfmdata.characters do + local v, pl, pr = setup[i], nil, nil + if v then + pl, pr = v[1], v[2] + 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 + end + end +end + +otffeatures.register { + name = "protrusion", + description = "shift characters into the left and or right margin", + initializers = { + base = initializeprotrusion, + node = initializeprotrusion, + } +} + +fonts.expansions = fonts.expansions or { } +fonts.expansions.setups = fonts.expansions.setups or { } + +local setups = fonts.expansions.setups + +local function initializeexpansion(tfmdata,value) + if value then + local setup = setups[value] + if setup then + local factor = setup.factor or 1 + tfmdata.parameters.expansion = { + stretch = 10 * (setup.stretch or 0), + shrink = 10 * (setup.shrink or 0), + step = 10 * (setup.step or 0), + auto = true, + } + for i, chr in next, tfmdata.characters do + local v = setup[i] + if v and v ~= 0 then + chr.expansion_factor = v*factor + else -- can be option + chr.expansion_factor = factor + end + end + end + end +end + +otffeatures.register { + name = "expansion", + description = "apply hz optimization", + initializers = { + base = initializeexpansion, + node = initializeexpansion, + } +} + +-- left over + +function fonts.loggers.onetimemessage() end + +-- example vectors + +local byte = string.byte + +fonts.expansions.setups['default'] = { + + stretch = 2, shrink = 2, step = .5, factor = 1, + + [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, +} + +fonts.protrusions.setups['default'] = { + + factor = 1, left = 1, right = 1, + + [0x002C] = { 0, 1 }, -- comma + [0x002E] = { 0, 1 }, -- period + [0x003A] = { 0, 1 }, -- colon + [0x003B] = { 0, 1 }, -- semicolon + [0x002D] = { 0, 1 }, -- 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 ۔ + +} + +-- normalizer + +fonts.handlers.otf.features.normalize = function(t) + if t.rand then + t.rand = "random" + end + return t +end + +-- bonus + +function fonts.helpers.nametoslot(name) + local t = type(name) + if t == "string" then + local tfmdata = fonts.hashes.identifiers[currentfont()] + local shared = tfmdata and tfmdata.shared + local fntdata = shared and shared.rawdata + return fntdata and fntdata.resources.unicodes[name] + elseif t == "number" then + return n + end +end + +-- \font\test=file:somefont:reencode=mymessup +-- +-- fonts.encodings.reencodings.mymessup = { +-- [109] = 110, -- m +-- [110] = 109, -- n +-- } + +fonts.encodings = fonts.encodings or { } +local reencodings = { } +fonts.encodings.reencodings = reencodings + +local function specialreencode(tfmdata,value) + -- we forget about kerns as we assume symbols and we + -- could issue a message if ther are kerns but it's + -- a hack anyway so we odn't care too much here + local encoding = value and reencodings[value] + if encoding then + local temp = { } + local char = tfmdata.characters + for k, v in next, encoding do + temp[k] = char[v] + end + for k, v in next, temp do + char[k] = temp[k] + end + -- if we use the font otherwise luatex gets confused so + -- we return an additional hash component for fullname + return string.format("reencoded:%s",value) + end +end + +local function reencode(tfmdata,value) + tfmdata.postprocessors = tfmdata.postprocessors or { } + table.insert(tfmdata.postprocessors, + function(tfmdata) + return specialreencode(tfmdata,value) + end + ) +end + +otffeatures.register { + name = "reencode", + description = "reencode characters", + manipulators = { + base = reencode, + node = reencode, + } +} diff --git a/tex/generic/context/luatex-fonts-lua.lua b/tex/generic/context/luatex-fonts-lua.lua new file mode 100644 index 000000000..ec3fe38be --- /dev/null +++ b/tex/generic/context/luatex-fonts-lua.lua @@ -0,0 +1,33 @@ +if not modules then modules = { } end modules ['luatex-fonts-lua'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +fonts.formats.lua = "lua" + +function fonts.readers.lua(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 + local fullname = resolvers.findfile(fullname) or "" + if fullname ~= "" then + local loader = loadfile(fullname) + loader = loader and loader() + return loader and loader(specification) + end +end diff --git a/tex/generic/context/luatex-fonts-merged.lua b/tex/generic/context/luatex-fonts-merged.lua index ff5ade637..d627f1d4c 100644 --- a/tex/generic/context/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 02/25/11 22:03:53 +-- merge date : 03/25/11 18:03:32 do -- begin closure to overcome local limits and interference @@ -667,6 +667,8 @@ function lpeg.is_lpeg(p) return p and lpegtype(p) == "pattern" end +--~ Cf(Ct("") * (Cg(C(...) * "=" * Cs(...)))^0, rawset) + end -- closure do -- begin closure to overcome local limits and interference @@ -864,24 +866,24 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed + 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 kind == 3 then + if category == 3 then -- no further check else local tkey = type(key) if tkey == "string" then - kind = (kind == 2 and 3) or 1 + category = (category == 2 and 3) or 1 elseif tkey == "number" then - kind = (kind == 1 and 3) or 2 + category = (category == 1 and 3) or 2 else - kind = 3 + category = 3 end end end - if kind == 0 or kind == 3 then + if category == 0 or category == 3 then sort(srt,compare) else sort(srt) @@ -1047,6 +1049,13 @@ end table.fastcopy = fastcopy table.copy = copy +function table.derive(parent) + local child = { } + if parent then + setmetatable(child,{ __index = parent }) + end + return child +end function table.tohash(t,value) local h = { } @@ -2355,7 +2364,7 @@ end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['luat-dum'] = { +if not modules then modules = { } end modules ['luat-basics-gen'] = { version = 1.100, comment = "companion to luatex-*.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", @@ -2363,6 +2372,11 @@ if not modules then modules = { } end modules ['luat-dum'] = { license = "see context related readme files" } +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + local dummyfunction = function() end local dummyreporter = function(c) return function(...) texio.write(c .. " : " .. string.format(...)) end end @@ -2372,34 +2386,42 @@ statistics = { stoptiming = dummyfunction, elapsedtime = nil, } + directives = { register = dummyfunction, enable = dummyfunction, disable = dummyfunction, } + trackers = { register = dummyfunction, enable = dummyfunction, disable = dummyfunction, } + experiments = { register = dummyfunction, enable = dummyfunction, disable = dummyfunction, } + storage = { -- probably no longer needed register = dummyfunction, shared = { }, } + logs = { new = dummyreporter, reporter = dummyreporter, messenger = dummyreporter, report = dummyfunction, } + callbacks = { register = function(n,f) return callback.register(n,f) end, + } + utilities = { storage = { allocate = function(t) return t or { } end, @@ -2426,21 +2448,21 @@ local remapper = { fea = "font feature files", } -function resolvers.findfile(name,kind) +function resolvers.findfile(name,fileformat) name = string.gsub(name,"\\","\/") - kind = kind and string.lower(kind) - local found = kpse.find_file(name,(kind and kind ~= "" and (remapper[kind] or kind)) or file.extname(name,"tex")) + fileformat = fileformat and string.lower(fileformat) + local found = kpse.find_file(name,(fileformat and fileformat ~= "" and (remapper[fileformat] or fileformat)) or file.extname(name,"tex")) if not found or found == "" then - found = kpse.find_file(name,"other text file") + found = kpse.find_file(name,"other text files") end return found end -function resolvers.findbinfile(name,kind) - if not kind or kind == "" then - kind = file.extname(name) -- string.match(name,"%.([^%.]-)$") +function resolvers.findbinfile(name,fileformat) + if not fileformat or fileformat == "" then + fileformat = file.extname(name) -- string.match(name,"%.([^%.]-)$") end - return resolvers.findfile(name,(kind and remapper[kind]) or kind) + return resolvers.findfile(name,(fileformat and remapper[fileformat]) or fileformat) end function resolvers.resolve(s) @@ -2707,46 +2729,23 @@ end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['node-dum'] = { +if not modules then modules = { } end modules ['luatex-fonts-nod'] = { version = 1.001, - comment = "companion to luatex-*.tex", + comment = "companion to luatex-fonts.lua", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -nodes = nodes or { } -fonts = fonts or { } -attributes = attributes or { } - -nodes.pool = nodes.pool or { } -nodes.handlers = nodes.handlers or { } - -local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end -local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end -local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } - -nodes.nodecodes = nodecodes -nodes.whatcodes = whatcodes -nodes.whatsitcodes = whatcodes -nodes.glyphcodes = glyphcodes - -local traverse_id = node.traverse_id -local free_node = node.free -local remove_node = node.remove -local new_node = node.new +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end -local glyph_code = nodecodes.glyph +-- Don't depend on code here as it is only needed to complement the +-- font handler code. -function nodes.simple_font_handler(head) --- lang.hyphenate(head) - head = nodes.handlers.characters(head) - nodes.injections.handler(head) - nodes.handlers.protectglyphs(head) - head = node.ligaturing(head) - head = node.kerning(head) - return head -end +-- Attributes: if tex.attribute[0] ~= 0 then @@ -2760,54 +2759,44 @@ if tex.attribute[0] ~= 0 then end -nodes.handlers.protectglyphs = node.protect_glyphs -nodes.handlers.unprotectglyphs = node.unprotect_glyphs +attributes = { } +attributes.unsetvalue = -0x7FFFFFFF -function nodes.handlers.characters(head) - local fontdata = fonts.identifiers - if fontdata then - local usedfonts, done, prevfont = { }, false, nil - for n in traverse_id(glyph_code,head) do - local font = n.font - if font ~= prevfont then - prevfont = font - local used = usedfonts[font] - if not used then - local tfmdata = fontdata[font] -- - if tfmdata then - local shared = tfmdata.shared -- we need to check shared, only when same features - if shared then - local processors = shared.processes - if processors and #processors > 0 then - usedfonts[font] = processors - done = true - end - end - end - end - end - end - if done then - for font, processors in next, usedfonts do - for i=1,#processors do - local h, d = processors[i](head,font,0) - head, done = h or head, done or d - end - end +local numbers, last = { }, 127 + +function attributes.private(name) + local number = numbers[name] + if not number then + if last < 255 then + last = last + 1 end - return head, true - else - return head, false + number = last + numbers[name] = number end + return number end --- helper +-- Nodes: -function nodes.pool.kern(k) - local n = new_node("kern",1) - n.kern = k - return n -end +nodes = { } +nodes.pool = { } +nodes.handlers = { } + +local nodecodes = { } for k,v in next, node.types () do nodecodes[string.gsub(v,"_","")] = k end +local whatcodes = { } for k,v in next, node.whatsits() do whatcodes[string.gsub(v,"_","")] = k end +local glyphcodes = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" } + +nodes.nodecodes = nodecodes +nodes.whatcodes = whatcodes +nodes.whatsitcodes = whatcodes +nodes.glyphcodes = glyphcodes + +local free_node = node.free +local remove_node = node.remove +local new_node = node.new + +nodes.handlers.protectglyphs = node.protect_glyphs +nodes.handlers.unprotectglyphs = node.unprotect_glyphs function nodes.remove(head, current, free_too) local t = current @@ -2830,775 +2819,149 @@ end nodes.before = node.insert_before nodes.after = node.insert_after --- attributes +function nodes.pool.kern(k) + local n = new_node("kern",1) + n.kern = k + return n +end + +end -- closure -attributes.unsetvalue = -0x7FFFFFFF +do -- begin closure to overcome local limits and interference -local numbers, last = { }, 127 +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" +} -function attributes.private(name) - local number = numbers[name] - if not number then - if last < 255 then - last = last + 1 - end - number = last - numbers[name] = number - end - return number -end +-- basemethods -> can also be in list +-- presetcontext -> defaults +-- hashfeatures -> ctx version + +--[[ldx-- +

Not much is happening here.

+--ldx]]-- + +local lower = string.lower +local allocate, mark = utilities.storage.allocate, utilities.storage.mark + +local report_defining = logs.reporter("fonts","defining") + +fontloader.totable = fontloader.to_table + +fonts = fonts or { } -- already defined in context +local fonts = fonts + +-- some of these might move to where they are used first: + +fonts.hashes = { identifiers = allocate() } +fonts.analyzers = { } -- not needed here +fonts.readers = { } +fonts.tables = { } +fonts.definers = { methods = { } } +fonts.specifiers = fonts.specifiers or { } -- in format ! +fonts.loggers = { register = function() end } +fonts.helpers = { } + +fonts.tracers = { } -- for the moment till we have move to moduledata end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['node-inj'] = { +if not modules then modules = { } end modules ['font-con'] = { version = 1.001, - comment = "companion to node-ini.mkiv", + 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" } --- tricky ... fonts.identifiers is not yet defined .. to be solved (maybe general tex ini) --- 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. +local utf = unicode.utf8 -local next = next +local next, tostring, setmetatable, rawget = next, tostring, setmetatable, 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 trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) +local allocate = utilities.storage.allocate -local report_injections = logs.reporter("nodes","injections") +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 attributes, nodes, node = attributes, nodes, node +local report_defining = logs.reporter("fonts","defining") -fonts = fonts or { } -fonts.tfm = fonts.tfm or { } -fonts.identifiers = fonts.identifiers or { } +-- watch out: no negative depths and negative eights permitted in regular fonts -nodes.injections = nodes.injections or { } -local injections = nodes.injections +--[[ldx-- +

Here we only implement a few helper functions.

+--ldx]]-- -local fontdata = fonts.identifiers -local nodecodes = nodes.nodecodes -local glyph_code = nodecodes.glyph -local nodepool = nodes.pool -local newkern = nodepool.kern +local fonts = fonts +local constructors = { } +fonts.constructors = constructors +local handlers = { } +fonts.handlers = handlers -local traverse_id = node.traverse_id -local unset_attribute = node.unset_attribute -local has_attribute = node.has_attribute -local set_attribute = node.set_attribute -local insert_node_before = node.insert_before -local insert_node_after = node.insert_after +local specifiers = fonts.specifiers +local contextsetups = specifiers.contextsetups +local contextnumbers = specifiers.contextnumbers -local markbase = attributes.private('markbase') -local markmark = attributes.private('markmark') -local markdone = attributes.private('markdone') -local cursbase = attributes.private('cursbase') -local curscurs = attributes.private('curscurs') -local cursdone = attributes.private('cursdone') -local kernpair = attributes.private('kernpair') +-- will be directives -local cursives = { } -local marks = { } -local kerns = { } +constructors.sharebasekerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too) +constructors.dontembed = allocate() +constructors.mathactions = { } +constructors.autocleanup = true +constructors.namemode = "fullpath" -- will be a function --- 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 +constructors.version = 1.01 +constructors.cache = containers.define("fonts", "constructors", constructors.version, false) --- for the moment we pass the r2l key ... volt/arabtype tests +constructors.privateoffset = 0xF0000 -- 0x10FFFF -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 - set_attribute(start,cursbase,bound) - set_attribute(nxt,curscurs,bound) - cursives[bound] = { rlmode, dx, dy, ws, wn } - return dx, dy, bound +-- This might become an interface; + +local designsizes = allocate() +constructors.designsizes = designsizes +local loadedfonts = allocate() +constructors.loadedfonts = loadedfonts + +--[[ldx-- +

We need to normalize the scale factor (in scaled points). This has to +do with the fact that uses a negative multiple of 1000 as +a signal for a font scaled based on the design size.

+--ldx]]-- + +local factors = { + pt = 65536.0, + bp = 65781.8, +} + +function constructors.setfactor(f) + constructors.factor = factors[f or 'pt'] or factors.pt 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 = has_attribute(current,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 +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 - bound = #kerns + 1 - set_attribute(current,kernpair,bound) - kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } + return (- scaledpoints/1000) * 10 * factor 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 - set_attribute(current,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]) - local bound = has_attribute(base,markbase) - if bound then - local mb = marks[bound] - if mb then - if not index then index = #mb + 1 end - mb[index] = { dx, dy } - set_attribute(start,markmark,bound) - set_attribute(start,markdone,index) - return dx, dy, bound - else - report_injections("possible problem, U+%04X is base mark without data (id: %s)",base.char,bound) - end - end - index = index or 1 - bound = #marks + 1 - set_attribute(base,markbase,bound) - set_attribute(start,markmark,bound) - set_attribute(start,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 = has_attribute(n,kernpair) - local mb = has_attribute(n,markbase) - local mm = has_attribute(n,markmark) - local md = has_attribute(n,markdone) - local cb = has_attribute(n,cursbase) - local cc = has_attribute(n,curscurs) - report_injections("char U+%05X, font=%s",n.char,n.font) - if kp then - local k = kerns[kp] - if k[3] then - report_injections(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") - else - report_injections(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") - end - end - if mb then - report_injections(" markbase: bound=%s",mb) - end - if mm then - local m = marks[mm] - if mb then - local m = m[mb] - if m then - report_injections(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") - else - report_injections(" markmark: bound=%s, missing index",mm) - end - else - m = m[1] - report_injections(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") - end - end - if cb then - report_injections(" cursbase: bound=%s",cb) - end - if cc then - local c = cursives[cc] - report_injections(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") - 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 - -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 has_marks or has_cursives or has_kerns 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 - if n.subtype < 256 then - nofvalid = nofvalid + 1 - valid[nofvalid] = n - if n.font ~= nf then - nf = n.font - tm = fontdata[nf].marks - end - mk[n] = tm[n.char] - local k = has_attribute(n,kernpair) - if k then ---~ unset_attribute(k,kernpair) - 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].marks - end - mk[n] = tm[n.char] - 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 = has_attribute(n,cursbase) - if p_cursbase then - local n_curscurs = has_attribute(n,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 = has_attribute(p,markbase) - if p_markbase then - local mrks = marks[p_markbase] - for n in traverse_id(glyph_code,p.next) do - local n_markmark = has_attribute(n,markmark) - if p_markbase == n_markmark then - local index = has_attribute(n,markdone) or 1 - local d = mrks[index] - if d then - local rlmode = d[3] - if rlmode and rlmode > 0 then - -- new per 2010-10-06 - local k = wx[p] - if k then -- maybe (d[1] - p.width) and/or + k[2] - n.xoffset = p.xoffset - (p.width - d[1]) - k[2] - else - n.xoffset = p.xoffset - (p.width - d[1]) - end - else - local k = wx[p] - if k then - n.xoffset = p.xoffset - d[1] - k[2] - 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 - end - else - break - 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, can be sped up when w == nil - local rl, x, w, r2l = k[1], k[2] or 0, k[4] or 0, k[6] - local wx = w - x - if r2l then - 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 - 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)) - else - insert_node_before(head,n,newkern(k)) - end - end - end - end - if not keep then - kerns = { } - 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 = has_attribute(n,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 r2l then - 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 - return head, true - else - -- no tracing needed - end - return head, false -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -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" -} - --- The font code will be upgraded and reorganized so that we have a --- leaner generic code base and can do more tuning for context. - ---[[ldx-- -

Not much is happening here.

---ldx]]-- - -local utf = unicode.utf8 -local format, serialize = string.format, table.serialize -local write_nl = texio.write_nl -local lower = string.lower -local allocate, mark = utilities.storage.allocate, utilities.storage.mark - -local report_defining = logs.reporter("fonts","defining") - -fontloader.totable = fontloader.to_table - --- vtf comes first --- fix comes last - -fonts = fonts or { } - --- beware, some already defined - -fonts.identifiers = mark(fonts.identifiers or { }) -- fontdata ------.characters = mark(fonts.characters or { }) -- chardata ------.csnames = mark(fonts.csnames or { }) -- namedata ------.quads = mark(fonts.quads or { }) -- quaddata - ---~ fonts.identifiers[0] = { -- nullfont ---~ characters = { }, ---~ descriptions = { }, ---~ name = "nullfont", ---~ } - -fonts.tfm = fonts.tfm or { } -fonts.vf = fonts.vf or { } -fonts.afm = fonts.afm or { } -fonts.pfb = fonts.pfb or { } -fonts.otf = fonts.otf or { } - -fonts.privateoffset = 0xF0000 -- 0x10FFFF -fonts.verbose = false -- more verbose cache tables (will move to context namespace) - -fonts.methods = fonts.methods or { - base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, - node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, -} - -fonts.initializers = fonts.initializers or { - base = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } }, - node = { tfm = { }, afm = { }, otf = { }, vtf = { }, fix = { } } -} - -fonts.triggers = fonts.triggers or { - 'mode', - 'language', - 'script', - 'strategy', -} - -fonts.processors = fonts.processors or { -} - -fonts.analyzers = fonts.analyzers or { - useunicodemarks = false, -} - -fonts.manipulators = fonts.manipulators or { -} - -fonts.tracers = fonts.tracers or { -} - -fonts.typefaces = fonts.typefaces or { -} - -fonts.definers = fonts.definers or { } -fonts.definers.specifiers = fonts.definers.specifiers or { } -fonts.definers.specifiers.synonyms = fonts.definers.specifiers.synonyms or { } - --- tracing - -if not fonts.colors then - - fonts.colors = allocate { - set = function() end, - reset = function() end, - } - -end - --- format identification - -fonts.formats = allocate() - -function fonts.fontformat(filename,default) - local extname = lower(file.extname(filename)) - local format = fonts.formats[extname] - if format then - return format - else - report_defining("unable to determine font format for '%s'",filename) - return default - end -end - --- readers - -fonts.tfm.readers = fonts.tfm.readers or { } - -end -- closure - -do -- begin closure to overcome local limits and interference - -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 utf = unicode.utf8 - -local next, format, match, lower, gsub = next, string.format, string.match, string.lower, string.gsub -local concat, sortedkeys, utfbyte, serialize = table.concat, table.sortedkeys, utf.byte, table.serialize - -local allocate = utilities.storage.allocate - -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") - --- tfmdata has also fast access to indices and unicodes --- to be checked: otf -> tfm -> tfmscaled --- --- watch out: no negative depths and negative eights permitted in regular fonts - ---[[ldx-- -

Here we only implement a few helper functions.

---ldx]]-- - -local fonts = fonts -local tfm = fonts.tfm - -fonts.loaded = allocate() -fonts.dontembed = allocate() -fonts.triggers = fonts.triggers or { } -- brrr -fonts.initializers = fonts.initializers or { } -fonts.initializers.common = fonts.initializers.common or { } - -local set_attribute = node.set_attribute -local findbinfile = resolvers.findbinfile - -local readers = fonts.tfm.readers -local fontdata = fonts.identifiers -local nodecodes = nodes.nodecodes - -local disc_code = nodecodes.disc -local glyph_code = nodecodes.glyph - ---[[ldx-- -

The next function encapsulates the standard loader as -supplied by .

---ldx]]-- - -tfm.resolvevirtualtoo = true -- false -tfm.sharebasekerns = false -- true (.5 sec slower on mk but brings down mem from 410M to 310M, beware: then script/lang share too) -tfm.mathactions = { } -tfm.fontnamemode = "fullpath" - -tfm.enhance = tfm.enhance or function() end - -local function read_from_tfm(specification) - local fname, tfmdata = specification.filename or "", nil - if fname ~= "" then - if trace_defining then - report_defining("loading tfm file %s at size %s",fname,specification.size) - end - tfmdata = font.read_tfm(fname,specification.size) -- not cached, fast enough - if tfmdata then - tfmdata.descriptions = tfmdata.descriptions or { } - if tfm.resolvevirtualtoo then - fonts.logger.save(tfmdata,file.extname(fname),specification) -- strange, why here - fname = findbinfile(specification.name, 'ovf') - if fname and fname ~= "" then - local vfdata = font.read_vf(fname,specification.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 - tfmdata.type = 'virtual' - tfmdata.fonts = vfdata.fonts - end - end - end - tfm.enhance(tfmdata,specification) - end - elseif trace_defining then - report_defining("loading tfm with name %s fails",specification.name) - end - return tfmdata -end - ---[[ldx-- -

We need to normalize the scale factor (in scaled points). This has to -do with the fact that uses a negative multiple of 1000 as -a signal for a font scaled based on the design size.

---ldx]]-- - -local factors = { - pt = 65536.0, - bp = 65781.8, -} - -function tfm.setfactor(f) - tfm.factor = factors[f or 'pt'] or factors.pt -end - -tfm.setfactor() - -function tfm.scaled(scaledpoints, designsize) -- handles designsize in sp as well - if scaledpoints < 0 then - if designsize then - if designsize > tfm.factor then -- or just 1000 / when? mp? - return (- scaledpoints/1000) * designsize -- sp's - else - return (- scaledpoints/1000) * designsize * tfm.factor - end - else - return (- scaledpoints/1000) * 10 * tfm.factor - end - else - return scaledpoints - end -end - ---[[ldx-- -

Before a font is passed to we scale it. Here we also need -to scale virtual characters.

---ldx]]-- - ---~ function tfm.getvirtualid(tfmdata) ---~ -- since we don't know the id yet, we use 0 as signal ---~ local tf = tfmdata.fonts ---~ if not tf then ---~ tfmdata.type = "virtual" ---~ tfmdata.fonts = { { id = 0 } } ---~ return 1 ---~ else ---~ local ntf = #tf + 1 ---~ tf[ntf] = { id = 0 } ---~ return ntf ---~ end ---~ end - -function tfm.getvirtualid(tfmdata) - -- since we don't know the id yet, we use 0 as signal - local tf = tfmdata.fonts - if not tf then - tf = { } - tfmdata.type = "virtual" - tfmdata.fonts = tf - end - local ntf = #tf + 1 - tf[ntf] = { id = 0 } - return ntf -end - -function tfm.checkvirtualid(tfmdata, id) - if tfmdata and tfmdata.type == "virtual" then - if not tfmdata.fonts or #tfmdata.fonts == 0 then - tfmdata.type, tfmdata.fonts = "real", nil - else - local vfonts = tfmdata.fonts - for f=1,#vfonts do - local fnt = vfonts[f] - if fnt.id and fnt.id == 0 then - fnt.id = id - end - end - end + return scaledpoints end end @@ -3608,15 +2971,13 @@ 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.)

--ldx]]-- -fonts.trace_scaling = false - -- the following hack costs a bit of runtime but safes memory -- -- basekerns are scaled and will be hashed by table id -- sharedkerns are unscaled and are be hashed by concatenated indexes ---~ function tfm.check_base_kerns(tfmdata) ---~ if tfm.sharebasekerns then +--~ function constructors.check_base_kerns(tfmdata) +--~ if constructors.sharebasekerns then --~ local sharedkerns = tfmdata.sharedkerns --~ if sharedkerns then --~ local basekerns = { } @@ -3627,8 +2988,8 @@ fonts.trace_scaling = false --~ return nil, nil --~ end ---~ function tfm.prepare_base_kerns(tfmdata) ---~ if tfm.sharebasekerns and not tfmdata.sharedkerns then +--~ function constructors.prepare_base_kerns(tfmdata) +--~ if constructors.sharebasekerns and not tfmdata.sharedkerns then --~ local sharedkerns = { } --~ tfmdata.sharedkerns = sharedkerns --~ for u, chr in next, tfmdata.characters do @@ -3646,16 +3007,15 @@ fonts.trace_scaling = false --~ end --~ end --- we can have cache scaled characters when we are in node mode and don't have +-- we can cache scaled characters when we are in node mode and don't have -- protruding and expansion: hash == fullname @ size @ protruding @ expansion -- but in practice (except from mk) the otf hash will be enough already so it --- makes no sense to mess up the code now - -local charactercache = { } +-- makes no sense to mess up the code now -- The scaler is only used for otf and afm and virtual fonts. If -- a virtual font has italic correction make sure to set the --- has_italic flag. Some more flags will be added in the future. +-- italic_correction flag. Some more flags will be added in +-- the future. --[[ldx--

The reason why the scaler was originally split, is that for a while we experimented @@ -3664,156 +3024,302 @@ make this profitable and the based variant was just faster. A days wasted day but an experience richer.

--ldx]]-- -tfm.autocleanup = true - -local lastfont = nil - -- 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 soem 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 --- we don't need the glyph data as we can use the description .. but we will --- have to wait till we can access the internal tfm table efficiently in which --- case characters will become a metatable afterwards - -function tfm.cleanuptable(tfmdata) -- we need a cleanup callback, now we miss the last one - if tfm.autocleanup then -- ok, we can hook this into everyshipout or so ... todo - if tfmdata.type == 'virtual' or tfmdata.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 - else - -- for k, v in next, tfmdata.characters do - -- if v.kerns then v.kerns = nil end - -- end +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 -function tfm.cleanup(tfmdata) -- we need a cleanup callback, now we miss the last one -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 tfm.calculatescale(tfmtable, scaledpoints) +function constructors.calculatescale(tfmdata,scaledpoints) + local parameters = tfmdata.parameters if scaledpoints < 0 then - scaledpoints = (- scaledpoints/1000) * tfmtable.designsize -- already in sp + scaledpoints = (- scaledpoints/1000) * (tfmdata.designsize or parameters.designsize) -- already in sp end - local units = tfmtable.units or 1000 - local delta = scaledpoints/units -- brr, some open type fonts have 2048 - return scaledpoints, delta, units + return scaledpoints, scaledpoints / (parameters.units or 1000) -- delta end -function tfm.scale(tfmtable, scaledpoints, relativeid) - -- tfm.prepare_base_kerns(tfmtable) -- optimalization - local t = { } -- the new table - local scaledpoints, delta, units = tfm.calculatescale(tfmtable, scaledpoints, relativeid) - -- is just a trigger for the backend - t.units_per_em = units or 1000 +function constructors.scale(tfmdata,specification) + local target = { } -- the new table -- - local hdelta, vdelta = delta, delta - -- unicoded unique descriptions shared cidinfo characters changed parameters indices - for k,v in next, tfmtable do - if type(v) == "table" then - -- print(k) - else - t[k] = v - end + 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 MathConstants = tfmdata.mathconstants or { } + -- + local targetcharacters = { } + local targetdescriptions = table.derive(descriptions) + local targetparameters = table.derive(parameters) + local targetmathparameters = table.derive(mathparameters) + local targetproperties = table.derive(properties) + local targetgoodies = table.derive(goodies) + target.characters = targetcharacters + target.descriptions = targetdescriptions + target.parameters = targetparameters + target.mathparameters = targetmathparameters + target.properties = targetproperties + target.goodies = targetgoodies + target.shared = shared + target.resources = resources + target.unscaled = tfmdata -- the original unscaled one (temp) + -- + -- 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 + 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 + -- + 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 - local extend_factor = tfmtable.extend_factor or 0 + target.fontname = fontname + target.fullname = fullname + target.filename = filename + target.psname = psname + target.name = name + -- + properties.fontname = fontname + properties.fullname = fullname + properties.filename = filename + properties.psname = psname + properties.name = name + -- expansion (hz) + local expansion = parameters.expansion + if expansion then + target.stretch = expansion.stretch + target.shrink = expansion.shrink + target.step = expansion.step + target.auto_expand = expansion.auto + end + -- protrusion + local protrusion = parameters.protrusion + if protrusion then + target.auto_protrude = protrusion.auto + end + -- widening + local extend_factor = parameters.extend_factor or 0 if extend_factor ~= 0 and extend_factor ~= 1 then hdelta = hdelta * extend_factor - t.extend = extend_factor * 1000 + target.extend = extend_factor * 1000 -- extent ? else - t.extend = 1000 + target.extend = 1000 -- extent ? end - local slant_factor = tfmtable.slant_factor or 0 + -- slanting + local slant_factor = parameters.slant_factor or 0 if slant_factor ~= 0 then - t.slant = slant_factor * 1000 + target.slant = slant_factor * 1000 else - t.slant = 0 - end - -- status - local isvirtual = tfmtable.type == "virtual" or tfmtable.virtualized - local hasmath = (tfmtable.mathparameters ~= nil and next(tfmtable.mathparameters) ~= nil) or (tfmtable.MathConstants ~= nil and next(tfmtable.MathConstants) ~= nil) - local nodemode = tfmtable.mode == "node" - local hasquality = tfmtable.auto_expand or tfmtable.auto_protrude - local hasitalic = tfmtable.has_italic - local descriptions = tfmtable.descriptions or { } + target.slant = 0 + end -- - if hasmath then - t.has_math = true -- this will move to elsewhere + targetparameters.hfactor = hdelta + targetparameters.vfactor = vdelta + targetparameters.factor = delta + 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 hasitalic = properties.italic_correction + local stackmath = not properties.no_stackmath + local nonames = properties.noglyphnames + local nodemode = properties.mode == "node" + -- + if changed and not next(changed) then + changed = false end -- - t.parameters = { } - t.characters = { } - t.MathConstants = { } - -- fast access - t.unscaled = tfmtable -- the original unscaled one (temp) - t.unicodes = tfmtable.unicodes - t.indices = tfmtable.indices - t.marks = tfmtable.marks + target.type = isvirtual and "virtual" or "real" + -- more extensive test + local hasmath = (properties.math or next(mathparameters) or next(MathConstants)) and true + if hasmath then + properties.has_math = true + target.nomath = false + target.MathConstants = MathConstants + target.mathconstants = MathConstants + else + properties.has_math = false + target.nomath = true + target.mathparameters = nil -- nop + end -- this will move to some subtable so that it is copied at once - t.goodies = tfmtable.goodies - t.colorscheme = tfmtable.colorscheme - t.postprocessors = tfmtable.postprocessors + 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 + 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 + -- + if hasmath then + local ma = constructors.mathactions + local ta = type(ma) + if ta == "function" then -- context + ma(target,tfmdata) + elseif ta == "table" then -- generic (we keep the deltas) + for i=1,#ma do + ma[i](target,tfmdata,delta,hdelta,vdelta) + end + end + if not targetparameters[13] then targetparameters[13] = .86*targetx_height end -- mathsupdisplay + if not targetparameters[14] then targetparameters[14] = .86*targetx_height end -- mathsupnormal + if not targetparameters[15] then targetparameters[15] = .86*targetx_height end -- mathsupcramped + if not targetparameters[16] then targetparameters[16] = .48*targetx_height end -- mathsubnormal + if not targetparameters[17] then targetparameters[17] = .48*targetx_height end -- mathsubcombined + if not targetparameters[22] then targetparameters[22] = 0 end -- mathaxisheight + if target.MathConstants then target.MathConstants.AccentBaseHeight = nil end -- safeguard + if trace_defining then + report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'", + name or "noname",fullname or "nofullname",filename or "nofilename") + end + else + if trace_defining then + report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'", + name or "noname",fullname or "nofullname",filename or "nofilename") + end + end + -- + targetparameters.xheight = targetparameters.xheight or parameters.x_height + targetparameters.extraspace = targetparameters.extraspace or parameters.extra_space + targetparameters.spacestretch = targetparameters.spacestretch or parameters.space_stretch + targetparameters.spaceshrink = targetparameters.spaceshrink or parameters.space_shrink -- - -- t.embedding = tfmtable.embedding - t.descriptions = descriptions - if tfmtable.fonts then - t.fonts = table.fastcopy(tfmtable.fonts) -- hm also at the end - end - local tp = t.parameters - local mp = t.mathparameters - local tfmp = tfmtable.parameters -- let's check for indexes + local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0 + local scaledwidth = defaultwidth * hdelta + local scaledheight = defaultheight * vdelta + local scaleddepth = defaultdepth * vdelta -- - tp.slant = (tfmp.slant or tfmp[1] or 0) - tp.space = (tfmp.space or tfmp[2] or 0)*hdelta - tp.space_stretch = (tfmp.space_stretch or tfmp[3] or 0)*hdelta - tp.space_shrink = (tfmp.space_shrink or tfmp[4] or 0)*hdelta - tp.x_height = (tfmp.x_height or tfmp[5] or 0)*vdelta - tp.quad = (tfmp.quad or tfmp[6] or 0)*hdelta - tp.extra_space = (tfmp.extra_space or tfmp[7] or 0)*hdelta - local protrusionfactor = (tp.quad ~= 0 and 1000/tp.quad) or 0 - local tc = t.characters - local characters = tfmtable.characters - local nameneeded = not tfmtable.shared.otfdata --hack - local changed = tfmtable.changed or { } -- for base mode - local ischanged = changed and next(changed) - local indices = tfmtable.indices - local luatex = tfmtable.luatex - local tounicode = luatex and luatex.tounicode - local defaultwidth = luatex and luatex.defaultwidth or 0 - local defaultheight = luatex and luatex.defaultheight or 0 - local defaultdepth = luatex and luatex.defaultdepth or 0 - -- experimental, sharing kerns (unscaled and scaled) saves memory - -- local sharedkerns, basekerns = tfm.check_base_kerns(tfmtable) - -- loop over descriptions (afm and otf have descriptions, tfm not) - -- there is no need (yet) to assign a value to chr.tonunicode - local scaledwidth = defaultwidth * hdelta - local scaledheight = defaultheight * vdelta - local scaleddepth = defaultdepth * vdelta - local stackmath = tfmtable.ignore_stack_math ~= true - local private = fonts.privateoffset - local sharedkerns = { } - for k,v in next, characters do + local sharedkerns = { } + -- + for unicode, character in next, characters do local chr, description, index - if ischanged then + if changed then -- basemode hack - local c = changed[k] + local c = changed[unicode] if c then - description = descriptions[c] or v - v = characters[c] or v - index = (indices and indices[c]) or c + description = descriptions[c] or character + character = characters[c] or character + index = description.index or c else - description = descriptions[k] or v - index = (indices and indices[k]) or k + description = descriptions[unicode] or character + index = description.index or unicode end else - description = descriptions[k] or v - index = (indices and indices[k]) or k + description = descriptions[unicode] or character + index = description.index or unicode end local width = description.width local height = description.height @@ -3823,9 +3329,8 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) -- if depth then depth = vdelta*depth else depth = scaleddepth end if depth and depth ~= 0 then depth = delta*depth - if nameneeded then + if nonames then chr = { - name = description.name, index = index, height = height, depth = depth, @@ -3833,6 +3338,7 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) } else chr = { + name = description.name, index = index, height = height, depth = depth, @@ -3841,15 +3347,15 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) end else -- this saves a little bit of memory time and memory, esp for big cjk fonts - if nameneeded then + if nonames then chr = { - name = description.name, index = index, height = height, width = width, } else chr = { + name = description.name, index = index, height = height, width = width, @@ -3867,22 +3373,22 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) end if hasquality then -- we could move these calculations elsewhere (saves calculations) - local ve = v.expansion_factor + local ve = character.expansion_factor if ve then chr.expansion_factor = ve*1000 -- expansionfactor, hm, can happen elsewhere end - local vl = v.left_protruding + local vl = character.left_protruding if vl then chr.left_protruding = protrusionfactor*width*vl end - local vr = v.right_protruding + local vr = character.right_protruding if vr then chr.right_protruding = protrusionfactor*width*vr end end -- todo: hasitalic if hasitalic then - local vi = description.italic or v.italic + local vi = description.italic or character.italic if vi and vi ~= 0 then chr.italic = vi*hdelta end @@ -3890,14 +3396,14 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) -- to be tested if hasmath then -- todo, just operate on descriptions.math - local vn = v.next + local vn = character.next if vn then chr.next = vn - --~ if v.vert_variants or v.horiz_variants then - --~ report_defining("glyph 0x%05X has combination of next, vert_variants and horiz_variants",index) - --~ end + -- if character.vert_variants or character.horiz_variants then + -- report_defining("glyph 0x%05X has combination of next, vert_variants and horiz_variants",index) + -- end else - local vv = v.vert_variants + local vv = character.vert_variants if vv then local t = { } for i=1,#vv do @@ -3911,13 +3417,8 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) } end chr.vert_variants = t - --~ local ic = v.vert_italic_correction - --~ if ic then - --~ chr.italic = ic * hdelta - --~ print(format("0x%05X -> %s",k,chr.italic)) - --~ end else - local hv = v.horiz_variants + local hv = character.horiz_variants if hv then local t = { } for i=1,#hv do @@ -3934,12 +3435,12 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) end end end - local vt = description.top_accent - if vt then - chr.top_accent = vdelta*vt + local va = character.top_accent + if va then + chr.top_accent = vdelta*va end if stackmath then - local mk = v.mathkerns + 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] @@ -3954,294 +3455,631 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) 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 + chr.mathkern = kerns -- singular -> should be patched in luatex ! + end + end + end + if not nodemode then + local vk = character.kerns + if vk then + -- if sharedkerns then + -- local base = basekerns[vk] -- hashed by table id, not content + -- if not base then + -- base = {} + -- for k,v in next, vk do base[k] = v*hdelta end + -- basekerns[vk] = base + -- end + -- chr.kerns = base + -- else + -- local tt = {} + -- for k,v in next, vk do tt[k] = v*hdelta end + -- chr.kerns = tt + -- end + 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 + 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.extend_factor then + parameters.extend_factor = tfmdata.extend or 0 + end + -- + if not parameters.slant_factor then + parameters.slant_factor = 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 + setmetatable(descriptions, { __index = 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 + -- tfmdata.mathconstants + -- + if not properties.has_math then + properties.has_math = 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-- +

A unique hash value is generated by:

+--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 not nodemode then - local vk = v.kerns - if vk then - --~ if sharedkerns then - --~ local base = basekerns[vk] -- hashed by table id, not content - --~ if not base then - --~ base = {} - --~ for k,v in next, vk do base[k] = v*hdelta end - --~ basekerns[vk] = base - --~ end - --~ chr.kerns = base - --~ else - --~ local tt = {} - --~ for k,v in next, vk do tt[k] = v*hdelta end - --~ chr.kerns = tt - --~ end - 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 = v.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 + 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 k ~= "number" and k ~= "features" then -- I need to figure this out, features + 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-- +

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 .

+--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: '%s'",specname) end end - if isvirtual then - local vc = v.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 +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 '%s' is not found",askedfilename) + foundfilename = resolvers.findbinfile(file.basename(askedfilename),"") or "" + if foundfilename ~= "" then + report_defining("using source file '%s' (cache mismatch)",foundfilename) end - chr.index = nil end end - tc[k] = chr + data.foundfilename = foundfilename end - -- t.encodingbytes, t.filename, t.fullname, t.name: elsewhere - t.size = scaledpoints - t.factor = delta - t.hfactor = hdelta - t.vfactor = vdelta - if t.fonts then - t.fonts = table.fastcopy(t.fonts) -- maybe we virtualize more afterwards - end - if hasmath then - -- mathematics.extras.copy(t) -- can be done elsewhere if needed - local ma = tfm.mathactions - for i=1,#ma do - ma[i](t,tfmtable,delta,hdelta,vdelta) -- what delta? + return foundfilename +end + +local formats = allocate() +fonts.formats = formats + +setmetatable(formats, { + __index = function(t,k) + local l = lower(k) + if rawget(t,k) then + t[k] = l + return l end + return rawget(t,file.extname(l)) end - -- needed for \high cum suis - local tpx = tp.x_height - if hasmath then - if not tp[13] then tp[13] = .86*tpx end -- mathsupdisplay - if not tp[14] then tp[14] = .86*tpx end -- mathsupnormal - if not tp[15] then tp[15] = .86*tpx end -- mathsupcramped - if not tp[16] then tp[16] = .48*tpx end -- mathsubnormal - if not tp[17] then tp[17] = .48*tpx end -- mathsubcombined - if not tp[22] then tp[22] = 0 end -- mathaxisheight - if t.MathConstants then t.MathConstants.AccentBaseHeight = nil end -- safeguard - end - t.tounicode = 1 - t.cidinfo = tfmtable.cidinfo - -- we have t.name=metricfile and t.fullname=RealName and t.filename=diskfilename - -- when collapsing fonts, luatex looks as both t.name and t.fullname as ttc files - -- can have multiple subfonts - if hasmath then - if trace_defining then - report_defining("math enabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") - 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 '%s', group '%s', mode '%s'",name or "?",group or "?",mode) + os.exit() + elseif position then + -- todo: remove existing + insert(t, position, { name = name, action = action }) else - if trace_defining then - report_defining("math disabled for: name '%s', fullname: '%s', filename: '%s'",t.name or "noname",t.fullname or "nofullname",t.filename or "nofilename") + for i=1,#t do + local ti = t[i] + if ti.name == name then + ti.action = action + return + end end - t.nomath, t.MathConstants = true, nil + insert(t, { name = name, action = action }) end - if not t.psname then - -- name used in pdf file as well as for selecting subfont in ttc/dfont - t.psname = t.fontname or (t.fullname and fonts.names.cleanname(t.fullname)) +end + +local function set(group,name,target,source) + target = target[group] + if not target then + report_defining("fatal target error in setting feature '%s', group '%s'",name or "?",group or "?") + os.exit() + end + local source = source[group] + if not source then + report_defining("fatal source error in setting feature '%s', group '%s'",name or "?",group or "?") + 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 - if trace_defining then - report_defining("used for accessing (sub)font: '%s'",t.psname or "nopsname") - report_defining("used for subsetting: '%s'",t.fontname or "nofontname") - end - -- this will move up (side effect of merging split call) - t.factor = delta - t.ascender = delta*(tfmtable.ascender or 0) - t.descender = delta*(tfmtable.descender or 0) - t.shared = tfmtable.shared or { } - t.unique = table.fastcopy(tfmtable.unique or {}) - tfm.cleanup(t) - -- print(t.fontname,table.serialize(t.MathConstants)) - return t end ---[[ldx-- -

Analyzers run per script and/or language and are needed in order to -process features right.

---ldx]]-- +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 -fonts.analyzers = fonts.analyzers or { } -local analyzers = fonts.analyzers +constructors.registerfeature = register -analyzers.aux = analyzers.aux or { } -analyzers.methods = analyzers.methods or { } -analyzers.initializers = analyzers.initializers or { } +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 --- todo: analyzers per script/lang, cross font, so we need an font id hash -> script --- e.g. latin -> hyphenate, arab -> 1/2/3 analyze +function constructors.newfeatures(what) + local features = handlers[what].features + if not features then + local tables = handlers[what].tables -- can be preloaded + features = { + defaults = { }, + descriptions = tables and tables.features or { }, + initializers = { base = { }, node = { } }, + processors = { base = { }, node = { } }, + manipulators = { base = { }, node = { } }, + } + features.register = function(specification) return register(features,specification) end + handlers[what].features = features -- will also become hidden + end + return features +end --- an example analyzer (should move to font-ota.lua) +--[[ldx-- +

We need to check for default features. For this we provide +a helper function.

+--ldx]]-- -local state = attributes.private('state') +function constructors.checkedfeatures(what,features) + if features and next(features) then + local done = false + for key, value in next, handlers[what].features.defaults do + if features[key] == nil then + features[key] = value + done = true + end + end + return features, done -- done signals a change + else + return fastcopy(defaults), true + end +end -function analyzers.aux.setstate(head,font) - local useunicodemarks = analyzers.useunicodemarks - local tfmdata = fontdata[font] - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean - while current do - local id = current.id - if id == glyph_code and current.font == font then - local char = current.char - local d = descriptions[char] - if d then - if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then - done = true - set_attribute(current,state,5) -- mark - elseif n == 0 then - first, last, n = current, current, 1 - set_attribute(current,state,1) -- init - else - last, n = current, n+1 - set_attribute(current,state,2) -- medi +-- 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 + local mode = properties.mode or (whatmodechecker and whatmodechecker(tfmdata,features)) or features.mode or "base" + properties.mode = mode -- also status + 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 + 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 %s to %s for mode %s for font %s",feature, + tostring(value),mode or 'unknown', tfmdata.properties.fullname or 'unknown') + end + action(tfmdata,value,features) -- can set mode (e.g. goodies) so it can trigger a restart + if mode ~= properties.mode then + mode = properties.mode + redo = true + end + done[feature] = true + end + if redo then + break + end end - else -- finish - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina + if not redo then + break end - first, last, n = nil, nil, 0 - end - elseif id == disc_code then - -- always in the middle - set_attribute(current,state,2) -- midi - last = current - else -- finish - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina + else + break end - first, last, n = nil, nil, 0 end - current = current.next - end - if first and first == last then - set_attribute(last,state,4) -- isol - elseif last then - set_attribute(last,state,3) -- fina + properties.mode = mode -- to be sure + return true + else + return false end - return head, done end -function tfm.replacements(tfm,value) - -- tfm.characters[0x0022] = table.fastcopy(tfm.characters[0x201D]) - -- tfm.characters[0x0027] = table.fastcopy(tfm.characters[0x2019]) - -- tfm.characters[0x0060] = table.fastcopy(tfm.characters[0x2018]) - -- tfm.characters[0x0022] = tfm.characters[0x201D] - tfm.characters[0x0027] = tfm.characters[0x2019] - -- tfm.characters[0x0060] = tfm.characters[0x2018] +-- 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 %s for mode %s for font %s",feature, + mode or 'unknown', tfmdata.properties.fullname or 'unknown') + end + if action then + nofprocesses = nofprocesses + 1 + processes[nofprocesses] = action + end + end + end + end + end + return processes end --- checking +-- after scaling -function tfm.checkedfilename(metadata,whatever) - local foundfilename = metadata.foundfilename - if not foundfilename then - local askedfilename = metadata.filename or "" - if askedfilename ~= "" then - askedfilename = resolvers.resolve(askedfilename) -- no shortcut - foundfilename = findbinfile(askedfilename,"") or "" - if foundfilename == "" then - report_defining("source file '%s' is not found",askedfilename) - foundfilename = findbinfile(file.basename(askedfilename),"") or "" - if foundfilename ~= "" then - report_defining("using source file '%s' (cache mismatch)",foundfilename) +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 %s for mode %s for font %s",feature, + mode or 'unknown', tfmdata.properties.fullname or 'unknown') + end + if action then + action(tfmdata,feature,value) + end end end - elseif whatever then - report_defining("no source file for '%s'",whatever) - foundfilename = "" end - metadata.foundfilename = foundfilename - -- report_defining("using source file '%s'",foundfilename) end - return foundfilename end --- status info - -statistics.register("fonts load time", function() - return statistics.elapsedseconds(fonts) -end) +end -- closure --- readers +do -- begin closure to overcome local limits and interference -fonts.formats.tfm = "type1" -- we need to have at least a value here +if not modules then modules = { } end modules ['luatex-font-enc'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} -local function check_tfm(specification,fullname) - -- ofm directive blocks local path search unless set; btw, in context we - -- don't support ofm files anyway as this format is obsolete - local foundname = findbinfile(fullname, 'tfm') or "" -- just to be sure - if foundname == "" then - foundname = findbinfile(fullname, 'ofm') or "" -- bonus for usage outside context - end - if foundname == "" then - foundname = fonts.names.getfilename(fullname,"tfm") - end - if foundname ~= "" then - specification.filename, specification.format = foundname, "ofm" - return read_from_tfm(specification) - end +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() end -readers.check_tfm = check_tfm - -function readers.tfm(specification) - local fullname, tfmtable = specification.filename or "", nil - if fullname == "" then - local forced = specification.forced or "" - if forced ~= "" then - tfmtable = check_tfm(specification,specification.name .. "." .. forced) - end - if not tfmtable then - tfmtable = check_tfm(specification,specification.name) - end +local fonts = fonts +fonts.encodings = { } +fonts.encodings.agl = { } + +setmetatable(fonts.encodings.agl, { __index = function(t,k) + if k == "unicodes" then + texio.write(" ") + local unicodes = dofile(resolvers.findfile("font-agl.lua")) + fonts.encodings.agl = { unicodes = unicodes } + return unicodes else - tfmtable = check_tfm(specification,fullname) + return nil end - return tfmtable -end +end }) + end -- closure @@ -4257,18 +4095,19 @@ if not modules then modules = { } end modules ['font-cid'] = { local format, match, lower = string.format, string.match, string.lower local tonumber = tonumber -local lpegmatch = lpeg.match +local P, S, R, C, V, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.match -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) +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 fonts = fonts -fonts.cid = fonts.cid or { } -local cid = fonts.cid -cid.map = cid.map or { } -cid.max = cid.max or 10 +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 -- @@ -4277,8 +4116,6 @@ cid.max = cid.max or 10 -- 1..95 0020 -- 99 3000 -local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C - local number = C(R("09","af","AF")^1) local space = S(" \n\r\t") local spaces = space^0 @@ -4286,7 +4123,7 @@ local period = P(".") local periods = period * period local name = P("/") * C((1-space)^1) -local unicodes, names = { }, { } +local unicodes, names = { }, { } -- we could use Carg now local function do_one(a,b) unicodes[tonumber(a)] = tonumber(b,16) @@ -4304,15 +4141,15 @@ local function do_name(a,b) names[tonumber(a)] = b end -local grammar = lpeg.P { "start", - start = number * spaces * number * lpeg.V("series"), - series = (spaces * (lpeg.V("one") + lpeg.V("range") + lpeg.V("named")) )^1, +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 } -function cid.load(filename) +local function loadcidfile(filename) local data = io.loaddata(filename) if data then unicodes, names = { }, { } @@ -4326,1043 +4163,94 @@ function cid.load(filename) unicodes = unicodes, names = names } - else - return nil 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 cidmap = cid.map[hashname] - if not cidmap then + local found = cidmap[hashname] + if not found then if trace_loading then report_otf("checking cidmap, registry: %s, ordering: %s, supplement: %s, filename: %s",registry,ordering,supplement,filename) end local fullname = resolvers.findfile(filename,'cid') or "" if fullname ~= "" then - cidmap = cid.load(fullname) - if cidmap then + found = loadcidfile(fullname) + if found then if trace_loading then report_otf("using cidmap file %s",filename) end - cid.map[hashname] = cidmap - cidmap.usedname = file.basename(filename) - return cidmap + cidmap[hashname] = found + found.usedname = file.basename(filename) end end end - return cidmap + return found end -function cid.getmap(registry,ordering,supplement) - -- cf Arthur R. we can safely scan upwards since cids are downward compatible - local supplement = tonumber(supplement) +-- 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("needed cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,supplement) end - local cidmap = locate(registry,ordering,supplement) - if not cidmap then + found = locate(registry,ordering,supplement) + if not found then + local supnum = tonumber(supplement) local cidnum = nil -- next highest (alternatively we could start high) - if supplement < cid.max then - for supplement=supplement+1,cid.max do - local c = locate(registry,ordering,supplement) + if supnum < cidmax then + for s=supnum+1,cidmax do + local c = locate(registry,ordering,s) if c then - cidmap, cidnum = c, supplement + found, cidnum = c, s break end end end -- next lowest (least worse fit) - if not cidmap and supplement > 0 then - for supplement=supplement-1,0,-1 do - local c = locate(registry,ordering,supplement) + if not found and supnum > 0 then + for s=supnum-1,0,-1 do + local c = locate(registry,ordering,s) if c then - cidmap, cidnum = c, supplement + found, cidnum = c, s break end end end - -- prevent further lookups - if cidmap and cidnum > 0 then + -- prevent further lookups -- somewhat tricky + registry = lower(registry) + ordering = lower(ordering) + if found and cidnum > 0 then for s=0,cidnum-1 do - filename = format(template,registry,ordering,s) - if not cid.map[filename] then - cid.map[filename] = cidmap -- copy of ref + local filename = format(template,registry,ordering,s) + if not cidmap[filename] then + cidmap[filename] = found end end end end - return cidmap -end - -end -- closure - -do -- begin closure to overcome local limits and interference - -if not modules then modules = { } end modules ['font-otf'] = { - 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" -} - -local type, next, tonumber, tostring = type, next, tonumber, tostring -local gsub, lower, format = string.gsub, string.lower, string.format -local is_boolean = string.is_boolean - -local allocate = utilities.storage.allocate - -fonts = fonts or { } -- needed for font server -local fonts = fonts -fonts.otf = fonts.otf or { } -local otf = fonts.otf - -otf.tables = otf.tables or { } -local tables = otf.tables - -otf.meanings = otf.meanings or { } -local meanings = otf.meanings - -local scripts = allocate { - ['dflt'] = 'Default', - - ['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 { - ['dflt'] = 'Default', - - ['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', -} - -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' -} - - -local function swap(h) -- can be a tables.swap when we get a better name - local r = { } - for k, v in next, h do - r[v] = lower(gsub(k," ","")) - end - return r -end - -local verbosescripts = allocate(swap(scripts )) -local verboselanguages = allocate(swap(languages)) -local verbosefeatures = allocate(swap(features )) - -tables.scripts = scripts -tables.languages = languages -tables.features = features -tables.baselines = baselines - -tables.verbosescripts = verbosescripts -tables.verboselanguages = verboselanguages -tables.verbosefeatures = verbosefeatures - -for k, v in next, verbosefeatures do - local stripped = gsub(k,"%-"," ") - verbosefeatures[stripped] = v - local stripped = gsub(k,"[^a-zA-Z0-9]","") - verbosefeatures[stripped] = v -end -for k, v in next, verbosefeatures do - verbosefeatures[lower(k)] = v -end - -local function resolve(tab,id) - if tab and id then - id = lower(id) - return tab[id] or tab[gsub(id," ","")] or tab['dflt'] or '' - else - return "unknown" - end + return found end -function meanings.script (id) return resolve(scripts, id) end -function meanings.language(id) return resolve(languages,id) end -function meanings.feature (id) return resolve(features, id) end -function meanings.baseline(id) return resolve(baselines,id) end - -local checkers = { - rand = function(v) - return v and "random" - end -} - -meanings.checkers = checkers - -function meanings.normalize(features) - if features then - local h = { } - for k,v in next, features do - k = lower(k) - if k == "language" or k == "lang" then - v = gsub(lower(v),"[^a-z0-9%-]","") - if not languages[v] then - h.language = verboselanguages[v] or "dflt" - else - h.language = v - end - elseif k == "script" then - v = gsub(lower(v),"[^a-z0-9%-]","") - if not scripts[v] then - h.script = verbosescripts[v] or "dflt" - else - h.script = v - end - 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 - k = verbosefeatures[k] or k - local c = checkers[k] - h[k] = c and c(v) or v - end - end - return h - end -end - --- 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", ---~ }, ---~ } - end -- closure do -- begin closure to overcome local limits and interference @@ -5375,15 +4263,18 @@ if not modules then modules = { } end modules ['font-map'] = { license = "see context related readme files" } -local utf = unicode.utf8 local match, format, find, concat, gsub, lower = string.match, string.format, string.find, table.concat, string.gsub, string.lower -local lpegmatch = lpeg.match +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 trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local trace_unimapping = false trackers.register("otf.unimapping", function(v) trace_unimapping = v end) +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_otf = logs.reporter("fonts","otf loading") +local report_fonts = logs.reporter("fonts","loading") -- not otf only + +local fonts = fonts +local mappings = { } +fonts.mappings = mappings --[[ldx--

Eventually this code will disappear because map files are kind @@ -5391,23 +4282,18 @@ of obsolete. Some code may move to runtime or auxiliary modules.

The name to unciode related code will stay of course.

--ldx]]-- -local fonts = fonts -fonts.map = fonts.map or { } - 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_unimapping then - report_otf("enhance: loading %s ",lumfile) + if trace_loading or trace_mapping then + report_fonts("enhance: loading %s ",lumfile) end lumunic = dofile(lumfile) return lumunic, lumfile end end -local P, R, S, C, Ct, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc - local hex = R("AF","09") local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end local hexsix = (hex^1) / function(s) return tonumber(s,16) end @@ -5434,8 +4320,8 @@ local function makenameparser(str) end end ---~ local parser = fonts.map.makenameparser("Japan1") ---~ local parser = fonts.map.makenameparser() +--~ local parser = mappings.makenameparser("Japan1") +--~ local parser = mappings.makenameparser() --~ local function test(str) --~ local b, a = lpegmatch(parser,str) --~ print((a and table.serialize(b)) or b) @@ -5476,7 +4362,7 @@ end --~ --~ local cache = { } --~ ---~ function fonts.map.tounicode16(unicode) +--~ function mappings.tounicode16(unicode) --~ local s = cache[unicode] --~ if not s then --~ if unicode < 0x10000 then @@ -5489,10 +4375,10 @@ end --~ return s --~ end -fonts.map.loadlumtable = loadlumtable -fonts.map.makenameparser = makenameparser -fonts.map.tounicode16 = tounicode16 -fonts.map.tounicode16sequence = tounicode16sequence +mappings.loadlumtable = loadlumtable +mappings.makenameparser = makenameparser +mappings.tounicode16 = tounicode16 +mappings.tounicode16sequence = tounicode16sequence local separator = S("_.") local other = C((1 - separator)^1) @@ -5504,8 +4390,11 @@ local ligsplitter = Ct(other * (separator * other)^0) --~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more"))) --~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that"))) -fonts.map.addtounicode = function(data,filename) - local unicodes = data.luatex and data.luatex.unicodes +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 @@ -5515,28 +4404,35 @@ fonts.map.addtounicode = function(data,filename) unicodes['zwj'] = unicodes['zwj'] or 0x200D unicodes['zwnj'] = unicodes['zwnj'] or 0x200C -- the tounicode mapping is sparse and only needed for alternatives - local tounicode, originals, ns, nl, private, unknown = { }, { }, 0, 0, fonts.privateoffset, format("%04X",utfbyte("?")) - data.luatex.tounicode, data.luatex.originals = tounicode, originals + 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 - local cidinfo, cidnames, cidcodes = data.cidinfo - local usedmap = cidinfo and cidinfo.usedname - usedmap = usedmap and lower(usedmap) - usedmap = usedmap and fonts.cid.map[usedmap] + -- + cidinfo = properties.cidinfo + usedmap = cidinfo and fonts.cid.getmap(cidinfo) + -- if usedmap then - oparser = usedmap and makenameparser(cidinfo.ordering) + oparser = usedmap and makenameparser(cidinfo.ordering) cidnames = usedmap.names cidcodes = usedmap.unicodes end uparser = makenameparser() - local unicodevector = fonts.enc.agl.unicodes -- loaded runtime in context - for index, glyph in next, data.glyphs do - local name, unic = glyph.name, glyph.unicode or -1 -- play safe + 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] + local unicode = lumunic and lumunic[name] or unicodevector[name] if unicode then originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 end @@ -5627,18 +4523,20 @@ fonts.map.addtounicode = function(data,filename) end end end - if trace_unimapping then - for index, glyph in table.sortedhash(data.glyphs) do - local toun, name, unic = tounicode[index], glyph.name, glyph.unicode or -1 -- play safe + 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_otf("internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) + report_fonts("internal: 0x%05X, name: %s, unicode: 0x%05X, tounicode: %s",index,name,unic,toun) else - report_otf("internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) + report_fonts("internal: 0x%05X, name: %s, unicode: 0x%05X",index,name,unic) end end end if trace_loading and (ns > 0 or nl > 0) then - report_otf("enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) + report_fonts("enhance: %s tounicode entries added (%s ligatures)",nl+ns, ns) end end @@ -5646,57 +4544,138 @@ end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['font-lua'] = { +if not modules then modules = { } end modules ['luatex-fonts-syn'] = { version = 1.001, - comment = "companion to font-ini.mkiv", + comment = "companion to luatex-*.tex", 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) +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end -local report_lua = logs.reporter("fonts","lua loading") +-- Generic font names support. +-- +-- Watch out, the version number is the same as the one used in +-- the mtx-fonts.lua function scripts.fonts.names as we use a +-- simplified font database in the plain solution and by using +-- a different number we're less dependent on context. +-- +-- mtxrun --script font --reload --simple +-- +-- The format of the file is as follows: +-- +-- return { +-- ["version"] = 1.001, +-- ["mappings"] = { +-- ["somettcfontone"] = { "Some TTC Font One", "SomeFontA.ttc", 1 }, +-- ["somettcfonttwo"] = { "Some TTC Font Two", "SomeFontA.ttc", 2 }, +-- ["somettffont"] = { "Some TTF Font", "SomeFontB.ttf" }, +-- ["someotffont"] = { "Some OTF Font", "SomeFontC.otf" }, +-- }, +-- } -fonts.formats.lua = "lua" +local fonts = fonts +fonts.names = fonts.names or { } -local readers = fonts.tfm.readers +fonts.names.version = 1.001 -- not the same as in context +fonts.names.basename = "luatex-fonts-names.lua" +fonts.names.new_to_old = { } +fonts.names.old_to_new = { } -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) +local data, loaded = nil, false + +local fileformats = { "lua", "tex", "other text files" } + +function fonts.names.resolve(name,sub) + if not loaded then + local basename = fonts.names.basename + if basename and basename ~= "" then + for i=1,#fileformats do + local format = fileformats[i] + local foundname = resolvers.findfile(basename,format) or "" + if foundname ~= "" then + data = dofile(foundname) + texio.write("") + break + end + end + end + loaded = true + end + if type(data) == "table" and data.version == fonts.names.version then + local condensed = string.gsub(string.lower(name),"[^%a%d]","") + local found = data.mappings and data.mappings[condensed] + if found then + local fontname, filename, subfont = found[1], found[2], found[3] + if subfont then + return filename, fontname + else + return filename, false + end + else + return name, false -- fallback to filename + end end end -function readers.lua(specification) - local original = specification.specification - if trace_defining then - report_lua("using lua reader for '%s'",original) - end - local fullname, tfmtable = specification.filename or "", nil +fonts.names.resolvespec = fonts.names.resolve -- only supported in mkiv + +function fonts.names.getfilename(askedname,suffix) -- only supported in mkiv + return "" +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules = { } end modules ['luatex-fonts-tfm'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local tfm = { } +fonts.handlers.tfm = tfm +fonts.formats.tfm = "type1" -- we need to have at least a value here + +function fonts.readers.tfm(specification) + local fullname = specification.filename or "" if fullname == "" then local forced = specification.forced or "" if forced ~= "" then - tfmtable = check_lua(specification,specification.name .. "." .. forced) - end - if not tfmtable then - tfmtable = check_lua(specification,specification.name) + fullname = specification.name .. "." .. forced + else + fullname = specification.name end - else - tfmtable = check_lua(specification,fullname) end - return tfmtable + local foundname = resolvers.findbinfile(fullname, 'tfm') or "" + if foundname == "" then + foundname = resolvers.findbinfile(fullname, 'ofm') or "" + end + if foundname ~= "" then + specification.filename = foundname + specification.format = "ofm" + return font.read_tfm(specification.filename,specification.size) + end end end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['font-otf'] = { +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", @@ -5704,106 +4683,182 @@ if not modules then modules = { } end modules ['font-otf'] = { license = "see context related readme files" } --- langs -> languages enz --- anchor_classes vs kernclasses --- modification/creationtime in subfont is runtime dus zinloos --- to_table -> totable +local lower = string.lower -local utf = unicode.utf8 +local allocate = utilities.storage.allocate -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 = table.reversed, table.concat -local ioflush = io.flush +local fonts = fonts +local otf = { } +fonts.handlers.otf = otf -local allocate = utilities.storage.allocate +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +registerotffeature { + name = "features", + description = "initialization of feature handler", + default = true, +} -local trace_private = false trackers.register("otf.private", function(v) trace_private = v end) -local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end) -local trace_features = false trackers.register("otf.features", function(v) trace_features = v end) -local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) -local trace_sequences = false trackers.register("otf.sequences", function(v) trace_sequences = v end) -local trace_math = false trackers.register("otf.math", function(v) trace_math = v end) -local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +-- these are later hooked into node and base initializaters -local report_otf = logs.reporter("fonts","otf loading") +local otftables = otf.tables -- not always defined + +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 starttiming, stoptiming, elapsedtime = statistics.starttiming, statistics.stoptiming, statistics.elapsedtime +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 -local findbinfile = resolvers.findbinfile +registerotffeature { + name = "mode", + description = "mode", + initializers = { + base = setmode, + node = setmode, + } +} -local fonts = fonts +registerotffeature { + name = "language", + description = "language", + initializers = { + base = setlanguage, + node = setlanguage, + } +} -fonts.otf = fonts.otf or { } -local otf = fonts.otf -local tfm = fonts.tfm +registerotffeature { + name = "script", + description = "script", + initializers = { + base = setscript, + node = setscript, + } +} -local fontdata = fonts.identifiers -local chardata = characters and characters.data -- not used --- todo: probably first time so local first +end -- closure -otf.features = otf.features or { } -local features = otf.features -features.list = features.list or { } -local featurelist = features.list -features.default = features.default or { } -local defaultfeatures = features.default +do -- begin closure to overcome local limits and interference -otf.enhancers = allocate() -local enhancers = otf.enhancers -enhancers.patches = { } -local patches = enhancers.patches +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" +} -local definers = fonts.definers -local readers = fonts.tfm.readers +-- langs -> languages enz +-- anchor_classes vs kernclasses +-- modification/creationtime in subfont is runtime dus zinloos +-- to_table -> totable +-- ascent descent -otf.glists = { "gsub", "gpos" } +local utf = unicode.utf8 -otf.version = 2.710 -- beware: also sync font-mis.lua -otf.cache = containers.define("fonts", "otf", otf.version, true) +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 = table.fastcopy + +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 loadmethod = "table" -- table, mixed, sparse -local forceload = false -local cleanup = 0 -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 fonts = fonts +local otf = fonts.handlers.otf -local wildcard = "*" -local default = "dflt" +otf.glists = { "gsub", "gpos" } -local fontloaderfields = fontloader.fields -local mainfields = nil -local glyphfields = nil -- not used yet +otf.version = 2.710 -- beware: also sync font-mis.lua +otf.cache = containers.define("fonts", "otf", otf.version, true) -directives.register("fonts.otf.loader.method", function(v) - if v == "sparse" and fontloaderfields then - loadmethod = "sparse" - elseif v == "mixed" then - loadmethod = "mixed" - elseif v == "table" then - loadmethod = "table" - else - loadmethod = "table" - report_otf("no loader method '%s', using '%s' instead",v,loadmethod) - end -end) +local fontdata = fonts.hashes.identifiers +local chardata = characters and characters.data -- not used -directives.register("fonts.otf.loader.cleanup",function(v) - cleanup = tonumber(v) or (v and 1) or 0 -end) +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +local enhancers = allocate() +otf.enhancers = enhancers +local patches = { } +enhancers.patches = patches -directives.register("fonts.otf.loader.force", function(v) forceload = v end) -directives.register("fonts.otf.loader.usemetatables", function(v) usemetatables = v end) -directives.register("fonts.otf.loader.pack", function(v) packdata = v end) -directives.register("fonts.otf.loader.syncspace", function(v) syncspace = v end) -directives.register("fonts.otf.loader.forcenotdef", function(v) forcenotdef = v end) +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 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 @@ -5814,19 +4869,19 @@ local function load_featurefile(raw,featurefile) end end -local function showfeatureorder(otfdata,filename) - local sequences = otfdata.luatex.sequences +local function showfeatureorder(rawdata,filename) + local sequences = rawdata.resources.sequences if sequences and #sequences > 0 then if trace_loading then report_otf("font %s 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 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 + local features = sequence.features if trace_loading then report_otf("%3i %-15s %-20s [%s]",nos,name,typ,concat(subtables,",")) end @@ -5858,25 +4913,6 @@ end

We start with a lot of tables and related functions.

--ldx]]-- -local global_fields = table.tohash { - "metadata", - "lookups", - "glyphs", - "subfonts", - "luatex", - "pfminfo", - "cidinfo", - "tables", - "names", - "unicodes", - "names", - -- "math", - "anchor_classes", - "kern_classes", - "gpos", - "gsub" -} - local valid_fields = table.tohash { -- "anchor_classes", "ascent", @@ -5892,32 +4928,32 @@ local valid_fields = table.tohash { "extrema_bound", "familyname", "fontname", + "fontname", "fontstyle_id", "fontstyle_name", "fullname", -- "glyphs", "hasvmetrics", - "head_optimized_for_cleartype", + -- "head_optimized_for_cleartype", "horiz_base", "issans", "isserif", "italicangle", -- "kerns", -- "lookups", - -- "luatex", "macstyle", -- "modificationtime", "onlybitmaps", "origname", "os2_version", - -- "pfminfo", + "pfminfo", -- "private", "serifcheck", "sfd_version", -- "size", "strokedfont", "strokewidth", - "subfonts", + -- "subfonts", "table_version", -- "tables", -- "ttf_tab_saved", @@ -5929,7 +4965,6 @@ local valid_fields = table.tohash { "use_typo_metrics", "uwidth", -- "validation_state", - "verbose", "version", "vert_base", "weight", @@ -5956,43 +4991,47 @@ local ordered_enhancers = { "reorganize glyph lookups", "reorganize glyph anchors", + "merge kern classes", + "reorganize features", "reorganize subtables", "check glyphs", "check metadata", - "check math parameters", "check extra features", -- after metadata + + "cleanup tables", } --[[ldx--

Here we go.

--ldx]]-- -local actions = { } +local actions = allocate() +local before = allocate() +local after = allocate() -patches.before = allocate() -patches.after = allocate() +patches.before = before +patches.after = after -local before = patches.before -local after = patches.after - -local function enhance(name,data,filename,raw,verbose) +local function enhance(name,data,filename,raw) local enhancer = actions[name] if enhancer then - if verbose then + if trace_loading then report_otf("enhance: %s (%s)",name,filename) ioflush() end enhancer(data,filename,raw) - else - report_otf("enhance: %s is undefined",name) + elseif trace_loading then + -- report_otf("enhance: %s is undefined",name) end end -function enhancers.apply(data,filename,raw,verbose) +function enhancers.apply(data,filename,raw) local basename = file.basename(lower(filename)) - report_otf("start enhancing: %s",filename) + if trace_loading then + report_otf("start enhancing: %s",filename) + end ioflush() -- we want instant messages for e=1,#ordered_enhancers do local enhancer = ordered_enhancers[e] @@ -6004,7 +5043,7 @@ function enhancers.apply(data,filename,raw,verbose) end end end - enhance(enhancer,data,filename,raw,verbose) + enhance(enhancer,data,filename,raw) local a = after[enhancer] if a then for pattern, action in next, a do @@ -6015,7 +5054,9 @@ function enhancers.apply(data,filename,raw,verbose) end ioflush() -- we want instant messages end - report_otf("stop enhancing") + if trace_loading then + report_otf("stop enhancing") + end ioflush() -- we want instant messages end @@ -6043,11 +5084,14 @@ end function otf.load(filename,format,sub,featurefile) local name = file.basename(file.removesuffix(filename)) local attr = lfs.attributes(filename) - local size, time = attr and attr.size or 0, attr and attr.modification or 0 + 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 + if sub == "" then + sub = false + end local hash = name if sub then hash = hash .. "-" .. sub @@ -6064,8 +5108,8 @@ function otf.load(filename,format,sub,featurefile) local attr = lfs.attributes(name) featurefiles[#featurefiles+1] = { name = name, - size = attr.size or 0, - time = attr.modification or 0, + size = size, + time = time, } end end @@ -6074,7 +5118,7 @@ function otf.load(filename,format,sub,featurefile) end end local data = containers.read(otf.cache,hash) - local reload = not data or data.verbose ~= fonts.verbose or data.size ~= size or data.time ~= time + local reload = not data or data.size ~= size or data.time ~= time if forceload then report_otf("loading: forced reload due to hard coded flag") reload = true @@ -6102,7 +5146,7 @@ function otf.load(filename,format,sub,featurefile) end if reload then report_otf("loading: %s (hash: %s)",filename,hash) - local fontdata, messages, rawdata + local fontdata, messages if sub then fontdata, messages = fontloader.open(filename,sub) else @@ -6128,51 +5172,66 @@ function otf.load(filename,format,sub,featurefile) load_featurefile(fontdata,featurefiles[i].name) end end - report_otf("loading method: %s",loadmethod) - if loadmethod == "sparse" then - rawdata = fontdata - else - rawdata = fontloader.to_table(fontdata) - fontloader.close(fontdata) - end - if rawdata then - data = { } - starttiming(data) - local verboseindeed = verbose ~= nil and verbose or trace_loading - report_otf("file size: %s", size) - enhancers.apply(data,filename,rawdata,verboseindeed) - if packdata and not fonts.verbose then - enhance("pack",data,filename,nil,verboseindeed) - end - data.size = size - data.time = time - data.format = format - if featurefiles then - data.featuredata = featurefiles - end - data.verbose = fonts.verbose - report_otf("saving in cache: %s",filename) - data = containers.write(otf.cache, hash, data) + 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 = { + -- unicodes to names + }, + 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) + if packdata then if cleanup > 0 then collectgarbage("collect") end - stoptiming(data) - if elapsedtime then -- not in generic - report_otf("preprocessing and caching took %s seconds",elapsedtime(data)) - end - data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one - if cleanup > 1 then - collectgarbage("collect") - end - else - data = nil - report_otf("loading failed (table conversion error)") + enhance("pack",data,filename,nil) end - if loadmethod == "sparse" then - fontloader.close(fontdata) - if cleanup > 2 then - -- collectgarbage("collect") - end + report_otf("saving in cache: %s",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 took %s seconds",elapsedtime(data)) + 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 @@ -6208,35 +5267,42 @@ local mt = { end } +actions["prepare tables"] = function(data,filename,raw) + data.properties.italic_correction = 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 luatex = data.luatex - local defaultwidth = luatex.defaultwidth or 0 - local defaultheight = luatex.defaultheight or 0 - local defaultdepth = luatex.defaultdepth or 0 + 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 if usemetatables then - for _, d in next, data.glyphs do + for _, d in next, descriptions do local wd = d.width if not wd then d.width = defaultwidth - elseif wd ~= 0 and d.class == "mark" then - d.width = -wd + elseif trace_markwidth and wd ~= 0 and d.class == "mark" then + report_otf("mark with width %s (%s) in %s",wd,d.name or "",file.basename(filename)) + -- d.width = -wd end setmetatable(d,mt) end else - for _, d in next, data.glyphs do + for _, d in next, descriptions do local bb, wd = d.boundingbox, d.width if not wd then d.width = defaultwidth - elseif wd ~= 0 and d.class == "mark" then - d.width = -wd - end - if forcenotdef and not d.name then - d.name = ".notdef" + elseif trace_markwidth and wd ~= 0 and d.class == "mark" then + report_otf("mark with width %s (%s) in %s",wd,d.name or "",file.basename(filename)) + -- 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 @@ -6255,16 +5321,6 @@ actions["add dimensions"] = function(data,filename) end end -actions["prepare tables"] = function(data,filename,raw) - local luatex = { - filename = resolvers.unresolve(filename), -- no shortcut - version = otf.version, - creator = "context mkiv", - } - data.luatex = luatex - data.metadata = { } -end - local function somecopy(old) -- fast one if old then local new = { } @@ -6301,192 +5357,220 @@ end -- table cronstruction can save some mem actions["prepare glyphs"] = function(data,filename,raw) - -- we can also move the names to data.luatex.names which might - -- save us some more memory (at the cost of harder tracing) - local rawglyphs = raw.glyphs - local glyphs, udglyphs - if loadmethod == "sparse" then - glyphs, udglyphs = { }, { } - elseif loadmethod == "mixed" then - glyphs, udglyphs = { }, rawglyphs - else - glyphs, udglyphs = rawglyphs, rawglyphs - end - data.glyphs, data.udglyphs = glyphs, udglyphs - local subfonts = raw.subfonts - if subfonts then - if data.glyphs and next(data.glyphs) then - report_otf("replacing existing glyph table due to subfonts") - end - local cidinfo = raw.cidinfo - if cidinfo.registry then - local cidmap, cidname = fonts.cid.getmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement) + 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 + + if rawsubfonts then + + metadata.subfonts = { } + properties.cidinfo = rawcidinfo + + if rawcidinfo.registry then + local cidmap = fonts.cid.getmap(rawcidinfo) if cidmap then - cidinfo.usedname = cidmap.usedname - local uni_to_int, int_to_uni, nofnames, nofunicodes = { }, { }, 0, 0 - local unicodes, names = cidmap.unicodes, cidmap.names - for cidindex=1,#subfonts do - local subfont = subfonts[cidindex] - if loadmethod == "sparse" then - local rawglyphs = subfont.glyphs - for index=0,subfont.glyphmax - 1 do - local g = rawglyphs[index] - if g then - local unicode, name = unicodes[index], names[index] - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - elseif name then - nofnames = nofnames + 1 - end - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name or name or "unknown", -- uniXXXX - cidindex = cidindex, - unicode = unicode, - } + 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 + metadata.subfonts[cidindex] = somecopy(subfont) + for index=0,subfont.glyphmax - 1 do + 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 - end - -- If we had more userdata, we would need more of this - -- and it would start working against us in terms of - -- convenience and speed. - subfont = somecopy(subfont) - subfont.glyphs = nil - subfont[cidindex] = subfont - elseif loadmethod == "mixed" then - for index, g in next, subfont.glyphs do - local unicode, name = unicodes[index], names[index] - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - elseif name then + 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("enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",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 - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name or name or "unknown", -- uniXXXX + indices[unicode] = index -- each index in unique (at least now) + + local description = { + -- width = glyph.width, + boundingbox = glyph.boundingbox, + name = glyph.name or name or "unknown", -- uniXXXX cidindex = cidindex, - unicode = unicode, + index = index, + glyph = glyph, } + + descriptions[unicode] = description end - subfont.glyphs = nil - else - for index, g in next, subfont.glyphs do - local unicode, name = unicodes[index], names[index] - if unicode then - uni_to_int[unicode] = index - int_to_uni[index] = unicode - nofunicodes = nofunicodes + 1 - g.unicode = unicode - elseif name then - nofnames = nofnames + 1 - end - g.cidindex = cidindex - glyphs[index] = g - end - subfont.glyphs = nil end end if trace_loading then report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames) end - data.map = data.map or { } - data.map.map = uni_to_int - data.map.backmap = int_to_uni elseif trace_loading then report_otf("unable to remap cid font, missing cid file for %s",filename) end - data.subfonts = subfonts elseif trace_loading then report_otf("font %s has no glyphs",filename) end + else - if loadmethod == "sparse" then - -- we get fields from the userdata glyph table and create - -- a minimal entry first - for index=0,raw.glyphmax - 1 do - local g = rawglyphs[index] - if g then - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name, - unicode = g.unicode, - } + + for index=0,raw.glyphmax-1 do + 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("enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) + end + private = private + 1 + else + unicodes[name] = unicode end - end - elseif loadmethod == "mixed" then - -- we get fields from the totable glyph table and copy to the - -- final glyph table so first we create a minimal entry - for index, g in next, rawglyphs do - udglyphs[index] = g - glyphs[index] = { - width = g.width, - italic = g.italic_correction, - boundingbox = g.boundingbox, - class = g.class, - name = g.name, - unicode = g.unicode, + indices[unicode] = index + if not name then + name = format("u%06X",unicode) + end + + descriptions[unicode] = { + -- width = glyph.width, + boundingbox = glyph.boundingbox, + name = name, + index = index, + glyph = glyph, } end - else - -- we use the totable glyph table directly and manipulate the - -- entries in this (also final) table end - data.map = raw.map + + 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["prepare unicodes"] = 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 + + -- begin of messy (not needed whwn 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("using embedded unicode map '%s'",encname) + end + local multiples, nofmultiples = { }, 0 + for unicode, index in next, unicodetoindex do + if unicode <= criterium and not descriptions[unicode] then + local parent = indices[index] + local description = descriptions[parent] + if description then + local c = fastcopy(description) + c.comment = format("copy of 0x%04X", parent) + descriptions[unicode] = c + local name = c.name + if not unicodes[name] then + unicodes[name] = unicode + end + nofmultiples = nofmultiples + 1 + multiples[nofmultiples] = name -- we can save duplicates if needed + else + -- make it a notdef + report_otf("weird unicode 0x%04X at index 0x%04X",unicode,index) + end + end + end + if trace_loading then + if nofmultiples > 0 then + report_otf("%s glyphs are reused: %s",nofmultiples,concat(multiples," ")) + else + report_otf("no glyphs are reused") + end + end + elseif properties.cidinfo then + report_otf("warning: no unicode map, used cidmap '%s'",properties.cidinfo.usedname or "?") + else + report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") + end + + if mapdata then + mapdata.map = { } -- clear some memory end - data.cidinfo = raw.cidinfo -- hack end --- watch copy of cidinfo: we can best make some more copies to data +-- 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 glyphs = data.glyphs - -- collect info - local has_italic, widths, marks = false, { }, { } - for index, glyph in next, glyphs do - local italic = glyph.italic_correction + local descriptions = data.descriptions + local resources = data.resources + local metadata = data.metadata + local properties = data.properties + local italic_correction = false + local widths = { } + local marks = { } + for unicode, description in next, descriptions do + local glyph = description.glyph if not italic then -- skip elseif italic == 0 then - glyph.italic_correction = nil - glyph.italic = nil + -- skip else - glyph.italic_correction = nil - glyph.italic = italic - has_italic = true + description.italic = italic + italic_correction = true end local width = glyph.width widths[width] = (widths[width] or 0) + 1 local class = glyph.class - local unicode = glyph.unicode - if class == "mark" then - marks[unicode] = true - -- elseif chardata[unicode].category == "mn" then - -- marks[unicode] = true - -- glyph.class = "mark" + if class then + if class == "mark" then + marks[unicode] = true + end + description.class = class end - local a = glyph.altuni if a then glyph.altuni = nil end - local d = glyph.dependents if d then glyph.dependents = nil end - local v = glyph.vwidth if v then glyph.vwidth = nil end end -- flag italic - data.metadata.has_italic = has_italic + properties.italic_correction = italic_correction -- flag marks - data.luatex.marks = marks + resources.marks = marks -- share most common width for cjk fonts local wd, most = 0, 1 for k,v in next, widths do @@ -6498,43 +5582,41 @@ actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this if trace_loading then report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most) end - for index, glyph in next, glyphs do - if glyph.width == wd then - glyph.width = nil + for unicode, description in next, descriptions do + if description.width == wd then + -- description.width = nil + else + description.width = description.glyph.width end end - data.luatex.defaultwidth = wd + 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 luatex = data.luatex - local unicodes = luatex.unicodes - local reverse = { } - luatex.markclasses = reverse + 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 - local us = unicodes[s] - if type(us) == "table" then - for u=1,#us do - t[us[u]] = true - end - else - t[us] = true - end + t[unicodes[s]] = true end - reverse[name] = t + markclasses[name] = t end - data.mark_classes = nil -- when using table end end actions["reorganize features"] = function(data,filename,raw) -- combine with other local features = { } - data.luatex.features = features + data.resources.features = features for k, what in next, otf.glists do local dw = raw[what] if dw then @@ -6547,7 +5629,11 @@ actions["reorganize features"] = function(data,filename,raw) -- combine with oth 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 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] @@ -6566,25 +5652,34 @@ actions["reorganize features"] = function(data,filename,raw) -- combine with oth end actions["reorganize anchor classes"] = function(data,filename,raw) - local classes = raw.anchor_classes -- anchor classes not in final table - local luatex = data.luatex - local anchor_to_lookup, lookup_to_anchor = { }, { } - luatex.anchor_to_lookup, luatex.lookup_to_anchor = anchor_to_lookup, lookup_to_anchor + 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 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 + 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 not l then l = { } lookup_to_anchor[lookup] = l end - l[anchor] = true + if l then + l[anchor] = true + else + l = { [anchor] = true } + lookup_to_anchor[lookup] = l + end a[lookup] = true end end @@ -6592,13 +5687,16 @@ actions["reorganize anchor classes"] = function(data,filename,raw) end actions["prepare tounicode"] = function(data,filename,raw) - fonts.map.addtounicode(data,filename) + fonts.mappings.addtounicode(data,filename) end actions["reorganize subtables"] = function(data,filename,raw) - local luatex = data.luatex - local sequences, lookups = { }, { } - luatex.sequences, luatex.lookups = sequences, lookups + 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 @@ -6613,9 +5711,7 @@ actions["reorganize subtables"] = function(data,filename,raw) if subtables then local t = { } for s=1,#subtables do - local subtable = subtables[s] - local name = subtable.name - t[#t+1] = name + t[s] = subtables[s].name end subtables = t end @@ -6629,7 +5725,7 @@ actions["reorganize subtables"] = function(data,filename,raw) } markclass = flags.mark_class if markclass then - markclass = luatex.markclasses[markclass] + markclass = resources.markclasses[markclass] end flags = t end @@ -6678,138 +5774,165 @@ actions["reorganize subtables"] = function(data,filename,raw) end end --- the next one is still messy but will get better when we have --- flattened map/enc tables in the font loader +-- test this: +-- +-- for _, what in next, otf.glists do +-- raw[what] = nil +-- end -actions["prepare unicodes"] = function(data,filename,raw) - local luatex = data.luatex - local indices, unicodes, multiples, internals= { }, { }, { }, { } - local mapdata = data.map or raw.map -- map already moved - local mapmap - if not mapdata then - report_otf("no mapdata in '%s'",filename) - mapmap = { } - mapdata = { map = mapmap } - data.map = mapdata - elseif not mapdata.map then - report_otf("no map in mapdata of '%s'",filename) - mapmap = { } - mapdata.map = mapmap - else - mapmap = mapdata.map - end - local encname = lower(data.enc_name or raw.enc_name or mapdata.enc_name or "") - local criterium = fonts.privateoffset - local private = criterium - local glyphs = data.glyphs - -- todo: nofmultiples - for index, glyph in next, glyphs do - if index > 0 then - local name = glyph.name -- really needed ? - if name then - local unicode = glyph.unicode - if not unicode or unicode == -1 or unicode >= criterium then - glyph.unicode = private - indices[private] = index - unicodes[name] = private - internals[index] = true - if trace_private then - report_otf("enhance: glyph %s at index U+%04X is moved to private unicode slot U+%04X",name,index,private) - end - private = private + 1 - else - indices[unicode] = index - unicodes[name] = unicode - end - -- maybe deal with altuni here in the future but first we need - -- to encounter a proper font that sets them; we have to wait till - -- a next luatex binary as currently the unicode numbers can be out - -- of bounds - if false then - local altuni = glyph.altuni - if altuni then - local un = { unicodes[name] } - for i=1,#altuni do - local unicode = altuni[i].unicode - multiples[#multiples+1] = name - un[i+1] = unicode - indices[unicode] = index -- maybe check for duplicates - end - unicodes[name] = un - end - end - else - -- message that something is wrong - end - end - end - -- beware: the indices table is used to initialize the tfm table - if find(encname,"unicode") then -- unicodebmp, unicodefull, ... - if trace_loading then - report_otf("using embedded unicode map '%s'",encname) - end - -- ok -- we can also consider using the altuni - for unicode, index in next, mapmap do - if not internals[index] then - local name = glyphs[index].name - if name then - local un = unicodes[name] - if not un then - unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then -- tonumber(un) - if un ~= unicode then - multiples[#multiples+1] = name - unicodes[name] = { un, unicode } - indices[unicode] = index - end - else - local ok = false - for u=1,#un do - if un[u] == unicode then - ok = true - break - end - end - if not ok then - multiples[#multiples+1] = name - un[#un+1] = unicode - indices[unicode] = index - end - end - end - end - end - else - report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") +actions["prepare lookups"] = function(data,filename,raw) + local lookups = raw.lookups + if lookups then + data.lookups = lookups end - if trace_loading then - if #multiples > 0 then - report_otf("%s glyphs are reused: %s",#multiples, concat(multiples," ")) - else - report_otf("no glyphs are reused") +end + +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 - luatex.indices = indices - luatex.unicodes = unicodes - luatex.private = private + return result end -actions["prepare lookups"] = function(data,filename,raw) - local lookups = raw.lookups - if lookups then - data.lookups = lookups +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) + cache[cover] = uncovered + end + return uncovered end end actions["reorganize lookups"] = function(data,filename,raw) -- we prefer the before lookups in a normal order if data.lookups then - for _, v in next, data.lookups do - if v.rules then - for _, vv in next, v.rules do - local c = vv.coverage - if c and c.before then - c.before = reversed(c.before) + local splitter = data.helpers.tounicodetable + local cache = { } + 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,cache,reversed(before_class)) + end + local current_class = lookup.current_class + if current_class then + current_class = t_uncover(splitter,cache,current_class) + end + local after_class = lookup.after_class + if after_class then + after_class = t_uncover(splitter,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 = before + end + local current = class.current + local lookups = rule.lookups + if current then + for i=1,#current do + current[i] = current_class[current[i]] or { } + if lookups and not lookups[i] then + lookups[i] = false + end + end + rule.current = current + end + local after = class.after + if after then + for i=1,#after do + after[i] = after_class[after[i]] or { } + end + rule.after = after + 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 + rule.before = t_uncover(splitter,cache,reversed(before)) + end + local current = coverage.current + if current then + rule.current = t_uncover(splitter,cache,current) + end + local after = coverage.after + if after then + rule.after = t_uncover(splitter,cache,after) + end + rule.coverage = nil + end + end + elseif format == "reversecoverage" then + for i=1,#rules do + local rule = rules[i] + local reversecoverage = rule.reversecoverage + if reversecoverage then + local before = reversecoverage.before + if before then + rule.before = t_uncover(splitter,cache,reversed(before)) + end + local current = reversecoverage.current + if current then + rule.current = t_uncover(splitter,cache,current) + end + local after = reversecoverage.after + if after then + rule.after = t_uncover(splitter,cache,after) + end + local replacements = reversecoverage.replacements + if replacements then + rule.replacements = s_uncover(splitter,cache,replacements) + end + rule.reversecoverage = nil + end + end + elseif format == "glyphs" then + for i=1,#rules do + local rule = rules[i] + local glyphs = rule.glyphs + if glyphs then + local fore = glyphs.fore + if fore then + rule.fore = s_uncover(splitter,cache,fore) + end + local back = glyphs.back + if back then + rule.back = s_uncover(splitter,cache,back) + end + local names = glyphs.names + if names then + rule.names = s_uncover(splitter,cache,names) + end + rule.glyphs = nil + end end end end @@ -6817,144 +5940,152 @@ actions["reorganize lookups"] = function(data,filename,raw) end end +-- to be checked italic_correction + actions["analyze math"] = function(data,filename,raw) if raw.math then -data.metadata.math = raw.math - -- we move the math stuff into a math subtable because we then can - -- test faster in the tfm copy - local glyphs, udglyphs = data.glyphs, data.udglyphs - local unicodes = data.luatex.unicodes - for index, udglyph in next, udglyphs do - local mk = udglyph.mathkern - local hv = udglyph.horiz_variants - local vv = udglyph.vert_variants - if mk or hv or vv then - local glyph = glyphs[index] + 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 = { } - glyph.math = math - if mk then - for k, v in next, mk do + 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 - mk[k] = nil + 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 = mk + math.kerns = mathkerns end - if hv then - math.horiz_variants = hv.variants - local p = hv.parts - if p and #p > 0 then - for i=1,#p do - local pi = p[i] + if horiz_variants then + local variants = horiz_variants.variants + if variants then -- use splitter + local glyphs = lpegmatch(splitter,variants) + for i=1,#glyphs do + if glyphs[i] == u then + remove(glyphs,i) + break + end + end + math.horiz_variants = glyphs + end + local parts = horiz_variants.parts + if parts and #parts > 0 then + for i=1,#parts do + local pi = parts[i] pi.glyph = unicodes[pi.component] or 0 + pi.component = nil end - math.horiz_parts = p + math.horiz_parts = parts end - local ic = hv.italic_correction - if ic and ic ~= 0 then - math.horiz_italic_correction = ic + local italic_correction = horiz_variants.italic_correction + if italic_correction and italic_correction ~= 0 then + math.horiz_italic_correction = italic_correction end end - if vv then - local uc = unicodes[index] - math.vert_variants = vv.variants - local p = vv.parts - if p and #p > 0 then - for i=1,#p do - local pi = p[i] + if vert_variants then + local variants = vert_variants.variants + if variants then + local glyphs = lpegmatch(splitter,variants) + for i=1,#glyphs do + if glyphs[i] == u then + remove(glyphs,i) + break + end + end + math.vert_variants = glyphs + end + local p = vert_variants.parts + if parts and #parts > 0 then + for i=1,#parts do + local pi = parts[i] pi.glyph = unicodes[pi.component] or 0 + pi.component = nil end - math.vert_parts = p + math.vert_parts = parts end - local ic = vv.italic_correction - if ic and ic ~= 0 then - math.vert_italic_correction = ic + local italic_correction = vert_variants.italic_correction + if italic_correction and italic_correction ~= 0 then + math.vert_italic_correction = italic_correction end end - local ic = glyph.italic_correction - if ic then - if ic ~= 0 then - math.italic_correction = ic - 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 luatex = data.luatex - local udglyphs, glyphs, mapmap, unicodes = data.udglyphs, data.glyphs, luatex.indices, luatex.unicodes - local mkdone = false - local function do_it(lookup,first_unicode,extrakerns) -- can be moved inline but seldom used - local glyph = glyphs[mapmap[first_unicode]] - if glyph then - local kerns = glyph.kerns - if not kerns then - kerns = { } -- unicode indexed ! - glyph.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+%04X", first_unicode) - end - end - for index, udglyph in next, data.udglyphs do - local kerns = udglyph.kerns + 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 glyph = glyphs[index] local newkerns = { } - for k,v in next, kerns do - local vc, vo, vl = v.char, v.off, v.lookup - if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones - local uvc = unicodes[vc] - if not uvc then - if trace_loading then - report_otf("problems with unicode %s of kern %s at glyph %s",vc,k,index) - end - else - if type(vl) ~= "table" then - vl = { vl } - end - for l=1,#vl do - local vll = vl[l] - local mkl = newkerns[vll] - if not mkl then - mkl = { } - newkerns[vll] = mkl - end - if type(uvc) == "table" then - for u=1,#uvc do - mkl[uvc[u]] = vo + 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 - mkl[uvc] = vo + newkerns[lookup] = { [unicode] = offset } end end + elseif trace_loading then + report_otf("problems with unicode %s of kern %s of glyph 0x%04X",name,k,unicode) end end end - glyph.kerns = newkerns -- udglyph.kerns = nil when in mixed mode - mkdone = true + description.kerns = newkerns end end - if trace_loading and mkdone then - report_otf("replacing 'kerns' tables by a new 'kerns' tables") - end - local dgpos = raw.gpos - if dgpos then - local separator = lpeg.P(" ") - local other = ((1 - separator)^0) / unicodes - local splitter = lpeg.Ct(other * (separator * other)^0) - for gp=1,#dgpos do - local gpos = dgpos[gp] +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 @@ -6964,56 +6095,71 @@ actions["reorganize glyph kerns"] = function(data,filename,raw) local split = { } -- saves time for k=1,#kernclass do local kcl = kernclass[k] - local firsts, seconds, offsets, lookups = kcl.firsts, kcl.seconds, kcl.offsets, kcl.lookup -- singular + 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 - local maxfirsts, maxseconds = getn(firsts), getn(seconds) - -- here we could convert split into a list of unicodes which is a bit - -- faster but as this is only done when caching it does not save us much - for _, s in next, firsts do + -- 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 - for _, s in next, seconds do + 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 + for fk=1,#firsts do -- maxfirsts ? local fv = firsts[fk] local splt = split[fv] if splt then - local kerns, baseoffset = { }, (fk-1) * maxseconds - for sk=2,maxseconds do - local sv = seconds[sk] + local extrakerns = { } + local baseoffset = (fk-1) * maxseconds + -- for sk=2,maxseconds do + -- local sv = seconds[sk] + for sk, sv in next, seconds do local splt = split[sv] - if splt then + if splt then -- redundant test local offset = offsets[baseoffset + sk] if offset then for i=1,#splt do - local second_unicode = splt[i] - if tonumber(second_unicode) then - kerns[second_unicode] = offset - else for s=1,#second_unicode do - kerns[second_unicode[s]] = offset - end end + extrakerns[splt[i]] = offset end end end end for i=1,#splt do local first_unicode = splt[i] - if tonumber(first_unicode) then - do_it(lookup,first_unicode,kerns) - else for f=1,#first_unicode do - do_it(lookup,first_unicode[f],kerns) - end end + 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+%04X", first_unicode) + end end end end end end - subtable.comment = "The kernclass table is merged into kerns in the indexed glyph tables." subtable.kernclass = { } end end @@ -7023,112 +6169,37 @@ actions["reorganize glyph kerns"] = function(data,filename,raw) end actions["check glyphs"] = function(data,filename,raw) - local verbose = fonts.verbose - local int_to_uni = data.luatex.unicodes - for k, v in next, data.glyphs do - if verbose then - local code = int_to_uni[k] - -- looks like this is done twice ... bug? - if code then - local vu = v.unicode - if not vu then - v.unicode = code - elseif type(vu) == "table" then - if vu[#vu] == code then - -- weird - else - vu[#vu+1] = code - end - elseif vu ~= code then - v.unicode = { vu, code } - end - end - else - v.unicode = nil - v.index = nil - end - -- only needed on non sparse/mixed mode - if v.math then - if v.mathkern then v.mathkern = nil end - if v.horiz_variant then v.horiz_variant = nil end - if v.vert_variants then v.vert_variants = nil end - end - -- + for unicode, description in next, data.descriptions do + description.glyph = nil end - data.luatex.comment = "Glyph tables have their original index. When present, kern tables are indexed by unicode." end -actions["check metadata"] = function(data,filename,raw) - local metadata = data.metadata - metadata.method = loadmethod - if loadmethod == "sparse" then - for _, k in next, mainfields do - if valid_fields[k] then - local v = raw[k] - if global_fields[k] then - if not data[k] then - data[k] = v - end - else - if not metadata[k] then - metadata[k] = v - end - end - end - end - else - for k, v in next, raw do - if valid_fields[k] then - if global_fields[k] then - if not data[k] then - data[v] = v - end - else - if not metadata[k] then - metadata[k] = v - 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 - local pfminfo = raw.pfminfo - if pfminfo then - data.pfminfo = pfminfo - metadata.isfixedpitch = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced") - metadata.charwidth = pfminfo and pfminfo.avgwidth - 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 - metadata.xuid = nil - data.udglyphs = nil - data.map = nil end -local private_mathparameters = { - "FractionDelimiterSize", - "FractionDelimiterDisplayStyleSize", -} - -actions["check math parameters"] = function(data,filename,raw) - local mathdata = data.metadata.math - if mathdata then - for m=1,#private_mathparameters do - local pmp = private_mathparameters[m] - if not mathdata[pmp] then - if trace_loading then - report_otf("setting math parameter '%s' to 0", pmp) - end - mathdata[pmp] = 0 - 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 @@ -7136,272 +6207,239 @@ end -- 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 glyphs = data.glyphs - for index, udglyph in next, data.udglyphs do - local lookups = udglyph.lookups + 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 - local glyph = glyphs[index] - local l = { } - for kk, vv in next, lookups do - local aa = { } - l[kk] = aa - for kkk=1,#vv do - local vvv = vv[kkk] - local s = vvv.specification - local t = vvv.type - -- #aa+1 - if t == "ligature" then - aa[kkk] = { "ligature", s.components, s.char } - elseif t == "alternate" then - aa[kkk] = { "alternate", s.components } - elseif t == "substitution" then - aa[kkk] = { "substitution", s.variant } - elseif t == "multiple" then - aa[kkk] = { "multiple", s.components } - elseif t == "position" then - aa[kkk] = { "position", { s.x or 0, s.y or 0, s.h or 0, s.v or 0 } } - elseif t == "pair" then - -- maybe flatten this one - local one, two, paired = s.offsets[1], s.offsets[2], s.paired or "" + 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: %s => %s and %s",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 - aa[kkk] = { "pair", 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 } } + 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 - aa[kkk] = { "pair", paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } + lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } } end else if two then - aa[kkk] = { "pair", paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } + lookuplist[l] = { paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { } else - aa[kkk] = { "pair", paired } + lookuplist[l] = { paired } end end end end end - -- we could combine this local slookups, mlookups - for kk, vv in next, l do - if #vv == 1 then - if not slookups then - slookups = { } - glyph.slookups = slookups + for tag, lookuplist in next, lookups do + if #lookuplist == 1 then + if slookups then + slookups[tag] = lookuplist[1] + else + slookups = { [tag] = lookuplist[1] } end - slookups[kk] = vv[1] else - if not mlookups then - mlookups = { } - glyph.mlookups = mlookups + if mlookups then + mlookups[tag] = lookuplist + else + mlookups = { [tag] = lookuplist } end - mlookups[kk] = vv end end - glyph.lookups = nil -- when using table + 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) - local glyphs = data.glyphs - for index, udglyph in next, data.udglyphs do - local anchors = udglyph.anchors +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 - local glyph = glyphs[index] - local a = { } - glyph.anchors = a - for kk, vv in next, anchors do - local aa = { } - a[kk] = aa - for kkk, vvv in next, vv do - if vvv.x or vvv.y then - aa[kkk] = { vvv.x , vvv.y } - else - local aaa = { } - aa[kkk] = aaa - for kkkk=1,#vvv do - local vvvv = vvv[kkkk] - aaa[kkkk] = { vvvv.x, vvvv.y } + 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 ---~ actions["check extra features"] = function(data,filename,raw) ---~ -- later, ctx only ---~ end - --- -- -- -- -- -- --- -- -- -- -- -- - -function features.register(name,default,description) - featurelist[#featurelist+1] = name - defaultfeatures[name] = default - if description and description ~= "" then - fonts.otf.tables.features[name] = description - end -end - --- for context this will become a task handler - -local lists = { -- why local - fonts.triggers, - fonts.processors, - fonts.manipulators, -} +-- modes: node, base, none function otf.setfeatures(tfmdata,features) - local processes = { } - if features and next(features) then - local mode = tfmdata.mode or features.mode or "base" - local initializers = fonts.initializers - local fi = initializers[mode] - if fi then - local fiotf = fi.otf - if fiotf then - local done = { } - for l=1,#lists do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - local value = features[f] - if value and fiotf[f] then -- brr - if not done[f] then -- so, we can move some to triggers - if trace_features then - report_otf("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown', tfmdata.fullname or 'unknown') - end - fiotf[f](tfmdata,value) -- can set mode (no need to pass otf) - mode = tfmdata.mode or features.mode or "base" - local im = initializers[mode] - if im then - fiotf = initializers[mode].otf - end - done[f] = true - end - end - end - end - end - end - end -tfmdata.mode = mode - local fm = fonts.methods[mode] -- todo: zonder node/mode otf/... - if fm then - local fmotf = fm.otf - if fmotf then - for l=1,#lists do - local list = lists[l] - if list then - for i=1,#list do - local f = list[i] - if fmotf[f] then -- brr - if trace_features then - report_otf("installing feature handler %s for mode %s for font %s",f,mode or 'unknown', tfmdata.fullname or 'unknown') - end - processes[#processes+1] = fmotf[f] - end - end - end - end - end - else - -- message - end + 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 - return processes, features 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 +-- 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) -- we can save a copy when we reorder the tma to unicode (nasty due to one->many) +local function copytotfm(data,cache_id) if data then - local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } - local luatex = data.luatex - local unicodes = luatex.unicodes -- names to unicodes - local indices = luatex.indices - local mode = data.mode or "base" - local characters, parameters, mathparameters, descriptions = { }, { }, { }, { } - local designsize = metadata.designsize or metadata.design_size or 100 + local metadata = data.metadata + local resources = data.resources + local properties = table.derive(data.properties) + local descriptions = table.derive(data.descriptions) + local goodies = table.derive(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 - local spaceunits, spacer = 500, "space" - -- indices maps from unicodes to indices - -- this wil stay as we can manipulate indices - -- beforehand - for u, i in next, indices do - characters[u] = { } -- we need this because for instance we add protruding info and loop over characters - descriptions[u] = glyphs[i] - end - -- math - if metadata.math then - -- parameters - for name, value in next, metadata.math do + if mathspecs then + for name, value in next, mathspecs do mathparameters[name] = value end - -- we could use a subset - for u, char in next, characters do - local d = descriptions[u] + 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 - -- we have them shared because that packs nicer - -- we could prepare the variants and keep 'm in descriptions if m then - local variants, parts, c = m.horiz_variants, m.horiz_parts, char + -- watch out: luatex uses horiz_variants for the parts + local variants, parts = m.horiz_variants, m.horiz_parts if variants then - for n in gmatch(variants,"[^ ]+") do - local un = unicodes[n] - if un and u ~= un then - c.next = un - c = characters[un] - end - end + local c = character + for i=1,#variants do + local un = variants[i] + c.next = un + c = characters[un] + end -- c is now last in chain c.horiz_variants = parts elseif parts then - c.horiz_variants = parts + character.horiz_variants = parts end - local variants, parts, c = m.vert_variants, m.vert_parts, char + local variants, parts = m.vert_variants, m.vert_parts if variants then - for n in gmatch(variants,"[^ ]+") do - local un = unicodes[n] - if un and u ~= un then - c.next = un - c = characters[un] - end + local c = character + for i=1,#variants do + local un = variants[i] + c.next = un + c = characters[un] end -- c is now last in chain c.vert_variants = parts elseif parts then - c.vert_variants = parts + character.vert_variants = parts end local italic_correction = m.vert_italic_correction if italic_correction then - c.vert_italic_correction = italic_correction + 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 - char.mathkerns = kerns + character.mathkerns = kerns end end end end -- end math - local space, emdash = 0x20, 0x2014 -- unicodes['space'], unicodes['emdash'] - if metadata.isfixedpitch then + 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 metadata.charwidth then - spaceunits, spacer = metadata.charwidth, "charwidth" + if not spaceunits and charwidth then + spaceunits, spacer = charwidth, "charwidth" end else if descriptions[space] then @@ -7410,20 +6448,17 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th if not spaceunits and descriptions[emdash] then spaceunits, spacer = descriptions[emdash].width/2, "emdash/2" end - if not spaceunits and metadata.charwidth then - spaceunits, spacer = metadata.charwidth, "charwidth" + 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 = fonts.tfm.checkedfilename(luatex) + local filename = constructors.checkedfilename(resources) local fontname = metadata.fontname local fullname = metadata.fullname or fontname - local cidinfo = data.cidinfo -- or { } local units = metadata.units_per_em or 1000 -- - cidinfo.registry = cidinfo and cidinfo.registry or "" -- weird here, fix upstream - -- parameters.slant = 0 parameters.space = spaceunits -- 3.333 (cmr10) parameters.space_stretch = units/2 -- 500 -- 1.666 (cmr10) @@ -7433,11 +6468,12 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th if spaceunits < 2*units/5 then -- todo: warning end - local italicangle = metadata.italicangle - if italicangle then -- maybe also in afm _ - parameters.slant = parameters.slant - math.round(math.tan(italicangle*math.pi/180)) + 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 metadata.isfixedpitch then + if monospaced then parameters.space_stretch = 0 parameters.space_shrink = 0 elseif syncspace then -- @@ -7445,8 +6481,8 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th parameters.space_shrink = spaceunits/3 end parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10) - if pfminfo.os2_xheight and pfminfo.os2_xheight > 0 then - parameters.x_height = pfminfo.os2_xheight + if charxheight then + parameters.x_height = charxheight else local x = 0x78 -- unicodes['x'] if x then @@ -7457,150 +6493,109 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th end end -- - local fileformat = data.format or fonts.fontformat(filename,"opentype") - if units > 1000 then - fileformat = "truetype" - 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, - parameters = parameters, - mathparameters = mathparameters, - descriptions = descriptions, - indices = indices, - unicodes = unicodes, - type = "real", - direction = 0, - boundarychar_label = 0, - boundarychar = 65536, - designsize = (designsize/10)*65536, - encodingbytes = 2, - mode = mode, - filename = filename, - fontname = fontname, - fullname = fullname, - psname = fontname or fullname, - name = filename or fullname, - units = units, - format = fileformat, - cidinfo = cidinfo, - ascender = abs(metadata.ascent or 0), - descender = abs(metadata.descent or 0), - spacer = spacer, - italicangle = italicangle, + characters = characters, + descriptions = descriptions, + parameters = parameters, + mathparameters = mathparameters, + resources = resources, + properties = properties, + goodies = goodies, } - else - return nil end end local function otftotfm(specification) - local name = specification.name - local sub = specification.sub - local filename = specification.filename - local format = specification.format - local features = specification.features.normal local cache_id = specification.hash - local tfmdata = containers.read(tfm.cache,cache_id) ---~ print(cache_id) + local tfmdata = containers.read(constructors.cache,cache_id) if not tfmdata then - local otfdata = otf.load(filename,format,sub,features and features.featurefile) - if otfdata and next(otfdata) then - otfdata.shared = otfdata.shared or { - featuredata = { }, - anchorhash = { }, - initialized = false, - } - tfmdata = copytotfm(otfdata,cache_id) + 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 - tfmdata.unique = tfmdata.unique or { } - tfmdata.shared = tfmdata.shared or { } -- combine - local shared = tfmdata.shared - shared.otfdata = otfdata - shared.features = features -- default - shared.dynamics = { } - shared.processes = { } - shared.setdynamics = otf.setdynamics -- fast access and makes other modules independent - -- this will be done later anyway, but it's convenient to have - -- them already for fast access - tfmdata.luatex = otfdata.luatex - tfmdata.indices = otfdata.luatex.indices - tfmdata.unicodes = otfdata.luatex.unicodes - tfmdata.marks = otfdata.luatex.marks - tfmdata.originals = otfdata.luatex.originals - tfmdata.changed = { } - tfmdata.has_italic = otfdata.metadata.has_italic - if not tfmdata.language then tfmdata.language = 'dflt' end - if not tfmdata.script then tfmdata.script = 'dflt' end - -- at this moment no characters are assinged yet, only empty slots - shared.processes, shared.features = otf.setfeatures(tfmdata,definers.check(features,defaultfeatures)) - end - end - containers.write(tfm.cache,cache_id,tfmdata) + -- 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 -features.register('mathsize') - -local function read_from_otf(specification) -- wrong namespace - local tfmtable = otftotfm(specification) - if tfmtable then - local otfdata = tfmtable.shared.otfdata - tfmtable.name = specification.name - tfmtable.sub = specification.sub - local s = specification.size - local m = otfdata.metadata.math - if m then - -- this will move to a function - local f = specification.features - if f then - local f = f.normal - if f and f.mathsize then - local mathsize = specification.mathsize or 0 - if mathsize == 2 then - local p = m.ScriptPercentScaleDown - if p then - local ps = p * specification.textsize / 100 - if trace_math then - report_otf("asked script size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s = ps - end - elseif mathsize == 3 then - local p = m.ScriptScriptPercentScaleDown - if p then - local ps = p * specification.textsize / 100 - if trace_math then - report_otf("asked scriptscript size: %s, used: %s (%2.2f %%)",s,ps,(ps/s)*100) - end - s = ps - end - end - end - end - end - tfmtable = tfm.scale(tfmtable,s,specification.relativeid) - if tfm.fontnamemode == "specification" then - -- not to be used in context ! - local specname = specification.specification - if specname then - tfmtable.name = specname - if trace_defining then - report_otf("overloaded fontname: '%s'",specname) - end - end - end - fonts.logger.save(tfmtable,file.extname(specification.filename),specification) +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) + constructors.applymanipulators("otf",tfmdata,specification.features.normal,trace_features,report_otf) + constructors.setname(tfmdata,specification) -- only otf? + fonts.loggers.register(tfmdata,file.extname(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 ---~ print(tfmtable.fullname) - return tfmtable end +registerotffeature { + name = "mathsize", + description = "apply mathsize as specified in the font", + initializers = { + base = checkmathsize, + node = checkmathsize, + } +} + -- helpers -function otf.collectlookups(otfdata,kind,script,language) - -- maybe store this in the font - local sequences = otfdata.luatex.sequences +function otf.collectlookups(rawdata,kind,script,language) + local sequences = rawdata.resources.sequences if sequences then local featuremap, featurelist = { }, { } for s=1,#sequences do @@ -7631,42 +6626,23 @@ end -- readers -fonts.formats.dfont = "truetype" -fonts.formats.ttc = "truetype" -fonts.formats.ttf = "truetype" -fonts.formats.otf = "opentype" - local function check_otf(forced,specification,suffix,what) local name = specification.name if forced then name = file.addsuffix(name,suffix,true) end - local fullname, tfmtable = findbinfile(name,suffix) or "", nil -- one shot - -- if false then -- can be enabled again when needed - -- if fullname == "" then - -- local fb = fonts.names.old_to_new[name] - -- if fb then - -- fullname = findbinfile(fb,suffix) or "" - -- end - -- end - -- if fullname == "" then - -- local fb = fonts.names.new_to_old[name] - -- if fb then - -- fullname = findbinfile(fb,suffix) or "" - -- end - -- end - -- end + local fullname, tfmdata = findbinfile(name,suffix) or "", nil -- one shot if fullname == "" then fullname = fonts.names.getfilename(name,suffix) end if fullname ~= "" then specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then - tfmtable = read_from_otf(specification) -- we need to do it for all matches / todo + tfmdata = read_from_otf(specification) -- we need to do it for all matches / todo end - return tfmtable + return tfmdata end -function readers.opentype(specification,suffix,what) +local function opentypereader(specification,suffix,what) local forced = specification.forced or "" if forced == "otf" then return check_otf(true,specification,forced,"opentype") @@ -7677,561 +6653,1071 @@ function readers.opentype(specification,suffix,what) end end -function readers.otf (specification) return readers.opentype(specification,"otf","opentype") end -function readers.ttf (specification) return readers.opentype(specification,"ttf","truetype") end -function readers.ttc (specification) return readers.opentype(specification,"ttf","truetype") end -- !! -function readers.dfont(specification) return readers.opentype(specification,"ttf","truetype") 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 end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['font-otd'] = { +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_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end) - -local report_otf = logs.reporter("fonts","otf loading") - -local fonts = fonts -local otf = fonts.otf -local fontdata = fonts.identifiers +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_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) -otf.features = otf.features or { } -otf.features.default = otf.features.default or { } +local report_prepare = logs.reporter("fonts","otf prepare") -local definers = fonts.definers -local contextsetups = definers.specifiers.contextsetups -local contextnumbers = definers.specifiers.contextnumbers +local fonts = fonts +local otf = fonts.handlers.otf --- todo: dynamics namespace +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -local a_to_script = { } -local a_to_language = { } +local wildcard = "*" +local default = "dflt" -function otf.setdynamics(font,dynamics,attribute) - local features = contextsetups[contextnumbers[attribute]] -- can be moved to caller - if features then - local script = features.script or 'dflt' - local language = features.language or 'dflt' - local ds = dynamics[script] - 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 dsla then - -- if trace_dynamics then - -- report_otf("using dynamics %s: attribute %s, script %s, language %s",contextnumbers[attribute],attribute,script,language) - -- end - return dsla +local function gref(descriptions,n) + if type(n) == "number" then + local name = descriptions[n].name + if name then + return format("U+%04X (%s)",n,name) else - local tfmdata = fontdata[font] - a_to_script [attribute] = script - a_to_language[attribute] = language - -- we need to save some values - local saved = { - script = tfmdata.script, - language = tfmdata.language, - mode = tfmdata.mode, - features = tfmdata.shared.features - } - tfmdata.mode = "node" - tfmdata.dynamics = true -- handy for tracing - tfmdata.language = language - tfmdata.script = script - tfmdata.shared.features = { } - -- end of save - local set = definers.check(features,otf.features.default) - dsla = otf.setfeatures(tfmdata,set) - if trace_dynamics then - report_otf("setting dynamics %s: attribute %s, script %s, language %s, set: %s",contextnumbers[attribute],attribute,script,language,table.sequenced(set)) - end - -- we need to restore some values - tfmdata.script = saved.script - tfmdata.language = saved.language - tfmdata.mode = saved.mode - tfmdata.shared.features = saved.features - -- end of restore - dynamics[script][language][attribute] = dsla -- cache - return dsla - end - end - return nil -- { } + return format("U+%04X") + end + elseif n then + local num, nam = { }, { } + for i=2,#n do -- first is likely a key + local ni = n[i] + num[i] = format("U+%04X",ni) + nam[i] = descriptions[ni].name or "?" + end + return format("%s (%s)",concat(num," "), concat(nam," ")) + else + return "?" + end end -function otf.scriptandlanguage(tfmdata,attr) - if attr and attr > 0 then - return a_to_script[attr] or tfmdata.script, a_to_language[attr] or tfmdata.language +local function cref(feature,lookupname) + if lookupname then + return format("feature %s, lookup %s",feature,lookupname) else - return tfmdata.script, tfmdata.language + return format("feature %s",feature) end end -end -- closure +local basemethods = { } +local basemethod = "" -do -- begin closure to overcome local limits and interference +local function applybasemethod(what,...) + local m = basemethods[basemethod][what] + if m then + return m(...) + end +end -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" +-- 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: '%s', featureset '%s'",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 + print("BUILDING",concat(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 + print(" DEFINING",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 + print("CODES",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 actions = { + substitution = function(lookupdata,lookupname,description,unicode) + if trace_baseinit and trace_singles then + report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), + gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = lookupdata + end, + alternate = function(lookupdata,lookupname,description,unicode) + local replacement = lookupdata[value] or lookupdata[#lookupdata] + if trace_baseinit and trace_alternatives then + report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = replacement + end, + ligature = function(lookupdata,lookupname,description,unicode) + if trace_baseinit and trace_alternatives then + report_prepare("%s: base ligature %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,lookupdata),gref(descriptions,unicode)) + 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_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,otherunicode),value) + end + elseif not newkerns[otherunicode] then -- first wins + newkerns[otherunicode] = value + done = true + if traceindeed then + report_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,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 lower = string.lower +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 fonts = fonts +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 %s: 0x%04X (%s), preceding 0x%04X (%s)",lookupname,v,utfchar(v),preceding,utfchar(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 = { } + + 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_baseinit and trace_singles then + report_prepare("%s: base substitution %s => %s",cref(feature,lookupname), + gref(descriptions,unicode),gref(descriptions,data)) + end + changed[unicode] = data + elseif lookuptype == "alternate" then + local replacement = data[value] or data[#data] + if trace_baseinit and trace_alternatives then + report_prepare("%s: base alternate %s %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,unicode),gref(descriptions,replacement)) + end + changed[unicode] = replacement + elseif lookuptype == "ligature" then + ligatures[#ligatures+1] = { unicode, data, lookupname } + end + end + end + + local nofligatures = #ligatures + + if nofligatures > 0 then + + local characters = tfmdata.characters + local present = { } + local done = trace_baseinit and trace_ligatures and { } -local otf = fonts.otf -local initializers = fonts.initializers + for i=1,nofligatures do + local ligature = ligatures[i] + local unicode, tree = ligature[1], ligature[2] + make_1(present,tree,"ctx_"..unicode) + end -local languages = otf.tables.languages -local scripts = otf.tables.scripts + 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 -local function set_language(tfmdata,value) - if value then - value = lower(value) - if languages[value] then - tfmdata.language = value + if done then + for lookupname, list in next, done do + report_prepare("%s: base ligatures %s => %s",cref(feature,lookupname), + tostring(value),gref(descriptions,done)) + end end + end + end -local function set_script(tfmdata,value) - if value then - value = lower(value) - if scripts[value] then - tfmdata.script = value +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_prepare("%s: base kern %s + %s => %s",cref(feature,lookup), + gref(descriptions,unicode),gref(descriptions,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 set_mode(tfmdata,value) - if value then - tfmdata.mode = lower(value) - end +local function initializehashes(tfmdata) + nodeinitializers.features(tfmdata) end -local base_initializers = initializers.base.otf -local node_initializers = initializers.node.otf +basemethods.shared = { + initializehashes = initializehashes, + preparesubstitutions = preparesubstitutions, + preparepositionings = preparepositionings, +} + +basemethod = "independent" -base_initializers.language = set_language -base_initializers.script = set_script -base_initializers.mode = set_mode -base_initializers.method = set_mode +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 basepositions then + for feature, data in next, basepositions 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 %s",os.clock()-t,tfmdata.properties.fullname or "?") + end + end +end -node_initializers.language = set_language -node_initializers.script = set_script -node_initializers.mode = set_mode -node_initializers.method = set_mode +registerotffeature { + name = "features", + description = "features", + default = true, + initializers = { + position = 1, + base = featuresinitializer, + } +} -otf.features.register("features",true) -- we always do features -table.insert(fonts.processors,"features") -- we need a proper function for doing this +-- independent : collect lookups independently (takes more runtime ... neglectable) +-- shared : shares lookups with node mode (takes more memory ... noticeable) +directives.register("fonts.otf.loader.basemethod", function(v) + if basemethods[v] then + basemethod = v + end +end) end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['font-otb'] = { +if not modules then modules = { } end modules ['node-inj'] = { version = 1.001, - comment = "companion to font-ini.mkiv", + 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 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 +-- 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. -local fonts = fonts -local otf = fonts.otf -local tfm = fonts.tfm +local next = next -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_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 trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) -local report_prepare = logs.reporter("fonts","otf prepare") +local report_injections = logs.reporter("nodes","injections") -local wildcard = "*" -local default = "dflt" +local attributes, nodes, node = attributes, nodes, node -local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway +fonts = fonts +local fontdata = fonts.hashes.identifiers -local pcache, fcache = { }, { } -- could be weak +nodes.injections = nodes.injections or { } +local injections = nodes.injections -local function gref(descriptions,n) - if type(n) == "number" then - local name = descriptions[n].name - if name then - return format("U+%04X (%s)",n,name) +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local nodepool = nodes.pool +local newkern = nodepool.kern + +local traverse_id = node.traverse_id +local unset_attribute = node.unset_attribute +local has_attribute = node.has_attribute +local set_attribute = node.set_attribute +local insert_node_before = node.insert_before +local insert_node_after = node.insert_after + +local markbase = attributes.private('markbase') +local markmark = attributes.private('markmark') +local markdone = attributes.private('markdone') +local cursbase = attributes.private('cursbase') +local curscurs = attributes.private('curscurs') +local cursdone = attributes.private('cursdone') +local kernpair = attributes.private('kernpair') + +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 + +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 + set_attribute(start,cursbase,bound) + set_attribute(nxt,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 = has_attribute(current,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 - return format("U+%04X") - end - elseif n then - local num, nam = { }, { } - for i=1,#n do - local ni = n[i] - -- ! ! ! could be a helper ! ! ! - if type(ni) == "table" then - local nnum, nnam = { }, { } - for j=1,#ni do - local nj = ni[j] - nnum[j] = format("U+%04X",nj) - nnam[j] = descriptions[nj].name or "?" - end - num[i] = concat(nnum,"|") - nam[i] = concat(nnam,"|") - else - num[i] = format("U+%04X",ni) - nam[i] = descriptions[ni].name or "?" - end + bound = #kerns + 1 + set_attribute(current,kernpair,bound) + kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } end - return format("%s (%s)",concat(num," "), concat(nam," ")) - else - return "?" + return x, y, w, h, bound end + return x, y, w, h -- no bound end -local function cref(kind,lookupname) - if lookupname then - return format("feature %s, lookup %s",kind,lookupname) +function injections.setkern(current,factor,rlmode,x,tfmchr) + local dx = factor*x + if dx ~= 0 then + local bound = #kerns + 1 + set_attribute(current,kernpair,bound) + kerns[bound] = { rlmode, dx } + return dx, bound else - return format("feature %s",kind) + return 0, 0 end end -local function resolve_ligatures(tfmdata,ligatures,kind) - kind = kind or "unknown" - local unicodes = tfmdata.unicodes - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local changed = tfmdata.changed - local done = { } - while true do - local ok = false - for k,v in next, ligatures do - local lig = v[1] - if not done[lig] then - local ligs = lpegmatch(split_at_space,lig) - if #ligs == 2 then - local uc = v[2] - local c, f, s = characters[uc], ligs[1], ligs[2] - local uft, ust = unicodes[f] or 0, unicodes[s] or 0 - if not uft or not ust then - report_prepare("%s: unicode problem with base ligature %s = %s + %s",cref(kind),gref(descriptions,uc),gref(descriptions,uft),gref(descriptions,ust)) - -- some kind of error +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]) + local bound = has_attribute(base,markbase) + if bound then + local mb = marks[bound] + if mb then + if not index then index = #mb + 1 end + mb[index] = { dx, dy } + set_attribute(start,markmark,bound) + set_attribute(start,markdone,index) + return dx, dy, bound + else + report_injections("possible problem, U+%04X is base mark without data (id: %s)",base.char,bound) + end + end + index = index or 1 + bound = #marks + 1 + set_attribute(base,markbase,bound) + set_attribute(start,markmark,bound) + set_attribute(start,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 = has_attribute(n,kernpair) + local mb = has_attribute(n,markbase) + local mm = has_attribute(n,markmark) + local md = has_attribute(n,markdone) + local cb = has_attribute(n,cursbase) + local cc = has_attribute(n,curscurs) + report_injections("char U+%05X, font=%s",n.char,n.font) + if kp then + local k = kerns[kp] + if k[3] then + report_injections(" pairkern: dir=%s, x=%s, y=%s, w=%s, h=%s",dir(k[1]),k[2] or "?",k[3] or "?",k[4] or "?",k[5] or "?") + else + report_injections(" kern: dir=%s, dx=%s",dir(k[1]),k[2] or "?") + end + end + if mb then + report_injections(" markbase: bound=%s",mb) + end + if mm then + local m = marks[mm] + if mb then + local m = m[mb] + if m then + report_injections(" markmark: bound=%s, index=%s, dx=%s, dy=%s",mm,md or "?",m[1] or "?",m[2] or "?") else - if type(uft) == "number" then uft = { uft } end - if type(ust) == "number" then ust = { ust } end - for ufi=1,#uft do - local uf = uft[ufi] - for usi=1,#ust do - local us = ust[usi] - if changed[uf] or changed[us] then - if trace_baseinit and trace_ligatures then - report_prepare("%s: base ligature %s + %s ignored",cref(kind),gref(descriptions,uf),gref(descriptions,us)) - end - else - local first, second = characters[uf], us - if first and second then - local t = first.ligatures - if not t then - t = { } - first.ligatures = t - end - if type(uc) == "number" then - t[second] = { type = 0, char = uc } - else - t[second] = { type = 0, char = uc[1] } -- can this still happen? - end - if trace_baseinit and trace_ligatures then - report_prepare("%s: base ligature %s + %s => %s",cref(kind),gref(descriptions,uf),gref(descriptions,us),gref(descriptions,uc)) - end - end - end + report_injections(" markmark: bound=%s, missing index",mm) + end + else + m = m[1] + report_injections(" markmark: bound=%s, dx=%s, dy=%s",mm,m[1] or "?",m[2] or "?") + end + end + if cb then + report_injections(" cursbase: bound=%s",cb) + end + if cc then + local c = cursives[cc] + report_injections(" curscurs: bound=%s, dir=%s, dx=%s, dy=%s",cc,dir(c[1]),c[2] or "?",c[3] or "?") + 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 + +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 = has_attribute(n,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 - ok, done[lig] = true, descriptions[uc].name end end - end - if ok then - -- done has "a b c" = "a_b_c" and ligatures the already set ligatures: "a b" = 123 - -- and here we add extras (f i i = fi + i and alike) - -- - -- we could use a hash for fnc and pattern - -- - -- this might be interfering ! - for d,n in next, done do - local pattern = pcache[d] if not pattern then pattern = "^(" .. d .. ") " pcache[d] = pattern end - local fnc = fcache[n] if not fnc then fnc = function() return n .. " " end fcache[n] = fnc end - for k,v in next, ligatures do - v[1] = gsub(v[1],pattern,fnc) + 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 - else - break end - end -end - -local splitter = lpeg.splitat(" ") - -local function prepare_base_substitutions(tfmdata,kind,value) -- we can share some code with the node features - if value then - local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local ligatures = { } - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local changed = tfmdata.changed - -- - local actions = { - substitution = function(p,lookup,k,glyph,unicode) - local pv = p[2] -- p.variant - if pv then - local upv = unicodes[pv] - if upv then - if type(upv) == "table" then -- zero change that table - upv = upv[1] - end - if characters[upv] then - if trace_baseinit and trace_singles then - report_prepare("%s: base substitution %s => %s",cref(kind,lookup),gref(descriptions,k),gref(descriptions,upv)) - end - changed[k] = upv - end - end - end - end, - alternate = function(p,lookup,k,glyph,unicode) - local pc = p[2] -- p.components - if pc then - -- a bit optimized ugliness - if value == 1 then - pc = lpegmatch(splitter,pc) - elseif value == 2 then - local a, b = lpegmatch(splitter,pc) - pc = b or a - else - pc = { lpegmatch(splitter,pc) } - pc = pc[value] or pc[#pc] - end - if pc then - local upc = unicodes[pc] - if upc then - if type(upc) == "table" then -- zero change that table - upc = upc[1] - end - if characters[upc] then - if trace_baseinit and trace_alternatives then - report_prepare("%s: base alternate %s %s => %s",cref(kind,lookup),tostring(value),gref(descriptions,k),gref(descriptions,upc)) + 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 = has_attribute(n,cursbase) + if p_cursbase then + local n_curscurs = has_attribute(n,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 - changed[k] = upc + -- 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 - end - end, - ligature = function(p,lookup,k,glyph,unicode) - local pc = p[2] - if pc then - if trace_baseinit and trace_ligatures then - local upc = { lpegmatch(splitter,pc) } - for i=1,#upc do upc[i] = unicodes[upc[i]] end - -- we assume that it's no table - report_prepare("%s: base ligature %s => %s",cref(kind,lookup),gref(descriptions,upc),gref(descriptions,k)) - end - ligatures[#ligatures+1] = { pc, k } - end - end, - } - -- - for k,c in next, characters do - local glyph = descriptions[k] - local lookups = glyph.slookups - if lookups then - for l=1,#lookuplist do - local lookup = lookuplist[l] - local p = lookups[lookup] - if p then - local a = actions[p[1]] - if a then - a(p,lookup,k,glyph,unicode) + 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 - local lookups = glyph.mlookups - if lookups then - for l=1,#lookuplist do - local lookup = lookuplist[l] - local ps = lookups[lookup] - if ps then - for i=1,#ps do - local p = ps[i] - local a = actions[p[1]] - if a then - a(p,lookup,k,glyph,unicode) - end - 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 - resolve_ligatures(tfmdata,ligatures,kind) - end - else - tfmdata.ligatures = tfmdata.ligatures or { } -- left over from what ? - end -end - -local function preparebasekerns(tfmdata,kind,value) -- todo what kind of kerns, currently all - if value then - local otfdata = tfmdata.shared.otfdata - local validlookups, lookuplist = otf.collectlookups(otfdata,kind,tfmdata.script,tfmdata.language) - if validlookups then - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local characters = tfmdata.characters - local descriptions = tfmdata.descriptions - local sharedkerns = { } - for u, chr in next, characters do - local d = descriptions[u] - if d then - local dk = d.kerns -- shared - if dk then - local s = sharedkerns[dk] - if s == false then - -- skip - elseif s then - chr.kerns = s - else - local t, done = chr.kerns or { }, false - for l=1,#lookuplist do - local lookup = lookuplist[l] - local kerns = dk[lookup] - if kerns then - for k, v in next, kerns do - if v ~= 0 and not t[k] then -- maybe no 0 test here - t[k], done = v, true - if trace_baseinit and trace_kerns then - report_prepare("%s: base kern %s + %s => %s",cref(kind,lookup),gref(descriptions,u),gref(descriptions,k),v) - end + if has_marks then + for i=1,nofvalid do + local p = valid[i] + local p_markbase = has_attribute(p,markbase) + if p_markbase then + local mrks = marks[p_markbase] + for n in traverse_id(glyph_code,p.next) do + local n_markmark = has_attribute(n,markmark) + if p_markbase == n_markmark then + local index = has_attribute(n,markdone) or 1 + local d = mrks[index] + if d then + local rlmode = d[3] + if rlmode and rlmode > 0 then + -- new per 2010-10-06, width adapted per 2010-02-03 + -- we used to negate the width of marks because in tfm + -- that makes sense but we no longer do that so as a + -- consequence the sign of p.width was changed (we need + -- to keep an eye on it as we don't have that many fonts + -- that enter this branch .. i'm still not sure if this + -- one is right + local k = wx[p] + if k then + n.xoffset = p.xoffset + p.width + d[1] - k[2] + else + n.xoffset = p.xoffset + p.width + d[1] + end + else + local k = wx[p] + if k then + n.xoffset = p.xoffset - d[1] - k[2] + 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 end - end - if done then - sharedkerns[dk] = t - chr.kerns = t -- no empty assignments else - sharedkerns[dk] = false + break end end end end + if not keep then + marks = { } + end end - end - end -end - --- In principle we could register each feature individually which was --- what we did in earlier versions. However, after the rewrite it --- made more sense to collect them in an overall features initializer --- just as with the node variant. There it was needed because we need --- to do complete mixed runs and not run featurewise (as we did before). - -local supported_gsub = { - 'liga', 'dlig', 'rlig', 'hlig', - 'pnum', 'onum', 'tnum', 'lnum', - 'zero', - 'smcp', 'cpsp', 'c2sc', 'ornm', 'aalt', - 'hwid', 'fwid', - 'ssty', 'rtlm', -- math --- 'tlig', 'trep', -} - -local supported_gpos = { - 'kern' -} - -function otf.features.registerbasesubstitution(tag) - supported_gsub[#supported_gsub+1] = tag -end -function otf.features.registerbasekern(tag) - supported_gsub[#supported_gpos+1] = tag -end - -local basehash, basehashes = { }, 1 - -function fonts.initializers.base.otf.features(tfmdata,value) - if true then -- value then - -- not shared - local t = trace_preparing and os.clock() - local features = tfmdata.shared.features - if features then - local h = { } - for f=1,#supported_gsub do - local feature = supported_gsub[f] - local value = features[feature] - prepare_base_substitutions(tfmdata,feature,value) - if value then - h[#h+1] = feature .. "=" .. tostring(value) + -- todo : combine + if next(wx) then + for n, k in next, wx do + -- only w can be nil, can be sped up when w == nil + local rl, x, w, r2l = k[1], k[2] or 0, k[4] or 0, k[6] + local wx = w - x + if r2l then + 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 end end - for f=1,#supported_gpos do - local feature = supported_gpos[f] - local value = features[feature] - preparebasekerns(tfmdata,feature,features[feature]) - if value then - h[#h+1] = feature .. "=" .. tostring(value) + 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)) + else + insert_node_before(head,n,newkern(k)) + end + end + end + end + if not keep then + kerns = { } + 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 = has_attribute(n,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 r2l then + 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 - local hash = concat(h," ") - local base = basehash[hash] - if not base then - basehashes = basehashes + 1 - base = basehashes - basehash[hash] = base - 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. - tfmdata.fullname = tfmdata.fullname .. "-" .. base -- tfmdata.psname is the original - --~ report_prepare("fullname base hash: '%s', featureset '%s'",tfmdata.fullname,hash) end - if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + if not keep then + kerns = { } end + return head, true + else + -- no tracing needed end + return head, false end end -- closure @@ -8250,14 +7736,6 @@ if not modules then modules = { } end modules ['font-otn'] = { -- much functionality could only be implemented thanks to the husayni font -- of Idris Samawi Hamid to who we dedicate this module. --- I'm in the process of cleaning up the code (which happens in another --- file) so don't rely on things staying the same. - --- some day when we can jit this, we can use more functions - --- we can use more lpegs when lpeg is extended with function args and so --- resolving to unicode does not gain much - -- in retrospect it always looks easy but believe it or not, it took a lot -- of work to get proper open type support done: buggy fonts, fuzzy specs, -- special made testfonts, many skype sessions between taco, idris and me, @@ -8272,10 +7750,7 @@ if not modules then modules = { } end modules ['font-otn'] = { -- alternative loop quitters -- check cursive and r2l -- find out where ignore-mark-classes went --- remove unused tables --- slide tail (always glue at the end so only needed once -- default features (per language, script) --- cleanup kern(class) code, remove double info -- handle positions (we need example fonts) -- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) @@ -8351,6 +7826,8 @@ results in different tables.

-- gpos_context ok -- -- gpos_contextchain ok -- -- +-- todo: contextpos and contextsub and class stuff +-- -- actions: -- -- handler : actions triggered by lookup @@ -8360,16 +7837,20 @@ results in different tables.

-- remark: the 'not implemented yet' variants will be done when we have fonts that use them -- remark: we need to check what to do with discretionaries +-- We used to have independent hashes for lookups but as the tags are unique +-- we now use only one hash. If needed we can have multiple again but in that +-- case I will probably prefix (i.e. rename) the lookups in the cached font file. + local concat, insert, remove = table.concat, table.insert, table.remove 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 random = math.random -local logs, trackers, fonts, nodes, attributes = logs, trackers, fonts, nodes, attributes +local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes -local otf = fonts.otf -local tfm = fonts.tfm +local fonts = fonts +local otf = fonts.handlers.otf local trace_lookups = false trackers.register("otf.lookups", function(v) trace_lookups = v end) local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end) @@ -8404,93 +7885,82 @@ trackers.register("otf.injections","nodes.injections") trackers.register("*otf.sample","otf.steps,otf.actions,otf.analyzing") -local insert_node_after = node.insert_after -local delete_node = nodes.delete -local copy_node = node.copy -local find_node_tail = node.tail or node.slide -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute +local insert_node_after = node.insert_after +local delete_node = nodes.delete +local copy_node = node.copy +local find_node_tail = node.tail or node.slide +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute -local zwnj = 0x200C -local zwj = 0x200D -local wildcard = "*" -local default = "dflt" +local zwnj = 0x200C +local zwj = 0x200D +local wildcard = "*" +local default = "dflt" -local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway +local nodecodes = nodes.nodecodes +local whatcodes = nodes.whatcodes +local glyphcodes = nodes.glyphcodes -local nodecodes = nodes.nodecodes -local whatcodes = nodes.whatcodes -local glyphcodes = nodes.glyphcodes +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue +local disc_code = nodecodes.disc +local whatsit_code = nodecodes.whatsit -local glyph_code = nodecodes.glyph -local glue_code = nodecodes.glue -local disc_code = nodecodes.disc -local whatsit_code = nodecodes.whatsit +local dir_code = whatcodes.dir +local localpar_code = whatcodes.localpar -local dir_code = whatcodes.dir -local localpar_code = whatcodes.localpar +local ligature_code = glyphcodes.ligature -local ligature_code = glyphcodes.ligature +local privateattribute = attributes.private -local state = attributes.private('state') -local markbase = attributes.private('markbase') -local markmark = attributes.private('markmark') -local markdone = attributes.private('markdone') -local cursbase = attributes.private('cursbase') -local curscurs = attributes.private('curscurs') -local cursdone = attributes.private('cursdone') -local kernpair = attributes.private('kernpair') +local state = privateattribute('state') +local markbase = privateattribute('markbase') +local markmark = privateattribute('markmark') +local markdone = privateattribute('markdone') +local cursbase = privateattribute('cursbase') +local curscurs = privateattribute('curscurs') +local cursdone = privateattribute('cursdone') +local kernpair = privateattribute('kernpair') + +local injections = nodes.injections +local setmark = injections.setmark +local setcursive = injections.setcursive +local setkern = injections.setkern +local setpair = injections.setpair -local injections = nodes.injections -local setmark = injections.setmark -local setcursive = injections.setcursive -local setkern = injections.setkern -local setpair = injections.setpair +local markonce = true +local cursonce = true +local kernonce = true -local markonce = true -local cursonce = true -local kernonce = true +local fonthashes = fonts.hashes +local fontdata = fonthashes.identifiers -local fontdata = fonts.identifiers +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register -otf.features.process = { } +local onetimemessage = fonts.loggers.onetimemessage -- we share some vars here, after all, we have no nested lookups and -- less code -local tfmdata = false -local otfdata = false -local characters = false -local descriptions = false -local marks = false -local indices = false -local unicodes = false -local currentfont = false -local lookuptable = false -local anchorlookups = false -local handlers = { } -local rlmode = 0 -local featurevalue = false - --- we cheat a bit and assume that a font,attr combination are kind of ranged - -local specifiers = fonts.definers.specifiers -local contextsetups = specifiers.contextsetups -local contextnumbers = specifiers.contextnumbers -local contextmerged = specifiers.contextmerged +local tfmdata = false +local characters = false +local descriptions = false +local resources = false +local marks = false +local currentfont = false +local lookuptable = false +local anchorlookups = false +local lookuptypes = false +local handlers = { } +local rlmode = 0 +local featurevalue = false -- we cannot optimize with "start = first_glyph(head)" because then we don't -- know which rlmode we're in which messes up cursive handling later on -- -- head is always a whatsit so we can safely assume that head is not changed -local special_attributes = { - init = 1, - medi = 2, - fina = 3, - isol = 4 -} - -- we use this for special testing and documentation local checkstep = (nodes and nodes.tracers and nodes.tracers.steppers.check) or function() end @@ -8503,6 +7973,7 @@ local function logprocess(...) end report_direct(...) end + local function logwarning(...) report_direct(...) end @@ -8522,9 +7993,11 @@ local function gref(n) local num, nam = { }, { } for i=1,#n do local ni = n[i] - local di = descriptions[ni] - num[i] = format("U+%04X",ni) - nam[i] = di and di.name or "?" + if tonumber(di) then -- later we will start at 2 + local di = descriptions[ni] + num[i] = format("U+%04X",ni) + nam[i] = di and di.name or "?" + end end return format("%s (%s)",concat(num," "), concat(nam," ")) end @@ -8567,84 +8040,72 @@ local function markstoligature(kind,lookupname,start,stop,char) end local function toligature(kind,lookupname,start,stop,char,markflag,discfound) -- brr head - if start ~= stop then ---~ if discfound then ---~ local lignode = copy_node(start) ---~ lignode.font = start.font ---~ lignode.char = char ---~ lignode.subtype = ligature_code ---~ start = node.do_ligature_n(start, stop, lignode) ---~ if start.id == disc_code then ---~ local prev = start.prev ---~ start = start.next ---~ end - if discfound then - -- print("start->stop",nodes.tosequence(start,stop)) - local lignode = copy_node(start) - lignode.font, lignode.char, lignode.subtype = start.font, char, ligature_code - local next, prev = stop.next, start.prev - stop.next = nil - lignode = node.do_ligature_n(start, stop, lignode) - prev.next = lignode - if next then - next.prev = lignode - end - lignode.next, lignode.prev = next, prev - start = lignode - -- print("start->end",nodes.tosequence(start)) - else -- start is the ligature - local deletemarks = markflag ~= "mark" - local n = copy_node(start) - local current - current, start = insert_node_after(start,start,n) - local snext = stop.next - current.next = snext - if snext then - snext.prev = current - end - start.prev, stop.next = nil, nil - current.char, current.subtype, current.components = char, ligature_code, start - local head = current - if deletemarks then - if trace_marks then - while start do - if marks[start.char] then - logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char)) - end - start = start.next - end - end - else - local i = 0 + if start == stop then + start.char = char + elseif discfound then + -- print("start->stop",nodes.tosequence(start,stop)) + local lignode = copy_node(start) + lignode.font, lignode.char, lignode.subtype = start.font, char, ligature_code + local next, prev = stop.next, start.prev + stop.next = nil + lignode = node.do_ligature_n(start, stop, lignode) + prev.next = lignode + if next then + next.prev = lignode + end + lignode.next, lignode.prev = next, prev + start = lignode + -- print("start->end",nodes.tosequence(start)) + else -- start is the ligature + local deletemarks = markflag ~= "mark" + local n = copy_node(start) + local current + current, start = insert_node_after(start,start,n) + local snext = stop.next + current.next = snext + if snext then + snext.prev = current + end + start.prev, stop.next = nil, nil + current.char, current.subtype, current.components = char, ligature_code, start + local head = current + if deletemarks then + if trace_marks then while start do if marks[start.char] then - set_attribute(start,markdone,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - head, current = insert_node_after(head,current,copy_node(start)) - else - i = i + 1 + logwarning("%s: remove mark %s",pref(kind,lookupname),gref(start.char)) end start = start.next end - start = current.next - while start and start.id == glyph_code do - if marks[start.char] then - set_attribute(start,markdone,i) - if trace_marks then - logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) - end - else - break + end + else + local i = 0 + while start do + if marks[start.char] then + set_attribute(start,markdone,i) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) + end + head, current = insert_node_after(head,current,copy_node(start)) + else + i = i + 1 + end + start = start.next + end + start = current.next + while start and start.id == glyph_code do + if marks[start.char] then + set_attribute(start,markdone,i) + if trace_marks then + logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(start.char),i) end - start = start.next + else + break end + start = start.next end - return head end - else - start.char = char + return head end return start end @@ -8687,6 +8148,30 @@ local function alternative_glyph(start,alternatives,kind,chainname,chainlookupna return choice, value end +local function multiple_glyphs(start,multiple) + local nofmultiples = #multiple + if nofmultiples > 0 then + start.char = multiple[1] + if nofmultiples > 1 then + local sn = start.next + for k=2,nofmultiples do -- todo: use insert_node + local n = copy_node(start) + n.char = multiple[k] + n.next = sn + n.prev = start + if sn then + sn.prev = n + end + start.next = n + start = n + end + end + return start, true + else + return start, false + end +end + function handlers.gsub_alternate(start,kind,lookupname,alternative,sequence) local choice, index = alternative_glyph(start,alternative,kind,lookupname) if trace_alternatives then @@ -8700,22 +8185,7 @@ function handlers.gsub_multiple(start,kind,lookupname,multiple) if trace_multiples then logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) end - start.char = multiple[1] - if #multiple > 1 then - for k=2,#multiple do - local n = copy_node(start) - n.char = multiple[k] - local sn = start.next - n.next = sn - n.prev = start - if sn then - sn.prev = n - end - start.next = n - start = n - end - end - return start, true + return multiple_glyphs(start,multiple) end function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or maybe pass lookup ref @@ -8724,17 +8194,12 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma if marks[startchar] then while s do local id = s.id - if id == glyph_code and s.subtype<256 then - if s.font == currentfont then - local char = s.char - local lg = ligature[1][char] - if not lg then - break - else - stop = s - ligature = lg - s = s.next - end + if id == glyph_code and s.subtype<256 and s.font == currentfont then + local lg = ligature[s.char] + if lg then + stop = s + ligature = lg + s = s.next else break end @@ -8742,13 +8207,14 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma break end end - if stop and ligature[2] then + if stop then + local lig = ligature.ligature if trace_ligatures then local stopchar = stop.char - start = markstoligature(kind,lookupname,start,stop,ligature[2]) + start = markstoligature(kind,lookupname,start,stop,lig) logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else - start = markstoligature(kind,lookupname,start,stop,ligature[2]) + start = markstoligature(kind,lookupname,start,stop,lig) end return start, true end @@ -8762,13 +8228,13 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma if skipmark and marks[char] then s = s.next else - local lg = ligature[1][char] - if not lg then - break - else + local lg = ligature[char] + if lg then stop = s ligature = lg s = s.next + else + break end end else @@ -8781,13 +8247,14 @@ function handlers.gsub_ligature(start,kind,lookupname,ligature,sequence) --or ma break end end - if stop and ligature[2] then + if stop then + local lig = ligature.ligature if trace_ligatures then local stopchar = stop.char - start = toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) + start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) logprocess("%s: replacing %s upto %s by ligature %s",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) else - start = toligature(kind,lookupname,start,stop,ligature[2],skipmark,discfound) + start = toligature(kind,lookupname,start,stop,lig,skipmark,discfound) end return start, true end @@ -8834,7 +8301,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -8849,7 +8316,7 @@ function handlers.gpos_mark2base(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.registermessage(currentfont,basechar,"no base anchors") + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -8902,7 +8369,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) if ma then ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,index) if trace_marks then logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) @@ -8919,7 +8386,7 @@ function handlers.gpos_mark2ligature(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.registermessage(currentfont,basechar,"no base anchors") + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end elseif trace_bugs then logwarning("%s: prev node is no char",pref(kind,lookupname)) @@ -8949,7 +8416,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -8965,7 +8432,7 @@ function handlers.gpos_mark2mark(start,kind,lookupname,markanchors,sequence) end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) - fonts.registermessage(currentfont,basechar,"no base anchors") + onetimemessage(currentfont,basechar,"no base anchors",report_fonts) end elseif trace_bugs then logwarning("%s: prev node is no mark",pref(kind,lookupname)) @@ -9007,7 +8474,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to if al[anchor] then local exit = exitanchors[anchor] if exit then - local dx, dy, bound = setcursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end @@ -9020,7 +8487,7 @@ function handlers.gpos_cursive(start,kind,lookupname,exitanchors,sequence) -- to end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.registermessage(currentfont,startchar,"no entry anchors") + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) end break end @@ -9037,7 +8504,8 @@ end function handlers.gpos_single(start,kind,lookupname,kerns,sequence) local startchar = start.char - local dx, dy, w, h = setpair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + local kerns = kerns[start.char] + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) end @@ -9052,7 +8520,8 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) return start, false else local prev, done = start, false - local factor = tfmdata.factor + local factor = tfmdata.parameters.factor + local lookuptype = lookuptypes[lookupname] while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do local nextchar = snext.char local krn = kerns[nextchar] @@ -9064,8 +8533,8 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) if not krn then -- skip elseif type(krn) == "table" then - if krn[1] == "pair" then - local a, b = krn[3], krn[4] + if lookuptype == "pair" then -- probably not needed + local a, b = krn[2], krn[3] if a and #a > 0 then local startchar = start.char local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) @@ -9080,18 +8549,18 @@ function handlers.gpos_pair(start,kind,lookupname,kerns,sequence) logprocess("%s: shifting second of pair %s and %s by (%s,%s) and correction (%s,%s)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) end end - else + else -- wrong ... position has different entries report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) - local a, b = krn[3], krn[7] - if a and a ~= 0 then - local k = setkern(snext,factor,rlmode,a) - if trace_kerns then - logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) - end - end - if b and b ~= 0 then - logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) - end + -- local a, b = krn[2], krn[6] + -- if a and a ~= 0 then + -- local k = setkern(snext,factor,rlmode,a) + -- if trace_kerns then + -- logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) + -- end + -- end + -- if b and b ~= 0 then + -- logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) + -- end end done = true elseif krn ~= 0 then @@ -9125,37 +8594,31 @@ end local logwarning = report_subchain --- ['coverage']={ --- ['after']={ "r" }, --- ['before']={ "q" }, --- ['current']={ "a", "b", "c" }, --- }, --- ['lookups']={ "ls_l_1", "ls_l_1", "ls_l_1" }, - -function chainmores.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname,n) +function chainmores.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) return start, false end -- handled later: -- --- function chainmores.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) --- return chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- function chainmores.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) -- end -function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +function chainmores.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) logprocess("%s: gsub_multiple not yet supported",cref(kind,chainname,chainlookupname)) return start, false end -function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) + +function chainmores.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) logprocess("%s: gsub_alternate not yet supported",cref(kind,chainname,chainlookupname)) return start, false end -- handled later: -- --- function chainmores.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) --- return chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,n) +-- function chainmores.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) +-- return chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,n) -- end local function logprocess(...) @@ -9170,7 +8633,7 @@ local logwarning = report_chain -- We could share functions but that would lead to extra function calls with many -- arguments, redundant tests and confusing messages. -function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,cache,lookuplist,chainlookupname) +function chainprocs.chainsub(start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) return start, false end @@ -9179,7 +8642,7 @@ end -- in a bit weird way. There is no lookup and the replacement comes from the lookup -- itself. It is meant mostly for dealing with Urdu. -function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,cache,replacements) +function chainprocs.reversesub(start,stop,kind,chainname,currentcontext,lookuphash,replacements) local char = start.char local replacement = replacements[char] if replacement then @@ -9225,18 +8688,21 @@ end match.

--ldx]]-- -function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) +function chainprocs.gsub_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -- todo: marks ? if not chainindex then - delete_till_stop(start,stop) -- ,currentlookup.flags[1]) + delete_till_stop(start,stop) -- ,currentlookup.flags[1] end local current = start local subtables = currentlookup.subtables +if #subtables > 1 then + log_warning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) +end while current do if current.id == glyph_code then local currentchar = current.char local lookupname = subtables[1] - local replacement = cache.gsub_single[lookupname] + local replacement = lookuphash[lookupname] if not replacement then if trace_bugs then logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) @@ -9271,12 +8737,12 @@ chainmores.gsub_single = chainprocs.gsub_single the match.

--ldx]]-- -function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) delete_till_stop(start,stop) local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local replacements = cache.gsub_multiple[lookupname] + local replacements = lookuphash[lookupname] if not replacements then if trace_bugs then logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) @@ -9291,21 +8757,7 @@ function chainprocs.gsub_multiple(start,stop,kind,chainname,currentcontext,cache if trace_multiples then logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) end - local sn = start.next - for k=1,#replacements do - if k == 1 then - start.char = replacements[k] - else - local n = copy_node(start) -- maybe delete the components and such - n.char = replacements[k] - n.next, n.prev = sn, start - if sn then - sn.prev = n - end - start.next, start = n, n - end - end - return start, true + return multiple_glyphs(start,replacements) end end return start, false @@ -9315,7 +8767,7 @@ end

Here we replace start by new glyph. First we delete the rest of the match.

--ldx]]-- -function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -- todo: marks ? delete_till_stop(start,stop) local current = start @@ -9324,7 +8776,7 @@ function chainprocs.gsub_alternate(start,stop,kind,chainname,currentcontext,cach if current.id == glyph_code then local currentchar = current.char local lookupname = subtables[1] - local alternatives = cache.gsub_alternate[lookupname] + local alternatives = lookuphash[lookupname] if not alternatives then if trace_bugs then logwarning("%s: no alternative hits",cref(kind,chainname,chainlookupname,lookupname)) @@ -9359,11 +8811,11 @@ this function (move code inline and handle the marks by a separate function). We assume rather stupid ligatures (no complex disc nodes).

--ldx]]-- -function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex) +function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local ligatures = cache.gsub_ligature[lookupname] + local ligatures = lookuphash[lookupname] if not ligatures then if trace_bugs then logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) @@ -9386,21 +8838,21 @@ function chainprocs.gsub_ligature(start,stop,kind,chainname,currentcontext,cache if marks[schar] then -- marks s = s.next else - local lg = ligatures[1][schar] - if not lg then - break - else + local lg = ligatures[schar] + if lg then ligatures, last, nofreplacements = lg, s, nofreplacements + 1 if s == stop then break else s = s.next end + else + break end end end end - local l2 = ligatures[2] + local l2 = ligatures.ligature if l2 then if chainindex then stop = last @@ -9428,12 +8880,12 @@ end chainmores.gsub_ligature = chainprocs.gsub_ligature -function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then local subtables = currentlookup.subtables local lookupname = subtables[1] - local markanchors = cache.gpos_mark2base[lookupname] + local markanchors = lookuphash[lookupname] if markanchors then markanchors = markanchors[markchar] end @@ -9466,7 +8918,7 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cach if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -9492,12 +8944,12 @@ function chainprocs.gpos_mark2base(start,stop,kind,chainname,currentcontext,cach return start, false end -function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then local subtables = currentlookup.subtables local lookupname = subtables[1] - local markanchors = cache.gpos_mark2ligature[lookupname] + local markanchors = lookuphash[lookupname] if markanchors then markanchors = markanchors[markchar] end @@ -9539,7 +8991,7 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, if ma then ba = ba[index] if ba then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma,index) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,index) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) @@ -9566,7 +9018,7 @@ function chainprocs.gpos_mark2ligature(start,stop,kind,chainname,currentcontext, return start, false end -function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local markchar = start.char if marks[markchar] then --~ local alreadydone = markonce and has_attribute(start,markmark) @@ -9574,7 +9026,7 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cach -- local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark local subtables = currentlookup.subtables local lookupname = subtables[1] - local markanchors = cache.gpos_mark2mark[lookupname] + local markanchors = lookuphash[lookupname] if markanchors then markanchors = markanchors[markchar] end @@ -9591,7 +9043,7 @@ function chainprocs.gpos_mark2mark(start,stop,kind,chainname,currentcontext,cach if al[anchor] then local ma = markanchors[anchor] if ma then - local dx, dy, bound = setmark(start,base,tfmdata.factor,rlmode,ba,ma) + local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) if trace_marks then logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%s,%s)", cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) @@ -9622,13 +9074,13 @@ end -- ! ! ! untested ! ! ! -function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname) +function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) local alreadydone = cursonce and has_attribute(start,cursbase) if not alreadydone then local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local exitanchors = cache.gpos_cursive[lookupname] + local exitanchors = lookuphash[lookupname] if exitanchors then exitanchors = exitanchors[startchar] end @@ -9657,7 +9109,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, if al[anchor] then local exit = exitanchors[anchor] if exit then - local dx, dy, bound = setcursive(start,nxt,tfmdata.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) + local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) if trace_cursive then logprocess("%s: moving %s to %s cursive (%s,%s) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) end @@ -9670,7 +9122,7 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, end else -- if trace_bugs then -- logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) - fonts.registermessage(currentfont,startchar,"no entry anchors") + onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) end break end @@ -9687,16 +9139,16 @@ function chainprocs.gpos_cursive(start,stop,kind,chainname,currentcontext,cache, return start, false end -function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) - -- untested +function chainprocs.gpos_single(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) + -- untested .. needs checking for the new model local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local kerns = cache.gpos_single[lookupname] + local kerns = lookuphash[lookupname] if kerns then kerns = kerns[startchar] if kerns then - local dx, dy, w, h = setpair(start,tfmdata.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) + local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) if trace_kerns then logprocess("%s: shifting single %s by (%s,%s) and correction (%s,%s)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) end @@ -9707,19 +9159,20 @@ end -- when machines become faster i will make a shared function -function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,currentlookup,chainlookupname,chainindex,sequence) +function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -- logwarning("%s: gpos_pair not yet supported",cref(kind,chainname,chainlookupname)) local snext = start.next if snext then local startchar = start.char local subtables = currentlookup.subtables local lookupname = subtables[1] - local kerns = cache.gpos_pair[lookupname] + local kerns = lookuphash[lookupname] if kerns then kerns = kerns[startchar] if kerns then + local lookuptype = lookuptypes[lookupname] local prev, done = start, false - local factor = tfmdata.factor + local factor = tfmdata.parameters.factor while snext and snext.id == glyph_code and snext.subtype<256 and snext.font == currentfont do local nextchar = snext.char local krn = kerns[nextchar] @@ -9730,8 +9183,8 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,cur if not krn then -- skip elseif type(krn) == "table" then - if krn[1] == "pair" then - local a, b = krn[3], krn[4] + if lookuptype == "pair" then + local a, b = krn[2], krn[3] if a and #a > 0 then local startchar = start.char local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) @@ -9748,7 +9201,7 @@ function chainprocs.gpos_pair(start,stop,kind,chainname,currentcontext,cache,cur end else report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) - local a, b = krn[3], krn[7] + local a, b = krn[2], krn[6] if a and a ~= 0 then local k = setkern(snext,factor,rlmode,a) if trace_kerns then @@ -9793,7 +9246,7 @@ local function show_skip(kind,chainname,char,ck,class) end end -local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) +local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,lookuphash) -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] local flags, done = sequence.flags, false local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] @@ -9994,35 +9447,11 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local chainlookup = lookuptable[chainlookupname] local cp = chainprocs[chainlookup.type] if cp then - start, done = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,nil,sequence) + start, done = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) else logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) end else - -- actually this needs a more complex treatment for which we will use chainmores ---~ local i = 1 ---~ repeat ---~ local chainlookupname = chainlookups[i] ---~ local chainlookup = lookuptable[chainlookupname] ---~ local cp = chainmores[chainlookup.type] ---~ if cp then ---~ local ok, n ---~ start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) ---~ -- messy since last can be changed ! ---~ if ok then ---~ done = true ---~ start = start.next ---~ if n then ---~ -- skip next one(s) if ligature ---~ i = i + n - 1 ---~ end ---~ end ---~ else ---~ logprocess("%s: multiple subchains for %s are not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) ---~ end ---~ i = i + 1 ---~ until i > nofchainlookups - local i = 1 repeat if skipped then @@ -10046,7 +9475,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local cp = chainmores[chainlookup.type] if cp then local ok, n - start, ok, n = cp(start,last,kind,chainname,ck,cache,chainlookup,chainlookupname,i,sequence) + start, ok, n = cp(start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) -- messy since last can be changed ! if ok then done = true @@ -10066,7 +9495,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence else local replacements = ck[7] if replacements then - start, done = chainprocs.reversesub(start,last,kind,chainname,ck,cache,replacements) -- sequence + start, done = chainprocs.reversesub(start,last,kind,chainname,ck,lookuphash,replacements) -- sequence else done = true -- can be meant to be skipped if trace_contexts then @@ -10131,122 +9560,120 @@ local function report_missing_cache(typ,lookup) local t = f[typ] if not t then t = { } f[typ] = t end if not t[lookup] then t[lookup] = true - logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.fullname) + logwarning("missing cache for lookup %s of type %s in font %s (%s)",lookup,typ,currentfont,tfmdata.properties.fullname) end end local resolved = { } -- we only resolve a font,script,language pair once -- todo: pass all these 'locals' in a table --- --- dynamics will be isolated some day ... for the moment we catch attribute zero --- not being set -function fonts.methods.node.otf.features(head,font,attr) +local lookuphashes = { } + +setmetatable(lookuphashes, { __index = + function(t,font) + local lookuphash = fontdata[font].resources.lookuphash + if not lookuphash or not next(lookuphash) then + lookuphash = false + end + t[font] = lookuphash + return lookuphash + end +}) + +-- fonts.hashes.lookups = lookuphashes + +local special_attributes = { + init = 1, + medi = 2, + fina = 3, + isol = 4 +} + +local function initialize(sequence,script,language,enabled) + local features = sequence.features + if features then + for kind, scripts in next, features do + local valid = enabled[kind] + if valid then + local languages = scripts[script] or scripts[wildcard] + if languages and (languages[language] or languages[wildcard]) then + return { valid, special_attributes[kind] or false, sequence.chain or 0, kind } + end + end + end + end + return false +end + +function otf.dataset(ftfmdata,sequences,font) -- generic variant, overloaded in context + local shared = tfmdata.shared + local properties = tfmdata.properties + local language = properties.language or "dflt" + local script = properties.script or "dflt" + local enabled = shared.features + local 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 + setmetatable(rl, { __index = function(t,k) + local v = enabled and initialize(sequences[k],script,language,enabled) + t[k] = v + return v + end}) + end + return rl +end + +local function featuresprocessor(head,font,attr) + + local lookuphash = lookuphashes[font] -- we can also check sequences here + + if not lookuphash then + return head, false + end + if trace_steps then checkstep(head) end - tfmdata = fontdata[font] - local shared = tfmdata.shared - otfdata = shared.otfdata - local luatex = otfdata.luatex - descriptions = tfmdata.descriptions - characters = tfmdata.characters - indices = tfmdata.indices - unicodes = tfmdata.unicodes - marks = tfmdata.marks - anchorlookups = luatex.lookup_to_anchor - currentfont = font - rlmode = 0 - local featuredata = otfdata.shared.featuredata -- can be made local to closure - local sequences = luatex.sequences - lookuptable = luatex.lookups - local done = false - local script, language, s_enabled, a_enabled, dyn - local attribute_driven = attr and attr ~= 0 - if attribute_driven then - local features = contextsetups[contextnumbers[attr]] -- could be a direct list - dyn = contextmerged[attr] or 0 - language, script = features.language or "dflt", features.script or "dflt" - a_enabled = features -- shared.features -- can be made local to the resolver - if dyn == 2 or dyn == -2 then - -- font based - s_enabled = shared.features - end - else - language, script = tfmdata.language or "dflt", tfmdata.script or "dflt" - s_enabled = shared.features -- can be made local to the resolver - dyn = 0 - end - -- we can save some runtime by caching feature tests - 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 ra = { } rl [attr] = ra end -- attr can be false - -- sequences always > 1 so no need for optimization + + tfmdata = fontdata[font] + descriptions = tfmdata.descriptions + characters = tfmdata.characters + resources = tfmdata.resources + + marks = resources.marks + anchorlookups = resources.lookup_to_anchor + lookuptable = resources.lookups + lookuptypes = resources.lookuptypes + + currentfont = font + rlmode = 0 + + local sequences = resources.sequences + local done = false + local datasets = otf.dataset(tfmdata,sequences,font,attr) + for s=1,#sequences do - local pardir, txtdir, success = 0, { }, false + local pardir, txtdir, success = 0, { }, false -- we could reuse txtdir and use a top pointer local sequence = sequences[s] - local r = ra[s] -- cache - if r == nil then - -- - -- this bit will move to font-ctx and become a function - --- - local chain = sequence.chain or 0 - local features = sequence.features - if not features then - -- indirect lookup, part of chain (todo: make this a separate table) - r = false -- { false, false, chain } - else - local valid, attribute, kind, what = false, false - for k,v in next, features do - -- we can quit earlier but for the moment we want the tracing - local s_e = s_enabled and s_enabled[k] - local a_e = a_enabled and a_enabled[k] - if s_e or a_e then - local l = v[script] or v[wildcard] - if l then - -- not l[language] or l[default] or l[wildcard] because we want tracing - -- only first attribute match check, so we assume simple fina's - -- default can become a font feature itself - if l[language] then - valid, what = s_e or a_e, language - -- elseif l[default] then - -- valid, what = true, default - elseif l[wildcard] then - valid, what = s_e or a_e, wildcard - end - if valid then - kind, attribute = k, special_attributes[k] or false - if a_e and dyn < 0 then - valid = false - end - if trace_applied then - local typ, action = match(sequence.type,"(.*)_(.*)") - report_process( - "%s font: %03i, dynamic: %03i, kind: %s, lookup: %3i, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s", - (valid and "+") or "-",font,attr or 0,kind,s,script,language,what,typ,action,sequence.name) - end - break - end - end - end - end - if valid then - r = { valid, attribute, chain, kind } - else - r = false -- { valid, attribute, chain, "generic" } -- false anyway, could be flag instead of table - end - end - ra[s] = r - end - featurevalue = r and r[1] -- todo: pass to function instead of using a global + local dataset = datasets[s] -- cache + featurevalue = dataset and dataset[1] -- todo: pass to function instead of using a global if featurevalue then - local attribute, chain, typ, subtables = r[2], r[3], sequence.type, sequence.subtables + local attribute, chain, typ, subtables = dataset[2], dataset[3], sequence.type, sequence.subtables if chain < 0 then -- this is a limited case, no special treatments like 'init' etc local handler = handlers[typ] - local thecache = featuredata[typ] or { } -- we need to get rid of this slide ! local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo while start do @@ -10262,11 +9689,11 @@ function fonts.methods.node.otf.features(head,font,attr) if a then for i=1,#subtables do local lookupname = subtables[i] - local lookupcache = thecache[lookupname] + local lookupcache = lookuphash[lookupname] if lookupcache then local lookupmatch = lookupcache[start.char] if lookupmatch then - start, success = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + start, success = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if success then break end @@ -10289,12 +9716,11 @@ function fonts.methods.node.otf.features(head,font,attr) else local handler = handlers[typ] local ns = #subtables - local thecache = featuredata[typ] or { } local start = head -- local ? rlmode = 0 -- to be checked ? if ns == 1 then local lookupname = subtables[1] - local lookupcache = thecache[lookupname] + local lookupcache = lookuphash[lookupname] if not lookupcache then report_missing_cache(typ,lookupname) else @@ -10313,7 +9739,7 @@ function fonts.methods.node.otf.features(head,font,attr) if lookupmatch then -- sequence kan weg local ok - start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) + start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) if ok then success = true end @@ -10395,13 +9821,13 @@ function fonts.methods.node.otf.features(head,font,attr) if a then for i=1,ns do local lookupname = subtables[i] - local lookupcache = thecache[lookupname] + local lookupcache = lookuphash[lookupname] if lookupcache then local lookupmatch = lookupcache[start.char] if lookupmatch then -- we could move all code inline but that makes things even more unreadable local ok - start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,i) + start, ok = handler(start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) if ok then success = true break @@ -10486,305 +9912,156 @@ function fonts.methods.node.otf.features(head,font,attr) return head, done end -otf.features.prepare = { } +local function generic(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if target then + target[unicode] = lookupdata + else + lookuphash[lookupname] = { [unicode] = lookupdata } + end +end + +local action = { --- we used to share code in the following functions but that costs a lot of --- memory due to extensive calls to functions (easily hundreds of thousands per --- document) + substitution = generic, + multiple = generic, + alternate = generic, + position = generic, -local function split(replacement,original,cache,unicodes) - -- we can cache this too, but not the same (although unicode is a unique enough hash) - local o, t, n, no = { }, { }, 0, 0 - for s in gmatch(original,"[^ ]+") do - local us = unicodes[s] - no = no + 1 - if type(us) == "number" then -- tonumber(us) - o[no] = us - else - o[no] = us[1] + ligature = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target end - end - for s in gmatch(replacement,"[^ ]+") do - n = n + 1 - local us = unicodes[s] - if type(us) == "number" then -- tonumber(us) - t[o[n]] = us - else - t[o[n]] = us[1] + for i=1,#lookupdata do + local li = lookupdata[i] + local tu = target[li] + if not tu then + tu = { } + target[li] = tu + end + target = tu end - end - return t -end + target.ligature = unicode + end, -local function uncover(covers,result,cache,unicodes) - -- lpeg hardly faster (.005 sec on mk) - local nofresults = #result - for n=1,#covers do - local c = covers[n] - local cc = cache[c] - nofresults = nofresults + 1 - if not cc then - local t = { } - for s in gmatch(c,"[^ ]+") do - local us = unicodes[s] - if type(us) == "number" then - t[us] = true - else - for i=1,#us do - t[us[i]] = true - end - end - end - cache[c] = t - result[nofresults] = t + pair = function(lookupdata,lookupname,unicode,lookuphash) + local target = lookuphash[lookupname] + if not target then + target = { } + lookuphash[lookupname] = target + end + local others = target[unicode] + local paired = lookupdata[1] + if others then + others[paired] = lookupdata else - result[nofresults] = cc + others = { [paired] = lookupdata } + target[unicode] = others end - end -end + end, + +} local function prepare_lookups(tfmdata) - local otfdata = tfmdata.shared.otfdata - local featuredata = otfdata.shared.featuredata - local anchor_to_lookup = otfdata.luatex.anchor_to_lookup - local lookup_to_anchor = otfdata.luatex.lookup_to_anchor - -- - local multiple = featuredata.gsub_multiple - local alternate = featuredata.gsub_alternate - local single = featuredata.gsub_single - local ligature = featuredata.gsub_ligature - local pair = featuredata.gpos_pair - local position = featuredata.gpos_single - local kerns = featuredata.gpos_pair - local mark = featuredata.gpos_mark2mark - local cursive = featuredata.gpos_cursive - -- - local unicodes = tfmdata.unicodes -- names to unicodes - local indices = tfmdata.indices - local descriptions = tfmdata.descriptions - -- - -- we can change the otf table after loading but then we need to adapt base mode - -- as well (no big deal) - -- - local action = { - substitution = function(p,lookup,glyph,unicode) - local old, new = unicode, unicodes[p[2]] - if type(new) == "table" then - new = new[1] - end - local s = single[lookup] - if not s then s = { } single[lookup] = s end - s[old] = new - --~ if trace_lookups then - --~ report_prepare("lookup %s: substitution %s => %s",lookup,old,new) - --~ end - end, - multiple = function (p,lookup,glyph,unicode) - local old, new, nnew = unicode, { }, 0 - local m = multiple[lookup] - if not m then m = { } multiple[lookup] = m end - m[old] = new - for pc in gmatch(p[2],"[^ ]+") do - local upc = unicodes[pc] - nnew = nnew + 1 - if type(upc) == "number" then - new[nnew] = upc - else - new[nnew] = upc[1] - end - end - --~ if trace_lookups then - --~ report_prepare("lookup %s: multiple %s => %s",lookup,old,concat(new," ")) - --~ end - end, - alternate = function(p,lookup,glyph,unicode) - local old, new, nnew = unicode, { }, 0 - local a = alternate[lookup] - if not a then a = { } alternate[lookup] = a end - a[old] = new - for pc in gmatch(p[2],"[^ ]+") do - local upc = unicodes[pc] - nnew = nnew + 1 - if type(upc) == "number" then - new[nnew] = upc - else - new[nnew] = upc[1] - end - end - --~ if trace_lookups then - --~ report_prepare("lookup %s: alternate %s => %s",lookup,old,concat(new,"|")) - --~ end - end, - ligature = function (p,lookup,glyph,unicode) - --~ if trace_lookups then - --~ report_prepare("lookup %s: ligature %s => %s",lookup,p[2],glyph.name) - --~ end - local first = true - local t = ligature[lookup] - if not t then t = { } ligature[lookup] = t end - for s in gmatch(p[2],"[^ ]+") do - if first then - local u = unicodes[s] - if not u then - report_prepare("lookup %s: ligature %s => %s ignored due to invalid unicode",lookup,p[2],glyph.name) - break - elseif type(u) == "number" then - if not t[u] then - t[u] = { { } } - end - t = t[u] - else - local tt = t - local tu - for i=1,#u do - local u = u[i] - if i==1 then - if not t[u] then - t[u] = { { } } - end - tu = t[u] - t = tu - else - if not t[u] then - tt[u] = tu - end - end - end - end - first = false - else - s = unicodes[s] - local t1 = t[1] - if not t1[s] then - t1[s] = { { } } - end - t = t1[s] - end - end - t[2] = unicode - end, - position = function(p,lookup,glyph,unicode) - -- not used - local s = position[lookup] - if not s then s = { } position[lookup] = s end - s[unicode] = p[2] -- direct pointer to kern spec - end, - pair = function(p,lookup,glyph,unicode) - local s = pair[lookup] - if not s then s = { } pair[lookup] = s end - local others = s[unicode] - if not others then others = { } s[unicode] = others end - -- todo: fast check for space - local two = p[2] - local upc = unicodes[two] - if not upc then - for pc in gmatch(two,"[^ ]+") do - local upc = unicodes[pc] - if type(upc) == "number" then - others[upc] = p -- direct pointer to main table - else - for i=1,#upc do - others[upc[i]] = p -- direct pointer to main table - end - end - end - elseif type(upc) == "number" then - others[upc] = p -- direct pointer to main table - else - for i=1,#upc do - others[upc[i]] = p -- direct pointer to main table - end - end - --~ if trace_lookups then - --~ report_prepare("lookup %s: pair for U+%04X",lookup,unicode) - --~ end - end, - } - -- - for unicode, glyph in next, descriptions do - local lookups = glyph.slookups + + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local anchor_to_lookup = resources.anchor_to_lookup + local lookup_to_anchor = resources.lookup_to_anchor + local lookuptypes = resources.lookuptypes + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + + -- we cannot free the entries in the descriptions as sometimes we access + -- then directly (for instance anchors) ... selectively freeing does save + -- much memory as it's only a reference to a table and the slot in the + -- description hash is not freed anyway + + for unicode, character in next, characters do -- we cannot loop over descriptions ! + + local description = descriptions[unicode] + + local lookups = description.slookups if lookups then - for lookup, p in next, lookups do - action[p[1]](p,lookup,glyph,unicode) + for lookupname, lookupdata in next, lookups do + action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) end end - local lookups = glyph.mlookups + + local lookups = description.mlookups if lookups then - for lookup, whatever in next, lookups do - for i=1,#whatever do -- normaly one - local p = whatever[i] - action[p[1]](p,lookup,glyph,unicode) + for lookupname, lookuplist in next, lookups do + local lookuptype = lookuptypes[lookupname] + for l=1,#lookuplist do + local lookupdata = lookuplist[l] + action[lookuptype](lookupdata,lookupname,unicode,lookuphash) end end end - local list = glyph.kerns - if list then - for lookup, krn in next, list do - local k = kerns[lookup] - if not k then k = { } kerns[lookup] = k end - k[unicode] = krn -- ref to glyph, saves lookup - --~ if trace_lookups then - --~ report_prepare("lookup %s: kern for U+%04X",lookup,unicode) - --~ end - end - end - local oanchor = glyph.anchors - if oanchor then - for typ, anchors in next, oanchor do -- types - if typ == "mark" then - for name, anchor in next, anchors do - local lookups = anchor_to_lookup[name] - if lookups then - for lookup, _ in next, lookups do - local f = mark[lookup] - if not f then f = { } mark[lookup] = f end - f[unicode] = anchors -- ref to glyph, saves lookup - --~ if trace_lookups then - --~ report_prepare("lookup %s: mark anchor %s for U+%04X",lookup,name,unicode) - --~ end - end - end - end - elseif typ == "cexit" then -- or entry? + + local list = description.kerns + if list then + for lookup, krn in next, list do -- ref to glyph, saves lookup + local target = lookuphash[lookup] + if target then + target[unicode] = krn + else + lookuphash[lookup] = { [unicode] = krn } + end + end + end + + local list = description.anchors + if list then + for typ, anchors in next, list do -- types + if typ == "mark" or typ == "cexit" then -- or entry? for name, anchor in next, anchors do local lookups = anchor_to_lookup[name] if lookups then for lookup, _ in next, lookups do - local f = cursive[lookup] - if not f then f = { } cursive[lookup] = f end - f[unicode] = anchors -- ref to glyph, saves lookup - --~ if trace_lookups then - --~ report_prepare("lookup %s: exit anchor %s for U+%04X",lookup,name,unicode) - --~ end + local target = lookuphash[lookup] + if target then + target[unicode] = anchors + else + lookuphash[lookup] = { [unicode] = anchors } + end end end end end end end + + end + +end + +local function split(replacement,original) + local result = { } + for i=1,#replacement do + result[original[i]] = replacement[i] end + return result end --- local cache = { } -luatex = luatex or {} -- this has to change ... we need a better one +local function uncover(covers,result) -- will change (we can store this in the raw table) + local nofresults = #result + for n=1,#covers do + nofresults = nofresults + 1 + result[nofresults] = covers[n] + end +end local function prepare_contextchains(tfmdata) - local otfdata = tfmdata.shared.otfdata - local lookups = otfdata.lookups + local rawdata = tfmdata.shared.rawdata + local resources = rawdata.resources + local lookuphash = resources.lookuphash + local lookups = rawdata.lookups if lookups then - local featuredata = otfdata.shared.featuredata - local contextchain = featuredata.gsub_contextchain -- shared with gpos - local reversecontextchain = featuredata.gsub_reversecontextchain -- shared with gpos - local characters = tfmdata.characters - local unicodes = tfmdata.unicodes - local indices = tfmdata.indices - local cache = luatex.covers - if not cache then - cache = { } - luatex.covers = cache - end - -- - for lookupname, lookupdata in next, otfdata.lookups do + for lookupname, lookupdata in next, rawdata.lookups do local lookuptype = lookupdata.type if not lookuptype then report_prepare("missing lookuptype for %s",lookupname) @@ -10792,39 +10069,37 @@ local function prepare_contextchains(tfmdata) local rules = lookupdata.rules if rules then local fmt = lookupdata.format - -- contextchain[lookupname][unicode] - if fmt == "coverage" then + -- lookuphash[lookupname][unicode] + if fmt == "coverage" then -- or fmt == "class" (converted into "coverage") if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then + -- todo: dejavu-serif has one (but i need to see what use it has) report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) else - local contexts = contextchain[lookupname] + local contexts = lookuphash[lookupname] if not contexts then contexts = { } - contextchain[lookupname] = contexts + lookuphash[lookupname] = contexts end local t, nt = { }, 0 for nofrules=1,#rules do -- does #rules>1 happen often? local rule = rules[nofrules] - local coverage = rule.coverage - if coverage and coverage.current then - local current, before, after, sequence = coverage.current, coverage.before, coverage.after, { } - if before then - uncover(before,sequence,cache,unicodes) - end - local start = #sequence + 1 - uncover(current,sequence,cache,unicodes) - local stop = #sequence - if after then - uncover(after,sequence,cache,unicodes) - end - if sequence[1] then - nt = nt + 1 - t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } - for unic, _ in next, sequence[start] do - local cu = contexts[unic] - if not cu then - contexts[unic] = t - end + local current, before, after, sequence = rule.current, rule.before, rule.after, { } + if before then + uncover(before,sequence) + end + local start = #sequence + 1 + uncover(current,sequence) + local stop = #sequence + if after then + uncover(after,sequence) + end + if sequence[1] then + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t end end end @@ -10834,79 +10109,69 @@ local function prepare_contextchains(tfmdata) if lookuptype ~= "reversesub" then report_prepare("unsupported reverse coverage %s for %s",lookuptype,lookupname) else - local contexts = reversecontextchain[lookupname] + local contexts = lookuphash[lookupname] if not contexts then contexts = { } - reversecontextchain[lookupname] = contexts + lookuphash[lookupname] = contexts end local t, nt = { }, 0 for nofrules=1,#rules do local rule = rules[nofrules] - local reversecoverage = rule.reversecoverage - if reversecoverage and reversecoverage.current then - local current, before, after, replacements, sequence = reversecoverage.current, reversecoverage.before, reversecoverage.after, reversecoverage.replacements, { } - if before then - uncover(before,sequence,cache,unicodes) - end - local start = #sequence + 1 - uncover(current,sequence,cache,unicodes) - local stop = #sequence - if after then - uncover(after,sequence,cache,unicodes) - end - if replacements then - replacements = split(replacements,current[1],cache,unicodes) - end - if sequence[1] then - -- this is different from normal coverage, we assume only replacements - nt = nt + 1 - t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } - for unic, _ in next, sequence[start] do - local cu = contexts[unic] - if not cu then - contexts[unic] = t - end + local current, before, after, replacements, sequence = rule.current, rule.before, rule.after, rule.replacements, { } + if before then + uncover(before,sequence) + end + local start = #sequence + 1 + uncover(current,sequence) + local stop = #sequence + if after then + uncover(after,sequence) + end + if replacements then + replacements = split(replacements,current[1]) + end + if sequence[1] then + -- this is different from normal coverage, we assume only replacements + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t end end end end end - elseif fmt == "glyphs" then + elseif fmt == "glyphs" then --maybe just make then before = { fore } and share with coverage if lookuptype ~= "chainsub" and lookuptype ~= "chainpos" then report_prepare("unsupported coverage %s for %s",lookuptype,lookupname) else - local contexts = contextchain[lookupname] + local contexts = lookuphash[lookupname] if not contexts then contexts = { } - contextchain[lookupname] = contexts + lookuphash[lookupname] = contexts end local t, nt = { }, 0 for nofrules=1,#rules do - -- nearly the same as coverage so we could as well rename it local rule = rules[nofrules] - local glyphs = rule.glyphs - if glyphs and glyphs.names then - local fore, back, names, sequence = glyphs.fore, glyphs.back, glyphs.names, { } - if fore and fore ~= "" then - fore = lpegmatch(split_at_space,fore) - uncover(fore,sequence,cache,unicodes) - end - local start = #sequence + 1 - names = lpegmatch(split_at_space,names) - uncover(names,sequence,cache,unicodes) - local stop = #sequence - if back and back ~= "" then - back = lpegmatch(split_at_space,back) - uncover(back,sequence,cache,unicodes) - end - if sequence[1] then - nt = nt + 1 - t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } - for unic, _ in next, sequence[start] do - local cu = contexts[unic] - if not cu then - contexts[unic] = t - end + local current, before, after, sequence = rule.names, rule.fore, rule.back, { } + if before then + uncover(before,sequence) + end + local start = #sequence + 1 + uncover(current,sequence) + local stop = #sequence + if after then + uncover(after,sequence) + end + if sequence[1] then + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + for unic, _ in next, sequence[start] do + local cu = contexts[unic] + if not cu then + contexts[unic] = t end end end @@ -10919,38 +10184,40 @@ local function prepare_contextchains(tfmdata) end end -function fonts.initializers.node.otf.features(tfmdata,value) +-- we can consider lookuphash == false (initialized but empty) vs lookuphash == table + +local function featuresinitializer(tfmdata,value) if true then -- value then - if not tfmdata.shared.otfdata.shared.initialized then - local t = trace_preparing and os.clock() - local otfdata = tfmdata.shared.otfdata - local featuredata = otfdata.shared.featuredata - -- caches - featuredata.gsub_multiple = { } - featuredata.gsub_alternate = { } - featuredata.gsub_single = { } - featuredata.gsub_ligature = { } - featuredata.gsub_contextchain = { } - featuredata.gsub_reversecontextchain = { } - featuredata.gpos_pair = { } - featuredata.gpos_single = { } - featuredata.gpos_mark2base = { } - featuredata.gpos_mark2ligature = featuredata.gpos_mark2base - featuredata.gpos_mark2mark = featuredata.gpos_mark2base - featuredata.gpos_cursive = { } - featuredata.gpos_contextchain = featuredata.gsub_contextchain - featuredata.gpos_reversecontextchain = featuredata.gsub_reversecontextchain - -- + -- beware we need to use the topmost properties table + local rawdata = tfmdata.shared.rawdata + local properties = rawdata.properties + if not properties.initialized then + local starttime = trace_preparing and os.clock() + local resources = rawdata.resources + resources.lookuphash = resources.lookuphash or { } prepare_contextchains(tfmdata) prepare_lookups(tfmdata) - otfdata.shared.initialized = true + properties.initialized = true if trace_preparing then - report_prepare("preparation time is %0.3f seconds for %s",os.clock()-t,tfmdata.fullname or "?") + report_prepare("preparation time is %0.3f seconds for %s",os.clock()-starttime,tfmdata.properties.fullname or "?") end end end end +registerotffeature { + name = "features", + description = "features", + default = true, + initializers = { + position = 1, + node = featuresinitializer, + }, + processors = { + node = featuresprocessor, + } +} + end -- closure do -- begin closure to overcome local limits and interference @@ -10970,50 +10237,110 @@ local type, tostring, match, format, concat = type, tostring, string.match, stri if not trackers then trackers = { register = function() end } end local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end) -local trace_cjk = false trackers.register("cjk.injections", function(v) trace_cjk = v end) - -trackers.register("cjk.analyzing","otf.analyzing") local fonts, nodes = fonts, nodes local node = node -local otf = fonts.otf -local tfm = fonts.tfm +local otf = fonts.handlers.otf -fonts.analyzers = fonts.analyzers or { } -local analyzers = fonts.analyzers +local analyzers = fonts.analyzers +local initializers = { } +local methods = { } -analyzers.initializers = analyzers.initializers or { node = { otf = { } } } -analyzers.methods = analyzers.methods or { node = { otf = { } } } +analyzers.initializers = initializers +analyzers.methods = methods +analyzers.useunicodemarks = false -local initializers = analyzers.initializers -local methods = analyzers.methods +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph -local nodecodes = nodes.nodecodes -local glyph_code = nodecodes.glyph +local set_attribute = node.set_attribute +local has_attribute = node.has_attribute +local traverse_id = node.traverse_id +local traverse_node_list = node.traverse -local set_attribute = node.set_attribute -local has_attribute = node.has_attribute -local traverse_id = node.traverse_id -local traverse_node_list = node.traverse +local fontdata = fonts.hashes.identifiers +local state = attributes.private('state') +local categories = characters and characters.categories or { } -- sorry, only in context + +local tracers = nodes.tracers +local colortracers = tracers and tracers.colors +local setnodecolor = colortracers and colortracers.set or function() end +local resetnodecolor = colortracers and colortracers.reset or function() end + +local otffeatures = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +--[[ldx-- +

Analyzers run per script and/or language and are needed in order to +process features right.

+--ldx]]-- + +-- todo: analyzers per script/lang, cross font, so we need an font id hash -> script +-- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace -local fontdata = fonts.identifiers -local state = attributes.private('state') -local categories = characters and characters.categories or { } -- sorry, only in context +-- an example analyzer (should move to font-ota.lua) -local fontscolors = fonts.colors -local fcs = (fontscolors and fontscolors.set) or function() end -local fcr = (fontscolors and fontscolors.reset) or function() end +local state = attributes.private('state') +function analyzers.setstate(head,font) + local useunicodemarks = analyzers.useunicodemarks + local tfmdata = fontdata[font] + local characters = tfmdata.characters + local descriptions = tfmdata.descriptions + local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean + while current do + local id = current.id + if id == glyph_code and current.font == font then + local char = current.char + local d = descriptions[char] + if d then + if d.class == "mark" or (useunicodemarks and categories[char] == "mn") then + done = true + set_attribute(current,state,5) -- mark + elseif n == 0 then + first, last, n = current, current, 1 + set_attribute(current,state,1) -- init + else + last, n = current, n+1 + set_attribute(current,state,2) -- medi + end + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + elseif id == disc_code then + -- always in the middle + set_attribute(current,state,2) -- midi + last = current + else -- finish + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + first, last, n = nil, nil, 0 + end + current = current.next + end + if first and first == last then + set_attribute(last,state,4) -- isol + elseif last then + set_attribute(last,state,3) -- fina + end + return head, done +end -- in the future we will use language/script attributes instead of the -- font related value, but then we also need dynamic features which is -- somewhat slower; and .. we need a chain of them -local scriptandlanguage = otf.scriptandlanguage - -function fonts.initializers.node.otf.analyze(tfmdata,value,attr) - local script, language = otf.scriptandlanguage(tfmdata,attr) +local function analyzeinitializer(tfmdata,value) -- attr + local script, language = otf.scriptandlanguage(tfmdata) -- attr local action = initializers[script] if action then if type(action) == "function" then @@ -11025,10 +10352,9 @@ function fonts.initializers.node.otf.analyze(tfmdata,value,attr) end end end - return nil end -function fonts.methods.node.otf.analyze(head,font,attr) +local function analyzeprocessor(head,font,attr) local tfmdata = fontdata[font] local script, language = otf.scriptandlanguage(tfmdata,attr) local action = methods[script] @@ -11045,12 +10371,22 @@ function fonts.methods.node.otf.analyze(head,font,attr) return head, false end -otf.features.register("analyze",true) -- we always analyze -table.insert(fonts.triggers,"analyze") -- we need a proper function for doing this +registerotffeature { + name = "analyze", + description = "analysis of (for instance) character classes", + default = true, + initializers = { + node = analyzeinitializer, + }, + processors = { + position = 1, + node = analyzeprocessor, + } +} -- latin -analyzers.methods.latn = analyzers.aux.setstate +methods.latn = analyzers.setstate -- this info eventually will go into char-def @@ -11132,10 +10468,10 @@ local function warning(current,what) end end -function analyzers.methods.nocolor(head,font,attr) +function methods.nocolor(head,font,attr) for n in traverse_id(glyph_code,head) do if not font or n.font == font then - fcr(n) + resetnodecolor(n) end end return head, true @@ -11147,22 +10483,22 @@ local function finish(first,last) local fc = first.char if isol_fina_medi_init[fc] or isol_fina[fc] then set_attribute(first,state,4) -- isol - if trace_analyzing then fcs(first,"font:isol") end + if trace_analyzing then setnodecolor(first,"font:isol") end else warning(first,"isol") set_attribute(first,state,0) -- error - if trace_analyzing then fcr(first) end + if trace_analyzing then resetnodecolor(first) end end else local lc = last.char if isol_fina_medi_init[lc] or isol_fina[lc] then -- why isol here ? -- if laststate == 1 or laststate == 2 or laststate == 4 then set_attribute(last,state,3) -- fina - if trace_analyzing then fcs(last,"font:fina") end + if trace_analyzing then setnodecolor(last,"font:fina") end else warning(last,"fina") set_attribute(last,state,0) -- error - if trace_analyzing then fcr(last) end + if trace_analyzing then resetnodecolor(last) end end end first, last = nil, nil @@ -11171,21 +10507,21 @@ local function finish(first,last) local fc = first.char if isol_fina_medi_init[fc] or isol_fina[fc] then set_attribute(first,state,4) -- isol - if trace_analyzing then fcs(first,"font:isol") end + if trace_analyzing then setnodecolor(first,"font:isol") end else warning(first,"isol") set_attribute(first,state,0) -- error - if trace_analyzing then fcr(first) end + if trace_analyzing then resetnodecolor(first) end end first = nil end return first, last end -function analyzers.methods.arab(head,font,attr) -- maybe make a special version with no trace +function methods.arab(head,font,attr) -- maybe make a special version with no trace local useunicodemarks = analyzers.useunicodemarks local tfmdata = fontdata[font] - local marks = tfmdata.marks + local marks = tfmdata.resources.marks local first, last, current, done = nil, nil, head, false while current do if current.id == glyph_code and current.subtype<256 and current.font == font and not has_attribute(current,state) then @@ -11193,20 +10529,20 @@ function analyzers.methods.arab(head,font,attr) -- maybe make a special version local char = current.char if marks[char] or (useunicodemarks and categories[char] == "mn") then set_attribute(current,state,5) -- mark - if trace_analyzing then fcs(current,"font:mark") end + if trace_analyzing then setnodecolor(current,"font:mark") end elseif isol[char] then -- can be zwj or zwnj too first, last = finish(first,last) set_attribute(current,state,4) -- isol - if trace_analyzing then fcs(current,"font:isol") end + if trace_analyzing then setnodecolor(current,"font:isol") end first, last = nil, nil elseif not first then if isol_fina_medi_init[char] then set_attribute(current,state,1) -- init - if trace_analyzing then fcs(current,"font:init") end + if trace_analyzing then setnodecolor(current,"font:init") end first, last = first or current, current elseif isol_fina[char] then set_attribute(current,state,4) -- isol - if trace_analyzing then fcs(current,"font:isol") end + if trace_analyzing then setnodecolor(current,"font:isol") end first, last = nil, nil else -- no arab first, last = finish(first,last) @@ -11214,18 +10550,18 @@ function analyzers.methods.arab(head,font,attr) -- maybe make a special version elseif isol_fina_medi_init[char] then first, last = first or current, current set_attribute(current,state,2) -- medi - if trace_analyzing then fcs(current,"font:medi") end + if trace_analyzing then setnodecolor(current,"font:medi") end elseif isol_fina[char] then if not has_attribute(last,state,1) then -- tricky, we need to check what last may be ! set_attribute(last,state,2) -- medi - if trace_analyzing then fcs(last,"font:medi") end + if trace_analyzing then setnodecolor(last,"font:medi") end end set_attribute(current,state,3) -- fina - if trace_analyzing then fcs(current,"font:fina") end + if trace_analyzing then setnodecolor(current,"font:fina") end first, last = nil, nil elseif char >= 0x0600 and char <= 0x06FF then - if trace_analyzing then fcs(current,"font:rest") end + if trace_analyzing then setnodecolor(current,"font:rest") end first, last = finish(first,last) else --no first, last = finish(first,last) @@ -11243,3991 +10579,40 @@ end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['font-otc'] = { +if not modules then modules = { } end modules ['luatex-fonts-lua'] = { version = 1.001, - comment = "companion to font-otf.lua (context)", + comment = "companion to luatex-*.tex", 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 - --- 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 fonts = fonts -local otf = fonts.otf - -local report_otf = logs.reporter("fonts","otf loading") - --- instead of "script = "DFLT", langs = { 'dflt' }" we now use wildcards (we used to --- have always); some day we can write a "force always when true" trick for other --- features as well --- --- we could have a tnum variant as well - --- 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 extra_lists = { - tlig = { - { - endash = "hyphen hyphen", - emdash = "hyphen hyphen hyphen", - -- quotedblleft = "quoteleft quoteleft", - -- quotedblright = "quoteright quoteright", - -- quotedblleft = "grave grave", - -- quotedblright = "quotesingle quotesingle", - -- quotedblbase = "comma comma", - }, - }, - trep = { - { - -- [0x0022] = 0x201D, - [0x0027] = 0x2019, - -- [0x0060] = 0x2018, - }, - }, - anum = { - { -- arabic - [0x0030] = 0x0660, - [0x0031] = 0x0661, - [0x0032] = 0x0662, - [0x0033] = 0x0663, - [0x0034] = 0x0664, - [0x0035] = 0x0665, - [0x0036] = 0x0666, - [0x0037] = 0x0667, - [0x0038] = 0x0668, - [0x0039] = 0x0669, - }, - { -- persian - [0x0030] = 0x06F0, - [0x0031] = 0x06F1, - [0x0032] = 0x06F2, - [0x0033] = 0x06F3, - [0x0034] = 0x06F4, - [0x0035] = 0x06F5, - [0x0036] = 0x06F6, - [0x0037] = 0x06F7, - [0x0038] = 0x06F8, - [0x0039] = 0x06F9, - }, - }, -} +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end -local extra_features = { -- maybe just 1..n so that we prescribe order - tlig = { - { - features = { ["*"] = { ["*"] = true } }, - name = "ctx_tlig_1", - subtables = { "ctx_tlig_1_s" }, - type = "gsub_ligature", - flags = { }, - }, - }, - trep = { - { - features = { ["*"] = { ["*"] = true } }, - name = "ctx_trep_1", - subtables = { "ctx_trep_1_s" }, - type = "gsub_single", - flags = { }, - }, - }, - anum = { - { - features = { arab = { URD = true, dflt = true } }, - name = "ctx_anum_1", - subtables = { "ctx_anum_1_s" }, - type = "gsub_single", - flags = { }, - }, - { - features = { arab = { URD = true } }, - name = "ctx_anum_2", - subtables = { "ctx_anum_2_s" }, - type = "gsub_single", - flags = { }, - }, - }, -} +local fonts = fonts +fonts.formats.lua = "lua" -local function enhancedata(data,filename,raw) - local luatex = data.luatex - local lookups = luatex.lookups - local sequences = luatex.sequences - local glyphs = data.glyphs - local indices = luatex.indices - local gsubfeatures = luatex.features.gsub - for kind, specifications in next, extra_features do - if gsub and gsub[kind] then - -- already present +function fonts.readers.lua(specification) + local fullname = specification.filename or "" + if fullname == "" then + local forced = specification.forced or "" + if forced ~= "" then + fullname = specification.name .. "." .. forced else - local done = 0 - for s=1,#specifications do - local added = false - local specification = specifications[s] - local features, subtables = specification.features, specification.subtables - local name, type, flags = specification.name, specification.type, specification.flags - local full = subtables[1] - local list = extra_lists[kind][s] - if type == "gsub_ligature" then - -- inefficient loop - for unicode, index in next, indices do - local glyph = glyphs[index] - local ligature = list[glyph.name] - if ligature then - if glyph.slookups then - glyph.slookups [full] = { "ligature", ligature, glyph.name } - else - glyph.slookups = { [full] = { "ligature", ligature, glyph.name } } - end - done, added = done+1, true - end - end - elseif type == "gsub_single" then - -- inefficient loop - for unicode, index in next, indices do - local glyph = glyphs[index] - local r = list[unicode] - if r then - local replacement = indices[r] - if replacement and glyphs[replacement] then - if glyph.slookups then - glyph.slookups [full] = { "substitution", glyphs[replacement].name } - else - glyph.slookups = { [full] = { "substitution", glyphs[replacement].name } } - end - done, added = done+1, true - end - end - end - end - if added then - sequences[#sequences+1] = { - chain = 0, - features = { [kind] = features }, - flags = flags, - name = name, - subtables = subtables, - type = type, - } - -- register in metadata (merge as there can be a few) - if not gsubfeatures then - gsubfeatures = { } - luatex.features.gsub = gsubfeatures - end - local k = gsubfeatures[kind] - if not k then - k = { } - gsubfeatures[kind] = k - end - for script, languages in next, features 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 - if done > 0 then - if trace_loading then - report_otf("enhance: registering %s feature (%s glyphs affected)",kind,done) - end - end + fullname = specification.name end end + local fullname = resolvers.findfile(fullname) or "" + if fullname ~= "" then + local loader = loadfile(fullname) + loader = loader and loader() + return loader and loader(specification) + end end -otf.enhancers.register("check extra features",enhancedata) - -local features = otf.tables.features - -features['tlig'] = 'TeX Ligatures' -features['trep'] = 'TeX Replacements' -features['anum'] = 'Arabic Digits' - -local registerbasesubstitution = otf.features.registerbasesubstitution - -registerbasesubstitution('tlig') -registerbasesubstitution('trep') -registerbasesubstitution('anum') - --- the functionality is defined elsewhere - -local initializers = fonts.initializers -local common_initializers = initializers.common -local base_initializers = initializers.base.otf -local node_initializers = initializers.node.otf - -base_initializers.equaldigits = common_initializers.equaldigits -node_initializers.equaldigits = common_initializers.equaldigits - -base_initializers.lineheight = common_initializers.lineheight -node_initializers.lineheight = common_initializers.lineheight - -base_initializers.compose = common_initializers.compose -node_initializers.compose = common_initializers.compose - -end -- closure - -do -- begin closure to overcome local limits and interference - -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 = "derived from http://www.adobe.com/devnet/opentype/archives/glyphlist.txt", - original = "Adobe Glyph List, version 2.0, September 20, 2002", -} - -fonts = fonts or { } -fonts.enc = fonts.enc or { } -fonts.enc.agl = fonts.enc.agl or { } - -fonts.enc.agl.unicodes = { -- generated - ["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, - ["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, - ["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, - ["Cdotaccent"]=266, - ["Chaarmenian"]=1353, - ["Cheabkhasiancyrillic"]=1212, - ["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, - ["Ddotaccent"]=7690, - ["Ddotbelow"]=7692, - ["Deicoptic"]=1006, - ["Deltagreek"]=916, - ["Dhook"]=394, - ["Digammagreek"]=988, - ["Dlinebelow"]=7694, - ["Dmonospace"]=65316, - ["Dslash"]=272, - ["Dtopbar"]=395, - ["Dz"]=498, - ["Dzcaron"]=453, - ["Dzeabkhasiancyrillic"]=1248, - ["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, - ["Edblgrave"]=516, - ["Edieresis"]=203, - ["Edotaccent"]=278, - ["Edotbelow"]=7864, - ["Egrave"]=200, - ["Eharmenian"]=1335, - ["Ehookabove"]=7866, - ["Eightroman"]=8551, - ["Einvertedbreve"]=518, - ["Eiotifiedcyrillic"]=1124, - ["Elevenroman"]=8554, - ["Emacron"]=274, - ["Emacronacute"]=7702, - ["Emacrongrave"]=7700, - ["Emonospace"]=65317, - ["Endescendercyrillic"]=1186, - ["Eng"]=330, - ["Enghecyrillic"]=1188, - ["Enhookcyrillic"]=1223, - ["Eogonek"]=280, - ["Eopen"]=400, - ["Epsilon"]=917, - ["Epsilontonos"]=904, - ["Ereversed"]=398, - ["Esdescendercyrillic"]=1194, - ["Esh"]=425, - ["Eta"]=919, - ["Etarmenian"]=1336, - ["Etatonos"]=905, - ["Eth"]=208, - ["Etilde"]=7868, - ["Etildebelow"]=7706, - ["Ezh"]=439, - ["Ezhcaron"]=494, - ["Ezhreversed"]=440, - ["F"]=70, - ["Fcircle"]=9403, - ["Fdotaccent"]=7710, - ["Feharmenian"]=1366, - ["Feicoptic"]=996, - ["Fhook"]=401, - ["Fiveroman"]=8548, - ["Fmonospace"]=65318, - ["Fourroman"]=8547, - ["G"]=71, - ["GBsquare"]=13191, - ["Gacute"]=500, - ["Gamma"]=915, - ["Gammaafrican"]=404, - ["Gangiacoptic"]=1002, - ["Gbreve"]=286, - ["Gcaron"]=486, - ["Gcircle"]=9404, - ["Gcircumflex"]=284, - ["Gcommaaccent"]=290, - ["Gdotaccent"]=288, - ["Ghadarmenian"]=1346, - ["Ghemiddlehookcyrillic"]=1172, - ["Ghestrokecyrillic"]=1170, - ["Ghook"]=403, - ["Gimarmenian"]=1331, - ["Gmacron"]=7712, - ["Gmonospace"]=65319, - ["Gsmallhook"]=667, - ["Gstroke"]=484, - ["H"]=72, - ["HPsquare"]=13259, - ["Haabkhasiancyrillic"]=1192, - ["Hadescendercyrillic"]=1202, - ["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, - ["IJ"]=306, - ["Iacute"]=205, - ["Ibreve"]=300, - ["Icaron"]=463, - ["Icircle"]=9406, - ["Icircumflex"]=206, - ["Idblgrave"]=520, - ["Idieresis"]=207, - ["Idieresisacute"]=7726, - ["Idieresiscyrillic"]=1252, - ["Idotaccent"]=304, - ["Idotbelow"]=7882, - ["Iebrevecyrillic"]=1238, - ["Ifraktur"]=8465, - ["Igrave"]=204, - ["Ihookabove"]=7880, - ["Iinvertedbreve"]=522, - ["Imacron"]=298, - ["Imacroncyrillic"]=1250, - ["Imonospace"]=65321, - ["Iniarmenian"]=1339, - ["Iogonek"]=302, - ["Iota"]=921, - ["Iotaafrican"]=406, - ["Iotadieresis"]=938, - ["Iotatonos"]=906, - ["Istroke"]=407, - ["Itilde"]=296, - ["Itildebelow"]=7724, - ["Izhitsadblgravecyrillic"]=1142, - ["J"]=74, - ["Jaarmenian"]=1345, - ["Jcircle"]=9407, - ["Jcircumflex"]=308, - ["Jheharmenian"]=1355, - ["Jmonospace"]=65322, - ["K"]=75, - ["KBsquare"]=13189, - ["KKsquare"]=13261, - ["Kabashkircyrillic"]=1184, - ["Kacute"]=7728, - ["Kadescendercyrillic"]=1178, - ["Kahookcyrillic"]=1219, - ["Kappa"]=922, - ["Kastrokecyrillic"]=1182, - ["Kaverticalstrokecyrillic"]=1180, - ["Kcaron"]=488, - ["Kcircle"]=9408, - ["Kcommaaccent"]=310, - ["Kdotbelow"]=7730, - ["Keharmenian"]=1364, - ["Kenarmenian"]=1343, - ["Kheicoptic"]=998, - ["Khook"]=408, - ["Klinebelow"]=7732, - ["Kmonospace"]=65323, - ["Koppacyrillic"]=1152, - ["Koppagreek"]=990, - ["Ksicyrillic"]=1134, - ["L"]=76, - ["LJ"]=455, - ["Lacute"]=313, - ["Lambda"]=923, - ["Lcaron"]=317, - ["Lcircle"]=9409, - ["Lcircumflexbelow"]=7740, - ["Lcommaaccent"]=315, - ["Ldotaccent"]=319, - ["Ldotbelow"]=7734, - ["Ldotbelowmacron"]=7736, - ["Liwnarmenian"]=1340, - ["Lj"]=456, - ["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, - ["Ncircle"]=9411, - ["Ncircumflexbelow"]=7754, - ["Ncommaaccent"]=325, - ["Ndotaccent"]=7748, - ["Ndotbelow"]=7750, - ["Nhookleft"]=413, - ["Nineroman"]=8552, - ["Nj"]=459, - ["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, - ["Odblgrave"]=524, - ["Odieresis"]=214, - ["Odieresiscyrillic"]=1254, - ["Odotbelow"]=7884, - ["Ograve"]=210, - ["Oharmenian"]=1365, - ["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, - ["Ostrokeacute"]=510, - ["Otcyrillic"]=1150, - ["Otilde"]=213, - ["Otildeacute"]=7756, - ["Otildedieresis"]=7758, - ["P"]=80, - ["Pacute"]=7764, - ["Pcircle"]=9413, - ["Pdotaccent"]=7766, - ["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, - ["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, - ["SF100000"]=9472, - ["SF110000"]=9474, - ["SF190000"]=9569, - ["SF200000"]=9570, - ["SF210000"]=9558, - ["SF220000"]=9557, - ["SF230000"]=9571, - ["SF240000"]=9553, - ["SF250000"]=9559, - ["SF260000"]=9565, - ["SF270000"]=9564, - ["SF280000"]=9563, - ["SF360000"]=9566, - ["SF370000"]=9567, - ["SF380000"]=9562, - ["SF390000"]=9556, - ["SF400000"]=9577, - ["SF410000"]=9574, - ["SF420000"]=9568, - ["SF430000"]=9552, - ["SF440000"]=9580, - ["SF450000"]=9575, - ["SF460000"]=9576, - ["SF470000"]=9572, - ["SF480000"]=9573, - ["SF490000"]=9561, - ["SF500000"]=9560, - ["SF510000"]=9554, - ["SF520000"]=9555, - ["SF530000"]=9579, - ["SF540000"]=9578, - ["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, - ["Sheicoptic"]=994, - ["Shhacyrillic"]=1210, - ["Shimacoptic"]=1004, - ["Sigma"]=931, - ["Sixroman"]=8549, - ["Smonospace"]=65331, - ["Stigmagreek"]=986, - ["T"]=84, - ["Tau"]=932, - ["Tbar"]=358, - ["Tcaron"]=356, - ["Tcircle"]=9417, - ["Tcircumflexbelow"]=7792, - ["Tcommaaccent"]=354, - ["Tdotaccent"]=7786, - ["Tdotbelow"]=7788, - ["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, - ["Twelveroman"]=8555, - ["Tworoman"]=8545, - ["U"]=85, - ["Uacute"]=218, - ["Ubreve"]=364, - ["Ucaron"]=467, - ["Ucircle"]=9418, - ["Ucircumflex"]=219, - ["Ucircumflexbelow"]=7798, - ["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, - ["Upsilonacutehooksymbolgreek"]=979, - ["Upsilonafrican"]=433, - ["Upsilondieresis"]=939, - ["Upsilondieresishooksymbolgreek"]=980, - ["Upsilonhooksymbol"]=978, - ["Upsilontonos"]=910, - ["Uring"]=366, - ["Ustraightcyrillic"]=1198, - ["Ustraightstrokecyrillic"]=1200, - ["Utilde"]=360, - ["Utildeacute"]=7800, - ["Utildebelow"]=7796, - ["V"]=86, - ["Vcircle"]=9419, - ["Vdotbelow"]=7806, - ["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, - ["Ycircle"]=9422, - ["Ycircumflex"]=374, - ["Ydieresis"]=376, - ["Ydotaccent"]=7822, - ["Ydotbelow"]=7924, - ["Yerudieresiscyrillic"]=1272, - ["Ygrave"]=7922, - ["Yhook"]=435, - ["Yhookabove"]=7926, - ["Yiarmenian"]=1349, - ["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, - ["Zdotaccent"]=379, - ["Zdotbelow"]=7826, - ["Zedescendercyrillic"]=1176, - ["Zedieresiscyrillic"]=1246, - ["Zeta"]=918, - ["Zhearmenian"]=1338, - ["Zhebrevecyrillic"]=1217, - ["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, - ["acutecomb"]=769, - ["acutedeva"]=2388, - ["acutelowmod"]=719, - ["acutetonecmb"]=833, - ["adblgrave"]=513, - ["addakgurmukhi"]=2673, - ["adeva"]=2309, - ["adieresis"]=228, - ["adieresiscyrillic"]=1235, - ["adieresismacron"]=479, - ["adotbelow"]=7841, - ["adotmacron"]=481, - ["ae"]=230, - ["aeacute"]=509, - ["aekorean"]=12624, - ["aemacron"]=483, - ["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, - ["afii10145"]=1039, - ["afii10146"]=1122, - ["afii10147"]=1138, - ["afii10148"]=1140, - ["afii299"]=8206, - ["afii300"]=8207, - ["afii301"]=8205, - ["afii57534"]=1749, - ["afii61573"]=8236, - ["afii61574"]=8237, - ["afii61575"]=8238, - ["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, - ["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, - ["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, - ["araeaekorean"]=12686, - ["araeakorean"]=12685, - ["arc"]=8978, - ["arighthalfring"]=7834, - ["aring"]=229, - ["aringacute"]=507, - ["aringbelow"]=7681, - ["arrowboth"]=8596, - ["arrowdashdown"]=8675, - ["arrowdashleft"]=8672, - ["arrowdashright"]=8674, - ["arrowdashup"]=8673, - ["arrowdbldown"]=8659, - ["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, - ["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, - ["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, - ["ayinaltonehebrew"]=64288, - ["ayinhebrew"]=1506, - ["b"]=98, - ["babengali"]=2476, - ["backslash"]=92, - ["backslashmonospace"]=65340, - ["badeva"]=2348, - ["bagujarati"]=2732, - ["bagurmukhi"]=2604, - ["bahiragana"]=12400, - ["bahtthai"]=3647, - ["bakatakana"]=12496, - ["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, - ["beta"]=946, - ["betasymbolgreek"]=976, - ["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, - ["blackleftpointingtriangle"]=9664, - ["blacklenticularbracketleft"]=12304, - ["blacklenticularbracketleftvertical"]=65083, - ["blacklenticularbracketright"]=12305, - ["blacklenticularbracketrightvertical"]=65084, - ["blacklowerlefttriangle"]=9699, - ["blacklowerrighttriangle"]=9698, - ["blackrightpointingtriangle"]=9654, - ["blacksmallsquare"]=9642, - ["blackstar"]=9733, - ["blackupperlefttriangle"]=9700, - ["blackupperrighttriangle"]=9701, - ["blackuppointingsmalltriangle"]=9652, - ["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, - ["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, - ["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, - ["circleot"]=8857, - ["circlepostalmark"]=12342, - ["circlewithlefthalfblack"]=9680, - ["circlewithrighthalfblack"]=9681, - ["circumflex"]=710, - ["circumflexbelowcmb"]=813, - ["circumflexcmb"]=770, - ["clear"]=8999, - ["clickalveolar"]=450, - ["clickdental"]=448, - ["clicklateral"]=449, - ["clickretroflex"]=451, - ["clubsuitblack"]=9827, - ["clubsuitwhite"]=9831, - ["cmcubedsquare"]=13220, - ["cmonospace"]=65347, - ["cmsquaredsquare"]=13216, - ["coarmenian"]=1409, - ["colon"]=58, - ["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, - ["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, - ["dageshhebrew"]=1468, - ["dagger"]=8224, - ["daggerdbl"]=8225, - ["dagujarati"]=2726, - ["dagurmukhi"]=2598, - ["dahiragana"]=12384, - ["dakatakana"]=12480, - ["dalarabic"]=1583, - ["daletdageshhebrew"]=64307, - ["dalettserehebrew"]=1491, - ["dalfinalarabic"]=65194, - ["dammalowarabic"]=1615, - ["dammatanarabic"]=1612, - ["danda"]=2404, - ["dargalefthebrew"]=1447, - ["dasiapneumatacyrilliccmb"]=1157, - ["dblanglebracketleft"]=12298, - ["dblanglebracketleftvertical"]=65085, - ["dblanglebracketright"]=12299, - ["dblanglebracketrightvertical"]=65086, - ["dblarchinvertedbelowcmb"]=811, - ["dblarrowleft"]=8660, - ["dblarrowright"]=8658, - ["dbldanda"]=2405, - ["dblgravecmb"]=783, - ["dblintegral"]=8748, - ["dbllowlinecmb"]=819, - ["dbloverlinecmb"]=831, - ["dblprimemod"]=698, - ["dblverticalbar"]=8214, - ["dblverticallineabovecmb"]=782, - ["dbopomofo"]=12553, - ["dbsquare"]=13256, - ["dcaron"]=271, - ["dcedilla"]=7697, - ["dcircle"]=9427, - ["dcircumflexbelow"]=7699, - ["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, - ["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, - ["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, - ["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, - ["dotbelowcomb"]=803, - ["dotkatakana"]=12539, - ["dotlessi"]=305, - ["dotlessjstrokehook"]=644, - ["dotmath"]=8901, - ["dottedcircle"]=9676, - ["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, - ["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, - ["eightbengali"]=2542, - ["eightcircle"]=9319, - ["eightcircleinversesansserif"]=10129, - ["eightdeva"]=2414, - ["eighteencircle"]=9329, - ["eighteenparen"]=9349, - ["eighteenperiod"]=9369, - ["eightgujarati"]=2798, - ["eightgurmukhi"]=2670, - ["eighthackarabic"]=1640, - ["eighthangzhou"]=12328, - ["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, - ["etnahtalefthebrew"]=1425, - ["eturned"]=477, - ["eukorean"]=12641, - ["euro"]=8364, - ["evowelsignbengali"]=2503, - ["evowelsigndeva"]=2375, - ["evowelsigngujarati"]=2759, - ["exclam"]=33, - ["exclamarmenian"]=1372, - ["exclamdbl"]=8252, - ["exclamdown"]=161, - ["exclammonospace"]=65281, - ["ezh"]=658, - ["ezhcaron"]=495, - ["ezhcurl"]=659, - ["ezhreversed"]=441, - ["ezhtail"]=442, - ["f"]=102, - ["fadeva"]=2398, - ["fagurmukhi"]=2654, - ["fahrenheit"]=8457, - ["fathalowarabic"]=1614, - ["fathatanarabic"]=1611, - ["fbopomofo"]=12552, - ["fcircle"]=9429, - ["fdotaccent"]=7711, - ["feharabic"]=1601, - ["feharmenian"]=1414, - ["fehfinalarabic"]=65234, - ["fehinitialarabic"]=65235, - ["fehmedialarabic"]=65236, - ["feicoptic"]=997, - ["ff"]=64256, - ["ffi"]=64259, - ["ffl"]=64260, - ["fi"]=64257, - ["fifteencircle"]=9326, - ["fifteenparen"]=9346, - ["fifteenperiod"]=9366, - ["figuredash"]=8210, - ["filledbox"]=9632, - ["filledrect"]=9644, - ["finalkafdageshhebrew"]=64314, - ["finalkafshevahebrew"]=1498, - ["finalmemhebrew"]=1501, - ["finalnunhebrew"]=1503, - ["finalpehebrew"]=1507, - ["finaltsadihebrew"]=1509, - ["firsttonechinese"]=713, - ["fisheye"]=9673, - ["fitacyrillic"]=1139, - ["five"]=53, - ["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, - ["four"]=52, - ["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, - ["gcircle"]=9430, - ["gcircumflex"]=285, - ["gcommaaccent"]=291, - ["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, - ["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, - ["grave"]=96, - ["gravebelowcmb"]=790, - ["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, - ["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, - ["hamzasukunarabic"]=1569, - ["hangulfiller"]=12644, - ["hardsigncyrillic"]=1098, - ["harpoonleftbarbup"]=8636, - ["harpoonrightbarbup"]=8640, - ["hasquare"]=13258, - ["hatafpatahwidehebrew"]=1458, - ["hatafqamatswidehebrew"]=1459, - ["hatafsegolwidehebrew"]=1457, - ["hbar"]=295, - ["hbopomofo"]=12559, - ["hbrevebelow"]=7723, - ["hcedilla"]=7721, - ["hcircle"]=9431, - ["hcircumflex"]=293, - ["hdieresis"]=7719, - ["hdotaccent"]=7715, - ["hdotbelow"]=7717, - ["heartsuitblack"]=9829, - ["heartsuitwhite"]=9825, - ["hedageshhebrew"]=64308, - ["hehaltonearabic"]=1729, - ["heharabic"]=1607, - ["hehebrew"]=1492, - ["hehfinalaltonearabic"]=64423, - ["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, - ["hethebrew"]=1495, - ["hhook"]=614, - ["hhooksuperior"]=689, - ["hieuhacirclekorean"]=12923, - ["hieuhaparenkorean"]=12827, - ["hieuhcirclekorean"]=12909, - ["hieuhkorean"]=12622, - ["hieuhparenkorean"]=12813, - ["hihiragana"]=12402, - ["hikatakana"]=12498, - ["hikatakanahalfwidth"]=65419, - ["hiriqwidehebrew"]=1460, - ["hlinebelow"]=7830, - ["hmonospace"]=65352, - ["hoarmenian"]=1392, - ["hohipthai"]=3627, - ["hohiragana"]=12411, - ["hokatakana"]=12507, - ["hokatakanahalfwidth"]=65422, - ["holamwidehebrew"]=1465, - ["honokhukthai"]=3630, - ["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, - ["iluyhebrew"]=1452, - ["imacron"]=299, - ["imacroncyrillic"]=1251, - ["imageorapproximatelyequal"]=8787, - ["imatragurmukhi"]=2623, - ["imonospace"]=65353, - ["increment"]=8710, - ["infinity"]=8734, - ["iniarmenian"]=1387, - ["integral"]=8747, - ["integralbt"]=8993, - ["integraltp"]=8992, - ["intersection"]=8745, - ["intisquare"]=13061, - ["invbullet"]=9688, - ["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, - ["kafarabic"]=1603, - ["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, - ["kasmallkatakana"]=12533, - ["kasquare"]=13188, - ["kasraarabic"]=1616, - ["kasratanarabic"]=1613, - ["kastrokecyrillic"]=1183, - ["katahiraprolongmarkhalfwidth"]=65392, - ["kaverticalstrokecyrillic"]=1181, - ["kbopomofo"]=12558, - ["kcalsquare"]=13193, - ["kcaron"]=489, - ["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, - ["lameddageshhebrew"]=64316, - ["lamedholamhebrew"]=1500, - ["lamfinalarabic"]=65246, - ["lamhahinitialarabic"]=64714, - ["lamjeeminitialarabic"]=64713, - ["lamkhahinitialarabic"]=64715, - ["lamlamhehisolatedarabic"]=65010, - ["lammedialarabic"]=65248, - ["lammeemhahinitialarabic"]=64904, - ["lammeeminitialarabic"]=64716, - ["lammeemkhahinitialarabic"]=65247, - ["largecircle"]=9711, - ["lbar"]=410, - ["lbelt"]=620, - ["lbopomofo"]=12556, - ["lcaron"]=318, - ["lcircle"]=9435, - ["lcircumflexbelow"]=7741, - ["lcommaaccent"]=316, - ["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, - ["logicalor"]=8744, - ["lolingthai"]=3621, - ["lowlinecenterline"]=65102, - ["lowlinecmb"]=818, - ["lowlinedashed"]=65101, - ["lozenge"]=9674, - ["lparen"]=9383, - ["lslash"]=322, - ["lsquare"]=8467, - ["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, - ["mahapakhlefthebrew"]=1444, - ["mahiragana"]=12414, - ["maichattawathai"]=3659, - ["maiekthai"]=3656, - ["maihanakatthai"]=3633, - ["maitaikhuthai"]=3655, - ["maithothai"]=3657, - ["maitrithai"]=3658, - ["maiyamokthai"]=3654, - ["makatakana"]=12510, - ["makatakanahalfwidth"]=65423, - ["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, - ["memdageshhebrew"]=64318, - ["memhebrew"]=1502, - ["menarmenian"]=1396, - ["merkhakefulalefthebrew"]=1446, - ["merkhalefthebrew"]=1445, - ["mhook"]=625, - ["mhzsquare"]=13202, - ["middledotkatakanahalfwidth"]=65381, - ["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, - ["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, - ["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, - ["nasquare"]=13185, - ["nbopomofo"]=12555, - ["ncaron"]=328, - ["ncircle"]=9437, - ["ncircumflexbelow"]=7755, - ["ncommaaccent"]=326, - ["ndotaccent"]=7749, - ["ndotbelow"]=7751, - ["nehiragana"]=12397, - ["nekatakana"]=12493, - ["nekatakanahalfwidth"]=65416, - ["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, - ["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, - ["nooninitialarabic"]=65255, - ["noonjeeminitialarabic"]=64722, - ["noonjeemisolatedarabic"]=64587, - ["noonmedialarabic"]=65256, - ["noonmeeminitialarabic"]=64725, - ["noonmeemisolatedarabic"]=64590, - ["noonnoonfinalarabic"]=64653, - ["notcontains"]=8716, - ["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, - ["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, - ["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, - ["omegacyrillic"]=1121, - ["omegalatinclosed"]=631, - ["omegaroundcyrillic"]=1147, - ["omegatitlocyrillic"]=1149, - ["omegatonos"]=974, - ["omgujarati"]=2768, - ["omicron"]=959, - ["omicrontonos"]=972, - ["omonospace"]=65359, - ["one"]=49, - ["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, - ["option"]=8997, - ["ordfeminine"]=170, - ["ordmasculine"]=186, - ["oshortdeva"]=2322, - ["oshortvowelsigndeva"]=2378, - ["oslash"]=248, - ["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, - ["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, - ["patahwidehebrew"]=1463, - ["pazerhebrew"]=1441, - ["pbopomofo"]=12550, - ["pcircle"]=9439, - ["pdotaccent"]=7767, - ["pecyrillic"]=1087, - ["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, - ["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, - ["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, - ["qamatswidehebrew"]=1464, - ["qarneyparahebrew"]=1439, - ["qbopomofo"]=12561, - ["qcircle"]=9440, - ["qhook"]=672, - ["qmonospace"]=65361, - ["qofdageshhebrew"]=64327, - ["qoftserehebrew"]=1511, - ["qparen"]=9388, - ["quarternote"]=9833, - ["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, - ["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, - ["rafehebrew"]=1471, - ["ragujarati"]=2736, - ["ragurmukhi"]=2608, - ["rahiragana"]=12425, - ["rakatakana"]=12521, - ["rakatakanahalfwidth"]=65431, - ["ralowerdiagonalbengali"]=2545, - ["ramiddlediagonalbengali"]=2544, - ["ramshorn"]=612, - ["ratio"]=8758, - ["rbopomofo"]=12566, - ["rcaron"]=345, - ["rcircle"]=9441, - ["rcommaaccent"]=343, - ["rdblgrave"]=529, - ["rdotaccent"]=7769, - ["rdotbelow"]=7771, - ["rdotbelowmacron"]=7773, - ["referencemark"]=8251, - ["registered"]=174, - ["reharmenian"]=1408, - ["rehfinalarabic"]=65198, - ["rehiragana"]=12428, - ["rehyehaleflamarabic"]=1585, - ["rekatakana"]=12524, - ["rekatakanahalfwidth"]=65434, - ["reshdageshhebrew"]=64328, - ["reshtserehebrew"]=1512, - ["reversedtilde"]=8765, - ["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, - ["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, - ["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, - ["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, - ["shaarmenian"]=1399, - ["shabengali"]=2486, - ["shacyrillic"]=1096, - ["shaddadammaarabic"]=64609, - ["shaddadammatanarabic"]=64606, - ["shaddafathaarabic"]=64608, - ["shaddafathatanarabic"]=1617, - ["shaddakasraarabic"]=64610, - ["shaddakasratanarabic"]=64607, - ["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, - ["sheqelhebrew"]=8362, - ["shevawidehebrew"]=1456, - ["shhacyrillic"]=1211, - ["shimacoptic"]=1005, - ["shindageshhebrew"]=64329, - ["shindageshshindothebrew"]=64300, - ["shindageshsindothebrew"]=64301, - ["shindothebrew"]=1473, - ["shinhebrew"]=1513, - ["shinshindothebrew"]=64298, - ["shinsindothebrew"]=64299, - ["shook"]=642, - ["sigma"]=963, - ["sigmafinal"]=962, - ["sigmalunatesymbolgreek"]=1010, - ["sihiragana"]=12375, - ["sikatakana"]=12471, - ["sikatakanahalfwidth"]=65404, - ["siluqlefthebrew"]=1469, - ["sindothebrew"]=1474, - ["siosacirclekorean"]=12916, - ["siosaparenkorean"]=12820, - ["sioscieuckorean"]=12670, - ["sioscirclekorean"]=12902, - ["sioskiyeokkorean"]=12666, - ["sioskorean"]=12613, - ["siosnieunkorean"]=12667, - ["siosparenkorean"]=12806, - ["siospieupkorean"]=12669, - ["siostikeutkorean"]=12668, - ["six"]=54, - ["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, - ["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, - ["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, - ["tavdageshhebrew"]=64330, - ["tavhebrew"]=1514, - ["tbar"]=359, - ["tbopomofo"]=12554, - ["tcaron"]=357, - ["tccurl"]=680, - ["tcheharabic"]=1670, - ["tchehfinalarabic"]=64379, - ["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, - ["tetdageshhebrew"]=64312, - ["tethebrew"]=1496, - ["tetsecyrillic"]=1205, - ["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, - ["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, - ["thousandsseparatorpersian"]=1644, - ["three"]=51, - ["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, - ["tildecomb"]=771, - ["tildedoublecmb"]=864, - ["tildeoperator"]=8764, - ["tildeoverlaycmb"]=820, - ["tildeverticalcmb"]=830, - ["timescircle"]=8855, - ["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, - ["tsadidageshhebrew"]=64326, - ["tsadihebrew"]=1510, - ["tsecyrillic"]=1094, - ["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, - ["twobengali"]=2536, - ["twocircle"]=9313, - ["twocircleinversesansserif"]=10123, - ["twodeva"]=2408, - ["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, - ["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, - ["vavdageshhebrew"]=64309, - ["vavhebrew"]=1493, - ["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, - ["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, - ["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, - ["zayindageshhebrew"]=64310, - ["zayinhebrew"]=1494, - ["zbopomofo"]=12567, - ["zcaron"]=382, - ["zcircle"]=9449, - ["zcircumflex"]=7825, - ["zcurl"]=657, - ["zdotaccent"]=380, - ["zdotbelow"]=7827, - ["zecyrillic"]=1079, - ["zedescendercyrillic"]=1177, - ["zedieresiscyrillic"]=1247, - ["zehiragana"]=12380, - ["zekatakana"]=12476, - ["zero"]=48, - ["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, - - -- extras - - ["Dcroat"]=272, - ["Delta"]=8710, - ["Euro"]=8364, - ["H18533"]=9679, - ["H18543"]=9642, - ["H18551"]=9643, - ["H22073"]=9633, - ["Ldot"]=319, - ["Oslashacute"]=510, - ["SF10000"]=9484, - ["SF20000"]=9492, - ["SF30000"]=9488, - ["SF40000"]=9496, - ["SF50000"]=9532, - ["SF60000"]=9516, - ["SF70000"]=9524, - ["SF80000"]=9500, - ["SF90000"]=9508, - ["Upsilon1"]=978, - ["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, - ["afii10193"]=1119, - ["afii10194"]=1123, - ["afii10195"]=1139, - ["afii10196"]=1141, - ["afii10846"]=1241, - ["afii208"]=8213, - ["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, - ["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, - ["afii57716"]=1520, - ["afii57717"]=1521, - ["afii57718"]=1522, - ["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, - ["afii61664"]=8204, - ["afii63167"]=1645, - ["afii64937"]=701, - ["arrowdblboth"]=8660, - ["arrowdblleft"]=8656, - ["arrowdblright"]=8658, - ["arrowupdnbse"]=8616, - ["bar"]=124, - ["circle"]=9675, - ["circlemultiply"]=8855, - ["circleplus"]=8853, - ["club"]=9827, - ["colonmonetary"]=8353, - ["dcroat"]=273, - ["dkshade"]=9619, - ["existential"]=8707, - ["female"]=9792, - ["gradient"]=8711, - ["heart"]=9829, - ["hookabovecomb"]=777, - ["invcircle"]=9689, - ["ldot"]=320, - ["longs"]=383, - ["ltshade"]=9617, - ["male"]=9794, - ["mu"]=181, - ["napostrophe"]=329, - ["notelement"]=8713, - ["omega1"]=982, - ["openbullet"]=9702, - ["orthogonal"]=8735, - ["oslashacute"]=511, - ["phi1"]=981, - ["propersubset"]=8834, - ["propersuperset"]=8835, - ["reflexsubset"]=8838, - ["reflexsuperset"]=8839, - ["shade"]=9618, - ["sigma1"]=962, - ["similar"]=8764, - ["smileface"]=9786, - ["spacehackarabic"]=32, - ["spade"]=9824, - ["theta1"]=977, - ["twodotenleader"]=8229, -} - end -- closure do -- begin closure to overcome local limits and interference @@ -15261,44 +10646,32 @@ default loader that only handles .

--ldx]]-- local fonts = fonts -local tfm = fonts.tfm -local vf = fonts.vf - -fonts.used = allocate() - -tfm.readers = tfm.readers or { } -tfm.fonts = allocate() - -local readers = tfm.readers -local sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -readers.sequence = sequence - -tfm.version = 1.01 -tfm.cache = containers.define("fonts", "tfm", tfm.version, false) -- better in font-tfm -tfm.autoprefixedafm = true -- this will become false some day (catches texnansi-blabla.*) - -fonts.definers = fonts.definers or { } +local fontdata = fonts.hashes.identifiers +local readers = fonts.readers local definers = fonts.definers +local specifiers = fonts.specifiers +local constructors = fonts.constructors -definers.specifiers = definers.specifiers or { } -local specifiers = definers.specifiers +readers.sequence = allocate { 'otf', 'ttf', 'afm', 'tfm', 'lua' } -- dfont ttc -specifiers.variants = allocate() -local variants = specifiers.variants +local variants = allocate() +specifiers.variants = variants -definers.method = "afm or tfm" -- afm, tfm, afm or tfm, tfm or afm definers.methods = definers.methods or { } -local findbinfile = resolvers.findbinfile +local internalized = allocate() -- internal tex numbers (private) + + +local loadedfonts = constructors.loadedfonts +local designsizes = constructors.designsizes --[[ldx--

We hardly gain anything when we cache the final (pre scaled) - table. But it can be handy for debugging.

+ 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.

--ldx]]-- -fonts.version = 1.05 -fonts.cache = containers.define("fonts", "def", fonts.version, false) - --[[ldx--

We can prefix a font specification by name: or file:. The first case will result in a lookup in the @@ -15386,92 +10759,14 @@ function definers.makespecification(specification, lookup, name, sub, method, de resolved = "", -- resolved font name forced = "", -- forced loader features = { }, -- preprocessed features - } - return t -end - -function definers.analyze(specification, size) - -- can be optimized with locals - local lookup, name, sub, method, detail = getspecification(specification or "") - return definers.makespecification(specification, lookup, name, sub, method, detail, size) -end - ---[[ldx-- -

A unique hash value is generated by:

---ldx]]-- - -local sortedhashkeys = table.sortedhashkeys - -function tfm.hashfeatures(specification) - local features = specification.features - if features then - local t, tn = { }, 0 - local normal = features.normal - if normal and next(normal) then - local f = sortedhashkeys(normal) - for i=1,#f do - local v = f[i] - if v ~= "number" and v ~= "features" then -- i need to figure this out, features - tn = tn + 1 - t[tn] = v .. '=' .. tostring(normal[v]) - end - end - end - local vtf = features.vtf - if vtf and next(vtf) then - local f = sortedhashkeys(vtf) - for i=1,#f do - local v = f[i] - tn = tn + 1 - t[tn] = v .. '=' .. tostring(vtf[v]) - end - end - -- if specification.mathsize then - -- tn = tn + 1 - -- t[tn] = "mathsize=" .. specification.mathsize - -- end - if tn > 0 then - return concat(t,"+") - end - end - return "unknown" -end - -fonts.designsizes = allocate() - ---[[ldx-- -

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 .

---ldx]]-- - -function tfm.hashinstance(specification,force) - local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks - if force or not hash then - hash = tfm.hashfeatures(specification) - specification.hash = hash - end - if size < 1000 and fonts.designsizes[hash] then - size = math.round(tfm.scaled(size,fonts.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 + } + return t +end + +function definers.analyze(specification, size) + -- can be optimized with locals + local lookup, name, sub, method, detail = getspecification(specification or "") + return definers.makespecification(specification, lookup, name, sub, method, detail, size) end --[[ldx-- @@ -15544,7 +10839,7 @@ function definers.resolve(specification) end end -- - specification.hash = lower(specification.name .. ' @ ' .. tfm.hashfeatures(specification)) + specification.hash = lower(specification.name .. ' @ ' .. constructors.hashfeatures(specification)) if specification.sub and specification.sub ~= "" then specification.hash = specification.sub .. ' @ ' .. specification.hash end @@ -15567,26 +10862,48 @@ features (esp in virtual fonts) so let's not do that now.

specification yet.

--ldx]]-- -function tfm.read(specification) - local hash = tfm.hashinstance(specification) - local tfmtable = tfm.fonts[hash] -- hashes by size ! - if not tfmtable then +-- not in context, at least not now: +-- +-- function definers.applypostprocessors(tfmdata) +-- local postprocessors = tfmdata.postprocessors +-- if postprocessors then +-- 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]","-") +-- tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) +-- end +-- end +-- end +-- return tfmdata +-- end + +function definers.applypostprocessors(tfmdata) + return tfmdata +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)] - tfmtable = reader and reader(specification) - if not tfmtable then + tfmdata = reader and reader(specification) + if not tfmdata then report_defining("forced type %s of %s not found",forced,specification.name) end else - for s=1,#sequence do -- reader sequence + 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 -- not really needed + if readers[reader] then -- we skip not loaded readers if trace_defining then report_defining("trying (reader sequence driven) type %s for %s with file %s",reader,specification.name,specification.filename or "unknown") end - tfmtable = readers[reader](specification) - if tfmtable then + tfmdata = readers[reader](specification) + if tfmdata then break else specification.filename = nil @@ -15594,82 +10911,56 @@ function tfm.read(specification) end end end - if tfmtable then + if tfmdata then + local properties = tfmdata.properties + local embedding if directive_embedall then - tfmtable.embedding = "full" - elseif tfmtable.filename and fonts.dontembed[tfmtable.filename] then - tfmtable.embedding = "no" + embedding = "full" + elseif properties.filename and constructors.dontembed[properties.filename] then + embedding = "no" else - tfmtable.embedding = "subset" + embedding = "subset" end - -- fonts.goodies.postprocessors.apply(tfmdata) -- only here - local postprocessors = tfmtable.postprocessors - if postprocessors then - for i=1,#postprocessors do - local extrahash = postprocessors[i](tfmtable) -- after scaling etc - if type(extrahash) == "string" and extrahash ~= "" then - -- e.g. a reencoding needs this - extrahash = gsub(lower(extrahash),"[^a-z]","-") - tfmtable.fullname = format("%s-%s",tfmtable.fullname,extrahash) - end - end + if properties then + properties.embedding = embedding + else + tfmdata.properties = { embedding = embedding } end - -- - tfm.fonts[hash] = tfmtable - fonts.designsizes[specification.hash] = tfmtable.designsize -- we only know this for sure after loading once - --~ tfmtable.mode = specification.features.normal.mode or "base" + tfmdata = definers.applypostprocessors(tfmdata) + loadedfonts[hash] = tfmdata + designsizes[specification.hash] = tfmdata.parameters.designsize end end - if not tfmtable then + if not tfmdata then report_defining("font with asked name '%s' is not found using lookup '%s'",specification.name,specification.lookup) end - return tfmtable + return tfmdata end --[[ldx--

For virtual fonts we need a slightly different approach:

--ldx]]-- -function tfm.readanddefine(name,size) -- no id +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 = tfm.hashinstance(specification) + local hash = constructors.hashinstance(specification) local id = definers.registered(hash) if not id then - local tfmdata = tfm.read(specification) + local tfmdata = definers.loadfont(specification) if tfmdata then - tfmdata.hash = hash + tfmdata.properties.hash = hash id = font.define(tfmdata) definers.register(tfmdata,id) - tfm.cleanuptable(tfmdata) else id = 0 -- signal end end - return fonts.identifiers[id], id -end - ---[[ldx-- -

We need to check for default features. For this we provide -a helper function.

---ldx]]-- - -function definers.check(features,defaults) -- nb adapts features ! - local done = false - if features and next(features) then - for k,v in next, defaults do - if features[k] == nil then - features[k], done = v, true - end - end - else - features, done = table.fastcopy(defaults), true - end - return features, done -- done signals a change + return fontdata[id], id end --[[ldx-- @@ -15691,42 +10982,24 @@ function definers.current() -- or maybe current return lastdefined end -function definers.register(tfmdata,id) -- will be overloaded +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.hash + local hash = tfmdata.properties.hash if not internalized[hash] then + internalized[hash] = id if trace_defining then report_defining("registering font, id: %s, hash: %s",id or "?",hash or "?") end - fonts.identifiers[id] = tfmdata - internalized[hash] = id + fontdata[id] = tfmdata end end end -function definers.registered(hash) -- will be overloaded - local id = internalized[hash] - return id, id and fonts.identifiers[id] -end - -local cache_them = false - -function tfm.make(specification) - -- 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.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.tfm.scale (and set the - -- factor there) - local fvm = definers.methods.variants[specification.features.vtf.preset] - if fvm then - return fvm(specification) - else - return nil - 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 @@ -15737,26 +11010,13 @@ function definers.read(specification,size,id) -- id can be optional, name can al specification = variants[method](specification) end specification = definers.resolve(specification) - local hash = tfm.hashinstance(specification) - if cache_them then - local tfmdata = containers.read(fonts.cache,hash) -- for tracing purposes - end + local hash = constructors.hashinstance(specification) local tfmdata = definers.registered(hash) -- id if not tfmdata then - if specification.features.vtf and specification.features.vtf.preset then - tfmdata = tfm.make(specification) - else - tfmdata = tfm.read(specification) - if tfmdata then - tfm.checkvirtualid(tfmdata) - end - end - if cache_them then - tfmdata = containers.write(fonts.cache,hash,tfmdata) -- for tracing purposes - end + tfmdata = definers.loadfont(specification) -- can be overloaded if tfmdata then - tfmdata.hash = hash - tfmdata.cache = "no" +--~ constructors.checkvirtualid(tfmdata) -- interferes + tfmdata.properties.hash = hash if id then definers.register(tfmdata,id) end @@ -15766,116 +11026,82 @@ function definers.read(specification,size,id) -- id can be optional, name can al if not tfmdata then -- or id? report_defining( "unknown font %s, loading aborted",specification.name) elseif trace_defining and type(tfmdata) == "table" then + constructors.finalize(tfmdata) + -- local properties = tfmdata.properties or { } + -- local parameters = tfmdata.parameters or { } report_defining("using %s font with id %s, name:%s size:%s bytes:%s encoding:%s fullname:%s filename:%s", - tfmdata.type or "unknown", - id or "?", - tfmdata.name or "?", - tfmdata.size or "default", - tfmdata.encodingbytes or "?", - tfmdata.encodingname or "unicode", - tfmdata.fullname or "?", - file.basename(tfmdata.filename or "?")) + properties.type or "unknown", + id or "?", + properties.name or "?", + parameters.size or "default", + properties.encodingbytes or "?", + properties.encodingname or "unicode", + properties.fullname or "?", + file.basename(properties.filename or "?")) end statistics.stoptiming(fonts) return tfmdata end -function vf.find(name) - name = file.removesuffix(file.basename(name)) - if tfm.resolvevirtualtoo then - local format = fonts.logger.format(name) - if format == 'tfm' or format == 'ofm' then - if trace_defining then - report_defining("locating vf for %s",name) - end - return findbinfile(name,"ovf") - else - if trace_defining then - report_defining("vf for %s is already taken care of",name) - end - return nil -- "" - end - else - if trace_defining then - report_defining("locating vf for %s",name) - end - return findbinfile(name,"ovf") - end -end - --[[ldx-- -

We overload both the and readers.

+

We overload the reader.

--ldx]]-- -callbacks.register('define_font' , definers.read, "definition of fonts (tfmtable preparation)") -callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more +callbacks.register('define_font' , definers.read, "definition of fonts (tfmdata preparation)") end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['font-xtx'] = { +if not modules then modules = { } end modules ['luatex-font-def'] = { version = 1.001, - comment = "companion to font-ini.mkiv", + comment = "companion to luatex-*.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -local texsprint, count = tex.sprint, tex.count -local format, concat, gmatch, match, find, lower = string.format, table.concat, string.gmatch, string.match, string.find, string.lower -local tostring, next = tostring, next -local lpegmatch = lpeg.match +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end -local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end) +local fonts = fonts ---[[ldx-- -

Choosing a font by name and specififying its size is only part of the -game. In order to prevent complex commands, introduced -a method to pass feature information as part of the font name. At the -risk of introducing nasty parsing and compatinility problems, this -syntax was expanded over time.

- -

For the sake of users who have defined fonts using that syntax, we -will support it, but we will provide additional methods as well. -Normally users will not use this direct way, but use a more abstract -interface.

- -

The next one is the official one. However, in the plain -variant we need to support the crappy [] specification as -well and that does not work too well with the general design -of the specifier.

---ldx]]-- +-- A bit of tuning for definitions. -local fonts = fonts -local definers = fonts.definers -local specifiers = definers.specifiers -local normalize_meanings = fonts.otf.meanings.normalize +fonts.constructors.namemode = "specification" -- somehow latex needs this (changed name!) => will change into an overload -local list = { } +-- tricky: we sort of bypass the parser and directly feed all into +-- the sub parser + +function fonts.definers.getspecification(str) + return "", str, "", ":", str +end -specifiers.colonizedpreference = "file" +-- the generic name parser (different from context!) -local function issome () list.lookup = specifiers.colonizedpreference end -local function isfile () list.lookup = 'file' end -local function isname () list.lookup = 'name' end -local function thename(s) list.name = s end -local function issub (v) list.sub = v end -local function iscrap (s) list.crap = string.lower(s) end -local function istrue (s) list[s] = 'yes' end -local function isfalse(s) list[s] = 'no' end -local function iskey (k,v) list[k] = v end +local list = { } -local function istrue (s) list[s] = true end -local function isfalse(s) list[s] = false end +local function issome () list.lookup = 'name' end -- xetex mode prefers name (not in context!) +local function isfile () list.lookup = 'file' end +local function isname () list.lookup = 'name' end +local function thename(s) list.name = s end +local function issub (v) list.sub = v end +local function iscrap (s) list.crap = string.lower(s) end +local function iskey (k,v) list[k] = v end +local function istrue (s) list[s] = true end +local function isfalse(s) list[s] = false end local P, S, R, C = lpeg.P, lpeg.S, lpeg.R, lpeg.C local spaces = P(" ")^0 local namespec = (1-S("/:("))^0 -- was: (1-S("/: ("))^0 local crapspec = spaces * P("/") * (((1-P(":"))^0)/iscrap) * spaces -local filename = (P("file:")/isfile * (namespec/thename)) + (P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]")) -local fontname = (P("name:")/isname * (namespec/thename)) + P(true)/issome * (namespec/thename) +local filename_1 = P("file:")/isfile * (namespec/thename) +local filename_2 = P("[") * P(true)/isname * (((1-P("]"))^0)/thename) * P("]") +local fontname_1 = P("name:")/isname * (namespec/thename) +local fontname_2 = P(true)/issome * (namespec/thename) local sometext = (R("az","AZ","09") + S("+-."))^1 local truevalue = P("+") * spaces * (sometext/istrue) local falsevalue = P("-") * spaces * (sometext/isfalse) @@ -15884,17 +11110,12 @@ local somevalue = sometext/istrue local subvalue = P("(") * (C(P(1-S("()"))^1)/issub) * P(")") -- for Kim local option = spaces * (keyvalue + falsevalue + truevalue + somevalue) * spaces local options = P(":") * spaces * (P(";")^0 * option)^0 -local pattern = (filename + fontname) * subvalue^0 * crapspec^0 * options^0 + +local pattern = (filename_1 + filename_2 + fontname_1 + fontname_2) * subvalue^0 * crapspec^0 * options^0 local function colonized(specification) -- xetex mode list = { } - lpegmatch(pattern,specification.specification) - -- for k, v in next, list do - -- list[k] = is_boolean(v) - -- if type(list[a]) == "nil" then - -- list[k] = v - -- end - -- end + lpeg.match(pattern,specification.specification) list.crap = nil -- style not supported, maybe some day if list.name then specification.name = list.name @@ -15908,18 +11129,33 @@ local function colonized(specification) -- xetex mode specification.sub = list.sub list.sub = nil end - -- specification.features.normal = list - specification.features.normal = normalize_meanings(list) + specification.features.normal = fonts.handlers.otf.features.normalize(list) return specification end -definers.registersplit(":",colonized,"cryptic") +fonts.definers.registersplit(":",colonized,"cryptic") +fonts.definers.registersplit("", colonized,"more cryptic") -- catches \font\text=[names] + +function definers.applypostprocessors(tfmdata) + local postprocessors = tfmdata.postprocessors + if postprocessors then + 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 = string.gsub(lower(extrahash),"[^a-z]","-") + tfmdata.properties.fullname = format("%s-%s",tfmdata.properties.fullname,extrahash) + end + end + end + return tfmdata +end end -- closure do -- begin closure to overcome local limits and interference -if not modules then modules = { } end modules ['font-dum'] = { +if not modules then modules = { } end modules ['luatex-fonts-ext'] = { version = 1.001, comment = "companion to luatex-*.tex", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", @@ -15927,125 +11163,46 @@ if not modules then modules = { } end modules ['font-dum'] = { license = "see context related readme files" } -fonts = fonts or { } - --- general - -fonts.otf.pack = false -- only makes sense in context -fonts.tfm.resolvevirtualtoo = false -- context specific (due to resolver) -fonts.tfm.fontnamemode = "specification" -- somehow latex needs this (changed name!) - --- readers - -fonts.tfm.readers = fonts.tfm.readers or { } -fonts.tfm.readers.sequence = { 'otf', 'ttf', 'tfm', 'lua' } -fonts.tfm.readers.afm = nil - --- define - -fonts.definers = fonts.definers or { } -fonts.definers.specifiers = fonts.definers.specifiers or { } - -fonts.definers.specifiers.colonizedpreference = "name" -- is "file" in context - -function fonts.definers.getspecification(str) - return "", str, "", ":", str -end - -fonts.definers.registersplit("",fonts.definers.specifiers.variants[":"]) -- we add another one for catching lone [names] - --- logger - -fonts.logger = fonts.logger or { } - -function fonts.logger.save() -end - --- names --- --- Watch out, the version number is the same as the one used in --- the mtx-fonts.lua function scripts.fonts.names as we use a --- simplified font database in the plain solution and by using --- a different number we're less dependent on context. - -fonts.names = fonts.names or { } - -fonts.names.version = 1.001 -- not the same as in context -fonts.names.basename = "luatex-fonts-names.lua" -fonts.names.new_to_old = { } -fonts.names.old_to_new = { } - -local data, loaded = nil, false - -local fileformats = { "lua", "tex", "other text files" } - -function fonts.names.resolve(name,sub) - if not loaded then - local basename = fonts.names.basename - if basename and basename ~= "" then - for i=1,#fileformats do - local format = fileformats[i] - local foundname = resolvers.findfile(basename,format) or "" - if foundname ~= "" then - data = dofile(foundname) - break - end - end - end - loaded = true - end - if type(data) == "table" and data.version == fonts.names.version then - local condensed = string.gsub(string.lower(name),"[^%a%d]","") - local found = data.mappings and data.mappings[condensed] - if found then - local fontname, filename, subfont = found[1], found[2], found[3] - if subfont then - return filename, fontname - else - return filename, false - end - else - return name, false -- fallback to filename - end - end -end - -fonts.names.resolvespec = fonts.names.resolve -- only supported in mkiv - -function fonts.names.getfilename(askedname,suffix) -- only supported in mkiv - return "" +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() end --- For the moment we put this (adapted) pseudo feature here. +local fonts = fonts +local otffeatures = fonts.constructors.newfeatures("otf") -table.insert(fonts.triggers,"itlc") +-- A few generic extensions. -local function itlc(tfmdata,value) +local function initializeitlc(tfmdata,value) if value then -- the magic 40 and it formula come from Dohyun Kim - local metadata = tfmdata.shared.otfdata.metadata - if metadata then - local italicangle = metadata.italicangle - if italicangle and italicangle ~= 0 then - local uwidth = (metadata.uwidth or 40)/2 - for unicode, d in next, tfmdata.descriptions do - local it = d.boundingbox[3] - d.width + uwidth - if it ~= 0 then - d.italic = it - end + local parameters = tfmdata.parameters + local italicangle = parameters.italicangle + if italicangle and italicangle ~= 0 then + local uwidth = (parameters.uwidth or 40)/2 + for unicode, d in next, tfmdata.descriptions do + local it = d.boundingbox[3] - d.width + uwidth + if it ~= 0 then + d.italic = it end - tfmdata.has_italic = true end + tfmdata.properties.italic_correction = true end end end -fonts.initializers.base.otf.itlc = itlc -fonts.initializers.node.otf.itlc = itlc +otffeatures.register { + name = "itlc", + description = "italic correction", + initializers = { + base = initializeitlc, + node = initializeitlc, + } +} -- slant and extend -function fonts.initializers.common.slant(tfmdata,value) +local function initializeslant(tfmdata,value) value = tonumber(value) if not value then value = 0 @@ -16054,10 +11211,19 @@ function fonts.initializers.common.slant(tfmdata,value) elseif value < -1 then value = -1 end - tfmdata.slant_factor = value + tfmdata.parameters.slant_factor = value end -function fonts.initializers.common.extend(tfmdata,value) +otffeatures.register { + name = "slant", + description = "slant glyphs", + initializers = { + base = initializeslant, + node = initializeslant, + } +} + +local function initializeextend(tfmdata,value) value = tonumber(value) if not value then value = 0 @@ -16066,31 +11232,34 @@ function fonts.initializers.common.extend(tfmdata,value) elseif value < -10 then value = -10 end - tfmdata.extend_factor = value + tfmdata.parameters.extend_factor = value end -table.insert(fonts.triggers,"slant") -table.insert(fonts.triggers,"extend") - -fonts.initializers.base.otf.slant = fonts.initializers.common.slant -fonts.initializers.node.otf.slant = fonts.initializers.common.slant -fonts.initializers.base.otf.extend = fonts.initializers.common.extend -fonts.initializers.node.otf.extend = fonts.initializers.common.extend +otffeatures.register { + name = "extend", + description = "scale glyphs horizontally", + initializers = { + base = initializeextend, + node = initializeextend, + } +} -- expansion and protrusion fonts.protrusions = fonts.protrusions or { } fonts.protrusions.setups = fonts.protrusions.setups or { } -local setups = fonts.protrusions.setups +local setups = fonts.protrusions.setups -function fonts.initializers.common.protrusion(tfmdata,value) +local function initializeprotrusion(tfmdata,value) if value then local setup = setups[value] if setup then local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1 local emwidth = tfmdata.parameters.quad - tfmdata.auto_protrude = true + tfmdata.parameters.protrusion = { + auto = true, + } for i, chr in next, tfmdata.characters do local v, pl, pr = setup[i], nil, nil if v then @@ -16103,17 +11272,31 @@ function fonts.initializers.common.protrusion(tfmdata,value) end end +otffeatures.register { + name = "protrusion", + description = "shift characters into the left and or right margin", + initializers = { + base = initializeprotrusion, + node = initializeprotrusion, + } +} + fonts.expansions = fonts.expansions or { } fonts.expansions.setups = fonts.expansions.setups or { } local setups = fonts.expansions.setups -function fonts.initializers.common.expansion(tfmdata,value) +local function initializeexpansion(tfmdata,value) if value then local setup = setups[value] if setup then - local stretch, shrink, step, factor = setup.stretch or 0, setup.shrink or 0, setup.step or 0, setup.factor or 1 - tfmdata.stretch, tfmdata.shrink, tfmdata.step, tfmdata.auto_expand = stretch * 10, shrink * 10, step * 10, true + local factor = setup.factor or 1 + tfmdata.parameters.expansion = { + stretch = 10 * (setup.stretch or 0), + shrink = 10 * (setup.shrink or 0), + step = 10 * (setup.step or 0), + auto = true, + } for i, chr in next, tfmdata.characters do local v = setup[i] if v and v ~= 0 then @@ -16126,18 +11309,18 @@ function fonts.initializers.common.expansion(tfmdata,value) end end -table.insert(fonts.manipulators,"protrusion") -table.insert(fonts.manipulators,"expansion") - -fonts.initializers.base.otf.protrusion = fonts.initializers.common.protrusion -fonts.initializers.node.otf.protrusion = fonts.initializers.common.protrusion -fonts.initializers.base.otf.expansion = fonts.initializers.common.expansion -fonts.initializers.node.otf.expansion = fonts.initializers.common.expansion +otffeatures.register { + name = "expansion", + description = "apply hz optimization", + initializers = { + base = initializeexpansion, + node = initializeexpansion, + } +} -- left over -function fonts.registermessage() -end +function fonts.loggers.onetimemessage() end -- example vectors @@ -16179,68 +11362,37 @@ fonts.protrusions.setups['default'] = { -- normalizer -fonts.otf.meanings = fonts.otf.meanings or { } - -fonts.otf.meanings.normalize = fonts.otf.meanings.normalize or function(t) +fonts.handlers.otf.features.normalize = function(t) if t.rand then t.rand = "random" end -end - --- needed (different in context) - -function fonts.otf.scriptandlanguage(tfmdata) - return tfmdata.script, tfmdata.language + return t end -- bonus -function fonts.otf.nametoslot(name) - local tfmdata = fonts.identifiers[font.current()] - if tfmdata and tfmdata.shared then - local otfdata = tfmdata.shared.otfdata - local unicode = otfdata.luatex.unicodes[name] - return unicode and (type(unicode) == "number" and unicode or unicode[1]) +function fonts.helpers.nametoslot(name) + local t = type(name) + if t == "string" then + local tfmdata = fonts.hashes.identifiers[currentfont()] + local shared = tfmdata and tfmdata.shared + local fntdata = shared and shared.rawdata + return fntdata and fntdata.resources.unicodes[name] + elseif t == "number" then + return n end end -function fonts.otf.char(n) - if type(n) == "string" then - n = fonts.otf.nametoslot(n) - end - if type(n) == "number" then - tex.sprint("\\char" .. n) - end -end - --- another one: - -fonts.strippables = 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, -} - -- \font\test=file:somefont:reencode=mymessup -- --- fonts.enc.reencodings.mymessup = { +-- fonts.encodings.reencodings.mymessup = { -- [109] = 110, -- m -- [110] = 109, -- n -- } -fonts.enc = fonts.enc or {} -local reencodings = { } -fonts.enc.reencodings = reencodings +fonts.encodings = fonts.encodings or { } +local reencodings = { } +fonts.encodings.reencodings = reencodings local function specialreencode(tfmdata,value) -- we forget about kerns as we assume symbols and we @@ -16271,7 +11423,86 @@ local function reencode(tfmdata,value) ) end -table.insert(fonts.manipulators,"reencode") -fonts.initializers.base.otf.reencode = reencode +otffeatures.register { + name = "reencode", + description = "reencode characters", + manipulators = { + base = reencode, + node = reencode, + } +} + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules = { } end modules ['luatex-fonts-cbk'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local nodes = nodes + +-- Fonts: (might move to node-gef.lua) + +local traverse_id = node.traverse_id +local glyph_code = nodes.nodecodes.glyph + +function nodes.handlers.characters(head) + local fontdata = fonts.hashes.identifiers + if fontdata then + local usedfonts, done, prevfont = { }, false, nil + for n in traverse_id(glyph_code,head) do + local font = n.font + if font ~= prevfont then + prevfont = font + local used = usedfonts[font] + if not used then + local tfmdata = fontdata[font] -- + if tfmdata then + local shared = tfmdata.shared -- we need to check shared, only when same features + if shared then + local processors = shared.processes + if processors and #processors > 0 then + usedfonts[font] = processors + done = true + end + end + end + end + end + end + if done then + for font, processors in next, usedfonts do + for i=1,#processors do + local h, d = processors[i](head,font,0) + head, done = h or head, done or d + end + end + end + return head, true + else + return head, false + end +end + +function nodes.simple_font_handler(head) +-- lang.hyphenate(head) + head = nodes.handlers.characters(head) + nodes.injections.handler(head) + nodes.handlers.protectglyphs(head) + head = node.ligaturing(head) + head = node.kerning(head) + return head +end end -- closure diff --git a/tex/generic/context/luatex-fonts-syn.lua b/tex/generic/context/luatex-fonts-syn.lua new file mode 100644 index 000000000..36a74d0f4 --- /dev/null +++ b/tex/generic/context/luatex-fonts-syn.lua @@ -0,0 +1,83 @@ +if not modules then modules = { } end modules ['luatex-fonts-syn'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +-- Generic font names support. +-- +-- Watch out, the version number is the same as the one used in +-- the mtx-fonts.lua function scripts.fonts.names as we use a +-- simplified font database in the plain solution and by using +-- a different number we're less dependent on context. +-- +-- mtxrun --script font --reload --simple +-- +-- The format of the file is as follows: +-- +-- return { +-- ["version"] = 1.001, +-- ["mappings"] = { +-- ["somettcfontone"] = { "Some TTC Font One", "SomeFontA.ttc", 1 }, +-- ["somettcfonttwo"] = { "Some TTC Font Two", "SomeFontA.ttc", 2 }, +-- ["somettffont"] = { "Some TTF Font", "SomeFontB.ttf" }, +-- ["someotffont"] = { "Some OTF Font", "SomeFontC.otf" }, +-- }, +-- } + +local fonts = fonts +fonts.names = fonts.names or { } + +fonts.names.version = 1.001 -- not the same as in context +fonts.names.basename = "luatex-fonts-names.lua" +fonts.names.new_to_old = { } +fonts.names.old_to_new = { } + +local data, loaded = nil, false + +local fileformats = { "lua", "tex", "other text files" } + +function fonts.names.resolve(name,sub) + if not loaded then + local basename = fonts.names.basename + if basename and basename ~= "" then + for i=1,#fileformats do + local format = fileformats[i] + local foundname = resolvers.findfile(basename,format) or "" + if foundname ~= "" then + data = dofile(foundname) + texio.write("") + break + end + end + end + loaded = true + end + if type(data) == "table" and data.version == fonts.names.version then + local condensed = string.gsub(string.lower(name),"[^%a%d]","") + local found = data.mappings and data.mappings[condensed] + if found then + local fontname, filename, subfont = found[1], found[2], found[3] + if subfont then + return filename, fontname + else + return filename, false + end + else + return name, false -- fallback to filename + end + end +end + +fonts.names.resolvespec = fonts.names.resolve -- only supported in mkiv + +function fonts.names.getfilename(askedname,suffix) -- only supported in mkiv + return "" +end diff --git a/tex/generic/context/luatex-fonts-tfm.lua b/tex/generic/context/luatex-fonts-tfm.lua new file mode 100644 index 000000000..b9bb1bd0f --- /dev/null +++ b/tex/generic/context/luatex-fonts-tfm.lua @@ -0,0 +1,38 @@ +if not modules then modules = { } end modules ['luatex-fonts-tfm'] = { + version = 1.001, + comment = "companion to luatex-*.tex", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +if context then + texio.write_nl("fatal error: this module is not for context") + os.exit() +end + +local fonts = fonts +local tfm = { } +fonts.handlers.tfm = tfm +fonts.formats.tfm = "type1" -- we need to have at least a value here + +function fonts.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 + local foundname = resolvers.findbinfile(fullname, 'tfm') or "" + if foundname == "" then + foundname = resolvers.findbinfile(fullname, 'ofm') or "" + end + if foundname ~= "" then + specification.filename = foundname + specification.format = "ofm" + return font.read_tfm(specification.filename,specification.size) + end +end diff --git a/tex/generic/context/luatex-fonts.lua b/tex/generic/context/luatex-fonts.lua index c96dada77..84d79a63f 100644 --- a/tex/generic/context/luatex-fonts.lua +++ b/tex/generic/context/luatex-fonts.lua @@ -6,8 +6,58 @@ if not modules then modules = { } end modules ['luatex-fonts'] = { license = "see context related readme files" } +-- The following code isolates the generic ConTeXt code from already +-- defined or to be defined namespaces. + +-- todo: all global namespaces in called modules will get local shortcuts + +utf = unicode.utf8 + +if not generic_context then + + generic_context = { } + +end + +if not generic_context.push_namespaces then + + function generic_context.push_namespaces() + texio.write(" ") + local normalglobal = { } + for k, v in next, _G do + normalglobal[k] = v + end + return normalglobal + end + + function generic_context.pop_namespaces(normalglobal,isolate) + if normalglobal then + texio.write(" ") + for k, v in next, _G do + if not normalglobal[k] then + generic_context[k] = v + if isolate then + _G[k] = nil + end + end + end + for k, v in next, normalglobal do + _G[k] = v + end + -- just to be sure: + setmetatable(generic_context,_G) + else + texio.write(" ") + os.exit() + end + end + +end + +local whatever = generic_context.push_namespaces() + -- We keep track of load time by storing the current time. That --- way we cannot be accused of slowing down luading too much. +-- way we cannot be accused of slowing down loading too much. -- -- Please don't update to this version without proper testing. It -- might be that this version lags behind stock context and the only @@ -58,7 +108,8 @@ if fonts then texio.write_nl("log", "! if you have ConTeXt installed you can try to delete the file") texio.write_nl("log", "! 'luatex-font-merged.lua' as I might then use the possibly") texio.write_nl("log", "! updated libraries. The merged version is not supported as it") - texio.write_nl("log", "! is a frozen instance.") + texio.write_nl("log", "! is a frozen instance. Problems can be reported to the ConTeXt") + texio.write_nl("log", "! mailing list.") texio.write_nl("log", "!") end @@ -83,24 +134,20 @@ else -- lack of other modules. -- First we load a few helper modules. This is about the miminum - -- needed to let the font modules do theuir work. + -- needed to let the font modules do their work. Don't depend on + -- their functions as we might strip them in future versions of + -- this generic variant. - loadmodule('luat-dum.lua') -- not used in context at all - loadmodule('data-con.lua') -- maybe some day we don't need this one + loadmodule('luatex-basics-gen.lua') + loadmodule('data-con.lua') - -- We do need some basic node support although the following - -- modules contain a little bit of code that is not used. It's - -- not worth weeding. Beware, in node-dum some functions use - -- fonts.* tables, not that nice but I don't want two dummy - -- files. Some day I will sort this out (no problem in context). + -- We do need some basic node support. The code in there is not for + -- general use as it might change. - loadmodule('node-dum.lua') - loadmodule('node-inj.lua') -- will be replaced (luatex >= .70) + loadmodule('luatex-basics-nod.lua') -- Now come the font modules that deal with traditional TeX fonts - -- as well as open type fonts. We don't load the afm related code - -- from font-enc.lua and font-afm.lua as only ConTeXt deals with - -- it. + -- as well as open type fonts. We only support OpenType fonts here. -- -- The font database file (if used at all) must be put someplace -- visible for kpse and is not shared with ConTeXt. The mtx-fonts @@ -108,37 +155,58 @@ else -- option). loadmodule('font-ini.lua') - loadmodule('font-tfm.lua') -- will be split (we may need font-log) + loadmodule('font-con.lua') + loadmodule('luatex-fonts-enc.lua') -- will load font-age on demand loadmodule('font-cid.lua') - loadmodule('font-ott.lua') -- might be split - loadmodule('font-map.lua') -- for loading lum file (will be stripped) - loadmodule('font-lua.lua') - loadmodule('font-otf.lua') - loadmodule('font-otd.lua') + loadmodule('font-map.lua') -- for loading lum file (will be stripped) + loadmodule('luatex-fonts-syn.lua') -- deals with font names (synonyms) + loadmodule('luatex-fonts-tfm.lua') loadmodule('font-oti.lua') + loadmodule('font-otf.lua') loadmodule('font-otb.lua') + loadmodule('node-inj.lua') -- will be replaced (luatex >= .70) loadmodule('font-otn.lua') loadmodule('font-ota.lua') - loadmodule('font-otc.lua') - loadmodule('font-age.lua') -- special for this variant + loadmodule('luatex-fonts-lua.lua') loadmodule('font-def.lua') - loadmodule('font-xtx.lua') - loadmodule('font-dum.lua') + loadmodule('luatex-fonts-def.lua') + loadmodule('luatex-fonts-ext.lua') -- some extensions + + -- We need to plug into a callback and the following module implements + -- the handlers. Actual plugging in happens later. + + loadmodule('luatex-fonts-cbk.lua') end resolvers.loadmodule = loadmodule -- In order to deal with the fonts we need to initialize some --- callbacks. One can overload them later on if needed. +-- callbacks. One can overload them later on if needed. First +-- a bit of abstraction. + +generic_context.callback_ligaturing = false +generic_context.callback_kerning = false +generic_context.callback_pre_linebreak_filter = nodes.simple_font_handler +generic_context.callback_hpack_filter = nodes.simple_font_handler +generic_context.callback_define_font = fonts.definers.read + +-- The next ones can be done at a different moment if needed. You can create +-- a generic_context namespace and set no_callbacks_yet to true, load this +-- module, and enable the callbacks later. + +if not generic_context.no_callbacks_yet then -callback.register('ligaturing', false) -callback.register('kerning', false) -callback.register('pre_linebreak_filter', nodes.simple_font_handler) -callback.register('hpack_filter', nodes.simple_font_handler) -callback.register('define_font' , fonts.definers.read) -callback.register('find_vf_file', nil) -- reset to normal + callback.register('ligaturing', generic_context.callback_ligaturing) + callback.register('kerning', generic_context.callback_kerning) + callback.register('pre_linebreak_filter', generic_context.callback_pre_linebreak_filter) + callback.register('hpack_filter', generic_context.callback_hpack_filter) + callback.register('define_font' , generic_context.callback_define_font) + +end -- We're done. texio.write(string.format(" ", os.gettimeofday()-starttime)) + +generic_context.pop_namespaces(whatever) -- cgit v1.2.3
%s  %s  %s  %s