From 69d17e62dd57cce4d59d020319edf1c5121f4319 Mon Sep 17 00:00:00 2001 From: Context Git Mirror Bot Date: Sun, 21 Dec 2014 23:15:04 +0100 Subject: 2014-12-21 22:28:00 --- tex/context/base/attr-ini.lua | 14 +- tex/context/base/attr-ini.mkiv | 2 + tex/context/base/catc-ini.mkiv | 1 + tex/context/base/cont-new.mkiv | 2 +- tex/context/base/context-version.pdf | Bin 4387 -> 4381 bytes tex/context/base/context.mkiv | 2 +- tex/context/base/font-inj.lua | 434 +++---- tex/context/base/font-nod.lua | 83 +- tex/context/base/font-otf.lua | 33 +- tex/context/base/font-otn.lua | 447 +++++-- tex/context/base/font-tra.mkiv | 13 +- tex/context/base/lang-dis.lua | 198 +++ tex/context/base/lang-hyp.lua | 236 ++-- tex/context/base/lang-hyp.mkiv | 12 +- tex/context/base/lang-ini.lua | 20 +- tex/context/base/lang-ini.mkiv | 2 + tex/context/base/mult-low.lua | 4 +- tex/context/base/node-ini.lua | 6 +- tex/context/base/node-nut.lua | 14 + tex/context/base/node-shp.lua | 27 +- tex/context/base/node-tra.lua | 8 +- tex/context/base/s-fonts-ligatures.mkiv | 284 +++++ tex/context/base/s-typesetting-kerning.mkiv | 127 ++ tex/context/base/status-files.pdf | Bin 24632 -> 24862 bytes tex/context/base/status-lua.pdf | Bin 344758 -> 344957 bytes tex/context/base/status-mkiv.lua | 18 + tex/context/base/syst-aux.mkiv | 5 +- tex/context/base/task-ini.lua | 8 +- tex/context/base/trac-vis.lua | 2 +- tex/context/base/typo-brk.mkiv | 5 +- tex/context/base/typo-krn.lua | 529 +++++--- tex/generic/context/luatex/luatex-basics-nod.lua | 8 +- tex/generic/context/luatex/luatex-fonts-cbk.lua | 44 +- tex/generic/context/luatex/luatex-fonts-inj.lua | 1286 +++++++++++++------- tex/generic/context/luatex/luatex-fonts-merged.lua | 1204 ++++++++++++------ tex/generic/context/luatex/luatex-fonts.lua | 9 +- tex/generic/context/luatex/luatex-test.tex | 14 +- 37 files changed, 3608 insertions(+), 1493 deletions(-) create mode 100644 tex/context/base/lang-dis.lua create mode 100644 tex/context/base/s-fonts-ligatures.mkiv create mode 100644 tex/context/base/s-typesetting-kerning.mkiv (limited to 'tex') diff --git a/tex/context/base/attr-ini.lua b/tex/context/base/attr-ini.lua index 1e518467c..aaa599d58 100644 --- a/tex/context/base/attr-ini.lua +++ b/tex/context/base/attr-ini.lua @@ -53,13 +53,13 @@ storage.register("attributes/list", list, "attributes.list") names[0], numbers["fontdynamic"] = "fontdynamic", 0 --[[ldx-- -

We can use the attributes in the range 127-255 (outside user space). These -are only used when no attribute is set at the \TEX\ end which normally -happens in .

+

private attributes are used by the system and public ones are for users. We use dedicated +ranges of numbers for them. Of course a the end a private attribute can be +accessible too, so a private attribute can have a public appearance.

--ldx]]-- -sharedstorage.attributes_last_private = sharedstorage.attributes_last_private or 127 -sharedstorage.attributes_last_public = sharedstorage.attributes_last_public or 1024 +sharedstorage.attributes_last_private = sharedstorage.attributes_last_private or 127 -- very private +sharedstorage.attributes_last_public = sharedstorage.attributes_last_public or 1024 -- less private function attributes.private(name) -- at the lua end (hidden from user) local number = numbers[name] @@ -97,8 +97,8 @@ end attributes.system = attributes.private -function attributes.define(name,number,category) - return (attributes[category or "public"] or attributes["public"])(name,number) +function attributes.define(name,category) + return (attributes[category or "public"] or attributes["public"])(name) end -- tracers diff --git a/tex/context/base/attr-ini.mkiv b/tex/context/base/attr-ini.mkiv index 0c5762534..d4912ed65 100644 --- a/tex/context/base/attr-ini.mkiv +++ b/tex/context/base/attr-ini.mkiv @@ -81,7 +81,9 @@ \expandafter\attributedef\csname\??attributecount#2\endcsname\scratchcounter \expandafter\newconstant \csname\??attributeid#2\endcsname \csname\??attributeid#2\endcsname\scratchcounter + % some attributes are always global \doifnotinset\s!global{#3}{\appendetoks\csname\??attributecount#2\endcsname\attributeunsetvalue\to\attributesresetlist}% + % here public means 'visible' so it's not to be confused with 'public' at the lua end \doifinset \s!public{#3}{\expandafter\let\csname#2\s!attribute\expandafter\endcsname\csname\??attributeid#2\endcsname}} \unexpanded\def\newattribute#1{\attr_basics_define_indeed{public}[\strippedcsname#1][]} diff --git a/tex/context/base/catc-ini.mkiv b/tex/context/base/catc-ini.mkiv index d8247217c..581fbfec3 100644 --- a/tex/context/base/catc-ini.mkiv +++ b/tex/context/base/catc-ini.mkiv @@ -54,6 +54,7 @@ \setnewconstant\ampersandasciicode 38 \setnewconstant\singlequoteasciicode 39 % ' \setnewconstant\primeasciicode 39 % ' +\setnewconstant\hyphenasciicode 45 \setnewconstant\forwardslashasciicode 47 % / \setnewconstant\colonasciicode 58 \setnewconstant\lessthanasciicode 60 % < used as alternative verbatim { diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv index 303fef490..6beccd49a 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{2014.12.11 12:02} +\newcontextversion{2014.12.21 22:25} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf index 750551066..f86b51291 100644 Binary files a/tex/context/base/context-version.pdf and b/tex/context/base/context-version.pdf differ diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv index 8b075404f..51b6e654a 100644 --- a/tex/context/base/context.mkiv +++ b/tex/context/base/context.mkiv @@ -28,7 +28,7 @@ %D up and the dependencies are more consistent. \edef\contextformat {\jobname} -\edef\contextversion{2014.12.11 12:02} +\edef\contextversion{2014.12.21 22:25} \edef\contextkind {beta} %D For those who want to use this: diff --git a/tex/context/base/font-inj.lua b/tex/context/base/font-inj.lua index 68b3e3f23..3b933829d 100644 --- a/tex/context/base/font-inj.lua +++ b/tex/context/base/font-inj.lua @@ -52,7 +52,6 @@ local getsubtype = nuts.getsubtype local getchar = nuts.getchar local traverse_id = nuts.traverse_id -local traverse = nuts.traverse local insert_node_before = nuts.insert_before local insert_node_after = nuts.insert_after local find_tail = nuts.tail @@ -68,6 +67,19 @@ local nofregisteredpairs = 0 local nofregisteredmarks = 0 local nofregisteredcursives = 0 ----- markanchors = { } -- one base can have more marks +local keepregisteredcounts = false + +function injections.keepcounts() + keepregisteredcounts = true +end + +function injections.resetcounts() + nofregisteredkerns = 0 + nofregisteredpairs = 0 + nofregisteredmarks = 0 + nofregisteredcursives = 0 + keepregisteredcounts = false +end function injections.reset(n) local p = rawget(properties,start) @@ -81,10 +93,14 @@ end function injections.setligaindex(n,index) local p = rawget(properties,n) if p then - p = p.injections - end - if p then - p.ligaindex = index + local i = p.injections + if i then + i.ligaindex = index + else + p.injections = { + ligaindex = index + } + end else properties[n] = { injections = { @@ -118,10 +134,14 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne -- local p = rawget(properties,start) if p then - p = p.injections - end - if p then - p.cursiveanchor = true + local i = p.injections + if i then + i.cursiveanchor = true + else + p.injections = { + cursiveanchor = true, + } + end else properties[start] = { injections = { @@ -131,11 +151,16 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne end local p = rawget(properties,nxt) if p then - p = p.injections - end - if p then - p.cursivex = dx - p.cursivey = dy + local i = p.injections + if i then + i.cursivex = dx + i.cursivey = dy + else + p.injections = { + cursivex = dx, + cursivey = dy, + } + end else properties[nxt] = { injections = { @@ -151,8 +176,8 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay? local yoffset = y - h - local leftkern = x - local rightkern = w - x + local leftkern = x -- both kerns are set in a pair kern compared + local rightkern = w - x -- to normal kerns where we set only leftkern if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then nofregisteredpairs = nofregisteredpairs + 1 if rlmode and rlmode < 0 then @@ -160,15 +185,25 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l end local p = rawget(properties,current) if p then - p = p.injections - end - if p then - if leftkern ~= 0 or rightkern ~= 0 then - p.leftkern = (p.leftkern or 0) + leftkern - p.rightkern = (p.rightkern or 0) + rightkern - end - if yoffset ~= 0 then - p.yoffset = (p.yoffset or 0) + yoffset + local i = p.injections + if i then + if leftkern ~= 0 or rightkern ~= 0 then + i.leftkern = i.leftkern or 0 + leftkern + i.rightkern = i.rightkern or 0 + rightkern + end + if yoffset ~= 0 then + i.yoffset = i.yoffset or 0 + yoffset + end + elseif leftkern ~= 0 or rightkern ~= 0 then + p.injections = { + leftkern = leftkern, + rightkern = rightkern, + yoffset = yoffset, + } + else + p.injections = { + yoffset = yoffset, + } end elseif leftkern ~= 0 or rightkern ~= 0 then properties[current] = { @@ -205,10 +240,14 @@ function injections.setkern(current,factor,rlmode,x,injection) injection = "injections" end if p then - p = p[injection] - end - if p then - p.leftkern = dx + (p.leftkern or 0) + local i = p[injection] + if i then + i.leftkern = dx + i.leftkern or 0 + else + p[injection] = { + leftkern = dx, + } + end else properties[current] = { [injection] = { @@ -231,14 +270,22 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean end local p = rawget(properties,start) if p then - p = p.injections - end - if p then - p.markx = dx - p.marky = dy - p.markdir = rlmode or 0 - p.markbase = nofregisteredmarks - p.markbasenode = base + local i = p.injections + if i then + i.markx = dx + i.marky = dy + i.markdir = rlmode or 0 + i.markbase = nofregisteredmarks + i.markbasenode = base + else + p.injections = { + markx = dx, + marky = dy, + markdir = rlmode or 0, + markbase = nofregisteredmarks, + markbasenode = base, + } + end else properties[start] = { injections = { @@ -395,46 +442,6 @@ local function collect_glyphs_1(head) return glyphs, nofglyphs, marks, nofmarks end --- local function run(n) --- local pn = rawget(properties,n) --- if pn then --- pn = pn.injections --- end --- local f = getfont(n) --- if f ~= nf then --- nf = f --- tm = fontdata[nf].resources.marks -- other hash in ctx --- end --- if tm and tm[getchar(n)] then -- also in disc? --- nofmarks = nofmarks + 1 --- marks[nofmarks] = n --- else --- nofglyphs = nofglyphs + 1 --- glyphs[nofglyphs] = n --- end --- -- yoffsets can influence curs steps --- if pn then --- local yoffset = pn.yoffset --- if yoffset and yoffset ~= 0 then --- setfield(n,"yoffset",yoffset) --- end --- end --- end --- local n = head --- while n do --- if getsubtype(n) < 256 then --- local id = getid(n) --- if id == glyph_code then --- run(n) --- elseif id == disc_code then --- local d = getfield(n,"pre") if d then for n in traverse(d) do run(d) end end --- local d = getfield(n,"post") if d then for n in traverse(d) do run(d) end end --- local d = getfield(n,"replace") if d then for n in traverse(d) do run(d) end end --- end --- end --- n = getnext(n) --- end - local function collect_glyphs_2(head) local glyphs, nofglyphs = { }, 0 local marks, nofmarks = { }, 0 @@ -458,36 +465,6 @@ local function collect_glyphs_2(head) return glyphs, nofglyphs, marks, nofmarks end --- local function run(n) --- local f = getfont(n) --- if f ~= nf then --- nf = f --- tm = fontdata[nf].resources.marks -- other hash in ctx --- end --- if tm and tm[getchar(n)] then --- nofmarks = nofmarks + 1 --- marks[nofmarks] = n --- else --- nofglyphs = nofglyphs + 1 --- glyphs[nofglyphs] = n --- end --- end --- -- --- local n = head --- while n do --- if getsubtype(n) < 256 then --- local id = getid(n) --- if id == glyph_code then --- run(n) --- elseif id == disc_code then --- local d = getfield(n,"pre") if d then for n in traverse(d) do run(d) end end --- local d = getfield(n,"post") if d then for n in traverse(d) do run(d) end end --- local d = getfield(n,"replace") if d then for n in traverse(d) do run(d) end end --- end --- end --- n = getnext(n) --- end - local function inject_marks(marks,nofmarks) for i=1,nofmarks do local n = marks[i] @@ -570,7 +547,7 @@ local function inject_cursives(glyphs,nofglyphs) if cursivex then if cursiveanchor then if cursivex ~= 0 then - pn.leftkern = (pn.leftkern or 0) + cursivex + pn.leftkern = pn.leftkern or 0 + cursivex end if lastanchor then if maxc == 0 then @@ -653,14 +630,14 @@ local function inject_kerns(head,glyphs,nofglyphs) insert_node_before(head,n,newkern(leftkern)) -- type 0/2 end local rightkern = pn.rightkern - if rightkern ~= 0 then + if rightkern and rightkern ~= 0 then insert_node_after(head,n,newkern(rightkern)) -- type 0/2 end end end end -local function inject_everything(head,where,keep) +local function inject_everything(head,where) head = tonut(head) if trace_injections then trace(head) @@ -680,7 +657,9 @@ local function inject_everything(head,where,keep) end inject_kerns(head,glyphs,nofglyphs) end - if not keep then + if keepregisteredcounts then + keepregisteredcounts = false + else nofregisteredkerns = 0 nofregisteredpairs = 0 nofregisteredmarks = 0 @@ -689,7 +668,7 @@ local function inject_everything(head,where,keep) return tonode(head), true end -local function inject_kerns_only(head,where,keep) +local function inject_kerns_only(head,where) head = tonut(head) if trace_injections then trace(head) @@ -751,23 +730,20 @@ local function inject_kerns_only(head,where,keep) local d = getfield(n,"pre") if d then local h = d - for n in traverse(d) do - local id = getid(n) - if id == glyph_code then - if getsubtype(n) < 256 then - local pn = rawget(properties,n) - if pn then - pn = pn.preinjections - end - if pn then - local leftkern = pn.leftkern - if leftkern ~= 0 then - h = insert_node_before(h,n,newkern(leftkern)) - end + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.preinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) end - else - break end + else + break end end if h ~= d then @@ -777,23 +753,20 @@ local function inject_kerns_only(head,where,keep) local d = getfield(n,"post") if d then local h = d - for n in traverse(d) do - local id = getid(n) - if id == glyph_code then - if getsubtype(n) < 256 then - local pn = rawget(properties,n) - if pn then - pn = pn.postinjections - end - if pn then - local leftkern = pn.leftkern - if leftkern ~= 0 then - h = insert_node_before(h,n,newkern(leftkern)) - end + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.postinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) end - else - break end + else + break end end if h ~= d then @@ -803,24 +776,20 @@ local function inject_kerns_only(head,where,keep) local d = getfield(n,"replace") if d then local h = d - for n in traverse(d) do - local id = getid(n) - if id == glyph_code then - if getsubtype(n) < 256 then - local pn = rawget(properties,n) -- why can it be empty { } - if pn then - pn = pn.replaceinjections - end - if pn then - local leftkern = pn.leftkern - if leftkern ~= 0 then - h = insert_node_before(h,n,newkern(leftkern)) - -- h = insert_node_after(h,n,newkern(leftkern)) - end + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) -- why can it be empty { } + if pn then + pn = pn.replaceinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) end - else - break end + else + break end end if h ~= d then @@ -834,13 +803,15 @@ local function inject_kerns_only(head,where,keep) n = getnext(n) end -- - if not keep then - nofregisteredkerns = 0 + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredkerns = 0 end return tonode(head), true end -local function inject_pairs_only(head,where,keep) +local function inject_pairs_only(head,where) head = tonut(head) if trace_injections then trace(head) @@ -928,32 +899,29 @@ local function inject_pairs_only(head,where,keep) local d = getfield(n,"pre") if d then local h = d - for n in traverse(d) do - local id = getid(n) - if id == glyph_code then - if getsubtype(n) < 256 then - local pn = rawget(properties,n) - if pn then - pn = pn.preinjections + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.preinjections + end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) end - if pn then - local yoffset = pn.yoffset - if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) - end - local leftkern = pn.leftkern - if leftkern ~= 0 then - h = insert_node_before(h,n,newkern(leftkern)) - end - local rightkern = pn.rightkern - if rightkern and rightkern ~= 0 then - insert_node_after(head,n,newkern(rightkern)) - n = getnext(n) -- to be checked - end + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked end - else - break end + else + break end end if h ~= d then @@ -963,32 +931,29 @@ local function inject_pairs_only(head,where,keep) local d = getfield(n,"post") if d then local h = d - for n in traverse(d) do - local id = getid(n) - if id == glyph_code then - if getsubtype(n) < 256 then - local pn = rawget(properties,n) - if pn then - pn = pn.postinjections + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.postinjections + end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) end - if pn then - local yoffset = pn.yoffset - if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) - end - local leftkern = pn.leftkern - if leftkern ~= 0 then - h = insert_node_before(h,n,newkern(leftkern)) - end - local rightkern = pn.rightkern - if rightkern and rightkern ~= 0 then - insert_node_after(head,n,newkern(rightkern)) - n = getnext(n) -- to be checked - end + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked end - else - break end + else + break end end if h ~= d then @@ -998,32 +963,29 @@ local function inject_pairs_only(head,where,keep) local d = getfield(n,"replace") if d then local h = d - for n in traverse(d) do - local id = getid(n) - if id == glyph_code then - if getsubtype(n) < 256 then - local pn = rawget(properties,n) - if pn then - pn = pn.replaceinjections + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.replaceinjections + end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) end - if pn then - local yoffset = pn.yoffset - if yoffset and yoffset ~= 0 then - setfield(n,"yoffset",yoffset) - end - local leftkern = pn.leftkern - if leftkern ~= 0 then - h = insert_node_before(h,n,newkern(leftkern)) - end - local rightkern = pn.rightkern - if rightkern and rightkern ~= 0 then - insert_node_after(head,n,newkern(rightkern)) - n = getnext(n) -- to be checked - end + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked end - else - break end + else + break end end if h ~= d then @@ -1037,20 +999,22 @@ local function inject_pairs_only(head,where,keep) n = getnext(n) end -- - if not keep then + if keepregisteredcounts then + keepregisteredcounts = false + else nofregisteredpairs = 0 nofregisteredkerns = 0 end return tonode(head), true end -function injections.handler(head,where,keep) -- optimize for n=1 ? +function injections.handler(head,where) -- optimize for n=1 ? if nofregisteredmarks > 0 or nofregisteredcursives > 0 then - return inject_everything(head,where,keep) + return inject_everything(head,where) elseif nofregisteredpairs > 0 then - return inject_pairs_only(head,where,keep) + return inject_pairs_only(head,where) elseif nofregisteredkerns > 0 then - return inject_kerns_only(head,where,keep) + return inject_kerns_only(head,where) else return head, false end diff --git a/tex/context/base/font-nod.lua b/tex/context/base/font-nod.lua index da3d9def9..636a668bc 100644 --- a/tex/context/base/font-nod.lua +++ b/tex/context/base/font-nod.lua @@ -75,6 +75,7 @@ local copy_node_list = nuts.copy_list local hpack_node_list = nuts.hpack local free_node_list = nuts.flush_list local traverse_nodes = nuts.traverse +local traverse_id = nuts.traverse_id local protect_glyphs = nuts.protect_glyphs local nodepool = nuts.pool @@ -115,6 +116,24 @@ function char_tracers.collect(head,list,tag,n) l[#l+1] = { c, f } elseif id == disc_code then -- skip +-- local replace = getfield(head,"replace") +-- if replace then +-- for n in traverse_id(glyph_code,replace) do +-- l[#l+1] = { c, f } +-- end +-- end +-- local pre = getfield(head,"pre") +-- if pre then +-- for n in traverse_id(glyph_code,pre) do +-- l[#l+1] = { c, f } +-- end +-- end +-- local post = getfield(head,"post") +-- if post then +-- for n in traverse_id(glyph_code,post) do +-- l[#l+1] = { c, f } +-- end +-- end else ok = false end @@ -313,21 +332,59 @@ function step_tracers.font(command) end end -function step_tracers.codes(i,command) +local colors = { + pre = { "darkred" }, + post = { "darkgreen" }, + replace = { "darkblue" }, +} + +function step_tracers.codes(i,command,space) local c = collection[i] + + local function showchar(c) + if command then + local f, c = getfont(c), getchar(c) + local d = fontdescriptions[f] + local d = d and d[c] + context[command](f,c,d and d.class or "") + else + context("[%s:U+%04X]",getfont(c),getchar(c)) + end + end + + local function showdisc(d,w,what) + if w then + context.startcolor(colors[what]) + context("%s:",what) + for c in traverse_id(glyph_code,w) do + showchar(c) + end + context[space]() + context.stopcolor() + end + end + while c do local id = getid(c) if id == glyph_code then - if command then - local f, c = getfont(c), getchar(c) - local d = fontdescriptions[f] - local d = d and d[c] - context[command](f,c,d and d.class or "") - else - context("[%s:U+%04X]",getfont(c),getchar(c)) - end + showchar(c) elseif id == whatsit_code and (getsubtype(c) == localpar_code or getsubtype(c) == dir_code) then context("[%s]",getfield(c,"dir")) + elseif id == disc_code then + local pre = getfield(c,"pre") + local post = getfield(c,"post") + local replace = getfield(c,"replace") + if pre or post or replace then + context("[") + context[space]() + showdisc(c,pre,"pre") + showdisc(c,post,"post") + showdisc(c,replace,"replace") + context[space]() + context("]") + else + context("[disc]") + end else context("[%s]",nodecodes[id]) end @@ -358,7 +415,8 @@ function step_tracers.check(head) if collecting then step_tracers.reset() local n = copy_node_list(tonut(head)) - injections.handler(n,nil,"trace",true) + injections.keepcounts(n) -- one-time + injections.handler(n,"trace") -- handlers.protectglyphs(n) -- can be option protect_glyphs(n) collection[1] = n @@ -370,8 +428,9 @@ function step_tracers.register(head) local nc = #collection+1 if messages[nc] then local n = copy_node_list(tonut(head)) - injections.handler(n,nil,"trace",true) - -- handlers.protectglyphs(n) -- can be option + injections.keepcounts(n) -- one-time + injections.handler(n,"trace") + -- handlers.protectglyph s(n) -- can be option protect_glyphs(n) collection[nc] = n end diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index 302d8eabc..1bb608fd5 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -2000,6 +2000,8 @@ actions["reorganize glyph lookups"] = function(data,filename,raw) end +local zero = { 0, 0 } + 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 @@ -2008,14 +2010,37 @@ actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we rep 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 } + -- for i=1,#specification do + -- local si = specification[i] + -- specification[i] = { si.x or 0, si.y or 0 } + -- end + -- can be sparse so we need to fill the holes + local n = 0 + for k, v in next, specification do + if k > n then + n = k + end + local x, y = v.x, v.y + if x or y then + specification[k] = { x or 0, y or 0 } + else + specification[k] = zero + end end + local t = { } + for i=1,n do + t[i] = specification[i] or zero + end + data[tag] = t -- so # is okay (nicer for packer) end else for tag, specification in next, data do - data[tag] = { specification.x or 0, specification.y or 0 } + local x, y = specification.x, specification.y + if x or y then + data[tag] = { x or 0, y or 0 } + else + data[tag] = zero + end end end end diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index 797c15d0a..e1be424b1 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -6,6 +6,9 @@ if not modules then modules = { } end modules ['font-otn'] = { license = "see context related readme files", } +-- todo: looks like we have a leak somewhere (probably in ligatures) +-- todo: copy attributes to disc + -- this is a context version which can contain experimental code, but when we -- have serious patches we also need to change the other two font-otn files @@ -222,6 +225,7 @@ local copy_node = nuts.copy local copy_node_list = nuts.copy_list local find_node_tail = nuts.tail local flush_node_list = nuts.flush_list +local free_node = nuts.free local end_of_math = nuts.end_of_math local traverse_nodes = nuts.traverse local traverse_id = nuts.traverse_id @@ -248,6 +252,8 @@ local dir_code = whatcodes.dir local localpar_code = whatcodes.localpar local discretionary_code = disccodes.discretionary +local regular_code = disccodes.regular +local automatic_code = disccodes.automatic local ligature_code = glyphcodes.ligature @@ -386,6 +392,53 @@ local function copy_glyph(g) -- next and prev are untouched ! end end +-- temp here (context) + +local function collapse_disc(start,next) + local replace1 = getfield(start,"replace") + local replace2 = getfield(next,"replace") + if replace1 and replace2 then + local pre2 = getfield(next,"pre") + local post2 = getfield(next,"post") + setfield(replace1,"prev",nil) + if pre2 then + local pre1 = getfield(start,"pre") + if pre1 then + flush_node_list(pre1) + end + local pre1 = copy_node_list(replace1) + local tail1 = find_node_tail(pre1) + setfield(tail1,"next",pre2) + setfield(pre2,"prev",tail1) + setfield(start,"pre",pre1) + setfield(next,"pre",nil) + else + setfield(start,"pre",nil) + end + if post2 then + local post1 = getfield(start,"post") + if post1 then + flush_node_list(post1) + end + setfield(start,"post",post2) + else + setfield(start,"post",nil) + end + local tail1 = find_node_tail(replace1) + setfield(tail1,"next",replace2) + setfield(replace2,"prev",tail1) + setfield(start,"replace",replace1) + setfield(next,"replace",nil) + -- + local nextnext = getnext(next) + setfield(nextnext,"prev",start) + setfield(start,"next",nextnext) + free_node(next) + else + -- maybe remove it + end +end + -- start is a mark and we need to keep that one local function markstoligature(kind,lookupname,head,start,stop,char) @@ -441,22 +494,37 @@ local function getcomponentindex(start) end end -local a_noligature = attributes.private("noligature") -local prehyphenchar = languages and languages.prehyphenchar -local posthyphenchar = languages and languages.posthyphenchar +local a_noligature = attributes.private("noligature") +local prehyphenchar = languages and languages.prehyphenchar +local posthyphenchar = languages and languages.posthyphenchar +----- preexhyphenchar = languages and languages.preexhyphenchar +----- postexhyphenchar = languages and languages.postexhyphenchar -if not prehyphenchar then +if prehyphenchar then - local newlang = lang.new - local getpre = lang.prehyphenchar - local getpost = lang.posthyphenchar + -- okay - prehyphenchar = function(l) local l = newlang(l) return l and getpre (l) or -1 end - posthyphenchar = function(l) local l = newlang(l) return l and getpost(l) or -1 end +elseif context then + + report_warning("no language support") os.exit() + +else + + local newlang = lang.new + local getpre = lang.prehyphenchar + local getpost = lang.posthyphenchar + -- local getpreex = lang.preexhyphenchar + -- local getpostex = lang.postexhyphenchar + + prehyphenchar = function(l) local l = newlang(l) return l and getpre (l) or -1 end + posthyphenchar = function(l) local l = newlang(l) return l and getpost (l) or -1 end + -- preexhyphenchar = function(l) local l = newlang(l) return l and getpreex (l) or -1 end + -- postexhyphenchar = function(l) local l = newlang(l) return l and getpostex(l) or -1 end end local function addhyphens(template,pre,post) + -- inserted by hyphenation algorithm local l = getfield(template,"lang") local p = prehyphenchar(l) if p and p > 0 then @@ -490,27 +558,6 @@ local function addhyphens(template,pre,post) return pre, post end -local function showdiscretionary(d) -- will move to tracer - local pre = getfield(d,"pre") - local post = getfield(d,"post") - local replace = getfield(d,"replace") - if pre then - for n in traverse_nodes(pre) - do print("<",nuts.tonode(n)) - end - end - if post then - for n in traverse_nodes(post) do - print(">",nuts.tonode(n)) - end - end - if replace then - for n in traverse_nodes(replace) do - print("=",nuts.tonode(n)) - end - end -end - local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head if getattr(start,a_noligature) == 1 then -- so we can do: e\noligature{ff}e e\noligature{f}fie (we only look at the first) @@ -521,6 +568,12 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun setfield(start,"char",char) return head, start end + -- needs testing (side effects): + local components = getfield(base,"components") + if components then + flush_node_list(components) + end + -- local prev = getprev(start) local next = getnext(stop) local comp = start @@ -581,33 +634,69 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun end start = getnext(start) end - elseif getsubtype(discfound) == discretionary_code then - -- maybe some day else - -- forget about marks .. probably no scripts that hyphenate and have marks - -- todo: use insert_before - local prev = getfield(discfound,"prev") - local next = getfield(discfound,"next") - if prev and next then - setfield(next,"prev",nil) -- also blocks funny assignments - setfield(prev,"next",nil) -- also blocks funny assignments - local pre, post = addhyphens(comp,comp,next) -- takes from components - setfield(discfound,"pre",pre) - setfield(discfound,"post",post) - local prev = getfield(base,"prev") - local next = getfield(base,"next") - setfield(prev,"next",discfound) - setfield(next,"prev",discfound) - setfield(discfound,"next",next) - setfield(discfound,"prev",prev) - setfield(base,"next",nil) - setfield(base,"prev",nil) - setfield(base,"components",nil) - setfield(discfound,"replace",base) - setfield(discfound,"subtype",discretionary_code) - base = next - else - -- weird disc .. maybe some day + -- discfound ... forget about marks .. probably no scripts that hyphenate and have marks + local discprev = getfield(discfound,"prev") + local discnext = getfield(discfound,"next") + if discprev and discnext then + local subtype = getsubtype(discfound) + if subtype == discretionary_code then + local pre = getfield(discfound,"pre") + local post = getfield(discfound,"post") + local replace = getfield(discfound,"replace") + if not replace then -- todo: signal simple hyphen + local prev = getfield(base,"prev") + local copied = copy_node_list(comp) + setfield(discnext,"prev",nil) -- also blocks funny assignments + setfield(discprev,"next",nil) -- also blocks funny assignments + if pre then + setfield(comp,"next",pre) + setfield(pre,"prev",comp) + end + pre = comp + if post then + local tail = find_node_tail(post) + setfield(tail,"next",discnext) + setfield(discnext,"prev",tail) + setfield(post,"prev",nil) + else + post = discnext + end + setfield(prev,"next",discfound) + setfield(next,"prev",discfound) + setfield(discfound,"next",next) + setfield(discfound,"prev",prev) + setfield(base,"next",nil) + setfield(base,"prev",nil) + setfield(base,"components",copied) + setfield(discfound,"pre",pre) + setfield(discfound,"post",post) + setfield(discfound,"replace",base) + setfield(discfound,"subtype",discretionary_code) + base = prev -- restart + end + elseif discretionary_code == regular_code then + -- local prev = getfield(base,"prev") + -- local next = getfield(base,"next") + local copied = copy_node_list(comp) + setfield(discnext,"prev",nil) -- also blocks funny assignments + setfield(discprev,"next",nil) -- also blocks funny assignments + local pre, post = addhyphens(comp,comp,discnext,subtype) -- takes from components + setfield(prev,"next",discfound) + setfield(next,"prev",discfound) + setfield(discfound,"next",next) + setfield(discfound,"prev",prev) + setfield(base,"next",nil) + setfield(base,"prev",nil) + setfield(base,"components",copied) + setfield(discfound,"pre",pre) + setfield(discfound,"post",post) + setfield(discfound,"replace",base) + setfield(discfound,"subtype",discretionary_code) + base = next -- or restart + else + -- forget about it in generic usage + end end end return head, base @@ -807,6 +896,20 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) return head, start, false, discfound end +-- function is_gsub_ligature(start,ligature) -- limited case: in disc nodes, only latin, always glyphs +-- local s = getnext(start) +-- while s do +-- local lg = ligature[getchar(s)] +-- if lg then +-- ligature = lg +-- s = getnext(s) +-- else +-- return +-- end +-- end +-- return ligature and ligature.ligature +-- end + --[[ldx--

