From 4ed30744220cf0763f968c837b0ff7dd367f19b2 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Fri, 25 Mar 2011 18:03:00 +0100 Subject: beta 2011.03.25 18:03 --- 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 +- 236 files changed, 20673 insertions(+), 22221 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 (limited to 'tex') 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