We get hits on a mark, but we're not sure if the it has to be applied so we need to explicitly test for basechar, baselig and basemark entries.

@@ -1262,38 +1365,76 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo resetinjection(current) if check_discretionaries then -- some fonts use a chain lookup to replace e.g. an f in a fi ligature - -- and there can be a isc node in between ... the next code tries to catch + -- and there can be a disc node in between ... the next code tries to catch -- this local next = getnext(current) local prev = getprev(current) -- todo: just remember it above - if getid(next) == disc_code and getsubtype(next) ~= discretionary_code then - -- maybe set a property - setfield(next,"prev",prev) - setfield(prev,"next",next) - setfield(current,"prev",nil) - setfield(current,"next",nil) - local pre = addhyphens(current,current) - local replace = copy_node(current) - setfield(replace,"char",replacement) - setfield(next,"subtype",discretionary_code) - setfield(next,"replace",replace) - setfield(next,"pre",pre) - start = next - elseif getid(prev) == disc_code and getsubtype(prev) == discretionary_code then - -- maybe check a property - setfield(next,"prev",prev) - setfield(prev,"next",next) - setfield(current,"prev",nil) - setfield(current,"next",nil) - local repl = copy_node(current) - setfield(repl,"char",replacement) - local _, post = addhyphens(current,nil,current) - local replace = getfield(prev,"replace") - local tail = find_node_tail(replace) -- we could do a check for length 1 - setfield(tail,"next",repl) - setfield(repl,"prev",tail) - setfield(prev,"post",post) - else + local done = false + if next then + if getid(next) == disc_code then + local subtype = getsubtype(next) + if subtype == discretionary_code then + setfield(next,"prev",prev) + setfield(prev,"next",next) + setfield(current,"prev",nil) + setfield(current,"next",nil) + local replace = getfield(next,"replace") + local pre = getfield(next,"pre") + local new = copy_node(current) + setfield(new,"char",replacement) + if replace then + setfield(new,"next",replace) + setfield(replace,"prev",new) + end + if pre then + setfield(current,"next",pre) + setfield(pre,"prev",current) + end + setfield(next,"replace",new) -- also updates tail + setfield(next,"pre",current) -- also updates tail + end + start = next + done = true + local next = getnext(start) + if next and getid(next) == disc_code then + collapse_disc(start,next) + end + end + end + if not done and prev then + if getid(prev) == disc_code then + local subtype = getsubtype(prev) + if subtype == discretionary_code then + setfield(next,"prev",prev) + setfield(prev,"next",next) + setfield(current,"prev",nil) + setfield(current,"next",nil) + local replace = getfield(prev,"replace") + local post = getfield(prev,"post") + local new = copy_node(current) + setfield(new,"char",replacement) + if replace then + local tail = find_node_tail(replace) + setfield(tail,"next",new) + setfield(new,"prev",tail) + else + replace = new + end + if post then + local tail = find_node_tail(post) + setfield(tail,"next",current) + setfield(current,"prev",tail) + else + post = current + end + setfield(prev,"replace",replace) -- also updates tail + setfield(prev,"post",post) -- also updates tail + start = prev + done = true + end + end + end + if not done then setfield(current,"char",replacement) end else @@ -1938,7 +2079,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if not replace then break elseif n > l then - match = false +-- match = false break end else @@ -2016,7 +2157,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if not replace or replace == finish then break elseif n < 1 then - match = false +-- match = false break end else @@ -2051,6 +2192,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq end elseif f == 2 then match = seq[1][32] +-- elseif f > 2 then +-- match = false -- KE ? else for n=f-1,1 do if not seq[n][32] then @@ -2106,7 +2249,6 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq if not replace then break elseif n > s then - match = false break end else @@ -2487,31 +2629,34 @@ local function kernrun(disc,run) -- we can assume that prev and next are glyphs end end +-- the if new test might be dangerous as luatex will check / set some tail stuff +-- in a temp node + local function comprun(disc,run) if trace_compruns then - report_run("comp") -- will be more detailed + report_run("comp: %s",languages.serializediscretionary(disc)) end -- local pre = getfield(disc,"pre") if pre then - local new = run(pre) - if new ~= pre then + local new, done = run(pre) + if done then setfield(disc,"pre",new) end end -- local post = getfield(disc,"post") if post then - local new = run(post) - if new ~= post then + local new, done = run(post) + if done then setfield(disc,"post",new) end end -- local replace = getfield(disc,"replace") if replace then - local new = run(replace) - if new ~= replace then + local new, done = run(replace) + if done then setfield(disc,"replace",new) end end @@ -2526,7 +2671,7 @@ local function testrun(disc,trun,crun) if prev then -- only look ahead local tail = find_node_tail(replace) - local nest = getprev(replace) + -- local nest = getprev(replace) setfield(tail,"next",next) setfield(next,"prev",tail) if trun(replace,next) then @@ -2538,7 +2683,7 @@ local function testrun(disc,trun,crun) setfield(disc,"prev",nil) setfield(disc,"next",nil) flush_node_list(disc) - return replace + return replace -- restart else setfield(tail,"next",nil) setfield(next,"prev",disc) @@ -2586,6 +2731,8 @@ local function discrun(disc,drun,krun) return next end +-- todo: maybe run lr and rl stretches + local function featuresprocessor(head,font,attr) local lookuphash = lookuphashes[font] -- we can also check sequences here @@ -2633,6 +2780,8 @@ local function featuresprocessor(head,font,attr) -- We don't goto the next node of a disc node is created so that we can then treat -- the pre, post and replace. It's abit of a hack but works out ok for most cases. + -- there can be less subtype and attr checking in the comprun etc helpers + for s=1,#datasets do local dataset = datasets[s] featurevalue = dataset[1] -- todo: pass to function instead of using a global @@ -2647,6 +2796,7 @@ local function featuresprocessor(head,font,attr) local gpossing = typ == "gpos_single" or typ == "gpos_pair" local subtables = sequence.subtables local handler = handlers[typ] + if chain < 0 then -- this is a limited case, no special treatments like 'init' etc -- we need to get rid of this slide! probably no longer needed in latest luatex @@ -2662,11 +2812,13 @@ local function featuresprocessor(head,font,attr) a = true end if a then + local char = getchar(start) for i=1,#subtables do local lookupname = subtables[i] local lookupcache = lookuphash[lookupname] if lookupcache then - local lookupmatch = lookupcache[getchar(start)] + -- local lookupmatch = lookupcache[getchar(start)] + local lookupmatch = lookupcache[start] if lookupmatch then -- todo: disc? head, start, success = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i) @@ -2700,12 +2852,15 @@ local function featuresprocessor(head,font,attr) report_missing_cache(typ,lookupname) else - local function c_run(start) + local function c_run(start) -- no need to check for 256 and attr probably also the same local head = start local done = false while start do local id = getid(start) - if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then + if id ~= glyph_code then + -- very unlikely + start = getnext(start) + elseif getfont(start) == font and getsubtype(start) < 256 then local a = getattr(start,0) if a then a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) @@ -2727,13 +2882,13 @@ local function featuresprocessor(head,font,attr) start = getnext(start) end else - start = getnext(start) + return head, false end end if done then - success = true + success = true -- needed in this subrun? end - return head + return head, done end local function t_run(start,stop) @@ -2748,8 +2903,22 @@ local function featuresprocessor(head,font,attr) end if a then local lookupmatch = lookupcache[getchar(start)] - if lookupmatch then - return true + if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check + -- if we need more than ligatures we can outline the code and use functions + local s = getnext(start) + local l = nil + while s do + local lg = lookupmatch[getchar(s)] + if lg then + l = lg + s = getnext(s) + else + break + end + end + if l and l.ligature then + return true + end end end start = getnext(start) @@ -2842,24 +3011,24 @@ local function featuresprocessor(head,font,attr) start = getnext(start) end elseif id == disc_code then - local discretionary = getsubtype(start) == discretionary_code - if gpossing then - if discretionary then - kernrun(start,k_run) - else - discrun(start,d_run,k_run) - end - start = getnext(start) - elseif discretionary then - if typ == "gsub_ligature" then - start = testrun(start,t_run,c_run) - else - comprun(start,c_run) - start = getnext(start) - end - else + local discretionary = getsubtype(start) == discretionary_code + if gpossing then + if discretionary then + kernrun(start,k_run) + else + discrun(start,d_run,k_run) + end + start = getnext(start) + elseif discretionary then + if typ == "gsub_ligature" then + start = testrun(start,t_run,c_run) + else + comprun(start,c_run) + start = getnext(start) + end + else start = getnext(start) - end + end elseif id == whatsit_code then -- will be function local subtype = getsubtype(start) if subtype == dir_code then @@ -2912,7 +3081,10 @@ local function featuresprocessor(head,font,attr) local done = false while start do local id = getid(start) - if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then + if id ~= glyph_code then + -- very unlikely + start = getnext(start) + elseif getfont(start) == font and getsubtype(start) < 256 then local a = getattr(start,0) if a then a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) @@ -2920,11 +3092,13 @@ local function featuresprocessor(head,font,attr) a = not attribute or getprop(start,a_state) == attribute end if a then + local char = getchar(start) for i=1,ns do local lookupname = subtables[i] local lookupcache = lookuphash[lookupname] if lookupcache then - local lookupmatch = lookupcache[getchar(start)] + -- local lookupmatch = lookupcache[getchar(start)] + local lookupmatch = lookupcache[char] if lookupmatch then -- we could move all code inline but that makes things even more unreadable local ok @@ -2946,13 +3120,13 @@ local function featuresprocessor(head,font,attr) start = getnext(start) end else - start = getnext(start) + return head, false end end if done then success = true end - return head + return head, done end local function d_run(prev) @@ -3034,10 +3208,6 @@ local function featuresprocessor(head,font,attr) a = not attribute or getprop(start,a_state) == attribute end if a then - -- local lookupmatch = lookupcache[getchar(start)] - -- if lookupmatch then - -- return true - -- end local char = getchar(start) for i=1,ns do local lookupname = subtables[i] @@ -3045,7 +3215,21 @@ local function featuresprocessor(head,font,attr) if lookupcache then local lookupmatch = lookupcache[char] if lookupmatch then - return true + -- if we need more than ligatures we can outline the code and use functions + local s = getnext(start) + local l = nil + while s do + local lg = lookupmatch[getchar(s)] + if lg then + l = lg + s = getnext(s) + else + break + end + end + if l and l.ligature then + return true + end end else report_missing_cache(typ,lookupname) @@ -3175,7 +3359,6 @@ local function featuresprocessor(head,font,attr) end - head = tonode(head) return head, done diff --git a/tex/context/base/font-tra.mkiv b/tex/context/base/font-tra.mkiv index 45d8a7280..6b9fb1f47 100644 --- a/tex/context/base/font-tra.mkiv +++ b/tex/context/base/font-tra.mkiv @@ -114,11 +114,14 @@ {\ctxlua{nodes.tracers.steppers.glyphs(\number\otfcollector,#1)}% \unhbox\otfcollector} -\unexpanded\def\otfstepcharcommand#1#2#3% font char class +\unexpanded\def\otfstepspace {\removeunwantedspaces - \hskip.5\emwidth \s!plus .125\emwidth\relax - \doif{#3}{mark}{\underbar}{U+\hexnumber{#2}}:\ruledhbox{\ctxlua{nodes.tracers.fontchar(#1,#2)}}% - \hskip.5\emwidth \s!plus .125\emwidth\relax} + \hskip.5\emwidth \s!plus .125\emwidth \s!minus .125\emwidth\relax} + +\unexpanded\def\otfstepcharcommand#1#2#3% font char class + {\otfstepspace + \doif{#3}{mark}{\underbar}{U+\hexnumber{#2}}:\ruledhbox{\ctxlua{nodes.tracers.fontchar(#1,#2)}}% + \otfstepspace} \unexpanded\def\otfstepfontcommand#1#2#3% id font size {\begingroup @@ -142,7 +145,7 @@ {\ctxlua{nodes.tracers.steppers.font("otfstepfontcommand")}} \unexpanded\def\showotfstepchars#1% - {\ctxlua{nodes.tracers.steppers.codes(#1,"otfstepcharcommand")}} + {\ctxlua{nodes.tracers.steppers.codes(#1,"otfstepcharcommand","otfstepspace")}} \unexpanded\def\showotfstepmessages#1% {\ctxlua{nodes.tracers.steppers.messages(#1,"otfstepmessagecommand",true)}} diff --git a/tex/context/base/lang-dis.lua b/tex/context/base/lang-dis.lua new file mode 100644 index 000000000..7a7b3f5de --- /dev/null +++ b/tex/context/base/lang-dis.lua @@ -0,0 +1,198 @@ +if not modules then modules = { } end modules ['lang-dis'] = { + version = 1.001, + comment = "companion to lang-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local concat = table.concat + +local nodes = nodes + +local tasks = nodes.tasks +local nuts = nodes.nuts +local nodepool = nuts.pool + +local tonode = nuts.tonode +local tonut = nuts.tonut + +local getfield = nuts.getfield +local setfield = nuts.setfield +local getnext = nuts.getnext +local getprev = nuts.getprev +local getid = nuts.getid +local getfont = nuts.getfont +local getattr = nuts.getattr +local getsubtype = nuts.getsubtype +local getchar = nuts.getchar + +local copy_node = nuts.copy +local free_node = nuts.free +local remove_node = nuts.remove +local traverse_id = nuts.traverse_id +local traverse_nodes = nuts.traverse + +local nodecodes = nodes.nodecodes +local disccodes = nodes.disccodes + +local disc_code = nodecodes.disc +local glyph_code = nodecodes.glyph +local discretionary_code = disccodes.discretionary + +local a_visualize = attributes.private("visualizediscretionary") +local setattribute = tex.setattribute + +local getlanguagedata = languages.getdata + +local expanders = { + [disccodes.discretionary] = function(d,template) + -- \discretionary + return template + end, + [disccodes.explicit] = function(d,template) + -- \- + local pre = getfield(d,"pre") + if pre and getid(pre) == glyph_code and getchar(pre) <= 0 then + setfield(d,"pre",nil) + end + local post = getfield(d,"post") + if post and getid(post) == glyph_code and getchar(post) <= 0 then + setfield(d,"post",nil) + end +-- setfield(d,"subtype",discretionary_code) -- to be checked + return template + end, + [disccodes.automatic] = function(d,template) + -- following a - : the pre and post chars are already appended and set + -- so we have pre=preex and post=postex .. however, the previous + -- hyphen is already injected ... downside: the font handler sees this + -- so this is another argument for doing a hyphenation pass in context + if getfield(d,"pre") then + -- we have a preex characters and want that one to replace the + -- character in front which is the trigger + if not template then + -- can there be font kerns already? + template = getprev(d) + if template and getid(template) ~= glyph_code then + template = getnext(d) + if template and getid(template) ~= glyph_code then + template = nil + end + end + end + if template then + local pseudohead = getprev(template) + if pseudohead then + while template ~= d do + pseudohead, template, removed = remove_node(pseudohead,template) + setfield(d,"replace",removed) + -- break ? + end + else + -- can't happen + end + setfield(d,"subtype",discretionary_code) + else + -- print("lone regular discretionary ignored") + end + end + return template + end, + [disccodes.regular] = function(d,template) + -- simple + if not template then + -- can there be font kerns already? + template = getprev(d) + if template and getid(template) ~= glyph_code then + template = getnext(d) + if template and getid(template) ~= glyph_code then + template = nil + end + end + end + if template then + local language = template and getfield(template,"lang") + local data = getlanguagedata(language) + local prechar = data.prehyphenchar + local postchar = data.posthyphenchar + if prechar and prechar > 0 then + local c = copy_node(template) + setfield(c,"char",prechar) + setfield(d,"pre",c) + end + if postchar and postchar > 0 then + local c = copy_node(template) + setfield(c,"char",postchar) + setfield(d,"post",c) + end + setfield(d,"subtype",discretionary_code) + else + -- print("lone regular discretionary ignored") + end + return template + end, + [disccodes.first] = function() + -- forget about them + end, + [disccodes.second] = function() + -- forget about them + end, +} + +languages.expanders = expanders + +function languages.expand(d,template,subtype) + if not subtype then + subtype = getsubtype(d) + end + if subtype ~= discretionary_code then + return expanders[subtype](d,template) + end +end + +local setlistcolor = nodes.tracers.colors.setlist + +function languages.visualizediscretionaries(head) + for d in traverse_id(disc_code,tonut(head)) do + if getattr(d,a_visualize) then + local pre = getfield(d,"pre") + local post = getfield(d,"post") + local replace = getfield(d,"replace") + if pre then + setlistcolor(pre,"darkred") + end + if post then + setlistcolor(post,"darkgreen") + end + if replace then + setlistcolor(replace,"darkblue") + end + end + end +end + +local enabled = false + +function commands.showdiscretionaries(v) + if v == false then + setattribute(a_visualize,unsetvalue) + else -- also nil + if not enabled then + nodes.tasks.enableaction("processors","languages.visualizediscretionaries") + enabled = true + end + setattribute(a_visualize,1) + end +end + +local toutf = nodes.listtoutf + +function languages.serializediscretionary(d) -- will move to tracer + return string.formatters["{%s}{%s}{%s}"]( + toutf(getfield(d,"pre")) or "", + toutf(getfield(d,"post")) or "", + toutf(getfield(d,"replace")) or "" + ) +end + diff --git a/tex/context/base/lang-hyp.lua b/tex/context/base/lang-hyp.lua index 2ab966fe6..60e1699ca 100644 --- a/tex/context/base/lang-hyp.lua +++ b/tex/context/base/lang-hyp.lua @@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['lang-hyp'] = { license = "see context related readme files" } +-- todo: hyphenate over range if needed + -- to be considered: reset dictionary.hyphenated when a pattern is added -- or maybe an explicit reset of the cache @@ -583,48 +585,63 @@ end if context then - local nodecodes = nodes.nodecodes - local glyph_code = nodecodes.glyph - local math_code = nodecodes.math + local nodecodes = nodes.nodecodes + local disccodes = nodes.disccodes + + local glyph_code = nodecodes.glyph + local disc_code = nodecodes.disc + local math_code = nodecodes.math + + local discretionary_code = disccodes.discretionary + local explicit_code = disccodes.explicit + local regular_code = disccodes.regular + local automatic_code = disccodes.automatic - local nuts = nodes.nuts - local tonut = nodes.tonut - local nodepool = nuts.pool + local nuts = nodes.nuts + local tonut = nodes.tonut + local tonode = nodes.tonode + local nodepool = nuts.pool - local new_disc = nodepool.disc + local new_disc = nodepool.disc + local new_glyph = nodepool.glyph - local setfield = nuts.setfield - local getfield = nuts.getfield - local getchar = nuts.getchar - local getid = nuts.getid - local getattr = nuts.getattr - local getnext = nuts.getnext - local getprev = nuts.getprev - local insert_before = nuts.insert_before - local insert_after = nuts.insert_after - local copy_node = nuts.copy - local remove_node = nuts.remove - local end_of_math = nuts.end_of_math - local node_tail = nuts.tail + local setfield = nuts.setfield + local getfield = nuts.getfield + local getfont = nuts.getfont + local getchar = nuts.getchar + local getid = nuts.getid + local getattr = nuts.getattr + local getnext = nuts.getnext + local getprev = nuts.getprev + local getsubtype = nuts.getsubtype + local insert_before = nuts.insert_before + local insert_after = nuts.insert_after + local copy_node = nuts.copy + local remove_node = nuts.remove + local end_of_math = nuts.end_of_math + local node_tail = nuts.tail + local traverse_id = nuts.traverse_id - local setcolor = nodes.tracers.colors.set + local setcolor = nodes.tracers.colors.set - local variables = interfaces.variables - local v_reset = variables.reset - local v_yes = variables.yes - local v_all = variables.all + local variables = interfaces.variables + local v_reset = variables.reset + local v_yes = variables.yes + local v_all = variables.all - local settings_to_array = utilities.parsers.settings_to_array + local settings_to_array = utilities.parsers.settings_to_array - local unsetvalue = attributes.unsetvalue - local texsetattribute = tex.setattribute + local unsetvalue = attributes.unsetvalue + local texsetattribute = tex.setattribute - local prehyphenchar = lang.prehyphenchar - local posthyphenchar = lang.posthyphenchar + local prehyphenchar = lang.prehyphenchar + local posthyphenchar = lang.posthyphenchar + local preexhyphenchar = lang.preexhyphenchar + local postexhyphenchar = lang.postexhyphenchar - local lccodes = characters.lccodes + local lccodes = characters.lccodes - local a_hyphenation = attributes.private("hyphenation") + local a_hyphenation = attributes.private("hyphenation") function traditional.loadpatterns(language) return dictionaries[language] @@ -891,6 +908,7 @@ if context then local instance = nil local characters = nil local unicodes = nil + local exhyphenchar = tex.exhyphenchar local extrachars = nil local hyphenchars = nil local language = nil @@ -900,6 +918,8 @@ if context then local size = 0 local leftchar = false local rightchar = false -- utfbyte("-") + local leftexchar = false + local rightexchar = false -- utfbyte("-") local leftmin = 0 local rightmin = 0 local leftcharmin = nil @@ -1089,6 +1109,8 @@ if context then local current = start + local attributes = getfield(start,"attr") -- todo: just copy the last disc .. faster + for i=1,rsize do local r = result[i] if r == true then @@ -1099,6 +1121,9 @@ if context then if leftchar then setfield(disc,"post",serialize(true,leftchar)) end + if attributes then + setfield(disc,"attr",attributes) + end -- could be a replace as well insert_before(first,current,disc) elseif type(r) == "table" then @@ -1117,6 +1142,9 @@ if context then if replace and replace ~= "" then setfield(disc,"replace",serialize(replace)) end + if attributes then + setfield(disc,"attr",attributes) + end insert_before(first,current,disc) else setfield(current,"char",characters[r]) @@ -1135,16 +1163,19 @@ if context then end - local function inject() + local function inject(leftchar,rightchar,code,attributes) if first ~= current then local disc = new_disc() first, current, glyph = remove_node(first,current) first, current = insert_before(first,current,disc) if trace_visualize then - setcolor(glyph,"darkred") -- these get checked in the colorizer - setcolor(disc,"darkgreen") -- these get checked in the colorizer + setcolor(glyph,"darkred") -- these get checked + setcolor(disc,"darkgreen") -- in the colorizer end setfield(disc,"replace",glyph) + if not leftchar then + leftchar = code + end if rightchar then local glyph = copy_node(glyph) setfield(glyph,"char",rightchar) @@ -1152,9 +1183,12 @@ if context then end if leftchar then local glyph = copy_node(glyph) - setfield(glyph,"char",rightchar) + setfield(glyph,"char",leftchar) setfield(disc,"post",glyph) end + if attributes then + setfield(disc,"attr",attributes) + end end return current end @@ -1195,11 +1229,13 @@ if context then unicodes = dictionary.unicodes -- local a = getattr(current,a_hyphenation) - attr = synchronizefeatureset(a) - leftchar = leftchar or (instance and posthyphenchar(instance)) - rightchar = rightchar or (instance and prehyphenchar (instance)) - leftmin = leftcharmin or getfield(current,"left") - rightmin = rightcharmin or getfield(current,"right") + attr = synchronizefeatureset(a) + leftchar = leftchar or (instance and posthyphenchar (instance)) -- we can make this more + rightchar = rightchar or (instance and prehyphenchar (instance)) -- efficient if needed + leftexchar = (instance and preexhyphenchar (instance)) + rightexchar = (instance and postexhyphenchar(instance)) + leftmin = leftcharmin or getfield(current,"left") + rightmin = rightcharmin or getfield(current,"right") if not leftchar or leftchar < 0 then leftchar = false end @@ -1237,18 +1273,24 @@ if context then end end size = 0 - if hyphenchars and hyphenchars[code] then - current = inject() + -- maybe also a strict mode here: no hyphenation before hyphenchars and skip + -- the next set (but then, strict is an option) + if code == exhyphenchar then + current = inject(leftexchar,rightexchar,code,getfield(current,"attr")) + elseif hyphenchars and hyphenchars[code] then + current = inject(leftchar,rightchar,code,getfield(current,"attr")) end end else local a = getattr(current,a_hyphenation) if a ~= attr then - attr = synchronizefeatureset(a) -- influences extrachars - leftchar = leftchar or (instance and posthyphenchar(instance)) - rightchar = rightchar or (instance and prehyphenchar (instance)) - leftmin = leftcharmin or getfield(current,"left") - rightmin = rightcharmin or getfield(current,"right") + attr = synchronizefeatureset(a) -- influences extrachars + leftchar = leftchar or (instance and posthyphenchar (instance)) -- we can make this more + rightchar = rightchar or (instance and prehyphenchar (instance)) -- efficient if needed + leftexchar = (instance and preexhyphenchar (instance)) + rightexchar = (instance and postexhyphenchar(instance)) + leftmin = leftcharmin or getfield(current,"left") + rightmin = rightcharmin or getfield(current,"right") if not leftchar or leftchar < 0 then leftchar = false end @@ -1266,11 +1308,38 @@ if context then end stop = current current = getnext(current) - elseif strict and strict[id] then - current = id == math_code and getnext(end_of_math(current)) or getnext(current) - size = 0 else - current = id == math_code and getnext(end_of_math(current)) or getnext(current) + if id == disc_code then + local subtype = getsubtype(current) + if subtype == discretionary_code then -- \discretionary + size = 0 + current = getnext(current) + elseif subtype == explicit_code then -- \- => only here + size = 0 + current = getnext(current) + while current do + local id = getid(current) + if id == glyph_code or id == disc_code then + current = getnext(current) + else + break + end + end + -- todo: change to discretionary_code + else + -- automatic (-) : the hyphenator turns an exhyphen into glyph+disc + -- first : done by the hyphenator + -- second : done by the hyphenator + -- regular : done by the hyphenator + size = 0 + current = getnext(current) + end + elseif strict and strict[id] then + current = id == math_code and getnext(end_of_math(current)) or getnext(current) + size = 0 + else + current = id == math_code and getnext(end_of_math(current)) or getnext(current) + end if size > 0 then if dictionary and leftmin + rightmin <= size then if categories[word[1]] == "lu" and getfield(start,"uchyph") < 0 then @@ -1286,7 +1355,8 @@ if context then end end end - -- we can have quit due to last so we need to flush the last seen word + -- we can have quit due to last so we need to flush the last seen word, we could move this in + -- the loop and test for current but ... messy if size > 0 and dictionary and leftmin + rightmin <= size then if categories[word[1]] == "lu" and getfield(start,"uchyph") < 0 then -- skip @@ -1324,41 +1394,63 @@ if context then -- \enabledirectives[hyphenators.method=traditional] -- \enabledirectives[hyphenators.method=builtin] - -- this avoids a wrapper - -- push / pop ? check first attribute - local replaceaction = nodes.tasks.replaceaction + -- local replaceaction = nodes.tasks.replaceaction -- no longer overload this way (too many local switches) - local function setmethod(method) - if type(method) == "string" then - local valid = hyphenators[method] - if valid and valid.hyphenate then - newmethod = "languages.hyphenators." .. method .. ".hyphenate" - else - newmethod = texmethod + local hyphenate = lang.hyphenate + local expanders = languages.expanders + local methods = { } + local usedmethod = false + local stack = { } + + local function original(head) + local done = hyphenate(head) + return head, done + end + + local function expanded(head) + local done = hyphenate(head) + if done then + for d in traverse_id(disc_code,tonut(head)) do + local s = getsubtype(d) + if s ~= discretionary_code then + expanders[s](d,template) + done = true + end end - else - newmethod = texmethod end - if oldmethod ~= newmethod then - replaceaction("processors","words",oldmethod,newmethod) + return head, done + end + + function hyphenators.handler(head) + if usedmethod then + return usedmethod(head) + else + return head, done end - oldmethod = newmethod end - hyphenators.setmethod = setmethod + methods.tex = original + methods.original = original + methods.expanded = expanded + methods.traditional = languages.hyphenators.traditional.hyphenate + methods.none = function(head) return head, false end - local stack = { } + usedmethod = original + local function setmethod(method) + usedmethod = type(method) == "string" and methods[method] or methods.tex + end local function pushmethod(method) - insert(stack,oldmethod) + insert(stack,usedmethod) setmethod(method) end local function popmethod() - setmethod(remove(stack)) + usedmethod = remove(stack) or methods.tex end + hyphenators.setmethod = setmethod hyphenators.pushmethod = pushmethod hyphenators.popmethod = popmethod diff --git a/tex/context/base/lang-hyp.mkiv b/tex/context/base/lang-hyp.mkiv index 966b000c6..b2281135c 100644 --- a/tex/context/base/lang-hyp.mkiv +++ b/tex/context/base/lang-hyp.mkiv @@ -30,6 +30,7 @@ \writestatus{loading}{ConTeXt Language Macros / Initialization} +\registerctxluafile{lang-dis}{1.001} \registerctxluafile{lang-hyp}{1.001} \unprotect @@ -50,6 +51,11 @@ % \enabledirectives[hyphenators.method]% % \endgroup} + +% \exhyphenchar \hyphenasciicode +% \preexhyphenchar \lessthanasciicode +% \postexhyphenchar\morethanasciicode + %D Here is the real way: \installcorenamespace{hyphenation} @@ -152,7 +158,6 @@ \unexpanded\def\registerhyphenationexception {\dodoubleempty\lang_hyphenation_register_exception} - \def\lang_hyphenation_register_exception[#1][#2]% {\ctxcommand{registerhyphenationexception( \ifsecondargument @@ -191,6 +196,11 @@ \stopthyphenation \endgroup} +%D For me: + +\unexpanded\def\showdiscretionaries + {\ctxcommand{showdiscretionaries()}} + %D These are (at least now) not cummulative: \definehyphenationfeatures % just an example diff --git a/tex/context/base/lang-ini.lua b/tex/context/base/lang-ini.lua index 302f5b34d..27c4f774e 100644 --- a/tex/context/base/lang-ini.lua +++ b/tex/context/base/lang-ini.lua @@ -30,10 +30,12 @@ local trace_patterns = false trackers.register("languages.patterns", function(v local report_initialization = logs.reporter("languages","initialization") -local prehyphenchar = lang.prehyphenchar -- global per language -local posthyphenchar = lang.posthyphenchar -- global per language -local lefthyphenmin = lang.lefthyphenmin -local righthyphenmin = lang.righthyphenmin +local prehyphenchar = lang.prehyphenchar -- global per language +local posthyphenchar = lang.posthyphenchar -- global per language +local preexhyphenchar = lang.preexhyphenchar -- global per language +local postexhyphenchar = lang.postexhyphenchar -- global per language +local lefthyphenmin = lang.lefthyphenmin +local righthyphenmin = lang.righthyphenmin local lang = lang lang.exceptions = lang.hyphenation @@ -301,10 +303,12 @@ end -- not that usefull, global values -function languages.prehyphenchar (what) return prehyphenchar (tolang(what)) end -function languages.posthyphenchar(what) return posthyphenchar(tolang(what)) end -function languages.lefthyphenmin (what) return lefthyphenmin (tolang(what)) end -function languages.righthyphenmin(what) return righthyphenmin(tolang(what)) end +function languages.prehyphenchar (what) return prehyphenchar (tolang(what)) end +function languages.posthyphenchar (what) return posthyphenchar (tolang(what)) end +function languages.preexhyphenchar (what) return preexhyphenchar (tolang(what)) end +function languages.postexhyphenchar(what) return postexhyphenchar(tolang(what)) end +function languages.lefthyphenmin (what) return lefthyphenmin (tolang(what)) end +function languages.righthyphenmin (what) return righthyphenmin (tolang(what)) end -- e['implementer']= 'imple{m}{-}{-}menter' -- e['manual'] = 'man{}{}{}' diff --git a/tex/context/base/lang-ini.mkiv b/tex/context/base/lang-ini.mkiv index bf2e42a75..5a745a7d8 100644 --- a/tex/context/base/lang-ini.mkiv +++ b/tex/context/base/lang-ini.mkiv @@ -705,4 +705,6 @@ {\ctxcommand{posthyphenchar()}post}% {replace}} +% todo: make this configurable + \protect \endinput diff --git a/tex/context/base/mult-low.lua b/tex/context/base/mult-low.lua index e6c444347..f96ebacbc 100644 --- a/tex/context/base/mult-low.lua +++ b/tex/context/base/mult-low.lua @@ -60,7 +60,7 @@ return { "lessthanasciicode", "morethanasciicode", "doublecommentsignal", "atsignasciicode", "exclamationmarkasciicode", "questionmarkasciicode", "doublequoteasciicode", "singlequoteasciicode", "forwardslashasciicode", - "primeasciicode", + "primeasciicode", "hyphenasciicode", -- "activemathcharcode", -- @@ -203,7 +203,7 @@ return { -- "normalbaselineskip", "normallineskip", "normallineskiplimit", -- - "availablehsize", "localhsize", "setlocalhsize", + "availablehsize", "localhsize", "setlocalhsize", "distributedhsize", -- "nextbox", "dowithnextbox", "dowithnextboxcs", "dowithnextboxcontent", "dowithnextboxcontentcs", -- diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua index fb01f2f42..02d4c7a3f 100644 --- a/tex/context/base/node-ini.lua +++ b/tex/context/base/node-ini.lua @@ -154,9 +154,9 @@ local disccodes = allocate { [0] = "discretionary", -- \discretionary [1] = "explicit", -- \- [2] = "automatic", -- following a - - [3] = "regular", -- simple - [4] = "first", -- hard first item - [5] = "second", -- hard second item + [3] = "regular", -- by hyphenator: simple + [4] = "first", -- by hyphenator: hard first item + [5] = "second", -- by hyphenator: hard second item } local accentcodes = allocate { diff --git a/tex/context/base/node-nut.lua b/tex/context/base/node-nut.lua index 50274c2ab..cbf1c686c 100644 --- a/tex/context/base/node-nut.lua +++ b/tex/context/base/node-nut.lua @@ -136,6 +136,20 @@ nuts.getsubtype = direct.getsubtype nuts.getlist = direct.getlist -- only hlist and vlist ! nuts.getleader = direct.getleader +-- local function track(name) +-- local n = 0 +-- local f = nuts[name] +-- function nuts[name](...) +-- n = n + 1 +-- if n % 1000 == 0 then +-- print(name,n) +-- end +-- return f(...) +-- end +-- end + +-- track("getsubtype") + -- local dgf = direct.getfield function nuts.getlist(n) return dgf(n,"list") end -- setters diff --git a/tex/context/base/node-shp.lua b/tex/context/base/node-shp.lua index 42b622878..a4fb5689d 100644 --- a/tex/context/base/node-shp.lua +++ b/tex/context/base/node-shp.lua @@ -73,14 +73,27 @@ local function cleanup_redundant(head) -- better name is: flatten_page if getsubtype(start) == fulldisc_code then local replace = getfield(start,"replace") if replace then - setfield(start,"replace",nil) - head, start = remove_node(head,start,true) - local tail = find_tail(replace) local prev = getprev(start) - setfield(tail,"next",start) - setfield(start,"prev",tail) - setfield(prev,"next",replace) - setfield(replace,"prev",prev) + local next = getnext(start) + local tail = find_tail(replace) + setfield(start,"replace",nil) + if start == head then + remove_node(head,start,true) + head = replace + else + remove_node(head,start,true) + end + if next then + setfield(tail,"next",next) + setfield(next,"prev",tail) + end + if prev then + setfield(prev,"next",replace) + setfield(replace,"prev",prev) + else + setfield(replace,"prev",nil) -- to be sure + end + start = next elseif wipedisc then -- pre and post can have values head, start = remove_node(head,start,true) diff --git a/tex/context/base/node-tra.lua b/tex/context/base/node-tra.lua index ea17c5738..5f060e4f3 100644 --- a/tex/context/base/node-tra.lua +++ b/tex/context/base/node-tra.lua @@ -319,8 +319,12 @@ local function listtoutf(h,joiner,textonly,last) end function nodes.listtoutf(h,joiner,textonly,last) - local joiner = joiner == true and utfchar(0x200C) or joiner -- zwnj - return listtoutf(tonut(h),joiner,textonly,last and tonut(last)) + if h then + local joiner = joiner == true and utfchar(0x200C) or joiner -- zwnj + return listtoutf(tonut(h),joiner,textonly,last and tonut(last)) + else + return "" + end end local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" } diff --git a/tex/context/base/s-fonts-ligatures.mkiv b/tex/context/base/s-fonts-ligatures.mkiv new file mode 100644 index 000000000..1b8ab30d8 --- /dev/null +++ b/tex/context/base/s-fonts-ligatures.mkiv @@ -0,0 +1,284 @@ +%D \module +%D [ file=s-fonts-ligatures, +%D version=2014.12.14, +%D title=\CONTEXT\ Style File, +%D subtitle=Show Fonts Ligatures, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +% begin info +% +% title : show some ligature building in fonts +% +% comment : we trace some ligatures that have rather different implementations in fontss +% status : experimental, used for luatex testing +% +% end info + +\definefontfeature + [otfligaturetest] + [analyze=off, + ccmp=yes, % brill uses that .. not really ligatures ! + %clig=yes, + script=latn, + language=dflt] + +\hyphenation{xf-fi-a} +\hyphenation{xff-i-b} +\hyphenation{xffi-c} +\hyphenation{xffid} + +\registerhyphenationexception[xf-fi-a] +\registerhyphenationexception[xff-i-b] +\registerhyphenationexception[xffi-c] +\registerhyphenationexception[xffid] + +\starttexdefinition showotfligaturescaption #1 + \bTD [width=18em,align={flushleft,lohi},nx=3] + \nohyphens + \ttbf + #1 + \eTD +\stoptexdefinition + +\starttexdefinition showotfligatureslegend #1 + \bTD [width=6em,align={flushleft,lohi}] + \nohyphens \ttxx original + \eTD + \bTD [width=6em,align={flushleft,lohi}] + \nohyphens \ttxx expanded + \eTD + \bTD [width=6em,align={flushleft,lohi}] + \nohyphens \ttxx traditional + \eTD +\stoptexdefinition + +\starttexdefinition showotfligaturesline #1#2 + \bTD[toffset=.5ex,frame=off] + \starthyphenation[#1] + \LigatureFont + \showfontkerns + \showdiscretionaries + \begstrut#2\endstrut + \par + \stophyphenation + \eTD +\stoptexdefinition + +\starttexdefinition showotfligaturesbanner #1 + \bTR[frame=off] + \bTD [nx=12,align={middle,lohi},height=4ex] + \tttf #1 + \eTD + \eTR +\stoptexdefinition + +\starttexdefinition showotfligaturescaptions #1 + \bTR[height=3ex,align={middle,lohi},nx=3,bottomframe=off] + \processcommalist[#1]\showotfligaturescaption + \eTR + \bTR[height=3ex,align={middle,lohi},nx=3,topframe=off] + \processcommalist[#1]\showotfligatureslegend + \eTR +\stoptexdefinition + +\starttexdefinition showotfligatureslineset #1 + \showotfligaturesline{original} {#1} + \showotfligaturesline{expanded} {#1} + \showotfligaturesline{traditional}{#1} +\stoptexdefinition + + +\starttexdefinition showotfligaturesparagraphset #1 + \showotfligatureslineset { + \hsize \zeropoint + \lefthyphenmin \plustwo + \righthyphenmin\plustwo + #1 + } +\stoptexdefinition + +\starttexdefinition showotfligaturesextremeset #1 + \showotfligatureslineset { + \hsize \zeropoint + \lefthyphenmin \plusone + \righthyphenmin\plusone + #1 + } +\stoptexdefinition + +\starttexdefinition showotfligatureslines #1 + \bTR[height=4ex,bottomframe=off] + \processcommalist[#1]\showotfligatureslineset + \eTR +\stoptexdefinition + +\starttexdefinition showotfligaturesparagraphs #1 + \bTR[cheight=12ex,topframe=off] + \processcommalist[#1]\showotfligaturesparagraphset + \eTR +\stoptexdefinition + +\starttexdefinition showotfligaturesextremes #1 + \bTR[cheight=12ex,topframe=off] + \processcommalist[#1]\showotfligaturesextremeset + \eTR +\stoptexdefinition + +\starttexdefinition showotfligaturesdefaults + \bTR + \bTD [nx=12,align=middle,height=4ex,frame=off] + \start \LigatureFont fb \stop \quad + \start \LigatureFont ff \stop \quad + \start \LigatureFont fi \stop \quad + \start \LigatureFont fk \stop \quad + \start \LigatureFont fl \stop \quad + \start \LigatureFont ft \stop \quad + \start \LigatureFont ffb \stop \quad + \start \LigatureFont fff \stop \quad + \start \LigatureFont ffi \stop \quad + \start \LigatureFont ffl \stop \quad + \start \LigatureFont ffk \stop \quad + \start \LigatureFont fft \stop + \eTD + \eTR +\stoptexdefinition + +\starttexdefinition showotfligaturesexample #1#2 + \showotfligaturescaptions {#1} + \showotfligatureslines {#2} + \showotfligaturesparagraphs{#2} + \showotfligaturesextremes {#2} +\stoptexdefinition + +\starttexdefinition showotfligaturesexamples + \showotfligaturesexample + {leafing,baffling,efficient,shifffahrt} + {leafing,baffling,efficient,shifffahrt} + \showotfligaturesexample + {offbeat,effect,ef-fective,ef\-fective} + {offbeat,effect,ef-fective,ef\-fective} + \showotfligaturesexample + {xf+fi+a,xff+i+b,xffi+c,xffid} + {xffia, xffib, xffic, xffid} +\stoptexdefinition + +\starttexdefinition showotfligatures [#1] + \begingroup + \getdummyparameters[font=Regular,features=default,#1] + \definefont[LigatureFont][\dummyparameter{font}*\dummyparameter{features},otfligaturetest ht 2ex] + \bTABLE[leftframe=off,rightframe=off] + \showotfligaturesbanner{\dummyparameter{font} * \dummyparameter{features}} + \showotfligaturesdefaults + \showotfligaturesexamples + \eTABLE + \endgroup +\stoptexdefinition + +\continueifinputfile{s-fonts-ligatures.mkiv} + +\starttext + + \startTEXpage \showotfligatures[font=lmroman10-regular.otf, features=default] \stopTEXpage + \startTEXpage \showotfligatures[font=dejavu-serif.ttf, features=default] \stopTEXpage + \startTEXpage \showotfligatures[font=minionpro.otf, features=default] \stopTEXpage + \startTEXpage \showotfligatures[font=minionpro.otf, features=smallcaps] \stopTEXpage + \startTEXpage \showotfligatures[font=brill.otf, features=default] \stopTEXpage + \startTEXpage \showotfligatures[font=gentiumplus-r.ttf, features=default] \stopTEXpage + \startTEXpage \showotfligatures[font=cambria, features=default] \stopTEXpage + +\stoptext + +% \startluacode +% +% local f = fonts.hashes.identifiers[true] +% +% local sequences = f.resources.sequences +% local descriptions = f.shared.rawdata.descriptions +% local lookuptypes = f.resources.lookuptypes +% local lookups = f.resources.lookups +% +% local ligatures = { "liga", "dlig", "rlig", "clig", "tlig", "ccmp" } +% local found = { } +% +% for i=1,#sequences do +% local sequence = sequences[i] +% local features = sequence.features +% for i=1,#ligatures do +% local l = ligatures[i] +% if features[l] then +% local subtables = sequence.subtables +% if subtables then +% for i=1,#subtables do +% local subtable = subtables[i] +% local lookup = found[subtable] +% if lookup then +% lookup[l] = true +% else +% found[subtable] = { [l] = true } +% end +% end +% end +% end +% end +% end +% +% context.starttabulate { "|||T|T|T|" } +% +% local function flush(l,v,start,unicode,data,done) +% local features = found[l] +% if features then +% local lookuptype = lookuptypes[l] +% if lookuptype == "ligature" then +% local t = { } +% for i=1,#v do +% t[i] = utf.char(v[i]) +% end +% t = table.concat(t," ") +% if not done[t] then +% context.NC() +% context(t) +% context.NC() +% context(utf.char(unicode)) +% context.NC() +% context(" %t",table.sortedkeys(features)) +% context.NC() +% local name = data.name +% if name then +% context(name) +% end +% context.NC() +% context("%U",unicode) +% context.NC() +% context.NR() +% done[t] = true +% end +% end +% end +% end +% +% for unicode, data in table.sortedhash(descriptions) do +% local slookups = data.slookups +% local mlookups = data.mlookups +% local done = { } +% if slookups then +% for l, v in next, slookups do +% flush(l,v,1,unicode,data,done) +% end +% end +% if mlookups then +% for i=1,#mlookups do +% local v = mlookups[i] +% flush(v[1],v,2,unicode,data,done) +% end +% end +% end +% +% context.stoptabulate() +% +% \stopluacode diff --git a/tex/context/base/s-typesetting-kerning.mkiv b/tex/context/base/s-typesetting-kerning.mkiv new file mode 100644 index 000000000..074861713 --- /dev/null +++ b/tex/context/base/s-typesetting-kerning.mkiv @@ -0,0 +1,127 @@ +%D \module +%D [ file=s-typesetting-kerning, +%D version=2014.12.14, +%D title=\CONTEXT\ Style File, +%D subtitle=Show Character Kerning, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +\definecharacterkerning + [typesetting-kerning-demo] + [factor=.5] + +\startbuffer[showcharacterkerning:boxes] + \starttextrule{boxes} + \showfontkerns + \dontcomplain + \startlines + test \hbox{!} test + test\hbox{!} test + test \hbox{!}test + test:$x$ test + \setcharacterkerning[typesetting-kerning-demo] + test \hbox{!} test + test\hbox{!} test + test \hbox{!}test + test:$x$ test + \stoplines + \stoptextrule +\stopbuffer + +\startbuffer[showcharacterkerning:ligatures] + \starttextrule{ligatures} + \dontcomplain + \startlines + effe flink effectief efficient fietsen + \blank + \setcharacterkerning[typesetting-kerning-demo] + effe flink effectief efficient fietsen + \blank \hsize\zeropoint + effe + flink + effectief + efficient + fietsen + \stoplines + \stoptextrule +\stopbuffer + +\startbuffer[showcharacterkerning:discretionaries] + \starttextrule{discretionary} + \dontcomplain + \startlines + \hbox{\samplediscretionary} + \hbox{xxx\samplediscretionary} + \hbox{\samplediscretionary xxx} + \hbox{xxx\samplediscretionary xxx} + \blank + \setcharacterkerning[typesetting-kerning-demo] + \hbox{\samplediscretionary} + \hbox{xxx\samplediscretionary} + \hbox{\samplediscretionary xxx} + \hbox{xxx\samplediscretionary xxx} + \blank \hsize\zeropoint + \samplediscretionary + xxx\samplediscretionary + \samplediscretionary xxx + xxx\samplediscretionary xxx + \stoplines + \stoptextrule +\stopbuffer + +\startbuffer[showcharacterkerning:explicits] + \starttextrule{explicits} + \exhyphenchar \hyphenasciicode + \preexhyphenchar \lessthanasciicode + \postexhyphenchar\morethanasciicode + \def\TestDisc + {\discretionary + {\kern\emwidth<}% + {>\kern\emwidth}% + {\kern\emwidth=\kern\emwidth}% + } + \dontcomplain + \startlines + \hbox{super-charged} + \hbox{super\-charged} + \hbox{super\TestDisc charged} + \hbox{super\discretionary{[}{]}{[]}charged} + \blank + \setcharacterkerning[typesetting-kerning-demo] + \hbox{super-charged} + \hbox{super\-charged} + \hbox{super\TestDisc charged} + \hbox{super\discretionary{[}{]}{[]}charged} + \blank \hsize\zeropoint + super-charged + super\-charged + super\TestDisc charged + super\discretionary{[}{]}{[]}charged + \stoplines + \stoptextrule +\stopbuffer + +\starttexdefinition unexpanded showcharacterkerning + \getbuffer[showcharacterkerning:boxes] + \getbuffer[showcharacterkerning:ligatures] + \getbuffer[showcharacterkerning:discretionaries] + \getbuffer[showcharacterkerning:explicits] +\stoptexdefinition + +\continueifinputfile{s-typesetting-kerning.mkiv} + +\starttext + + \showcharacterkerning + +\stoptext + + +% {\hsize1mm efficient\discretionary{\kern1pt!\kern1pt}{\kern1pt!\kern1pt}{\kern1pt!\kern1pt}efficient\par} +% {\hsize1mm\definedfont[Regular]\setcharacterkerning[typesetting-kerning-demo]efficient\-efficient\par} + diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf index 2444449e2..28a543e8f 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/status-lua.pdf b/tex/context/base/status-lua.pdf index 7612df67e..23bd6e0ec 100644 Binary files a/tex/context/base/status-lua.pdf and b/tex/context/base/status-lua.pdf differ diff --git a/tex/context/base/status-mkiv.lua b/tex/context/base/status-mkiv.lua index 65df5dd40..11d9991d6 100644 --- a/tex/context/base/status-mkiv.lua +++ b/tex/context/base/status-mkiv.lua @@ -625,6 +625,12 @@ return { loading = "always", status = "okay", }, + { + category = "mkiv", + filename = "lang-hyp", + loading = "always", + status = "okay", + }, { category = "mkiv", filename = "lang-frq", @@ -3506,6 +3512,18 @@ return { loading = "lang-def", status = "okay", }, + { + category = "lua", + filename = "lang-dis", + loading = "lang-ini", + status = "okay", + }, + { + category = "lua", + filename = "lang-hyp", + loading = "lang-hyp", + status = "okay", + }, { category = "lua", filename = "lang-ini", diff --git a/tex/context/base/syst-aux.mkiv b/tex/context/base/syst-aux.mkiv index d3e34f0b9..84d1b10b6 100644 --- a/tex/context/base/syst-aux.mkiv +++ b/tex/context/base/syst-aux.mkiv @@ -5295,7 +5295,7 @@ \let\popmacro \localpopmacro %D \macros -%D {setlocalhsize} +%D {setlocalhsize,distributedhsize} %D %D Sometimes we need to work with the \type{\hsize} that is %D corrected for indentation and left and right skips. The @@ -5333,6 +5333,9 @@ \fi \relax} +\def\distributedhsize#1#2#3% + {\dimexpr(#1-\numexpr#3-1\relax\dimexpr#2\relax)/#3\relax} + %D \macros %D {doifvalue,doifnotvalue,doifelsevalue, %D doifnothing,doifsomething,doifelsenothing, diff --git a/tex/context/base/task-ini.lua b/tex/context/base/task-ini.lua index d9ede85c4..f1df46ec8 100644 --- a/tex/context/base/task-ini.lua +++ b/tex/context/base/task-ini.lua @@ -39,7 +39,9 @@ appendaction("processors", "characters", "typesetters.breakpoints.handler") appendaction("processors", "characters", "scripts.injectors.handler") -- disabled appendaction("processors", "words", "languages.replacements.handler") -- disabled -appendaction("processors", "words", "builders.kernel.hyphenation") -- always on + +appendaction("processors", "words", "languages.hyphenators.handler") -- always on + appendaction("processors", "words", "languages.words.check") -- disabled -- might move up, no disc check needed then appendaction("processors", "words", "typesetters.initials.handler") -- disabled -- might move up @@ -59,8 +61,9 @@ appendaction("processors", "lists", "typesetters.spacings.handler") appendaction("processors", "lists", "typesetters.kerns.handler") -- disabled appendaction("processors", "lists", "typesetters.digits.handler") -- disabled (after otf handling) appendaction("processors", "lists", "typesetters.italics.handler") -- disabled (after otf/kern handling) +appendaction("processors", "lists", "languages.visualizediscretionaries") -- disabled --- appendaction("processors", "lists", "typesetters.initials.handler") -- disabled +-- appendaction("processors", "lists", "typesetters.initials.handler") -- disabled appendaction("shipouts", "normalizers", "nodes.handlers.cleanuppage") -- disabled appendaction("shipouts", "normalizers", "builders.paragraphs.expansion.trace") -- disabled @@ -151,6 +154,7 @@ disableaction("processors", "typesetters.firstlines.handler") disableaction("processors", "typesetters.spacings.handler") disableaction("processors", "typesetters.kerns.handler") disableaction("processors", "typesetters.italics.handler") +disableaction("processors", "languages.visualizediscretionaries") disableaction("processors", "nodes.handlers.stripping") disableaction("shipouts", "builders.paragraphs.expansion.trace") diff --git a/tex/context/base/trac-vis.lua b/tex/context/base/trac-vis.lua index 20b89bdf0..0ee40db80 100644 --- a/tex/context/base/trac-vis.lua +++ b/tex/context/base/trac-vis.lua @@ -820,7 +820,7 @@ local function visualize(head,vertical,forced) if trace_fontkern or prev_trace_fontkern then head, current = fontkern(head,current) end - elseif subtype == user_kern_code then + else -- if subtype == user_kern_code then if trace_kern then head, current = ruledkern(head,current,vertical) end diff --git a/tex/context/base/typo-brk.mkiv b/tex/context/base/typo-brk.mkiv index af498bfec..a42bed1b7 100644 --- a/tex/context/base/typo-brk.mkiv +++ b/tex/context/base/typo-brk.mkiv @@ -25,7 +25,7 @@ \definesystemattribute[breakpoint][public,global] -\exhyphenchar\minusone % we use a different order then base tex, so we really need this +% see below: \exhyphenchar \minusone % we use a different order tha n base tex, so we really need this \unexpanded\def\definebreakpoints {\dosingleargument\typo_breakpoints_define} @@ -50,7 +50,8 @@ \endgroup} \unexpanded\def\setbreakpoints[#1]% - {\ctxcommand{setbreakpoints("#1")}} + {\exhyphenchar\minusone % we use a different order tha n base tex, so we really need this + \ctxcommand{setbreakpoints("#1")}} \unexpanded\def\resetbreakpoints {\attribute\breakpointattribute\attributeunsetvalue} diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua index 5729c72c0..1616577dc 100644 --- a/tex/context/base/typo-krn.lua +++ b/tex/context/base/typo-krn.lua @@ -11,7 +11,8 @@ if not modules then modules = { } end modules ['typo-krn'] = { local next, type, tonumber = next, type, tonumber local utfchar = utf.char -local nodes, node, fonts = nodes, node, fonts +local nodes = nodes +local fonts = fonts local tasks = nodes.tasks local nuts = nodes.nuts @@ -20,11 +21,10 @@ local nodepool = nuts.pool local tonode = nuts.tonode local tonut = nuts.tonut +-- check what is used + local find_node_tail = nuts.tail local free_node = nuts.free -local free_nodelist = nuts.flush_list -local copy_node = nuts.copy -local copy_nodelist = nuts.copy_list local insert_node_before = nuts.insert_before local insert_node_after = nuts.insert_after local end_of_math = nuts.end_of_math @@ -51,6 +51,7 @@ local nodecodes = nodes.nodecodes local kerncodes = nodes.kerncodes local skipcodes = nodes.skipcodes local disccodes = nodes.disccodes +local listcodes = nodes.listcodes local glyph_code = nodecodes.glyph local kern_code = nodecodes.kern @@ -60,7 +61,12 @@ local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local math_code = nodecodes.math +local box_list_code = listcodes.box +local user_list_code = listcodes.unknown + local discretionary_code = disccodes.discretionary +local automatic_code = disccodes.automatic + local kerning_code = kerncodes.kerning local userkern_code = kerncodes.userkern local userskip_code = skipcodes.userskip @@ -203,20 +209,178 @@ local function spec_injector(fillup,width,stretch,shrink) end end --- needs checking ... base mode / node mode -- also use insert_before/after etc +-- a simple list injector, no components and such .. just disable ligatures in +-- kern mode .. maybe not even hyphenate ... anyway, the next one is for simple +-- sublists .. beware: we can have char -1 + +local function inject_begin(boundary,prev,keeptogether,krn,ok) -- prev is a glyph + local id = getid(boundary) + if id == kern_code then + if getsubtype(boundary) == kerning_code or getattr(boundary,a_fontkern) then + local inject = true + if keeptogether then + local next = getnext(boundary) + if not next or (getid(next) == glyph_code and keeptogether(prev,next)) then + inject = false + end + end + if inject then + -- not yet ok, as injected kerns can be overlays (from node-inj.lua) + setfield(boundary,"subtype",userkern_code) + setfield(boundary,"kern",getfield(boundary,"kern") + quaddata[getfont(prev)]*krn) + return boundary, true + end + end + elseif id == glyph_code then + if keeptogether and keeptogether(boundary,prev) then + -- keep 'm + else + local charone = getchar(prev) + if charone > 0 then + local font = getfont(boundary) + local chartwo = getchar(boundary) + local kerns = chardata[font][charone].kerns + local kern = new_kern((kerns and kerns[chartwo] or 0) + quaddata[font]*krn) + setfield(boundary,"prev",kern) + setfield(kern,"next",boundary) + return kern, true + end + end + end + return boundary, ok +end + +local function inject_end(boundary,next,keeptogether,krn,ok) + local tail = find_node_tail(boundary) + local id = getid(tail) + if id == kern_code then + if getsubtype(tail) == kerning_code or getattr(tail,a_fontkern) then + local inject = true + if keeptogether then + local prev = getprev(tail) + if getid(prev) == glyph_code and keeptogether(prev,two) then + inject = false + end + end + if inject then + -- not yet ok, as injected kerns can be overlays (from node-inj.lua) + setfield(tail,"subtype",userkern_code) + setfield(tail,"kern",getfield(tail,"kern") + quaddata[getfont(next)]*krn) + return boundary, true + end + end + elseif id == glyph_code then + if keeptogether and keeptogether(tail,two) then + -- keep 'm + else + local charone = getchar(tail) + if charone > 0 then + local font = getfont(tail) + local chartwo = getchar(next) + local kerns = chardata[font][charone].kerns + local kern = (kerns and kerns[chartwo] or 0) + quaddata[font]*krn + insert_node_after(boundary,tail,new_kern(kern)) + return boundary, true + end + end + end + return boundary, ok +end + +local function process_list(head,keeptogether,krn,font,okay) + local start = head + local prev = nil + local pid = nil + local kern = 0 + local mark = font and markdata[font] + while start do + local id = getid(start) + if id == glyph_code then + if not font then + font = getfont(start) + mark = markdata[font] + kern = quaddata[font]*krn + end + if prev then + local char = getchar(start) + if mark[char] then + -- skip + elseif pid == kern_code then + if getsubtype(prev) == kerning_code or getattr(prev,a_fontkern) then + local inject = true + if keeptogether then + local prevprev = getprev(prev) + if getid(prevprev) == glyph_code and keeptogether(prevprev,start) then + inject = false + end + end + if inject then + -- not yet ok, as injected kerns can be overlays (from node-inj.lua) + setfield(prev,"subtype",userkern_code) + setfield(prev,"kern",getfield(prev,"kern") + kern) + okay = true + end + end + elseif pid == glyph_code then + if keeptogether and keeptogether(prev,start) then + -- keep 'm + else + local prevchar = getchar(prev) + local kerns = chardata[font][prevchar].kerns + -- if kerns then + -- print("it happens indeed, basemode kerns not yet injected") + -- end + insert_node_before(head,start,new_kern((kerns and kerns[char] or 0) + kern)) + okay = true + end + end + end + end + if start then + prev = start + pid = id + start = getnext(start) + end + end + return head, okay, prev +end -local function do_process(head,force) -- todo: glue so that we can fully stretch +local function closest_bound(b,get) + b = get(b) + if b and getid(b) ~= glue_code then + while b do + if not getattr(b,a_kerns) then + break + elseif getid(b) == glyph_code then + return b, getfont(b) + else + b = get(b) + end + end + end +end + +function kerns.handler(head) + local head = tonut(head) local start = head local done = false local lastfont = nil local keepligature = kerns.keepligature local keeptogether = kerns.keeptogether local fillup = false + local bound = false + local prev = nil + local previd = nil + local prevchar = nil + local prevfont = nil + local prevmark = nil while start do - -- faster to test for attr first - local attr = force or getattr(start,a_kerns) + -- fontkerns don't get the attribute but they always sit between glyphs so + -- are always valid bound .. disc nodes also somtimes don't get them + local id = getid(start) + local attr = getattr(start,a_kerns) if attr and attr > 0 then - setattr(start,a_kerns,unsetvalue) + setattr(start,a_kerns,0) -- unsetvalue) local krn = mapping[attr] if krn == v_max then krn = .25 @@ -224,21 +388,21 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch else fillup = false end - if krn and krn ~= 0 then - local id = getid(start) - if id == glyph_code then -- we could use the subtype ligature - lastfont = getfont(start) - local c = getfield(start,"components") - if not c then - -- fine - elseif keepligature and keepligature(start) then - -- keep 'm - else - c = do_process(c,attr) - local s = start - local p = getprev(s) - local n = getnext(s) - local tail = find_node_tail(c) + if not krn or krn == 0 then + bound = false + elseif id == glyph_code then -- we could use the subtype ligature + local c = getfield(start,"components") + if not c then + -- fine + elseif keepligature and keepligature(start) then + -- keep 'm + c = nil + else + while c do + local s = start + local t = find_node_tail(c) + local p = getprev(s) + local n = getnext(s) if p then setfield(p,"next",c) setfield(c,"prev",p) @@ -246,172 +410,190 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch head = c end if n then - setfield(n,"prev",tail) + setfield(n,"prev",t) + setfield(t,"next",n) end - setfield(tail,"next",n) start = c setfield(s,"components",nil) - -- we now leak nodes ! - -- free_node(s) - done = true + free_node(s) + c = getfield(start,"components") end - local prev = getprev(start) - if not prev then - -- skip - elseif markdata[lastfont][getchar(start)] then - -- skip - else - local pid = getid(prev) - if not pid then - -- nothing - elseif pid == kern_code then - if getsubtype(prev) == kerning_code or getattr(prev,a_fontkern) then - if keeptogether and getid(getprev(prev)) == glyph_code and keeptogether(getprev(prev),start) then -- we could also pass start - -- keep 'm - else - -- not yet ok, as injected kerns can be overlays (from node-inj.lua) - setfield(prev,"subtype",userkern_code) - setfield(prev,"kern",getfield(prev,"kern") + quaddata[lastfont]*krn) -- here - done = true - end - end - elseif pid == glyph_code then - if getfont(prev) == lastfont then - local prevchar, lastchar = getchar(prev), getchar(start) - if keeptogether and keeptogether(prev,start) then - -- keep 'm - else - local kerns = chardata[lastfont][prevchar].kerns - local kern = kerns and kerns[lastchar] or 0 - krn = kern + quaddata[lastfont]*krn -- here - insert_node_before(head,start,kern_injector(fillup,krn)) - done = true - end - else - krn = quaddata[lastfont]*krn -- here - insert_node_before(head,start,kern_injector(fillup,krn)) - done = true - end - elseif pid == disc_code then - -- a bit too complicated, we can best not copy and just calculate - -- but we could have multiple glyphs involved so ... - local disc = prev -- disc - local prv = getprev(disc) - local nxt = getnext(disc) - if getsubtype(disc) == discretionary_code then - -- maybe we should forget about this variant as there is no glue - -- possible .. hardly used so a copy doesn't hurt much - local pre = getfield(disc,"pre") - local post = getfield(disc,"post") - local replace = getfield(disc,"replace") - if pre and prv then -- must pair with getprev(start) - local before = copy_node(prv) - setfield(pre,"prev",before) - setfield(before,"next",pre) - setfield(before,"prev",nil) - pre = do_process(before,attr) - pre = getnext(pre) - setfield(pre,"prev",nil) - setfield(disc,"pre",pre) - free_node(before) - end - if post and nxt then -- must pair with start - local after = copy_node(nxt) - local tail = find_node_tail(post) - setfield(tail,"next",after) - setfield(after,"prev",tail) - setfield(after,"next",nil) - post = do_process(post,attr) - setfield(tail,"next",nil) - setfield(disc,"post",post) - free_node(after) - end - if replace and prv and nxt then -- must pair with start and start.prev - local before = copy_node(prv) - local after = copy_node(nxt) - local tail = find_node_tail(replace) - setfield(replace,"prev",before) - setfield(before,"next",replace) - setfield(before,"prev",nil) - setfield(tail,"next",after) - setfield(after,"prev",tail) - setfield(after,"next",nil) - replace = do_process(before,attr) - replace = getnext(replace) - setfield(replace,"prev",nil) - setfield(getfield(after,"prev"),"next",nil) - setfield(disc,"replace",replace) - free_node(after) - free_node(before) - elseif prv and getid(prv) == glyph_code and getfont(prv) == lastfont then - local prevchar = getchar(prv) - local lastchar = getchar(start) - local kerns = chardata[lastfont][prevchar].kerns - local kern = kerns and kerns[lastchar] or 0 - krn = kern + quaddata[lastfont]*krn -- here - setfield(disc,"replace",kern_injector(false,krn)) -- only kerns permitted, no glue - else - krn = quaddata[lastfont]*krn -- here - setfield(disc,"replace",kern_injector(false,krn)) -- only kerns permitted, no glue - end - else - -- this one happens in most cases: automatic (-), explicit (\-), regular (patterns) - if prv and getid(prv) == glyph_code and getfont(prv) == lastfont then - -- the normal case - local prevchar = getchar(prv) - local lastchar = getchar(start) - local kerns = chardata[lastfont][prevchar].kerns - local kern = kerns and kerns[lastchar] or 0 - krn = kern + quaddata[lastfont]*krn - else - krn = quaddata[lastfont]*krn - end - insert_node_before(head,start,kern_injector(fillup,krn)) + end + local char = getchar(start) + local font = getfont(start) + local mark = markdata[font] + if not bound then + -- yet + elseif mark[char] then + -- skip + elseif previd == kern_code then + if getsubtype(prev) == kerning_code or getattr(prev,a_fontkern) then + local inject = true + if keeptogether then + if previd == glyph_code and keeptogether(prev,start) then + inject = false end end + if inject then + -- not yet ok, as injected kerns can be overlays (from node-inj.lua) + setfield(prev,"subtype",userkern_code) + setfield(prev,"kern",getfield(prev,"kern") + quaddata[font]*krn) + done = true + end end - elseif id == glue_code then - local subtype = getsubtype(start) - if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then - local s = getfield(start,"spec") - local w = getfield(s,"width") - if w > 0 then - local width = w+gluefactor*w*krn - local stretch = getfield(s,"stretch") - local shrink = getfield(s,"shrink") - setfield(start,"spec",spec_injector(fillup,width,stretch*width/w,shrink*width/w)) + elseif previd == glyph_code then + if prevfont == font then + if keeptogether and keeptogether(prev,start) then + -- keep 'm + else + local kerns = chardata[font][prevchar].kerns + local kern = (kerns and kerns[char] or 0) + quaddata[font]*krn + insert_node_before(head,start,kern_injector(fillup,kern)) done = true end + else + insert_node_before(head,start,kern_injector(fillup,quaddata[font]*krn)) + done = true + end + end + prev = start + prevchar = char + prevfont = font + prevmark = mark + previd = id + bound = true + elseif id == disc_code then + local prev, next, pglyph, nglyph -- delayed till needed + local subtype = getsubtype(start) + if subtype == automatic_code then + -- this is kind of special, as we have already injected the + -- previous kern + local prev = getprev(start) + local pglyph = prev and getid(prev) == glyph_code + languages.expand(start,pglyph and prev) + -- we can have a different start now + elseif subtype ~= discretionary_code then + prev = getprev(start) + pglyph = prev and getid(prev) == glyph_code + languages.expand(start,pglyph and prev) + end + local pre = getfield(start,"pre") + local post = getfield(start,"post") + local replace = getfield(start,"replace") + -- we really need to reasign the fields as luatex keeps track of + -- the tail in a temp preceding head .. kind of messy so we might + -- want to come up with a better solution some day like a real + -- pretail etc fields in a disc node + -- + -- maybe i'll merge the now split functions + if pre then + local okay = false + if not prev then + prev = prev or getprev(start) + pglyph = prev and getid(prev) == glyph_code + end + if pglyph then + pre, okay = inject_begin(pre,prev,keeptogether,krn,okay) + end + pre, okay = process_list(pre,keeptogether,krn,false,okay) + if okay then + setfield(start,"pre",pre) + done = true + end + end + if post then + local okay = false + if not next then + next = getnext(start) + nglyph = next and getid(next) == glyph_code + end + if nglyph then + post, okay = inject_end(post,next,keeptogether,krn,okay) end - elseif id == kern_code then - -- if getsubtype(start) == kerning_code then -- handle with glyphs - -- local sk = getfield(start,"kern") - -- if sk > 0 then - -- setfield(start,"kern",sk*krn) - -- done = true - -- end - -- end - elseif lastfont and (id == hlist_code or id == vlist_code) then -- todo: lookahead - local p = getprev(start) - if p and getid(p) ~= glue_code then - insert_node_before(head,start,kern_injector(fillup,quaddata[lastfont]*krn)) + post, okay = process_list(post,keeptogether,krn,false,okay) + if okay then + setfield(start,"post",post) done = true end - local n = getnext(start) - if n and getid(n) ~= glue_code then - insert_node_after(head,start,kern_injector(fillup,quaddata[lastfont]*krn)) + end + if replace then + local okay = false + if not prev then + prev = prev or getprev(start) + pglyph = prev and getid(prev) == glyph_code + end + if pglyph then + replace, okay = inject_begin(replace,prev,keeptogether,krn,okay) + end + if not next then + next = getnext(start) + nglyph = next and getid(next) == glyph_code + end + if nglyph then + replace, okay = inject_end(replace,next,keeptogether,krn,okay) + end + replace, okay = process_list(replace,keeptogether,krn,false,okay) + if okay then + setfield(start,"replace",replace) done = true end - elseif id == math_code then - start = end_of_math(start) + elseif prevfont then + setfield(start,"replace",new_kern(quaddata[prevfont]*krn)) + done = true end + bound = false + elseif id == kern_code then + bound = getsubtype(start) == kerning_code or getattr(start,a_fontkern) + prev = start + previd = id + elseif id == glue_code then + local subtype = getsubtype(start) + if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then + local s = getfield(start,"spec") + local w = getfield(s,"width") + if w > 0 then + local width = w+gluefactor*w*krn + local stretch = getfield(s,"stretch") + local shrink = getfield(s,"shrink") + setfield(start,"spec",spec_injector(fillup,width,stretch*width/w,shrink*width/w)) + done = true + end + end + bound = false + elseif id == hlist_code or id == vlist_code then + local subtype = getsubtype(start) + if subtype == user_list_code or subtype == box_list_code then + -- special case + local b, f = closest_bound(start,getprev) + if b then + insert_node_before(head,start,kern_injector(fillup,quaddata[f]*krn)) + done = true + end + local b, f = closest_bound(start,getnext) + if b then + insert_node_after(head,start,kern_injector(fillup,quaddata[f]*krn)) + done = true + end + end + bound = false + elseif id == math_code then + start = end_of_math(start) + bound = false end - end - if start then + if start then + start = getnext(start) + end + elseif id == kern_code then + bound = getsubtype(start) == kerning_code or getattr(start,a_fontkern) + prev = start + previd = id + start = getnext(start) + else + bound = false start = getnext(start) end end - return head, done + return tonode(head), done end local enabled = false @@ -438,11 +620,6 @@ function kerns.set(factor) return factor end -function kerns.handler(head) - local head, done = do_process(tonut(head)) -- no direct map, because else fourth argument is tail == true - return tonode(head), done -end - -- interface commands.setcharacterkerning = kerns.set diff --git a/tex/generic/context/luatex/luatex-basics-nod.lua b/tex/generic/context/luatex/luatex-basics-nod.lua index ea539f3f6..1ec2895ba 100644 --- a/tex/generic/context/luatex/luatex-basics-nod.lua +++ b/tex/generic/context/luatex/luatex-basics-nod.lua @@ -154,8 +154,8 @@ nodes.unset_attribute = node.unset_attribute nodes.protect_glyphs = node.protect_glyphs nodes.unprotect_glyphs = node.unprotect_glyphs -nodes.kerning = node.kerning -nodes.ligaturing = node.ligaturing +-----.kerning = node.kerning +-----.ligaturing = node.ligaturing nodes.mlist_to_hlist = node.mlist_to_hlist -- in generic code, at least for some time, we stay nodes, while in context @@ -194,8 +194,12 @@ nuts.insert_before = direct.insert_before nuts.insert_after = direct.insert_after nuts.delete = direct.delete nuts.copy = direct.copy +nuts.copy_list = direct.copy_list nuts.tail = direct.tail nuts.flush_list = direct.flush_list +nuts.free = direct.free +nuts.remove = direct.remove +nuts.is_node = direct.is_node nuts.end_of_math = direct.end_of_math nuts.traverse = direct.traverse nuts.traverse_id = direct.traverse_id diff --git a/tex/generic/context/luatex/luatex-fonts-cbk.lua b/tex/generic/context/luatex/luatex-fonts-cbk.lua index 965b96893..414cafb25 100644 --- a/tex/generic/context/luatex/luatex-fonts-cbk.lua +++ b/tex/generic/context/luatex/luatex-fonts-cbk.lua @@ -29,11 +29,28 @@ local kerning = node.kerning local basepass = true +local function l_warning() texio.write_nl("warning: node.ligaturing called directly") l_warning = nil end +local function k_warning() texio.write_nl("warning: node.kerning called directly") k_warning = nil end + +function node.ligaturing(...) + if basepass and l_warning then + l_warning() + end + return ligaturing(...) +end + +function node.kerning(...) + if basepass and k_warning then + k_warning() + end + return kerning(...) +end + function nodes.handlers.setbasepass(v) basepass = v end -function nodes.handlers.characters(head) +function nodes.handlers.nodepass(head) local fontdata = fonts.hashes.identifiers if fontdata then local usedfonts = { } @@ -115,14 +132,27 @@ function nodes.handlers.characters(head) end end -function nodes.simple_font_handler(head) - -- lang.hyphenate(head) - head = nodes.handlers.characters(head) - nodes.injections.handler(head) +function nodes.handlers.basepass(head) if not basepass then head = ligaturing(head) head = kerning(head) end - nodes.handlers.protectglyphs(head) - return head + return head, true +end + +local nodepass = nodes.handlers.nodepass +local basepass = nodes.handlers.basepass +local injectpass = nodes.injections.handler +local protectpass = nodes.handlers.protectglyphs + +function nodes.simple_font_handler(head) + if head then + head = nodepass(head) + head = injectpass(head) + head = basepass(head) + protectpass(head) + return head, true + else + return head, false + end end diff --git a/tex/generic/context/luatex/luatex-fonts-inj.lua b/tex/generic/context/luatex/luatex-fonts-inj.lua index 402403529..3b933829d 100644 --- a/tex/generic/context/luatex/luatex-fonts-inj.lua +++ b/tex/generic/context/luatex/luatex-fonts-inj.lua @@ -1,23 +1,25 @@ -if not modules then modules = { } end modules ['node-inj'] = { +if not modules then modules = { } end modules ['font-inj'] = { version = 1.001, - comment = "companion to node-ini.mkiv", + comment = "companion to font-lib.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files", } --- This 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. Some optimizations can go away when we have faster machines. +-- This property based variant is not faster but looks nicer than the attribute one. We +-- need to use rawget (which is apbout 4 times slower than a direct access but we cannot +-- get/set that one for our purpose! --- todo: ignore kerns between disc and glyph +if not nodes.properties then return end -local next = next +local next, rawget = next, rawget local utfchar = utf.char -local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end) +local trace_injections = false trackers.register("fonts.injections", function(v) trace_injections = v end) -local report_injections = logs.reporter("nodes","injections") +local report_injections = logs.reporter("fonts","injections") + +report_injections("using experimental injector") local attributes, nodes, node = attributes, nodes, node @@ -29,6 +31,7 @@ local injections = nodes.injections local nodecodes = nodes.nodecodes local glyph_code = nodecodes.glyph +local disc_code = nodecodes.disc local kern_code = nodecodes.kern local nuts = nodes.nuts @@ -40,207 +43,351 @@ local tonode = nuts.tonode local tonut = nuts.tonut local getfield = nuts.getfield +local setfield = nuts.setfield local getnext = nuts.getnext local getprev = nuts.getprev local getid = nuts.getid -local getattr = nuts.getattr local getfont = nuts.getfont local getsubtype = nuts.getsubtype local getchar = nuts.getchar -local setfield = nuts.setfield -local setattr = nuts.setattr - local traverse_id = nuts.traverse_id local insert_node_before = nuts.insert_before local insert_node_after = nuts.insert_after +local find_tail = nuts.tail -local a_kernpair = attributes.private('kernpair') -local a_ligacomp = attributes.private('ligacomp') -local a_markbase = attributes.private('markbase') -local a_markmark = attributes.private('markmark') -local a_markdone = attributes.private('markdone') -local a_cursbase = attributes.private('cursbase') -local a_curscurs = attributes.private('curscurs') -local a_cursdone = attributes.private('cursdone') - -local unsetvalue = attributes.unsetvalue - --- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as --- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner --- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure --- that this code is not 100% okay but examples are needed to figure things out. +local properties = nodes.properties.data function injections.installnewkern(nk) newkern = nk or newkern end -local cursives = { } -local marks = { } -local kerns = { } +local nofregisteredkerns = 0 +local nofregisteredpairs = 0 +local nofregisteredmarks = 0 +local nofregisteredcursives = 0 +----- markanchors = { } -- one base can have more marks +local keepregisteredcounts = false --- 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. +function injections.keepcounts() + keepregisteredcounts = true +end --- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs --- checking with husayni (volt and fontforge). +function injections.resetcounts() + nofregisteredkerns = 0 + nofregisteredpairs = 0 + nofregisteredmarks = 0 + nofregisteredcursives = 0 + keepregisteredcounts = false +end function injections.reset(n) --- if getattr(n,a_kernpair) then --- setattr(n,a_kernpair,unsetvalue) --- end --- if getattr(n,a_markdone) then --- setattr(n,a_markbase,unsetvalue) --- setattr(n,a_markmark,unsetvalue) --- setattr(n,a_markdone,unsetvalue) --- end --- if getattr(n,a_cursdone) then --- setattr(n,a_cursbase,unsetvalue) --- setattr(n,a_curscurs,unsetvalue) --- setattr(n,a_cursdone,unsetvalue) --- end --- if getattr(n,a_ligacomp) then --- setattr(n,a_ligacomp,unsetvalue) --- end + local p = rawget(properties,start) + if p and p.injections then + -- todo: decrement counters? tricky as we then need to change the nof* to not increment + -- when we change a property + p.injections = nil -- should we keep the liga index? + end end function injections.setligaindex(n,index) - setattr(n,a_ligacomp,index) + local p = rawget(properties,n) + if p then + local i = p.injections + if i then + i.ligaindex = index + else + p.injections = { + ligaindex = index + } + end + else + properties[n] = { + injections = { + ligaindex = index + } + } + end end function injections.getligaindex(n,default) - return getattr(n,a_ligacomp) or default + local p = rawget(properties,n) + if p then + p = p.injections + if p then + return p.ligaindex or default + end + end + return default end -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) - local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes + local dx = factor*(exit[1]-entry[1]) + local dy = -factor*(exit[2]-entry[2]) local ws, wn = tfmstart.width, tfmnext.width - local bound = #cursives + 1 - setattr(start,a_cursbase,bound) - setattr(nxt,a_curscurs,bound) - cursives[bound] = { rlmode, dx, dy, ws, wn } - return dx, dy, bound + nofregisteredcursives = nofregisteredcursives + 1 + if rlmode < 0 then + dx = -(dx + wn) + else + dx = dx - ws + end + -- + local p = rawget(properties,start) + if p then + local i = p.injections + if i then + i.cursiveanchor = true + else + p.injections = { + cursiveanchor = true, + } + end + else + properties[start] = { + injections = { + cursiveanchor = true, + }, + } + end + local p = rawget(properties,nxt) + if p then + local i = p.injections + if i then + i.cursivex = dx + i.cursivey = dy + else + p.injections = { + cursivex = dx, + cursivey = dy, + } + end + else + properties[nxt] = { + injections = { + cursivex = dx, + cursivey = dy, + }, + } + end + return dx, dy, nofregisteredcursives end -function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) +function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used 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 = getattr(current,a_kernpair) - if bound then - local kb = kerns[bound] - -- inefficient but singles have less, but weird anyway, needs checking - kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h - else - bound = #kerns + 1 - setattr(current,a_kernpair,bound) - kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } - end - return x, y, w, h, bound + if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay? + local yoffset = y - h + local leftkern = x -- both kerns are set in a pair kern compared + local rightkern = w - x -- to normal kerns where we set only leftkern + if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then + nofregisteredpairs = nofregisteredpairs + 1 + if rlmode and rlmode < 0 then + leftkern, rightkern = rightkern, leftkern + end + local p = rawget(properties,current) + if p then + local i = p.injections + if i then + if leftkern ~= 0 or rightkern ~= 0 then + i.leftkern = i.leftkern or 0 + leftkern + i.rightkern = i.rightkern or 0 + rightkern + end + if yoffset ~= 0 then + i.yoffset = i.yoffset or 0 + yoffset + end + elseif leftkern ~= 0 or rightkern ~= 0 then + p.injections = { + leftkern = leftkern, + rightkern = rightkern, + yoffset = yoffset, + } + else + p.injections = { + yoffset = yoffset, + } + end + elseif leftkern ~= 0 or rightkern ~= 0 then + properties[current] = { + injections = { + leftkern = leftkern, + rightkern = rightkern, + yoffset = yoffset, + }, + } + else + properties[current] = { + injections = { + yoffset = yoffset, + }, + } + end + return x, y, w, h, nofregisteredpairs + end end return x, y, w, h -- no bound end -function injections.setkern(current,factor,rlmode,x,tfmchr) - local dx = factor*x +-- this needs checking for rl < 0 but it is unlikely that a r2l script +-- uses kernclasses between glyphs so we're probably safe (KE has a +-- problematic font where marks interfere with rl < 0 in the previous +-- case) + +function injections.setkern(current,factor,rlmode,x,injection) + local dx = factor * x if dx ~= 0 then - local bound = #kerns + 1 - setattr(current,a_kernpair,bound) - kerns[bound] = { rlmode, dx } - return dx, bound + nofregisteredkerns = nofregisteredkerns + 1 + local p = rawget(properties,current) + if not injection then + injection = "injections" + end + if p then + local i = p[injection] + if i then + i.leftkern = dx + i.leftkern or 0 + else + p[injection] = { + leftkern = dx, + } + end + else + properties[current] = { + [injection] = { + leftkern = dx, + }, + } + end + return dx, nofregisteredkerns else return 0, 0 end end -function injections.setmark(start,base,factor,rlmode,ba,ma) -- ba=baseanchor, ma=markanchor +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=baseanchor, ma=markanchor local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) - local bound = getattr(base,a_markbase) - local index = 1 - if bound then - local mb = marks[bound] - if mb then - -- if not index then index = #mb + 1 end - index = #mb + 1 - mb[index] = { dx, dy, rlmode } - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) - return dx, dy, bound + nofregisteredmarks = nofregisteredmarks + 1 + -- markanchors[nofregisteredmarks] = base + if rlmode >= 0 then + dx = tfmbase.width - dx -- see later commented ox + end + local p = rawget(properties,start) + if p then + local i = p.injections + if i then + i.markx = dx + i.marky = dy + i.markdir = rlmode or 0 + i.markbase = nofregisteredmarks + i.markbasenode = base else - report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound) + p.injections = { + markx = dx, + marky = dy, + markdir = rlmode or 0, + markbase = nofregisteredmarks, + markbasenode = base, + } end + else + properties[start] = { + injections = { + markx = dx, + marky = dy, + markdir = rlmode or 0, + markbase = nofregisteredmarks, + markbasenode = base, + }, + } end - index = index or 1 - bound = #marks + 1 - setattr(base,a_markbase,bound) - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) - marks[bound] = { [index] = { dx, dy, rlmode } } - return dx, dy, bound + return dx, dy, nofregisteredmarks end local function dir(n) return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" end -local function trace(head) - report_injections("begin run") - for n in traverse_id(glyph_code,head) do - if getsubtype(n) < 256 then - local kp = getattr(n,a_kernpair) - local mb = getattr(n,a_markbase) - local mm = getattr(n,a_markmark) - local md = getattr(n,a_markdone) - local cb = getattr(n,a_cursbase) - local cc = getattr(n,a_curscurs) - local char = getchar(n) - report_injections("font %s, char %U, glyph %c",getfont(n),char,char) - if kp then - local k = kerns[kp] - if k[3] then - report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) - else - report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) +local function showchar(n,nested) + local char = getchar(n) + report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char) +end + +local function show(n,what,nested,symbol) + if n then + local p = rawget(properties,n) + if p then + local p = p[what] + if p then + local leftkern = p.leftkern or 0 + local rightkern = p.rightkern or 0 + local yoffset = p.yoffset or 0 + local markx = p.markx or 0 + local marky = p.marky or 0 + local markdir = p.markdir or 0 + local markbase = p.markbase or 0 -- will be markbasenode + local cursivex = p.cursivex or 0 + local cursivey = p.cursivey or 0 + local ligaindex = p.ligaindex or 0 + local margin = nested and 4 or 2 + -- + if rightkern ~= 0 or yoffset ~= 0 then + report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset) + elseif leftkern ~= 0 then + report_injections("%w%s kern: dx %p",margin,symbol,leftkern) end - end - if mb then - report_injections(" markbase: bound %a",mb) - end - if mm then - local m = marks[mm] - if mb then - local m = m[mb] - if m then - report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) - else - report_injections(" markmark: bound %a, missing index",mm) - end - else - m = m[1] - report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) + if markx ~= 0 or marky ~= 0 or markbase ~= 0 then + report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no") + end + if cursivex ~= 0 or cursivey ~= 0 then + report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey) + end + if ligaindex ~= 0 then + report_injections("%w%s liga: index %i",margin,symbol,ligaindex) end end - if cb then - report_injections(" cursbase: bound %a",cb) + end + end +end + +local function showsub(n,what,where) + report_injections("begin subrun: %s",where) + for n in traverse_id(glyph_code,n) do + showchar(n,where) + show(n,what,where," ") + end + report_injections("end subrun") +end + +local function trace(head) + report_injections("begin run: %s kerns, %s pairs, %s marks and %s cursives registered", + nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives) + local n = head + while n do + local id = getid(n) + if id == glyph_code then + showchar(n) + show(n,"injections",false," ") + show(n,"preinjections",false,"<") + show(n,"postinjections",false,">") + show(n,"replaceinjections",false,"=") + elseif id == disc_code then + local pre = getfield(n,"pre") + local post = getfield(n,"post") + local replace = getfield(n,"replace") + if pre then + showsub(pre,"preinjections","pre") end - if cc then - local c = cursives[cc] - report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) + if post then + showsub(post,"postinjections","post") + end + if replace then + showsub(replace,"replaceinjections","replace") end end + n = getnext(n) end report_injections("end run") end --- todo: reuse tables (i.e. no collection), but will be extra fields anyway --- todo: check for attribute - --- We can have a fast test on a font being processed, so we can check faster for marks etc --- but I'll make a context variant anyway. - local function show_result(head) - local current = head + local current = head local skipping = false while current do local id = getid(current) @@ -259,345 +406,616 @@ local function show_result(head) end end -function injections.handler(head,where,keep) - head = tonut(head) - 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) +-- we could also check for marks here but maybe not all are registered (needs checking) + +local function collect_glyphs_1(head) + local glyphs, nofglyphs = { }, 0 + local marks, nofmarks = { }, 0 + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + local f = getfont(n) + if f ~= nf then + nf = f + tm = fontdata[nf].resources.marks -- other hash in ctx + end + if tm and tm[getchar(n)] then + nofmarks = nofmarks + 1 + marks[nofmarks] = n + else + nofglyphs = nofglyphs + 1 + glyphs[nofglyphs] = n + end + -- yoffsets can influence curs steps + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) + end + end 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 getsubtype(n) < 256 then - nofvalid = nofvalid + 1 - valid[nofvalid] = n - local f = getfont(n) - if f ~= nf then - nf = f - tm = fontdata[nf].resources.marks -- other hash in ctx + end + return glyphs, nofglyphs, marks, nofmarks +end + +local function collect_glyphs_2(head) + local glyphs, nofglyphs = { }, 0 + local marks, nofmarks = { }, 0 + local nf, tm = nil, nil + for n in traverse_id(glyph_code,head) do + if getsubtype(n) < 256 then + local f = getfont(n) + if f ~= nf then + nf = f + tm = fontdata[nf].resources.marks -- other hash in ctx + end + if tm and tm[getchar(n)] then + nofmarks = nofmarks + 1 + marks[nofmarks] = n + else + nofglyphs = nofglyphs + 1 + glyphs[nofglyphs] = n + end + end + end + return glyphs, nofglyphs, marks, nofmarks +end + +local function inject_marks(marks,nofmarks) + for i=1,nofmarks do + local n = marks[i] + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + if pn then + -- local markbase = pn.markbase + -- if markbase then + -- local p = markanchors[markbase] + local p = pn.markbasenode + if p then + local px = getfield(p,"xoffset") + local ox = 0 + local pp = rawget(properties,p) + local rightkern = pp and pp.rightkern + if rightkern then -- x and w ~= 0 + if pn.markdir < 0 then + -- kern(w-x) glyph(p) kern(x) mark(n) + ox = px - pn.markx - rightkern + -- report_injections("r2l case 1: %p",ox) + else + -- kern(x) glyph(p) kern(w-x) mark(n) + -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern + ox = px - pn.markx - pp.leftkern + -- report_injections("l2r case 1: %p",ox) + end + else + -- we need to deal with fonts that have marks with width + -- if pn.markdir < 0 then + -- ox = px - pn.markx + -- -- report_injections("r2l case 3: %p",ox) + -- else + -- -- ox = px - getfield(p,"width") + pn.markx + ox = px - pn.markx + -- report_injections("l2r case 3: %p",ox) + -- end + local wn = getfield(n,"width") -- in arial marks have widths + if wn ~= 0 then + -- bad: we should center + -- insert_node_before(head,n,newkern(-wn/2)) + -- insert_node_after(head,n,newkern(-wn/2)) + pn.leftkern = -wn/2 + pn.rightkern = -wn/2 + -- wx[n] = { 0, -wn/2, 0, -wn } + end + -- so far end - if tm then - mk[n] = tm[getchar(n)] + setfield(n,"xoffset",ox) + -- + local py = getfield(p,"yoffset") + local oy = 0 + if marks[p] then + oy = py + pn.marky + else + oy = getfield(n,"yoffset") + py + pn.marky end - local k = getattr(n,a_kernpair) - if k then - local kk = kerns[k] - if kk then - local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0 - local dy = y - h - if dy ~= 0 then - ky[n] = dy - end - if w ~= 0 or x ~= 0 then - wx[n] = kk - end - rl[n] = kk[1] -- could move in test + setfield(n,"yoffset",oy) + else + -- normally this can't happen (only when in trace mode which is a special case anyway) + -- report_injections("missing mark anchor %i",pn.markbase or 0) + end + -- end + end + end +end + +local function inject_cursives(glyphs,nofglyphs) + local cursiveanchor, lastanchor = nil, nil + local minc, maxc, last = 0, 0, nil + for i=1,nofglyphs do + local n = glyphs[i] + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + if pn then + local cursivex = pn.cursivex + if cursivex then + if cursiveanchor then + if cursivex ~= 0 then + pn.leftkern = pn.leftkern or 0 + cursivex + end + if lastanchor then + if maxc == 0 then + minc = lastanchor end + maxc = lastanchor + properties[cursiveanchor].cursivedy = pn.cursivey end + last = n + else + maxc = 0 + end + elseif maxc > 0 then + local ny = getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",ny) -- why not add ? end + maxc = 0 end - else - local nf, tm = nil, nil - for n in traverse_id(glyph_code,head) do - if getsubtype(n) < 256 then - nofvalid = nofvalid + 1 - valid[nofvalid] = n - local f = getfont(n) - if f ~= nf then - nf = f - tm = fontdata[nf].resources.marks -- other hash in ctx - end - if tm then - mk[n] = tm[getchar(n)] + if pn.cursiveanchor then + cursiveanchor = n + lastanchor = i + else + cursiveanchor = nil + lastanchor = nil + if maxc > 0 then + local ny = getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",ny) -- why not add ? end + maxc = 0 end end + elseif maxc > 0 then + local ny = getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) -- ? + end + maxc = 0 + cursiveanchor = nil + lastanchor = nil 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 - setfield(n,"yoffset",k) - end + -- if maxc > 0 and not cursiveanchor then + -- local ny = getfield(n,"yoffset") + -- for i=maxc,minc,-1 do + -- local ti = glyphs[i] + -- ny = ny + properties[ti].cursivedy + -- setfield(ti,"yoffset",ny) -- why not add ? + -- end + -- maxc = 0 + -- end + end + if last and maxc > 0 then + local ny = getfield(last,"yoffset") + for i=maxc,minc,-1 do + local ti = glyphs[i] + ny = ny + properties[ti].cursivedy + setfield(ti,"yoffset",ny) -- why not add ? + end + end +end + +local function inject_kerns(head,glyphs,nofglyphs) + -- todo: pre/post/replace + for i=1,#glyphs do + local n = glyphs[i] + local pn = rawget(properties,n) + if pn then + pn = pn.injections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + insert_node_before(head,n,newkern(leftkern)) -- type 0/2 + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) -- type 0/2 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 = getattr(n,a_cursbase) - if p_cursbase then - local n_curscurs = getattr(n,a_curscurs) - if p_cursbase == n_curscurs then - local c = cursives[n_curscurs] - if c then - local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] - if rlmode >= 0 then - dx = dx - ws - else - dx = dx + wn - end - if dx ~= 0 then - cx[n] = dx - rl[n] = rlmode - end - -- if rlmode and rlmode < 0 then - dy = -dy - -- end - maxt = maxt + 1 - t[maxt] = p - d[maxt] = dy - else - maxt = 0 + end + end +end + +local function inject_everything(head,where) + head = tonut(head) + if trace_injections then + trace(head) + end + local glyphs, nofglyphs, marks, nofmarks + if nofregisteredpairs > 0 then + glyphs, nofglyphs, marks, nofmarks = collect_glyphs_1(head) + else + glyphs, nofglyphs, marks, nofmarks = collect_glyphs_2(head) + end + if nofglyphs > 0 then + if nofregisteredcursives > 0 then + inject_cursives(glyphs,nofglyphs) + end + if nofregisteredmarks > 0 then + inject_marks(marks,nofmarks) + end + inject_kerns(head,glyphs,nofglyphs) + end + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredkerns = 0 + nofregisteredpairs = 0 + nofregisteredmarks = 0 + nofregisteredcursives = 0 + end + return tonode(head), true +end + +local function inject_kerns_only(head,where) + head = tonut(head) + if trace_injections then + trace(head) + end + local n = head + local p = nil + while n do + local id = getid(n) + if id == glyph_code then + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + if p then + local d = getfield(p,"post") + if d then + local pn = pn.postinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) end end - elseif maxt > 0 then - local ny = getfield(n,"yoffset") - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) + end + local d = getfield(p,"replace") + if d then + local pn = pn.replaceinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) + end + end + else + local pn = pn.injections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + setfield(p,"replace",newkern(leftkern)) + end end - maxt = 0 end - if not n_cursbase and maxt > 0 then - local ny = getfield(n,"yoffset") - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - setfield(ti,"yoffset",ny) -- maybe add to current yoffset + else + local pn = pn.injections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + head = insert_node_before(head,n,newkern(leftkern)) end - maxt = 0 end - p_cursbase, p = n_cursbase, n end end - if maxt > 0 then - local ny = getfield(n,"yoffset") -- hm, n unset ? - for i=maxt,1,-1 do - ny = ny + d[i] - local ti = t[i] - setfield(ti,"yoffset",ny) + else + break + end + p = nil + elseif id == disc_code then + local d = getfield(n,"pre") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.preinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + end + else + break end - maxt = 0 end - if not keep then - cursives = { } + if h ~= d then + setfield(n,"pre",h) end end - if has_marks then - for i=1,nofvalid do - local p = valid[i] - local p_markbase = getattr(p,a_markbase) - if p_markbase then - local mrks = marks[p_markbase] - local nofmarks = #mrks - for n in traverse_id(glyph_code,getnext(p)) do - local n_markmark = getattr(n,a_markmark) - if p_markbase == n_markmark then - local index = getattr(n,a_markdone) or 1 - local d = mrks[index] - if d then - local rlmode = d[3] - -- - local k = wx[p] - local px = getfield(p,"xoffset") - local ox = 0 - if k then - local x = k[2] - local w = k[4] - if w then - if rlmode and rlmode >= 0 then - -- kern(x) glyph(p) kern(w-x) mark(n) - ox = px - getfield(p,"width") + d[1] - (w-x) - -- report_injections("l2r case 1: %p",ox) - else - -- kern(w-x) glyph(p) kern(x) mark(n) - ox = px - d[1] - x - -- report_injections("r2l case 1: %p",ox) - end - else - if rlmode and rlmode >= 0 then - -- okay for husayni - ox = px - getfield(p,"width") + d[1] - -- report_injections("r2l case 2: %p",ox) - else - -- needs checking: is x ok here? - ox = px - d[1] - x - -- report_injections("r2l case 2: %p",ox) - end - end - else - -- if rlmode and rlmode >= 0 then - -- ox = px - getfield(p,"width") + d[1] - -- -- report_injections("l2r case 3: %p",ox) - -- else - -- ox = px - d[1] - -- -- report_injections("r2l case 3: %p",ox) - -- end - -- - -- we need to deal with fonts that have marks with width - -- - local wp = getfield(p,"width") - local wn = getfield(n,"width") -- in arial marks have widths - if rlmode and rlmode >= 0 then - ox = px - wp + d[1] - -- report_injections("l2r case 3: %p",ox) - else - ox = px - d[1] - -- report_injections("r2l case 3: %p",ox) - end - if wn ~= 0 then - -- bad: we should center - insert_node_before(head,n,newkern(-wn/2)) - insert_node_after(head,n,newkern(-wn/2)) - -- wx[n] = { 0, -wn/2, 0, -wn } - end - -- so far - end - setfield(n,"xoffset",ox) - -- - local py = getfield(p,"yoffset") - local oy = 0 - if mk[p] then - oy = py + d[2] - else - oy = getfield(n,"yoffset") + py + d[2] - end - setfield(n,"yoffset",oy) - -- - if nofmarks == 1 then - break - else - nofmarks = nofmarks - 1 - end - end - elseif not n_markmark then - break -- HH: added 2013-09-12: no need to deal with non marks - else - -- KE: there can be sequences in ligatures + local d = getfield(n,"post") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.postinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) end end + else + break end end - if not keep then - marks = { } + if h ~= d then + setfield(n,"post",h) end end - -- todo : combine - if next(wx) then - for n, k in next, wx do - -- only w can be nil (kernclasses), can be sped up when w == nil - local x = k[2] - local w = k[4] - if w then - local rl = k[1] -- r2l = k[6] - local wx = w - x - if rl < 0 then -- KE: don't use r2l here - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) -- type 0/2 + local d = getfield(n,"replace") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) -- why can it be empty { } + if pn then + pn = pn.replaceinjections + end + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) -- type 0/2 + end + else + break + end + end + if h ~= d then + setfield(n,"replace",h) + end + end + p = n + else + p = nil + end + n = getnext(n) + end + -- + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredkerns = 0 + end + return tonode(head), true +end + +local function inject_pairs_only(head,where) + head = tonut(head) + if trace_injections then + trace(head) + end + -- + local n = head + local p = nil + while n do + local id = getid(n) + if id == glyph_code then + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + if p then + local d = getfield(p,"post") + if d then + local pn = pn.postinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) + end + -- local rightkern = pn.rightkern + -- if rightkern and rightkern ~= 0 then + -- insert_node_after(head,n,newkern(rightkern)) + -- n = getnext(n) -- to be checked + -- end + end + end + local d = getfield(p,"replace") + if d then + local pn = pn.replaceinjections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + local t = find_tail(d) + insert_node_after(d,t,newkern(leftkern)) + end + -- local rightkern = pn.rightkern + -- if rightkern and rightkern ~= 0 then + -- insert_node_after(head,n,newkern(rightkern)) + -- n = getnext(n) -- to be checked + -- end end else - if x ~= 0 then - insert_node_before(head,n,newkern(x)) -- type 0/2 + local pn = pn.injections + if pn then + local leftkern = pn.leftkern + if leftkern ~= 0 then + setfield(p,"replace",newkern(leftkern)) + end + -- local rightkern = pn.rightkern + -- if rightkern and rightkern ~= 0 then + -- insert_node_after(head,n,newkern(rightkern)) + -- n = getnext(n) -- to be checked + -- end + end + end + else + -- this is the most common case + local pn = pn.injections + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) end - if wx ~= 0 then - insert_node_after (head,n,newkern(wx)) -- type 0/2 + local leftkern = pn.leftkern + if leftkern ~= 0 then + insert_node_before(head,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked end end - elseif x ~= 0 then - -- this needs checking for rl < 0 but it is unlikely that a r2l script - -- uses kernclasses between glyphs so we're probably safe (KE has a - -- problematic font where marks interfere with rl < 0 in the previous - -- case) - insert_node_before(head,n,newkern(x)) -- a real font kern, type 0 end end + else + break end - if next(cx) then - for n, k in next, cx do - if k ~= 0 then - local rln = rl[n] - if rln and rln < 0 then - insert_node_before(head,n,newkern(-k)) -- type 0/2 - else - insert_node_before(head,n,newkern(k)) -- type 0/2 + p = nil + elseif id == disc_code then + local d = getfield(n,"pre") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.preinjections end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) + end + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked + end + end + else + break end end + if h ~= d then + setfield(n,"pre",h) + end end - if not keep then - kerns = { } + local d = getfield(n,"post") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.postinjections + end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) + end + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked + end + end + else + break + end + end + if h ~= d then + setfield(n,"post",h) + end end - -- if trace_injections then - -- show_result(head) - -- end - return tonode(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 getsubtype(n) < 256 then - local k = getattr(n,a_kernpair) - if k then - local kk = kerns[k] - if kk then - local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4] - if y and y ~= 0 then - setfield(n,"yoffset",y) -- todo: h ? + local d = getfield(n,"replace") + if d then + local h = d + for n in traverse_id(glyph_code,d) do + if getsubtype(n) < 256 then + local pn = rawget(properties,n) + if pn then + pn = pn.replaceinjections end - if w then - -- copied from above - -- local r2l = kk[6] - local wx = w - x - if rl < 0 then -- KE: don't use r2l here - if wx ~= 0 then - insert_node_before(head,n,newkern(wx)) - end - if x ~= 0 then - insert_node_after (head,n,newkern(x)) - end - else - if x ~= 0 then - insert_node_before(head,n,newkern(x)) - end - if wx ~= 0 then - insert_node_after(head,n,newkern(wx)) - end + if pn then + local yoffset = pn.yoffset + if yoffset and yoffset ~= 0 then + setfield(n,"yoffset",yoffset) end - else - -- simple (e.g. kernclass kerns) - if x ~= 0 then - insert_node_before(head,n,newkern(x)) + local leftkern = pn.leftkern + if leftkern ~= 0 then + h = insert_node_before(h,n,newkern(leftkern)) + end + local rightkern = pn.rightkern + if rightkern and rightkern ~= 0 then + insert_node_after(head,n,newkern(rightkern)) + n = getnext(n) -- to be checked end end + else + break end end + if h ~= d then + setfield(n,"replace",h) + end end + p = n + else + p = nil end - if not keep then - kerns = { } - end - -- if trace_injections then - -- show_result(head) - -- end - return tonode(head), true + n = getnext(n) + end + -- + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredpairs = 0 + nofregisteredkerns = 0 + end + return tonode(head), true +end + +function injections.handler(head,where) -- optimize for n=1 ? + if nofregisteredmarks > 0 or nofregisteredcursives > 0 then + return inject_everything(head,where) + elseif nofregisteredpairs > 0 then + return inject_pairs_only(head,where) + elseif nofregisteredkerns > 0 then + return inject_kerns_only(head,where) else - -- no tracing needed + return head, false end - return tonode(head), false end diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index f11a74ca5..96a7dd361 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 12/11/14 12:02:53 +-- merge date : 12/21/14 22:25:48 do -- begin closure to overcome local limits and interference @@ -3889,8 +3889,6 @@ nodes.set_attribute=node.set_attribute nodes.unset_attribute=node.unset_attribute nodes.protect_glyphs=node.protect_glyphs nodes.unprotect_glyphs=node.unprotect_glyphs -nodes.kerning=node.kerning -nodes.ligaturing=node.ligaturing nodes.mlist_to_hlist=node.mlist_to_hlist local direct=node.direct local nuts={} @@ -3917,8 +3915,12 @@ nuts.insert_before=direct.insert_before nuts.insert_after=direct.insert_after nuts.delete=direct.delete nuts.copy=direct.copy +nuts.copy_list=direct.copy_list nuts.tail=direct.tail nuts.flush_list=direct.flush_list +nuts.free=direct.free +nuts.remove=direct.remove +nuts.is_node=direct.is_node nuts.end_of_math=direct.end_of_math nuts.traverse=direct.traverse nuts.traverse_id=direct.traverse_id @@ -8730,6 +8732,7 @@ actions["reorganize glyph lookups"]=function(data,filename,raw) end end end +local zero={ 0,0 } actions["reorganize glyph anchors"]=function(data,filename,raw) local descriptions=data.descriptions for unicode,description in next,descriptions do @@ -8738,14 +8741,32 @@ actions["reorganize glyph anchors"]=function(data,filename,raw) 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 } + local n=0 + for k,v in next,specification do + if k>n then + n=k + end + local x,y=v.x,v.y + if x or y then + specification[k]={ x or 0,y or 0 } + else + specification[k]=zero + end + end + local t={} + for i=1,n do + t[i]=specification[i] or zero end + data[tag]=t end else for tag,specification in next,data do - data[tag]={ specification.x or 0,specification.y or 0 } + local x,y=specification.x,specification.y + if x or y then + data[tag]={ x or 0,y or 0 } + else + data[tag]=zero + end end end end @@ -9819,17 +9840,19 @@ 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-inj']={ version=1.001, - comment="companion to node-ini.mkiv", + comment="companion to font-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files", } -local next=next +if not nodes.properties then return end +local next,rawget=next,rawget local utfchar=utf.char -local trace_injections=false trackers.register("nodes.injections",function(v) trace_injections=v end) -local report_injections=logs.reporter("nodes","injections") +local trace_injections=false trackers.register("fonts.injections",function(v) trace_injections=v end) +local report_injections=logs.reporter("fonts","injections") +report_injections("using experimental injector") local attributes,nodes,node=attributes,nodes,node fonts=fonts local fontdata=fonts.hashes.identifiers @@ -9837,6 +9860,7 @@ nodes.injections=nodes.injections or {} local injections=nodes.injections local nodecodes=nodes.nodecodes local glyph_code=nodecodes.glyph +local disc_code=nodecodes.disc local kern_code=nodecodes.kern local nuts=nodes.nuts local nodepool=nuts.pool @@ -9844,149 +9868,316 @@ local newkern=nodepool.kern local tonode=nuts.tonode local tonut=nuts.tonut local getfield=nuts.getfield +local setfield=nuts.setfield local getnext=nuts.getnext local getprev=nuts.getprev local getid=nuts.getid -local getattr=nuts.getattr local getfont=nuts.getfont local getsubtype=nuts.getsubtype local getchar=nuts.getchar -local setfield=nuts.setfield -local setattr=nuts.setattr local traverse_id=nuts.traverse_id local insert_node_before=nuts.insert_before local insert_node_after=nuts.insert_after -local a_kernpair=attributes.private('kernpair') -local a_ligacomp=attributes.private('ligacomp') -local a_markbase=attributes.private('markbase') -local a_markmark=attributes.private('markmark') -local a_markdone=attributes.private('markdone') -local a_cursbase=attributes.private('cursbase') -local a_curscurs=attributes.private('curscurs') -local a_cursdone=attributes.private('cursdone') -local unsetvalue=attributes.unsetvalue +local find_tail=nuts.tail +local properties=nodes.properties.data function injections.installnewkern(nk) newkern=nk or newkern end -local cursives={} -local marks={} -local kerns={} +local nofregisteredkerns=0 +local nofregisteredpairs=0 +local nofregisteredmarks=0 +local nofregisteredcursives=0 +local keepregisteredcounts=false +function injections.keepcounts() + keepregisteredcounts=true +end +function injections.resetcounts() + nofregisteredkerns=0 + nofregisteredpairs=0 + nofregisteredmarks=0 + nofregisteredcursives=0 + keepregisteredcounts=false +end function injections.reset(n) + local p=rawget(properties,start) + if p and p.injections then + p.injections=nil + end end function injections.setligaindex(n,index) - setattr(n,a_ligacomp,index) + local p=rawget(properties,n) + if p then + local i=p.injections + if i then + i.ligaindex=index + else + p.injections={ + ligaindex=index + } + end + else + properties[n]={ + injections={ + ligaindex=index + } + } + end end function injections.getligaindex(n,default) - return getattr(n,a_ligacomp) or default + local p=rawget(properties,n) + if p then + p=p.injections + if p then + return p.ligaindex or default + end + end + return default end -function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) - local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2]) +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) + local dx=factor*(exit[1]-entry[1]) + local dy=-factor*(exit[2]-entry[2]) local ws,wn=tfmstart.width,tfmnext.width - local bound=#cursives+1 - setattr(start,a_cursbase,bound) - setattr(nxt,a_curscurs,bound) - cursives[bound]={ rlmode,dx,dy,ws,wn } - return dx,dy,bound -end -function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) - local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4] - if x~=0 or w~=0 or y~=0 or h~=0 then - local bound=getattr(current,a_kernpair) - if bound then - local kb=kerns[bound] - 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 + nofregisteredcursives=nofregisteredcursives+1 + if rlmode<0 then + dx=-(dx+wn) + else + dx=dx-ws + end + local p=rawget(properties,start) + if p then + local i=p.injections + if i then + i.cursiveanchor=true + else + p.injections={ + cursiveanchor=true, + } + end + else + properties[start]={ + injections={ + cursiveanchor=true, + }, + } + end + local p=rawget(properties,nxt) + if p then + local i=p.injections + if i then + i.cursivex=dx + i.cursivey=dy else - bound=#kerns+1 - setattr(current,a_kernpair,bound) - kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width } + p.injections={ + cursivex=dx, + cursivey=dy, + } end - return x,y,w,h,bound + else + properties[nxt]={ + injections={ + cursivex=dx, + cursivey=dy, + }, + } + end + return dx,dy,nofregisteredcursives +end +function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) + local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4] + if x~=0 or w~=0 or y~=0 or h~=0 then + local yoffset=y-h + local leftkern=x + local rightkern=w-x + if leftkern~=0 or rightkern~=0 or yoffset~=0 then + nofregisteredpairs=nofregisteredpairs+1 + if rlmode and rlmode<0 then + leftkern,rightkern=rightkern,leftkern + end + local p=rawget(properties,current) + if p then + local i=p.injections + if i then + if leftkern~=0 or rightkern~=0 then + i.leftkern=i.leftkern or 0+leftkern + i.rightkern=i.rightkern or 0+rightkern + end + if yoffset~=0 then + i.yoffset=i.yoffset or 0+yoffset + end + elseif leftkern~=0 or rightkern~=0 then + p.injections={ + leftkern=leftkern, + rightkern=rightkern, + yoffset=yoffset, + } + else + p.injections={ + yoffset=yoffset, + } + end + elseif leftkern~=0 or rightkern~=0 then + properties[current]={ + injections={ + leftkern=leftkern, + rightkern=rightkern, + yoffset=yoffset, + }, + } + else + properties[current]={ + injections={ + yoffset=yoffset, + }, + } + end + return x,y,w,h,nofregisteredpairs + end end return x,y,w,h end -function injections.setkern(current,factor,rlmode,x,tfmchr) +function injections.setkern(current,factor,rlmode,x,injection) local dx=factor*x if dx~=0 then - local bound=#kerns+1 - setattr(current,a_kernpair,bound) - kerns[bound]={ rlmode,dx } - return dx,bound + nofregisteredkerns=nofregisteredkerns+1 + local p=rawget(properties,current) + if not injection then + injection="injections" + end + if p then + local i=p[injection] + if i then + i.leftkern=dx+i.leftkern or 0 + else + p[injection]={ + leftkern=dx, + } + end + else + properties[current]={ + [injection]={ + leftkern=dx, + }, + } + end + return dx,nofregisteredkerns else return 0,0 end end -function injections.setmark(start,base,factor,rlmode,ba,ma) +function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2]) - local bound=getattr(base,a_markbase) - local index=1 - if bound then - local mb=marks[bound] - if mb then - index=#mb+1 - mb[index]={ dx,dy,rlmode } - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) - return dx,dy,bound + nofregisteredmarks=nofregisteredmarks+1 + if rlmode>=0 then + dx=tfmbase.width-dx + end + local p=rawget(properties,start) + if p then + local i=p.injections + if i then + i.markx=dx + i.marky=dy + i.markdir=rlmode or 0 + i.markbase=nofregisteredmarks + i.markbasenode=base else - report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound) + p.injections={ + markx=dx, + marky=dy, + markdir=rlmode or 0, + markbase=nofregisteredmarks, + markbasenode=base, + } end + else + properties[start]={ + injections={ + markx=dx, + marky=dy, + markdir=rlmode or 0, + markbase=nofregisteredmarks, + markbasenode=base, + }, + } end - index=index or 1 - bound=#marks+1 - setattr(base,a_markbase,bound) - setattr(start,a_markmark,bound) - setattr(start,a_markdone,index) - marks[bound]={ [index]={ dx,dy,rlmode } } - return dx,dy,bound + return dx,dy,nofregisteredmarks end local function dir(n) return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" end -local function trace(head) - report_injections("begin run") - for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then - local kp=getattr(n,a_kernpair) - local mb=getattr(n,a_markbase) - local mm=getattr(n,a_markmark) - local md=getattr(n,a_markdone) - local cb=getattr(n,a_cursbase) - local cc=getattr(n,a_curscurs) - local char=getchar(n) - report_injections("font %s, char %U, glyph %c",getfont(n),char,char) - if kp then - local k=kerns[kp] - if k[3] then - report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) - else - report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2]) +local function showchar(n,nested) + local char=getchar(n) + report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char) +end +local function show(n,what,nested,symbol) + if n then + local p=rawget(properties,n) + if p then + local p=p[what] + if p then + local leftkern=p.leftkern or 0 + local rightkern=p.rightkern or 0 + local yoffset=p.yoffset or 0 + local markx=p.markx or 0 + local marky=p.marky or 0 + local markdir=p.markdir or 0 + local markbase=p.markbase or 0 + local cursivex=p.cursivex or 0 + local cursivey=p.cursivey or 0 + local ligaindex=p.ligaindex or 0 + local margin=nested and 4 or 2 + if rightkern~=0 or yoffset~=0 then + report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset) + elseif leftkern~=0 then + report_injections("%w%s kern: dx %p",margin,symbol,leftkern) end - end - if mb then - report_injections(" markbase: bound %a",mb) - end - if mm then - local m=marks[mm] - if mb then - local m=m[mb] - if m then - report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) - else - report_injections(" markmark: bound %a, missing index",mm) - end - else - m=m[1] - report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) + if markx~=0 or marky~=0 or markbase~=0 then + report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase~=0 and "yes" or "no") + end + if cursivex~=0 or cursivey~=0 then + report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey) + end + if ligaindex~=0 then + report_injections("%w%s liga: index %i",margin,symbol,ligaindex) end end - if cb then - report_injections(" cursbase: bound %a",cb) + end + end +end +local function showsub(n,what,where) + report_injections("begin subrun: %s",where) + for n in traverse_id(glyph_code,n) do + showchar(n,where) + show(n,what,where," ") + end + report_injections("end subrun") +end +local function trace(head) + report_injections("begin run: %s kerns, %s pairs, %s marks and %s cursives registered", + nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives) + local n=head + while n do + local id=getid(n) + if id==glyph_code then + showchar(n) + show(n,"injections",false," ") + show(n,"preinjections",false,"<") + show(n,"postinjections",false,">") + show(n,"replaceinjections",false,"=") + elseif id==disc_code then + local pre=getfield(n,"pre") + local post=getfield(n,"post") + local replace=getfield(n,"replace") + if pre then + showsub(pre,"preinjections","pre") + end + if post then + showsub(post,"postinjections","post") end - if cc then - local c=cursives[cc] - report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) + if replace then + showsub(replace,"replaceinjections","replace") end end + n=getnext(n) end report_injections("end run") end @@ -10009,298 +10200,553 @@ local function show_result(head) current=getnext(current) end end -function injections.handler(head,where,keep) - head=tonut(head) - 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 - local done,ky,rl,valid,cx,wx,mk,nofvalid=false,{},{},{},{},{},{},0 - if has_kerns then - local nf,tm=nil,nil - for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then - nofvalid=nofvalid+1 - valid[nofvalid]=n - local f=getfont(n) - if f~=nf then - nf=f - tm=fontdata[nf].resources.marks +local function collect_glyphs_1(head) + local glyphs,nofglyphs={},0 + local marks,nofmarks={},0 + local nf,tm=nil,nil + for n in traverse_id(glyph_code,head) do + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + pn=pn.injections + end + local f=getfont(n) + if f~=nf then + nf=f + tm=fontdata[nf].resources.marks + end + if tm and tm[getchar(n)] then + nofmarks=nofmarks+1 + marks[nofmarks]=n + else + nofglyphs=nofglyphs+1 + glyphs[nofglyphs]=n + end + if pn then + local yoffset=pn.yoffset + if yoffset and yoffset~=0 then + setfield(n,"yoffset",yoffset) + end + end + end + end + return glyphs,nofglyphs,marks,nofmarks +end +local function collect_glyphs_2(head) + local glyphs,nofglyphs={},0 + local marks,nofmarks={},0 + local nf,tm=nil,nil + for n in traverse_id(glyph_code,head) do + if getsubtype(n)<256 then + local f=getfont(n) + if f~=nf then + nf=f + tm=fontdata[nf].resources.marks + end + if tm and tm[getchar(n)] then + nofmarks=nofmarks+1 + marks[nofmarks]=n + else + nofglyphs=nofglyphs+1 + glyphs[nofglyphs]=n + end + end + end + return glyphs,nofglyphs,marks,nofmarks +end +local function inject_marks(marks,nofmarks) + for i=1,nofmarks do + local n=marks[i] + local pn=rawget(properties,n) + if pn then + pn=pn.injections + end + if pn then + local p=pn.markbasenode + if p then + local px=getfield(p,"xoffset") + local ox=0 + local pp=rawget(properties,p) + local rightkern=pp and pp.rightkern + if rightkern then + if pn.markdir<0 then + ox=px-pn.markx-rightkern + else + ox=px-pn.markx-pp.leftkern + end + else + ox=px-pn.markx + local wn=getfield(n,"width") + if wn~=0 then + pn.leftkern=-wn/2 + pn.rightkern=-wn/2 + end + end + setfield(n,"xoffset",ox) + local py=getfield(p,"yoffset") + local oy=0 + if marks[p] then + oy=py+pn.marky + else + oy=getfield(n,"yoffset")+py+pn.marky end - if tm then - mk[n]=tm[getchar(n)] + setfield(n,"yoffset",oy) + else + end + end + end +end +local function inject_cursives(glyphs,nofglyphs) + local cursiveanchor,lastanchor=nil,nil + local minc,maxc,last=0,0,nil + for i=1,nofglyphs do + local n=glyphs[i] + local pn=rawget(properties,n) + if pn then + pn=pn.injections + end + if pn then + local cursivex=pn.cursivex + if cursivex then + if cursiveanchor then + if cursivex~=0 then + pn.leftkern=pn.leftkern or 0+cursivex end - local k=getattr(n,a_kernpair) - if k then - local kk=kerns[k] - if kk then - local x,y,w,h=kk[2] or 0,kk[3] or 0,kk[4] or 0,kk[5] or 0 - local dy=y-h - if dy~=0 then - ky[n]=dy - end - if w~=0 or x~=0 then - wx[n]=kk - end - rl[n]=kk[1] + if lastanchor then + if maxc==0 then + minc=lastanchor end + maxc=lastanchor + properties[cursiveanchor].cursivedy=pn.cursivey end + last=n + else + maxc=0 end + elseif maxc>0 then + local ny=getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setfield(ti,"yoffset",ny) + end + maxc=0 end - else - local nf,tm=nil,nil - for n in traverse_id(glyph_code,head) do - if getsubtype(n)<256 then - nofvalid=nofvalid+1 - valid[nofvalid]=n - local f=getfont(n) - if f~=nf then - nf=f - tm=fontdata[nf].resources.marks - end - if tm then - mk[n]=tm[getchar(n)] + if pn.cursiveanchor then + cursiveanchor=n + lastanchor=i + else + cursiveanchor=nil + lastanchor=nil + if maxc>0 then + local ny=getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setfield(ti,"yoffset",ny) end + maxc=0 end end + elseif maxc>0 then + local ny=getfield(n,"yoffset") + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setfield(ti,"yoffset",getfield(ti,"yoffset")+ny) + end + maxc=0 + cursiveanchor=nil + lastanchor=nil end - if nofvalid>0 then - local cx={} - if has_kerns and next(ky) then - for n,k in next,ky do - setfield(n,"yoffset",k) - end + end + if last and maxc>0 then + local ny=getfield(last,"yoffset") + for i=maxc,minc,-1 do + local ti=glyphs[i] + ny=ny+properties[ti].cursivedy + setfield(ti,"yoffset",ny) + end + end +end +local function inject_kerns(head,glyphs,nofglyphs) + for i=1,#glyphs do + local n=glyphs[i] + local pn=rawget(properties,n) + if pn then + pn=pn.injections + end + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + insert_node_before(head,n,newkern(leftkern)) end - if has_cursives then - local p_cursbase,p=nil,nil - local t,d,maxt={},{},0 - for i=1,nofvalid do - local n=valid[i] - if not mk[n] then - local n_cursbase=getattr(n,a_cursbase) - if p_cursbase then - local n_curscurs=getattr(n,a_curscurs) - if p_cursbase==n_curscurs then - local c=cursives[n_curscurs] - if c then - local rlmode,dx,dy,ws,wn=c[1],c[2],c[3],c[4],c[5] - if rlmode>=0 then - dx=dx-ws - else - dx=dx+wn - end - if dx~=0 then - cx[n]=dx - rl[n]=rlmode - end - dy=-dy - maxt=maxt+1 - t[maxt]=p - d[maxt]=dy - else - maxt=0 + local rightkern=pn.rightkern + if rightkern and rightkern~=0 then + insert_node_after(head,n,newkern(rightkern)) + end + end + end +end +local function inject_everything(head,where) + head=tonut(head) + if trace_injections then + trace(head) + end + local glyphs,nofglyphs,marks,nofmarks + if nofregisteredpairs>0 then + glyphs,nofglyphs,marks,nofmarks=collect_glyphs_1(head) + else + glyphs,nofglyphs,marks,nofmarks=collect_glyphs_2(head) + end + if nofglyphs>0 then + if nofregisteredcursives>0 then + inject_cursives(glyphs,nofglyphs) + end + if nofregisteredmarks>0 then + inject_marks(marks,nofmarks) + end + inject_kerns(head,glyphs,nofglyphs) + end + if keepregisteredcounts then + keepregisteredcounts=false + else + nofregisteredkerns=0 + nofregisteredpairs=0 + nofregisteredmarks=0 + nofregisteredcursives=0 + end + return tonode(head),true +end +local function inject_kerns_only(head,where) + head=tonut(head) + if trace_injections then + trace(head) + end + local n=head + local p=nil + while n do + local id=getid(n) + if id==glyph_code then + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + if p then + local d=getfield(p,"post") + if d then + local pn=pn.postinjections + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + local t=find_tail(d) + insert_node_after(d,t,newkern(leftkern)) end end - elseif maxt>0 then - local ny=getfield(n,"yoffset") - for i=maxt,1,-1 do - ny=ny+d[i] - local ti=t[i] - setfield(ti,"yoffset",getfield(ti,"yoffset")+ny) - end - maxt=0 - end - if not n_cursbase and maxt>0 then - local ny=getfield(n,"yoffset") - for i=maxt,1,-1 do - ny=ny+d[i] - local ti=t[i] - setfield(ti,"yoffset",ny) - end - maxt=0 - end - p_cursbase,p=n_cursbase,n - end - end - if maxt>0 then - local ny=getfield(n,"yoffset") - for i=maxt,1,-1 do - ny=ny+d[i] - local ti=t[i] - setfield(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=getattr(p,a_markbase) - if p_markbase then - local mrks=marks[p_markbase] - local nofmarks=#mrks - for n in traverse_id(glyph_code,getnext(p)) do - local n_markmark=getattr(n,a_markmark) - if p_markbase==n_markmark then - local index=getattr(n,a_markdone) or 1 - local d=mrks[index] - if d then - local rlmode=d[3] - local k=wx[p] - local px=getfield(p,"xoffset") - local ox=0 - if k then - local x=k[2] - local w=k[4] - if w then - if rlmode and rlmode>=0 then - ox=px-getfield(p,"width")+d[1]-(w-x) - else - ox=px-d[1]-x - end - else - if rlmode and rlmode>=0 then - ox=px-getfield(p,"width")+d[1] - else - ox=px-d[1]-x - end - end - else - local wp=getfield(p,"width") - local wn=getfield(n,"width") - if rlmode and rlmode>=0 then - ox=px-wp+d[1] - else - ox=px-d[1] - end - if wn~=0 then - insert_node_before(head,n,newkern(-wn/2)) - insert_node_after(head,n,newkern(-wn/2)) - end - end - setfield(n,"xoffset",ox) - local py=getfield(p,"yoffset") - local oy=0 - if mk[p] then - oy=py+d[2] - else - oy=getfield(n,"yoffset")+py+d[2] - end - setfield(n,"yoffset",oy) - if nofmarks==1 then - break - else - nofmarks=nofmarks-1 - end + end + local d=getfield(p,"replace") + if d then + local pn=pn.replaceinjections + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + local t=find_tail(d) + insert_node_after(d,t,newkern(leftkern)) + end + end + else + local pn=pn.injections + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + setfield(p,"replace",newkern(leftkern)) end - elseif not n_markmark then - break - else + end + end + else + local pn=pn.injections + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + head=insert_node_before(head,n,newkern(leftkern)) end end end end - if not keep then - marks={} - end + else + break end - if next(wx) then - for n,k in next,wx do - local x=k[2] - local w=k[4] - if w then - local rl=k[1] - local wx=w-x - if rl<0 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)) + p=nil + elseif id==disc_code then + local d=getfield(n,"pre") + if d then + local h=d + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + pn=pn.preinjections + end + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + h=insert_node_before(h,n,newkern(leftkern)) end end - elseif x~=0 then - insert_node_before(head,n,newkern(x)) + else + break end end + if h~=d then + setfield(n,"pre",h) + 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)) + local d=getfield(n,"post") + if d then + local h=d + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + pn=pn.postinjections end + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + h=insert_node_before(h,n,newkern(leftkern)) + end + end + else + break end end + if h~=d then + setfield(n,"post",h) + end end - if not keep then - kerns={} + local d=getfield(n,"replace") + if d then + local h=d + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + pn=pn.replaceinjections + end + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + h=insert_node_before(h,n,newkern(leftkern)) + end + end + else + break + end + end + if h~=d then + setfield(n,"replace",h) + end end - return tonode(head),true - elseif not keep then - kerns,cursives,marks={},{},{} - end - elseif has_kerns then - if trace_injections then - trace(head) + p=n + else + p=nil end - for n in traverse_id(glyph_code,head) do + n=getnext(n) + end + if keepregisteredcounts then + keepregisteredcounts=false + else + nofregisteredkerns=0 + end + return tonode(head),true +end +local function inject_pairs_only(head,where) + head=tonut(head) + if trace_injections then + trace(head) + end + local n=head + local p=nil + while n do + local id=getid(n) + if id==glyph_code then if getsubtype(n)<256 then - local k=getattr(n,a_kernpair) - if k then - local kk=kerns[k] - if kk then - local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4] - if y and y~=0 then - setfield(n,"yoffset",y) - end - if w then - local wx=w-x - if rl<0 then - if wx~=0 then - insert_node_before(head,n,newkern(wx)) - end - if x~=0 then - insert_node_after (head,n,newkern(x)) + local pn=rawget(properties,n) + if pn then + if p then + local d=getfield(p,"post") + if d then + local pn=pn.postinjections + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + local t=find_tail(d) + insert_node_after(d,t,newkern(leftkern)) 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 + local d=getfield(p,"replace") + if d then + local pn=pn.replaceinjections + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + local t=find_tail(d) + insert_node_after(d,t,newkern(leftkern)) end end else - if x~=0 then - insert_node_before(head,n,newkern(x)) + local pn=pn.injections + if pn then + local leftkern=pn.leftkern + if leftkern~=0 then + setfield(p,"replace",newkern(leftkern)) + end + end + end + else + local pn=pn.injections + if pn then + local yoffset=pn.yoffset + if yoffset and yoffset~=0 then + setfield(n,"yoffset",yoffset) + end + local leftkern=pn.leftkern + if leftkern~=0 then + insert_node_before(head,n,newkern(leftkern)) + end + local rightkern=pn.rightkern + if rightkern and rightkern~=0 then + insert_node_after(head,n,newkern(rightkern)) + n=getnext(n) end end end end + else + break end + p=nil + elseif id==disc_code then + local d=getfield(n,"pre") + if d then + local h=d + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + pn=pn.preinjections + end + if pn then + local yoffset=pn.yoffset + if yoffset and yoffset~=0 then + setfield(n,"yoffset",yoffset) + end + local leftkern=pn.leftkern + if leftkern~=0 then + h=insert_node_before(h,n,newkern(leftkern)) + end + local rightkern=pn.rightkern + if rightkern and rightkern~=0 then + insert_node_after(head,n,newkern(rightkern)) + n=getnext(n) + end + end + else + break + end + end + if h~=d then + setfield(n,"pre",h) + end + end + local d=getfield(n,"post") + if d then + local h=d + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + pn=pn.postinjections + end + if pn then + local yoffset=pn.yoffset + if yoffset and yoffset~=0 then + setfield(n,"yoffset",yoffset) + end + local leftkern=pn.leftkern + if leftkern~=0 then + h=insert_node_before(h,n,newkern(leftkern)) + end + local rightkern=pn.rightkern + if rightkern and rightkern~=0 then + insert_node_after(head,n,newkern(rightkern)) + n=getnext(n) + end + end + else + break + end + end + if h~=d then + setfield(n,"post",h) + end + end + local d=getfield(n,"replace") + if d then + local h=d + for n in traverse_id(glyph_code,d) do + if getsubtype(n)<256 then + local pn=rawget(properties,n) + if pn then + pn=pn.replaceinjections + end + if pn then + local yoffset=pn.yoffset + if yoffset and yoffset~=0 then + setfield(n,"yoffset",yoffset) + end + local leftkern=pn.leftkern + if leftkern~=0 then + h=insert_node_before(h,n,newkern(leftkern)) + end + local rightkern=pn.rightkern + if rightkern and rightkern~=0 then + insert_node_after(head,n,newkern(rightkern)) + n=getnext(n) + end + end + else + break + end + end + if h~=d then + setfield(n,"replace",h) + end + end + p=n + else + p=nil end - if not keep then - kerns={} - end - return tonode(head),true + n=getnext(n) + end + if keepregisteredcounts then + keepregisteredcounts=false else + nofregisteredpairs=0 + nofregisteredkerns=0 + end + return tonode(head),true +end +function injections.handler(head,where) + if nofregisteredmarks>0 or nofregisteredcursives>0 then + return inject_everything(head,where) + elseif nofregisteredpairs>0 then + return inject_pairs_only(head,where) + elseif nofregisteredkerns>0 then + return inject_kerns_only(head,where) + else + return head,false end - return tonode(head),false end end -- closure @@ -14565,10 +15011,24 @@ local disc_code=nodes.nodecodes.disc local ligaturing=node.ligaturing local kerning=node.kerning local basepass=true +local function l_warning() texio.write_nl("warning: node.ligaturing called directly") l_warning=nil end +local function k_warning() texio.write_nl("warning: node.kerning called directly") k_warning=nil end +function node.ligaturing(...) + if basepass and l_warning then + l_warning() + end + return ligaturing(...) +end +function node.kerning(...) + if basepass and k_warning then + k_warning() + end + return kerning(...) +end function nodes.handlers.setbasepass(v) basepass=v end -function nodes.handlers.characters(head) +function nodes.handlers.nodepass(head) local fontdata=fonts.hashes.identifiers if fontdata then local usedfonts={} @@ -14649,15 +15109,27 @@ function nodes.handlers.characters(head) return head,false end end -function nodes.simple_font_handler(head) - head=nodes.handlers.characters(head) - nodes.injections.handler(head) +function nodes.handlers.basepass(head) if not basepass then head=ligaturing(head) head=kerning(head) end - nodes.handlers.protectglyphs(head) - return head + return head,true +end +local nodepass=nodes.handlers.nodepass +local basepass=nodes.handlers.basepass +local injectpass=nodes.injections.handler +local protectpass=nodes.handlers.protectglyphs +function nodes.simple_font_handler(head) + if head then + head=nodepass(head) + head=injectpass(head) + head=basepass(head) + protectpass(head) + return head,true + else + return head,false + end end end -- closure diff --git a/tex/generic/context/luatex/luatex-fonts.lua b/tex/generic/context/luatex/luatex-fonts.lua index 678a28300..c81e8cd1a 100644 --- a/tex/generic/context/luatex/luatex-fonts.lua +++ b/tex/generic/context/luatex/luatex-fonts.lua @@ -29,11 +29,12 @@ if not modules then modules = { } end modules ['luatex-fonts'] = { texio.write_nl("") texio.write_nl("--------------------------------------------------------------------------------") -texio.write_nl("The font code has been brought in sync with the context version of 2014.12.01 so") +texio.write_nl("The font code has been brought in sync with the context version of 2014.12.21 so") texio.write_nl("if things don't work out as expected the interfacing needs to be checked. When") texio.write_nl("this works as expected a second upgrade will happen that gives a more complete") texio.write_nl("support and another sync with the context code (that new code is currently being") -texio.write_nl("tested. The base pass is now integrated in the main pass.") +texio.write_nl("tested. The base pass is now integrated in the main pass. The results can differ") +texio.write_nl("from those in context because there we integrate some mechanisms differently.") texio.write_nl("--------------------------------------------------------------------------------") texio.write_nl("") @@ -260,8 +261,8 @@ generic_context.callback_define_font = fonts.definers.read if not generic_context.no_callbacks_yet then - -- callback.register('ligaturing', generic_context.callback_ligaturing) - -- callback.register('kerning', generic_context.callback_kerning) + 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) diff --git a/tex/generic/context/luatex/luatex-test.tex b/tex/generic/context/luatex/luatex-test.tex index 169a260dd..6f48e0ced 100644 --- a/tex/generic/context/luatex/luatex-test.tex +++ b/tex/generic/context/luatex/luatex-test.tex @@ -35,14 +35,16 @@ \font\gothic=msgothic(ms-gothic) {\gothic whatever} -\font\testy=file:IranNastaliq.ttf:mode=node;script=arab;language=dflt;+calt;+ccmp;+init;+isol;+medi;+fina;+liga;+rlig;+kern;+mark;+mkmk at 14pt -\testy این یک متن نمونه است با قلم ذر که درست آمده است. +\bgroup -\pdfprotrudechars2 \pdfadjustspacing2 + \pdfprotrudechars2 + \pdfadjustspacing2 -\font\testb=file:lmroman12-regular:+liga;extend=1.5 at 12pt \testb \input tufte \par -\font\testb=file:lmroman12-regular:+liga;slant=0.8 at 12pt \testb \input tufte \par -\font\testb=file:lmroman12-regular:+liga;protrusion=default at 12pt \testb \input tufte \par + \font\testb=file:lmroman12-regular:+liga;extend=1.5 at 12pt \testb \input tufte \par + \font\testb=file:lmroman12-regular:+liga;slant=0.8 at 12pt \testb \input tufte \par + \font\testb=file:lmroman12-regular:+liga;protrusion=default at 12pt \testb \input tufte \par + +\egroup \setmplibformat{plain} -- cgit v1.2.3