font-otj.lua /size: 63 Kb    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['font-otj'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to font-lib.mkiv",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files",
8}
9
10-- This property based variant is not faster but looks nicer than the attribute one. We
11-- need to use rawget (which is about 4 times slower than a direct access but we cannot
12-- get/set that one for our purpose! This version does a bit more with discretionaries
13-- (and Kai has tested it with his collection of weird fonts.)
14
15-- There is some duplicate code here (especially in the the pre/post/replace branches) but
16-- we go for speed. We could store a list of glyph and mark nodes when registering but it's
17-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so
18-- being too clever here is dangerous.
19
20-- As we have a rawget on properties we don't need one on injections.
21
22-- The use_advance code was just a test and is meant for testing and manuals. There is no
23-- performance (or whatever) gain and using kerns is somewhat cleaner (at least for now).
24
25-- An alternative is to have a list per base of all marks and then do a run over the node
26-- list that resolves the accumulated l/r/x/y and then do an inject pass.
27
28-- if needed we can flag a kern node as immutable
29
30-- The thing with these positioning options is that it is not clear what Uniscribe does with
31-- the 2rl flag and we keep oscillating a between experiments.
32
33if not nodes.properties then return end
34
35local next, rawget, tonumber = next, rawget, tonumber
36local fastcopy = table.fastcopy
37
38local registertracker   = trackers.register
39local registerdirective = directives.register
40
41local trace_injections  = false  registertracker("fonts.injections",         function(v) trace_injections = v end)
42local trace_marks       = false  registertracker("fonts.injections.marks",   function(v) trace_marks      = v end)
43local trace_cursive     = false  registertracker("fonts.injections.cursive", function(v) trace_cursive    = v end)
44local trace_spaces      = false  registertracker("fonts.injections.spaces",  function(v) trace_spaces  = v end)
45
46-- local fix_cursive_marks = false
47--
48-- registerdirective("fonts.injections.fixcursivemarks", function(v)
49--     fix_cursive_marks = v
50-- end)
51
52local report_injections = logs.reporter("fonts","injections")
53local report_spaces     = logs.reporter("fonts","spaces")
54
55local attributes, nodes, node = attributes, nodes, node
56
57fonts                    = fonts
58local hashes             = fonts.hashes
59local fontdata           = hashes.identifiers
60local fontmarks          = hashes.marks
61----- parameters         = fonts.hashes.parameters -- not in generic
62----- resources          = fonts.hashes.resources  -- not in generic
63
64nodes.injections         = nodes.injections or { }
65local injections         = nodes.injections
66
67local tracers            = nodes.tracers
68local setcolor           = tracers and tracers.colors.set
69local resetcolor         = tracers and tracers.colors.reset
70
71local nodecodes          = nodes.nodecodes
72local glyph_code         = nodecodes.glyph
73local disc_code          = nodecodes.disc
74local kern_code          = nodecodes.kern
75local glue_code          = nodecodes.glue
76
77local nuts               = nodes.nuts
78local nodepool           = nuts.pool
79
80local tonode             = nuts.tonode
81local tonut              = nuts.tonut
82
83local setfield           = nuts.setfield
84local getnext            = nuts.getnext
85local getprev            = nuts.getprev
86local getid              = nuts.getid
87local getfont            = nuts.getfont
88local getchar            = nuts.getchar
89local getoffsets         = nuts.getoffsets
90local getboth            = nuts.getboth
91local getdisc            = nuts.getdisc
92local setdisc            = nuts.setdisc
93local getreplace         = nuts.getreplace
94local setreplace         = nuts.setreplace
95local setoffsets         = nuts.setoffsets
96local ischar             = nuts.ischar
97local getkern            = nuts.getkern
98local setkern            = nuts.setkern
99local setlink            = nuts.setlink
100local setwidth           = nuts.setwidth
101local getwidth           = nuts.getwidth
102
103local nextchar           = nuts.traversers.char
104local nextglue           = nuts.traversers.glue
105
106local insertnodebefore   = nuts.insertbefore
107local insertnodeafter    = nuts.insertafter
108
109local properties         = nodes.properties.data
110
111local fontkern           = nuts.pool and nuts.pool.fontkern   -- context
112local italickern         = nuts.pool and nuts.pool.italickern -- context
113
114local useitalickerns     = false -- context only
115
116directives.register("fonts.injections.useitalics", function(v)
117    if v then
118        report_injections("using italics for space kerns (tracing only)")
119    end
120    useitalickerns = v
121end)
122
123if not fontkern then -- generic
124
125    local thekern   = nuts.new("kern",0) -- fontkern
126    local setkern   = nuts.setkern
127    local copy_node = nuts.copy
128
129    fontkern = function(k)
130        local n = copy_node(thekern)
131        setkern(n,k)
132        return n
133    end
134
135end
136
137if not italickern then -- generic
138
139    local thekern   = nuts.new("kern",3) -- italiccorrection
140    local setkern   = nuts.setkern
141    local copy_node = nuts.copy
142
143    italickern = function(k)
144        local n = copy_node(thekern)
145        setkern(n,k)
146        return n
147    end
148
149end
150
151function injections.installnewkern() end -- obsolete
152
153local nofregisteredkerns     = 0
154local nofregisteredpositions = 0
155local nofregisteredmarks     = 0
156local nofregisteredcursives  = 0
157local keepregisteredcounts   = false
158
159function injections.keepcounts()
160    keepregisteredcounts = true
161end
162
163function injections.resetcounts()
164    nofregisteredkerns     = 0
165    nofregisteredpositions = 0
166    nofregisteredmarks     = 0
167    nofregisteredcursives  = 0
168    keepregisteredcounts   = false
169end
170
171-- We need to make sure that a possible metatable will not kick in unexpectedly.
172
173function injections.reset(n)
174    local p = rawget(properties,n)
175    if p then
176        p.injections = false -- { } -- nil should work too as we use rawget
177    else
178        properties[n] = false -- { injections = { } } -- nil should work too as we use rawget
179    end
180end
181
182function injections.copy(target,source)
183    local sp = rawget(properties,source)
184    if sp then
185        local tp = rawget(properties,target)
186        local si = sp.injections
187        if si then
188            si = fastcopy(si)
189            if tp then
190                tp.injections = si
191            else
192                properties[target] = {
193                    injections = si,
194                }
195            end
196        elseif tp then
197            tp.injections = false -- { }
198        else
199            properties[target] = { injections = { } }
200        end
201    else
202        local tp = rawget(properties,target)
203        if tp then
204            tp.injections = false -- { }
205        else
206            properties[target] = false -- { injections = { } }
207        end
208    end
209end
210
211function injections.setligaindex(n,index) -- todo: don't set when 0
212    local p = rawget(properties,n)
213    if p then
214        local i = p.injections
215        if i then
216            i.ligaindex = index
217        else
218            p.injections = {
219                ligaindex = index
220            }
221        end
222    else
223        properties[n] = {
224            injections = {
225                ligaindex = index
226            }
227        }
228    end
229end
230
231function injections.getligaindex(n,default)
232    local p = rawget(properties,n)
233    if p then
234        local i = p.injections
235        if i then
236            return i.ligaindex or default
237        end
238    end
239    return default
240end
241
242function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag)
243
244    -- The standard says something about the r2lflag related to the first in a series:
245    --
246    --   When this bit is set, the last glyph in a given sequence to which the cursive
247    --   attachment lookup is applied, will be positioned on the baseline.
248    --
249    -- But it looks like we don't need to consider it.
250
251    local dx =  factor*(exit[1]-entry[1])
252    local dy = -factor*(exit[2]-entry[2])
253    local ws = tfmstart.width
254    local wn = tfmnext.width
255    nofregisteredcursives = nofregisteredcursives + 1
256    if rlmode < 0 then
257        dx = -(dx + wn)
258    else
259        dx = dx - ws
260    end
261    if dx == 0 then
262     -- get rid of funny -0
263        dx = 0
264    end
265    --
266    local p = rawget(properties,start)
267    if p then
268        local i = p.injections
269        if i then
270            i.cursiveanchor = true
271        else
272            p.injections = {
273                cursiveanchor = true,
274            }
275        end
276    else
277        properties[start] = {
278            injections = {
279                cursiveanchor = true,
280            },
281        }
282    end
283    local p = rawget(properties,nxt)
284    if p then
285        local i = p.injections
286        if i then
287            i.cursivex = dx
288            i.cursivey = dy
289        else
290            p.injections = {
291                cursivex = dx,
292                cursivey = dy,
293            }
294        end
295    else
296        properties[nxt] = {
297            injections = {
298                cursivex = dx,
299                cursivey = dy,
300            },
301        }
302    end
303    return dx, dy, nofregisteredcursives
304end
305
306-- kind: 0=single 1=first of pair, 2=second of pair
307
308function injections.setposition(kind,current,factor,rlmode,spec,injection)
309    local x = factor * (spec[1] or 0)
310    local y = factor * (spec[2] or 0)
311    local w = factor * (spec[3] or 0)
312    local h = factor * (spec[4] or 0)
313    if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
314        local yoffset   = y - h
315        local leftkern  = x      -- both kerns are set in a pair kern compared
316        local rightkern = w - x  -- to normal kerns where we set only leftkern
317        if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
318            nofregisteredpositions = nofregisteredpositions + 1
319            if rlmode and rlmode < 0 then
320                leftkern, rightkern = rightkern, leftkern
321            end
322            if not injection then
323                injection = "injections"
324            end
325            local p = rawget(properties,current)
326            if p then
327                local i = p[injection]
328                if i then
329                    if leftkern ~= 0 then
330                        i.leftkern  = (i.leftkern  or 0) + leftkern
331                    end
332                    if rightkern ~= 0 then
333                        i.rightkern = (i.rightkern or 0) + rightkern
334                    end
335                    if yoffset ~= 0 then
336                        i.yoffset = (i.yoffset or 0) + yoffset
337                    end
338                elseif leftkern ~= 0 or rightkern ~= 0 then
339                    p[injection] = {
340                        leftkern  = leftkern,
341                        rightkern = rightkern,
342                        yoffset   = yoffset,
343                    }
344                else
345                    p[injection] = {
346                        yoffset = yoffset,
347                    }
348                end
349            elseif leftkern ~= 0 or rightkern ~= 0 then
350                properties[current] = {
351                    [injection] = {
352                        leftkern  = leftkern,
353                        rightkern = rightkern,
354                        yoffset   = yoffset,
355                    },
356                }
357            else
358                properties[current] = {
359                    [injection] = {
360                        yoffset = yoffset,
361                    },
362                }
363            end
364            return x, y, w, h, nofregisteredpositions
365         end
366    end
367    return x, y, w, h -- no bound
368end
369
370-- The next one is used for simple kerns coming from a truetype kern table. The r2l
371-- variant variant needs checking but it is unlikely that a r2l script uses thsi
372-- feature.
373
374function injections.setkern(current,factor,rlmode,x,injection)
375    local dx = factor * x
376    if dx ~= 0 then
377        nofregisteredkerns = nofregisteredkerns + 1
378        local p = rawget(properties,current)
379        if not injection then
380            injection = "injections"
381        end
382        if p then
383            local i = p[injection]
384            if i then
385                i.leftkern = dx + (i.leftkern or 0)
386            else
387                p[injection] = {
388                    leftkern = dx,
389                }
390            end
391        else
392            properties[current] = {
393                [injection] = {
394                    leftkern = dx,
395                },
396            }
397        end
398        return dx, nofregisteredkerns
399    else
400        return 0, 0
401    end
402end
403
404-- This one is an optimization of pairs where we have only a "w" entry. This one is
405-- potentially different from the previous one wrt r2l. It needs checking. The
406-- optimization relates to smaller tma files.
407
408function injections.setmove(current,factor,rlmode,x,injection)
409    local dx = factor * x
410    if dx ~= 0 then
411        nofregisteredkerns = nofregisteredkerns + 1
412        local p = rawget(properties,current)
413        if not injection then
414            injection = "injections"
415        end
416        if rlmode and rlmode < 0 then
417            -- we need to swap with a single so then we also need to to it here
418            -- as move is just a simple single
419            if p then
420                local i = p[injection]
421                if i then
422                    i.rightkern = dx + (i.rightkern or 0)
423                else
424                    p[injection] = {
425                        rightkern = dx,
426                    }
427                end
428            else
429                properties[current] = {
430                    [injection] = {
431                        rightkern = dx,
432                    },
433                }
434            end
435        else
436            if p then
437                local i = p[injection]
438                if i then
439                    i.leftkern = dx + (i.leftkern or 0)
440                else
441                    p[injection] = {
442                        leftkern = dx,
443                    }
444                end
445            else
446                properties[current] = {
447                    [injection] = {
448                        leftkern = dx,
449                    },
450                }
451            end
452        end
453        return dx, nofregisteredkerns
454    else
455        return 0, 0
456    end
457end
458
459function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor
460    local dx = factor*(ba[1]-ma[1])
461    local dy = factor*(ba[2]-ma[2])
462    nofregisteredmarks = nofregisteredmarks + 1
463    if rlmode >= 0 then
464        dx = tfmbase.width - dx -- see later commented ox
465    end
466    local p = rawget(properties,start)
467    -- hm, dejavu serif does a sloppy mark2mark before mark2base
468    if p then
469        local i = p.injections
470        if i then
471            if i.markmark then
472                -- out of order mkmk: yes or no or option
473            else
474             -- if dx ~= 0 then
475             --     i.markx    = dx
476             -- end
477             -- if y ~= 0 then
478             --     i.marky    = dy
479             -- end
480             -- if rlmode then
481             --     i.markdir  = rlmode
482             -- end
483                i.markx        = dx
484                i.marky        = dy
485                i.markdir      = rlmode or 0
486                i.markbase     = nofregisteredmarks
487                i.markbasenode = base
488                i.markmark     = mkmk
489                i.checkmark    = checkmark
490            end
491        else
492            p.injections = {
493                markx        = dx,
494                marky        = dy,
495                markdir      = rlmode or 0,
496                markbase     = nofregisteredmarks,
497                markbasenode = base,
498                markmark     = mkmk,
499                checkmark    = checkmark,
500            }
501        end
502    else
503        properties[start] = {
504            injections = {
505                markx        = dx,
506                marky        = dy,
507                markdir      = rlmode or 0,
508                markbase     = nofregisteredmarks,
509                markbasenode = base,
510                markmark     = mkmk,
511                checkmark    = checkmark,
512            },
513        }
514    end
515    return dx, dy, nofregisteredmarks
516end
517
518local function dir(n)
519    return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
520end
521
522local function showchar(n,nested)
523    local char = getchar(n)
524    report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char)
525end
526
527local function show(n,what,nested,symbol)
528    if n then
529        local p = rawget(properties,n)
530        if p then
531            local i = p[what]
532            if i then
533                local leftkern  = i.leftkern  or 0
534                local rightkern = i.rightkern or 0
535                local yoffset   = i.yoffset   or 0
536                local markx     = i.markx     or 0
537                local marky     = i.marky     or 0
538                local markdir   = i.markdir   or 0
539                local markbase  = i.markbase  or 0
540                local cursivex  = i.cursivex  or 0
541                local cursivey  = i.cursivey  or 0
542                local ligaindex = i.ligaindex or 0
543                local cursbase  = i.cursiveanchor
544                local margin    = nested and 4 or 2
545                --
546                if rightkern ~= 0 or yoffset ~= 0 then
547                    report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
548                elseif leftkern ~= 0 then
549                    report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
550                end
551                if markx ~= 0 or marky ~= 0 or markbase ~= 0 then
552                    report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no")
553                end
554                if cursivex ~= 0 or cursivey ~= 0 then
555                    if cursbase then
556                        report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
557                    else
558                        report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
559                    end
560                elseif cursbase then
561                    report_injections("%w%s curs: base",margin,symbol)
562                end
563                if ligaindex ~= 0 then
564                    report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
565                end
566            end
567        end
568    end
569end
570
571local function showsub(n,what,where)
572    report_injections("begin subrun: %s",where)
573    for n in nextchar, n do
574        showchar(n,where)
575        show(n,what,where," ")
576    end
577    report_injections("end subrun")
578end
579
580local function trace(head,where)
581    report_injections()
582    report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered",
583        where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives)
584    local n = head
585    while n do
586        local id = getid(n)
587        if id == glyph_code then
588            showchar(n)
589            show(n,"injections",false," ")
590            show(n,"preinjections",false,"<")
591            show(n,"postinjections",false,">")
592            show(n,"replaceinjections",false,"=")
593            show(n,"emptyinjections",false,"*")
594        elseif id == disc_code then
595            local pre, post, replace = getdisc(n)
596            if pre then
597                showsub(pre,"preinjections","pre")
598            end
599            if post then
600                showsub(post,"postinjections","post")
601            end
602            if replace then
603                showsub(replace,"replaceinjections","replace")
604            end
605            show(n,"emptyinjections",false,"*")
606        end
607        n = getnext(n)
608    end
609    report_injections("end run")
610end
611
612local function show_result(head)
613    local current  = head
614    local skipping = false
615    while current do
616        local id = getid(current)
617        if id == glyph_code then
618            local w = getwidth(current)
619            local x, y = getoffsets(current)
620            report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y)
621            skipping = false
622        elseif id == kern_code then
623            report_injections("kern: %p",getkern(current))
624            skipping = false
625        elseif not skipping then
626            report_injections()
627            skipping = true
628        end
629        current = getnext(current)
630    end
631    report_injections()
632end
633
634-- G  +D-pre        G
635--     D-post+
636--    +D-replace+
637--
638-- G  +D-pre       +D-pre
639--     D-post      +D-post
640--    +D-replace   +D-replace
641
642local function inject_kerns_only(head,where)
643    if trace_injections then
644        trace(head,"kerns")
645    end
646    local current     = head
647    local prev        = nil
648    local next        = nil
649    local prevdisc    = nil
650 -- local prevglyph   = nil
651    local pre         = nil -- saves a lookup
652    local post        = nil -- saves a lookup
653    local replace     = nil -- saves a lookup
654    local pretail     = nil -- saves a lookup
655    local posttail    = nil -- saves a lookup
656    local replacetail = nil -- saves a lookup
657    while current do
658        local next = getnext(current)
659        local char, id = ischar(current)
660        if char then
661            local p = rawget(properties,current)
662            if p then
663                local i = p.injections
664                if i then
665                    -- left|glyph|right
666                    local leftkern = i.leftkern
667                    if leftkern and leftkern ~= 0 then
668                        if prev and getid(prev) == glue_code then
669                            if useitalickerns then
670                                head = insertnodebefore(head,current,italickern(leftkern))
671                            else
672                                setwidth(prev, getwidth(prev) + leftkern)
673                            end
674                        else
675                            head = insertnodebefore(head,current,fontkern(leftkern))
676                        end
677                    end
678                end
679                if prevdisc then
680                    local done = false
681                    if post then
682                        local i = p.postinjections
683                        if i then
684                            local leftkern = i.leftkern
685                            if leftkern and leftkern ~= 0 then
686                                setlink(posttail,fontkern(leftkern))
687                                done = true
688                            end
689                        end
690                    end
691                    if replace then
692                        local i = p.replaceinjections
693                        if i then
694                            local leftkern = i.leftkern
695                            if leftkern and leftkern ~= 0 then
696                                setlink(replacetail,fontkern(leftkern))
697                                done = true
698                            end
699                        end
700                    else
701                        local i = p.emptyinjections
702                        if i then
703                            -- glyph|disc|glyph (special case)
704                            local leftkern = i.leftkern
705                            if leftkern and leftkern ~= 0 then
706                                replace = fontkern(leftkern)
707                                done    = true
708                            end
709                        end
710                    end
711                    if done then
712                        setdisc(prevdisc,pre,post,replace)
713                    end
714                end
715            end
716            prevdisc  = nil
717         -- prevglyph = current
718        elseif char == false then
719            -- other font
720            prevdisc  = nil
721         -- prevglyph = current
722        elseif id == disc_code then
723            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
724            local done = false
725            if pre then
726                -- left|pre glyphs|right
727                for n in nextchar, pre do
728                    local p = rawget(properties,n)
729                    if p then
730                        local i = p.injections or p.preinjections
731                        if i then
732                            local leftkern = i.leftkern
733                            if leftkern and leftkern ~= 0 then
734                                pre  = insertnodebefore(pre,n,fontkern(leftkern))
735                                done = true
736                            end
737                        end
738                    end
739                end
740            end
741            if post then
742                -- left|post glyphs|right
743                for n in nextchar, post do
744                    local p = rawget(properties,n)
745                    if p then
746                        local i = p.injections or p.postinjections
747                        if i then
748                            local leftkern = i.leftkern
749                            if leftkern and leftkern ~= 0 then
750                                post = insertnodebefore(post,n,fontkern(leftkern))
751                                done = true
752                            end
753                        end
754                    end
755                end
756            end
757            if replace then
758                -- left|replace glyphs|right
759                for n in nextchar, replace do
760                    local p = rawget(properties,n)
761                    if p then
762                        local i = p.injections or p.replaceinjections
763                        if i then
764                            local leftkern = i.leftkern
765                            if leftkern and leftkern ~= 0 then
766                                replace = insertnodebefore(replace,n,fontkern(leftkern))
767                                done    = true
768                            end
769                        end
770                    end
771                end
772            end
773            if done then
774                setdisc(current,pre,post,replace)
775            end
776         -- prevglyph = nil
777            prevdisc  = current
778        else
779         -- prevglyph = nil
780            prevdisc  = nil
781        end
782        prev    = current
783        current = next
784    end
785    --
786    if keepregisteredcounts then
787        keepregisteredcounts = false
788    else
789        nofregisteredkerns   = 0
790    end
791    if trace_injections then
792        show_result(head)
793    end
794    return head
795end
796
797local function inject_positions_only(head,where)
798    if trace_injections then
799        trace(head,"positions")
800    end
801    local current     = head
802    local prev        = nil
803    local next        = nil
804    local prevdisc    = nil
805    local prevglyph   = nil
806    local pre         = nil -- saves a lookup
807    local post        = nil -- saves a lookup
808    local replace     = nil -- saves a lookup
809    local pretail     = nil -- saves a lookup
810    local posttail    = nil -- saves a lookup
811    local replacetail = nil -- saves a lookup
812    while current do
813        local next = getnext(current)
814        local char, id = ischar(current)
815        if char then
816            local p = rawget(properties,current)
817            if p then
818                local i = p.injections
819                if i then
820                    -- left|glyph|right
821                    local yoffset = i.yoffset
822                    if yoffset and yoffset ~= 0 then
823                        setoffsets(current,false,yoffset)
824                    end
825                    local leftkern  = i.leftkern
826                    local rightkern = i.rightkern
827                    if leftkern and leftkern ~= 0 then
828                        if rightkern and leftkern == -rightkern then
829                            setoffsets(current,leftkern,false)
830                            rightkern = 0
831                        elseif prev and getid(prev) == glue_code then
832                            if useitalickerns then
833                                head = insertnodebefore(head,current,italickern(leftkern))
834                            else
835                                setwidth(prev, getwidth(prev) + leftkern)
836                            end
837                        else
838                            head = insertnodebefore(head,current,fontkern(leftkern))
839                        end
840                    end
841                    if rightkern and rightkern ~= 0 then
842                        if next and getid(next) == glue_code then
843                            if useitalickerns then
844                                insertnodeafter(head,current,italickern(rightkern))
845                            else
846                                setwidth(next, getwidth(next) + rightkern)
847                            end
848                        else
849                            insertnodeafter(head,current,fontkern(rightkern))
850                        end
851                    end
852                elseif next then
853                    local i = p.emptyinjections
854                    if i then
855                        -- glyph|disc|glyph (special case)
856                        local rightkern = i.rightkern
857                        if rightkern and rightkern ~= 0 and getid(next) == disc_code then
858                            local replace = getreplace(next)
859                            if replace then
860                                -- can't happen
861                            else
862                                setreplace(next,fontkern(rightkern))
863                            end
864                        end
865                    end
866                end
867                if prevdisc then
868                    local done = false
869                    if post then
870                        local i = p.postinjections
871                        if i then
872                            local leftkern = i.leftkern
873                            if leftkern and leftkern ~= 0 then
874                                setlink(posttail,fontkern(leftkern))
875                                done = true
876                            end
877                        end
878                    end
879                    if replace then
880                        local i = p.replaceinjections
881                        if i then
882                            local leftkern = i.leftkern
883                            if leftkern and leftkern ~= 0 then
884                                setlink(replacetail,fontkern(leftkern))
885                                done = true
886                            end
887                        end
888                    else
889                        local i = p.emptyinjections
890                        if i then
891                            -- new .. okay?
892                            local leftkern = i.leftkern
893                            if leftkern and leftkern ~= 0 then
894                                replace = fontkern(leftkern)
895                                done = true
896                            end
897                        end
898                    end
899                    if done then
900                        setdisc(prevdisc,pre,post,replace)
901                    end
902                end
903            end
904            prevdisc  = nil
905            prevglyph = current
906        elseif char == false then
907            prevdisc  = nil
908            prevglyph = current
909        elseif id == disc_code then
910            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
911            local done = false
912            if pre then
913                -- left|pre glyphs|right
914                for n in nextchar, pre do
915                    local p = rawget(properties,n)
916                    if p then
917                        local i = p.injections or p.preinjections
918                        if i then
919                            local yoffset = i.yoffset
920                            if yoffset and yoffset ~= 0 then
921                                setoffsets(n,false,yoffset)
922                            end
923                            local leftkern = i.leftkern
924                            if leftkern and leftkern ~= 0 then
925                                pre  = insertnodebefore(pre,n,fontkern(leftkern))
926                                done = true
927                            end
928                            local rightkern = i.rightkern
929                            if rightkern and rightkern ~= 0 then
930                                insertnodeafter(pre,n,fontkern(rightkern))
931                                done = true
932                            end
933                        end
934                    end
935                end
936            end
937            if post then
938                -- left|post glyphs|right
939                for n in nextchar, post do
940                    local p = rawget(properties,n)
941                    if p then
942                        local i = p.injections or p.postinjections
943                        if i then
944                            local yoffset = i.yoffset
945                            if yoffset and yoffset ~= 0 then
946                                setoffsets(n,false,yoffset)
947                            end
948                            local leftkern = i.leftkern
949                            if leftkern and leftkern ~= 0 then
950                                post = insertnodebefore(post,n,fontkern(leftkern))
951                                done = true
952                            end
953                            local rightkern = i.rightkern
954                            if rightkern and rightkern ~= 0 then
955                                insertnodeafter(post,n,fontkern(rightkern))
956                                done = true
957                            end
958                        end
959                    end
960                end
961            end
962            if replace then
963                -- left|replace glyphs|right
964                for n in nextchar, replace do
965                    local p = rawget(properties,n)
966                    if p then
967                        local i = p.injections or p.replaceinjections
968                        if i then
969                            local yoffset = i.yoffset
970                            if yoffset and yoffset ~= 0 then
971                                setoffsets(n,false,yoffset)
972                            end
973                            local leftkern = i.leftkern
974                            if leftkern and leftkern ~= 0 then
975                                replace = insertnodebefore(replace,n,fontkern(leftkern))
976                                done    = true
977                            end
978                            local rightkern = i.rightkern
979                            if rightkern and rightkern ~= 0 then
980                                insertnodeafter(replace,n,fontkern(rightkern))
981                                done = true
982                            end
983                        end
984                    end
985                end
986            end
987            if prevglyph then
988                if pre then
989                    local p = rawget(properties,prevglyph)
990                    if p then
991                        local i = p.preinjections
992                        if i then
993                            -- glyph|pre glyphs
994                            local rightkern = i.rightkern
995                            if rightkern and rightkern ~= 0 then
996                                pre  = insertnodebefore(pre,pre,fontkern(rightkern))
997                                done = true
998                            end
999                        end
1000                    end
1001                end
1002                if replace then
1003                    local p = rawget(properties,prevglyph)
1004                    if p then
1005                        local i = p.replaceinjections
1006                        if i then
1007                            -- glyph|replace glyphs
1008                            local rightkern = i.rightkern
1009                            if rightkern and rightkern ~= 0 then
1010                                replace = insertnodebefore(replace,replace,fontkern(rightkern))
1011                                done    = true
1012                            end
1013                        end
1014                    end
1015                end
1016            end
1017            if done then
1018                setdisc(current,pre,post,replace)
1019            end
1020            prevglyph = nil
1021            prevdisc  = current
1022        else
1023            prevglyph = nil
1024            prevdisc  = nil
1025        end
1026        prev    = current
1027        current = next
1028    end
1029    --
1030    if keepregisteredcounts then
1031        keepregisteredcounts = false
1032    else
1033        nofregisteredpositions = 0
1034    end
1035    if trace_injections then
1036        show_result(head)
1037    end
1038    return head
1039end
1040
1041local function showoffset(n,flag)
1042    local x, y = getoffsets(n)
1043    if x ~= 0 or y ~= 0 then
1044        setcolor(n,"darkgray")
1045    end
1046end
1047
1048local function inject_everything(head,where)
1049    if trace_injections then
1050        trace(head,"everything")
1051    end
1052    local hascursives   = nofregisteredcursives > 0
1053    local hasmarks      = nofregisteredmarks    > 0
1054    --
1055    local current       = head
1056    local last          = nil
1057    local prev          = nil
1058    local next          = nil
1059    local prevdisc      = nil
1060    local prevglyph     = nil
1061    local pre           = nil -- saves a lookup
1062    local post          = nil -- saves a lookup
1063    local replace       = nil -- saves a lookup
1064    local pretail       = nil -- saves a lookup
1065    local posttail      = nil -- saves a lookup
1066    local replacetail   = nil -- saves a lookup
1067    --
1068    local cursiveanchor = nil
1069    local minc          = 0
1070    local maxc          = 0
1071    local glyphs        = { }
1072    local marks         = { }
1073    local nofmarks      = 0
1074    --
1075 -- local applyfix      = hascursives and fix_cursive_marks
1076    --
1077    -- move out
1078    --
1079    local function processmark(p,n,pn) -- p = basenode
1080        local px, py = getoffsets(p)
1081        local nx, ny = getoffsets(n)
1082        local ox = 0
1083        local rightkern = nil
1084        local pp = rawget(properties,p)
1085        if pp then
1086            pp = pp.injections
1087            if pp then
1088                rightkern = pp.rightkern
1089            end
1090        end
1091        local markdir = pn.markdir
1092        if rightkern then -- x and w ~= 0
1093            ox = px - (pn.markx or 0) - rightkern
1094            if markdir and markdir < 0 then
1095                -- kern(w-x) glyph(p) kern(x) mark(n)
1096                if not pn.markmark then
1097                    ox = ox + (pn.leftkern or 0)
1098                end
1099            else
1100                -- kern(x) glyph(p) kern(w-x) mark(n)
1101                --
1102                -- According to Kai we don't need to handle leftkern here but I'm
1103                -- pretty sure I've run into a case where it was needed so maybe
1104                -- some day we need something more clever here.
1105                --
1106                -- maybe we need to apply both then
1107                --
1108                if false then
1109                    -- a mark with kerning (maybe husayni needs it )
1110                    local leftkern = pp.leftkern
1111                    if leftkern then
1112                        ox = ox - leftkern
1113                    end
1114                end
1115            end
1116        else
1117            ox = px - (pn.markx or 0)
1118            if markdir and markdir < 0 then
1119                if not pn.markmark then
1120                    local leftkern = pn.leftkern
1121                    if leftkern then
1122                        ox = ox + leftkern -- husayni needs it
1123                    end
1124                end
1125            end
1126            if pn.checkmark then
1127                local wn = getwidth(n) -- in arial marks have widths
1128                if wn and wn ~= 0 then
1129                    wn = wn/2
1130                    if trace_injections then
1131                        report_injections("correcting non zero width mark %C",getchar(n))
1132                    end
1133                    -- -- bad: we should center
1134                    --
1135                    -- pn.leftkern  = -wn
1136                    -- pn.rightkern = -wn
1137                    --
1138                    -- -- we're too late anyway as kerns are already injected so we do it the
1139                    -- -- ugly way (no checking if the previous is already a kern) .. maybe we
1140                    -- -- should fix the font instead
1141                    --
1142                    -- todo: head and check for prev / next kern
1143                    --
1144                    insertnodebefore(n,n,fontkern(-wn))
1145                    insertnodeafter(n,n,fontkern(-wn))
1146                end
1147            end
1148        end
1149        local oy = ny + py + (pn.marky or 0)
1150        if not pn.markmark then
1151            local yoffset = pn.yoffset
1152            if yoffset then
1153                oy = oy + yoffset -- husayni needs it
1154            end
1155        end
1156        setoffsets(n,ox,oy)
1157        if trace_marks then
1158            showoffset(n,true)
1159        end
1160    end
1161    -- begin of temp fix --
1162 -- local base = nil -- bah, some arabic fonts have no mark anchoring
1163    -- end of temp fix --
1164    while current do
1165        local next = getnext(current)
1166        local char, id = ischar(current)
1167        if char then
1168            local p = rawget(properties,current)
1169            -- begin of temp fix --
1170         -- if applyfix then
1171         --     if not p then
1172         --         local m = fontmarks[getfont(current)]
1173         --         if m and m[char] then
1174         --             if base then
1175         --                 p = { injections = { markbasenode = base } }
1176         --                 nofmarks = nofmarks + 1
1177         --                 marks[nofmarks] = current
1178         --                 properties[current] = p
1179         --                 hasmarks = true
1180         --             end
1181         --         else
1182         --             base = current
1183         --         end
1184         --     end
1185         -- end
1186            -- end of temp fix
1187            if p then
1188                local i = p.injections
1189                -- begin of temp fix --
1190             -- if applyfix then
1191             --     if not i then
1192             --         local m = fontmarks[getfont(current)]
1193             --         if m and m[char] then
1194             --             if base then
1195             --                 i = { markbasenode = base }
1196             --                 nofmarks = nofmarks + 1
1197             --                 marks[nofmarks] = current
1198             --                 p.injections = i
1199             --                 hasmarks = true
1200             --             end
1201             --         else
1202             --             base = current
1203             --         end
1204             --     end
1205             -- end
1206                -- end of temp fix --
1207                if i then
1208                    local pm = i.markbasenode
1209                    -- begin of temp fix --
1210                 -- if applyfix then
1211                 --     if not pm then
1212                 --         local m = fontmarks[getfont(current)]
1213                 --         if m and m[char] then
1214                 --             if base then
1215                 --                 pm = base
1216                 --                 i.markbasenode = pm
1217                 --                 hasmarks = true
1218                 --             end
1219                 --         else
1220                 --             base = current
1221                 --         end
1222                 --     else
1223                 --         base = current
1224                 --     end
1225                 -- end
1226                    -- end of temp fix --
1227                    if pm then
1228                        nofmarks = nofmarks + 1
1229                        marks[nofmarks] = current
1230                    else
1231                        local yoffset = i.yoffset
1232                        if yoffset and yoffset ~= 0 then
1233                            setoffsets(current,false,yoffset)
1234                        end
1235                        if hascursives then
1236                            local cursivex = i.cursivex
1237                            if cursivex then
1238                                if cursiveanchor then
1239                                    if cursivex ~= 0 then
1240                                        i.leftkern = (i.leftkern or 0) + cursivex
1241                                    end
1242                                    if maxc == 0 then
1243                                        minc = 1
1244                                        maxc = 1
1245                                        glyphs[1] = cursiveanchor
1246                                    else
1247                                        maxc = maxc + 1
1248                                        glyphs[maxc] = cursiveanchor
1249                                    end
1250                                    properties[cursiveanchor].cursivedy = i.cursivey -- cursiveprops
1251                                    last = current
1252                                else
1253                                    maxc = 0
1254                                end
1255                            elseif maxc > 0 then
1256                                local nx, ny = getoffsets(current)
1257                                for i=maxc,minc,-1 do
1258                                    local ti = glyphs[i]
1259                                    ny = ny + properties[ti].cursivedy
1260                                    setoffsets(ti,false,ny) -- why not add ?
1261                                    if trace_cursive then
1262                                        showoffset(ti)
1263                                    end
1264                                end
1265                                maxc = 0
1266                                cursiveanchor = nil
1267                            end
1268                            if i.cursiveanchor then
1269                                cursiveanchor = current -- no need for both now
1270                            else
1271                                if maxc > 0 then
1272                                    local nx, ny = getoffsets(current)
1273                                    for i=maxc,minc,-1 do
1274                                        local ti = glyphs[i]
1275                                        ny = ny + properties[ti].cursivedy
1276                                        setoffsets(ti,false,ny) -- why not add ?
1277                                        if trace_cursive then
1278                                            showoffset(ti)
1279                                        end
1280                                    end
1281                                    maxc = 0
1282                                end
1283                                cursiveanchor = nil
1284                            end
1285                        end
1286                        -- left|glyph|right
1287                        local leftkern  = i.leftkern
1288                        local rightkern = i.rightkern
1289                        if leftkern and leftkern ~= 0 then
1290                            if rightkern and leftkern == -rightkern then
1291                                setoffsets(current,leftkern,false)
1292                                rightkern = 0
1293                            elseif prev and getid(prev) == glue_code then
1294                                if useitalickerns then
1295                                    head = insertnodebefore(head,current,italickern(leftkern))
1296                                else
1297                                    setwidth(prev, getwidth(prev) + leftkern)
1298                                end
1299                            else
1300                                head = insertnodebefore(head,current,fontkern(leftkern))
1301                            end
1302                        end
1303                        if rightkern and rightkern ~= 0 then
1304                            if next and getid(next) == glue_code then
1305                                if useitalickerns then
1306                                    insertnodeafter(head,current,italickern(rightkern))
1307                                else
1308                                    setwidth(next, getwidth(next) + rightkern)
1309                                end
1310                            else
1311                                insertnodeafter(head,current,fontkern(rightkern))
1312                            end
1313                        end
1314                    end
1315                elseif next then
1316                    local i = p.emptyinjections
1317                    if i then
1318                        -- glyph|disc|glyph (special case)
1319                        local rightkern = i.rightkern
1320                        if rightkern and rightkern ~= 0 and getid(next) == disc_code then
1321                            local replace = getreplace(next)
1322                            if replace then
1323                                -- can't happen
1324                            else
1325                                setreplace(next,fontkern(rightkern))
1326                            end
1327                        end
1328                    end
1329                end
1330                if prevdisc then
1331                    if p then
1332                        local done = false
1333                        if post then
1334                            local i = p.postinjections
1335                            if i then
1336                                local leftkern = i.leftkern
1337                                if leftkern and leftkern ~= 0 then
1338                                    setlink(posttail,fontkern(leftkern))
1339                                    done = true
1340                                end
1341                            end
1342                        end
1343                        if replace then
1344                            local i = p.replaceinjections
1345                            if i then
1346                                local leftkern = i.leftkern
1347                                if leftkern and leftkern ~= 0 then
1348                                    setlink(replacetail,fontkern(leftkern))
1349                                    done = true
1350                                end
1351                            end
1352                        else
1353                            local i = p.emptyinjections
1354                            if i then
1355                                local leftkern = i.leftkern
1356                                if leftkern and leftkern ~= 0 then
1357                                    replace = fontkern(leftkern)
1358                                    done    = true
1359                                end
1360                            end
1361                        end
1362                        if done then
1363                            setdisc(prevdisc,pre,post,replace)
1364                        end
1365                    end
1366                end
1367            else
1368                -- cursive
1369                if hascursives and maxc > 0 then
1370                    local nx, ny = getoffsets(current)
1371                    for i=maxc,minc,-1 do
1372                        local ti = glyphs[i]
1373                        ny = ny + properties[ti].cursivedy
1374                        local xi, yi = getoffsets(ti)
1375                        setoffsets(ti,xi,yi + ny) -- can be mark, we could use properties
1376                    end
1377                    maxc = 0
1378                    cursiveanchor = nil
1379                end
1380            end
1381            prevdisc  = nil
1382            prevglyph = current
1383        elseif char == false then
1384         -- base = nil
1385            prevdisc  = nil
1386            prevglyph = current
1387        elseif id == disc_code then
1388         -- base = nil
1389            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
1390            local done = false
1391            if pre then
1392                -- left|pre glyphs|right
1393                for n in nextchar, pre do
1394                    local p = rawget(properties,n)
1395                    if p then
1396                        local i = p.injections or p.preinjections
1397                        if i then
1398                            local yoffset = i.yoffset
1399                            if yoffset and yoffset ~= 0 then
1400                                setoffsets(n,false,yoffset)
1401                            end
1402                            local leftkern = i.leftkern
1403                            if leftkern and leftkern ~= 0 then
1404                                pre  = insertnodebefore(pre,n,fontkern(leftkern))
1405                                done = true
1406                            end
1407                            local rightkern = i.rightkern
1408                            if rightkern and rightkern ~= 0 then
1409                                insertnodeafter(pre,n,fontkern(rightkern))
1410                                done = true
1411                            end
1412                            if hasmarks then
1413                                local pm = i.markbasenode
1414                                if pm then
1415                                    processmark(pm,n,i)
1416                                end
1417                            end
1418                        end
1419                    end
1420                end
1421            end
1422            if post then
1423                -- left|post glyphs|right
1424                for n in nextchar, post do
1425                    local p = rawget(properties,n)
1426                    if p then
1427                        local i = p.injections or p.postinjections
1428                        if i then
1429                            local yoffset = i.yoffset
1430                            if yoffset and yoffset ~= 0 then
1431                                setoffsets(n,false,yoffset)
1432                            end
1433                            local leftkern = i.leftkern
1434                            if leftkern and leftkern ~= 0 then
1435                                post = insertnodebefore(post,n,fontkern(leftkern))
1436                                done = true
1437                            end
1438                            local rightkern = i.rightkern
1439                            if rightkern and rightkern ~= 0 then
1440                                insertnodeafter(post,n,fontkern(rightkern))
1441                                done = true
1442                            end
1443                            if hasmarks then
1444                                local pm = i.markbasenode
1445                                if pm then
1446                                    processmark(pm,n,i)
1447                                end
1448                            end
1449                        end
1450                    end
1451                end
1452            end
1453            if replace then
1454                -- left|replace glyphs|right
1455                for n in nextchar, replace do
1456                    local p = rawget(properties,n)
1457                    if p then
1458                        local i = p.injections or p.replaceinjections
1459                        if i then
1460                            local yoffset = i.yoffset
1461                            if yoffset and yoffset ~= 0 then
1462                                setoffsets(n,false,yoffset)
1463                            end
1464                            local leftkern = i.leftkern
1465                            if leftkern and leftkern ~= 0 then
1466                                replace = insertnodebefore(replace,n,fontkern(leftkern))
1467                                done    = true
1468                            end
1469                            local rightkern = i.rightkern
1470                            if rightkern and rightkern ~= 0 then
1471                                insertnodeafter(replace,n,fontkern(rightkern))
1472                                done = true
1473                            end
1474                            if hasmarks then
1475                                local pm = i.markbasenode
1476                                if pm then
1477                                    processmark(pm,n,i)
1478                                end
1479                            end
1480                        end
1481                    end
1482                end
1483            end
1484            if prevglyph then
1485                if pre then
1486                    local p = rawget(properties,prevglyph)
1487                    if p then
1488                        local i = p.preinjections
1489                        if i then
1490                            -- glyph|pre glyphs
1491                            local rightkern = i.rightkern
1492                            if rightkern and rightkern ~= 0 then
1493                                pre  = insertnodebefore(pre,pre,fontkern(rightkern))
1494                                done = true
1495                            end
1496                        end
1497                    end
1498                end
1499                if replace then
1500                    local p = rawget(properties,prevglyph)
1501                    if p then
1502                        local i = p.replaceinjections
1503                        if i then
1504                            -- glyph|replace glyphs
1505                            local rightkern = i.rightkern
1506                            if rightkern and rightkern ~= 0 then
1507                                replace = insertnodebefore(replace,replace,fontkern(rightkern))
1508                                done    = true
1509                            end
1510                        end
1511                    end
1512                end
1513            end
1514            if done then
1515                setdisc(current,pre,post,replace)
1516            end
1517            prevglyph = nil
1518            prevdisc  = current
1519        else
1520         -- base      = nil
1521            prevglyph = nil
1522            prevdisc  = nil
1523        end
1524        prev    = current
1525        current = next
1526    end
1527    -- cursive
1528    if hascursives and maxc > 0 then
1529        local nx, ny = getoffsets(last)
1530        for i=maxc,minc,-1 do
1531            local ti = glyphs[i]
1532            ny = ny + properties[ti].cursivedy
1533            setoffsets(ti,false,ny) -- why not add ?
1534            if trace_cursive then
1535                showoffset(ti)
1536            end
1537        end
1538    end
1539    --
1540    if nofmarks > 0 then
1541        for i=1,nofmarks do
1542            local m = marks[i]
1543            local p = rawget(properties,m)
1544            local i = p.injections
1545            local b = i.markbasenode
1546            processmark(b,m,i)
1547        end
1548    elseif hasmarks then
1549        -- sometyhing bad happened
1550    end
1551    --
1552    if keepregisteredcounts then
1553        keepregisteredcounts = false
1554    else
1555        nofregisteredkerns     = 0
1556        nofregisteredpositions = 0
1557        nofregisteredmarks     = 0
1558        nofregisteredcursives  = 0
1559    end
1560    if trace_injections then
1561        show_result(head)
1562    end
1563    return head
1564end
1565
1566-- space triggers
1567
1568local triggers = false
1569
1570function nodes.injections.setspacekerns(font,sequence)
1571    if triggers then
1572        triggers[font] = sequence
1573    else
1574        triggers = { [font] = sequence }
1575    end
1576end
1577
1578local getthreshold
1579
1580if context then
1581
1582    local threshold  =  1 -- todo: add a few methods for context
1583    local parameters = fonts.hashes.parameters
1584
1585    directives.register("otf.threshold", function(v) threshold = tonumber(v) or 1 end)
1586
1587    getthreshold  = function(font)
1588        local p = parameters[font]
1589        local f = p.factor
1590        local s = p.spacing
1591        local t = threshold * (s and s.width or p.space or 0) - 2
1592        return t > 0 and t or 0, f
1593    end
1594
1595else
1596
1597    injections.threshold = 0
1598
1599    getthreshold  = function(font)
1600        local p = fontdata[font].parameters
1601        local f = p.factor
1602        local s = p.spacing
1603        local t = injections.threshold * (s and s.width or p.space or 0) - 2
1604        return t > 0 and t or 0, f
1605    end
1606
1607end
1608
1609injections.getthreshold = getthreshold
1610
1611function injections.isspace(n,threshold,id)
1612    if (id or getid(n)) == glue_code then
1613        local w = getwidth(n)
1614        if threshold and w > threshold then -- was >=
1615            return 32
1616        end
1617    end
1618end
1619
1620-- We have a plugin so that Kai can use the next in plain. Such a plugin is rather application
1621-- specific.
1622--
1623-- local getboth = nodes.direct.getboth
1624-- local getid   = nodes.direct.getid
1625-- local getprev = nodes.direct.getprev
1626-- local getnext = nodes.direct.getnext
1627--
1628-- local whatsit_code = nodes.nodecodes.whatsit
1629-- local glyph_code   = nodes.nodecodes.glyph
1630--
1631-- local function getspaceboth(n) -- fragile: what it prev/next has no width field
1632--     local prev, next = getboth(n)
1633--     while prev and (getid(prev) == whatsit_code or (getwidth(prev) == 0 and getid(prev) ~= glyph_code)) do
1634--         prev = getprev(prev)
1635--     end
1636--     while next and (getid(next) == whatsit_code or (getwidth(next) == 0 and getid(next) ~= glyph_code)) do
1637--         next = getnext(next)
1638--     end
1639-- end
1640--
1641-- injections.installgetspaceboth(getspaceboth)
1642
1643local getspaceboth = getboth
1644
1645function injections.installgetspaceboth(gb)
1646    getspaceboth = gb or getboth
1647end
1648
1649local function injectspaces(head)
1650
1651    if not triggers then
1652        return head
1653    end
1654    local lastfont   = nil
1655    local spacekerns = nil
1656    local leftkerns  = nil
1657    local rightkerns = nil
1658    local factor     = 0
1659    local threshold  = 0
1660    local leftkern   = false
1661    local rightkern  = false
1662
1663    local function updatefont(font,trig)
1664        leftkerns  = trig.left
1665        rightkerns = trig.right
1666        lastfont   = font
1667        threshold,
1668        factor     = getthreshold(font)
1669    end
1670
1671    for n in nextglue, head do
1672        local prev, next = getspaceboth(n)
1673        local prevchar = prev and ischar(prev)
1674        local nextchar = next and ischar(next)
1675        if nextchar then
1676            local font = getfont(next)
1677            local trig = triggers[font]
1678            if trig then
1679                if lastfont ~= font then
1680                    updatefont(font,trig)
1681                end
1682                if rightkerns then
1683                    rightkern = rightkerns[nextchar]
1684                end
1685            end
1686        end
1687        if prevchar then
1688            local font = getfont(prev)
1689            local trig = triggers[font]
1690            if trig then
1691                if lastfont ~= font then
1692                    updatefont(font,trig)
1693                end
1694                if leftkerns then
1695                    leftkern = leftkerns[prevchar]
1696                end
1697            end
1698        end
1699        if leftkern then
1700            local old = getwidth(n)
1701            if old > threshold then
1702                if rightkern then
1703                    if useitalickerns then
1704                        local lnew = leftkern  * factor
1705                        local rnew = rightkern * factor
1706                        if trace_spaces then
1707                            report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar)
1708                        end
1709                        head = insertnodebefore(head,n,italickern(lnew))
1710                        insertnodeafter(head,n,italickern(rnew))
1711                    else
1712                        local new = old + (leftkern + rightkern) * factor
1713                        if trace_spaces then
1714                            report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
1715                        end
1716                        setwidth(n,new)
1717                    end
1718                    rightkern = false
1719                else
1720                    if useitalickerns then
1721                        local new = leftkern * factor
1722                        if trace_spaces then
1723                            report_spaces("%C [%p + %p]",prevchar,old,new)
1724                        end
1725                        insertnodeafter(head,n,italickern(new)) -- tricky with traverse but ok
1726                    else
1727                        local new = old + leftkern * factor
1728                        if trace_spaces then
1729                            report_spaces("%C [%p -> %p]",prevchar,old,new)
1730                        end
1731                        setwidth(n,new)
1732                    end
1733                end
1734            end
1735            leftkern  = false
1736        elseif rightkern then
1737            local old = getwidth(n)
1738            if old > threshold then
1739                if useitalickerns then
1740                    local new = rightkern * factor
1741                    if trace_spaces then
1742                        report_spaces("[%p + %p] %C",old,new,nextchar)
1743                    end
1744                    insertnodeafter(head,n,italickern(new))
1745                else
1746                    local new = old + rightkern * factor
1747                    if trace_spaces then
1748                        report_spaces("[%p -> %p] %C",old,new,nextchar)
1749                    end
1750                    setwidth(n,new)
1751                end
1752            else
1753                -- message
1754            end
1755            rightkern = false
1756        end
1757    end
1758
1759    triggers = false
1760
1761    return head
1762end
1763
1764--
1765
1766function injections.handler(head,where)
1767    if triggers then
1768        head = injectspaces(head)
1769    end
1770    -- todo: marks only run too
1771    if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
1772        if trace_injections then
1773            report_injections("injection variant %a","everything")
1774        end
1775        return inject_everything(head,where)
1776    elseif nofregisteredpositions > 0 then
1777        if trace_injections then
1778            report_injections("injection variant %a","positions")
1779        end
1780        return inject_positions_only(head,where)
1781    elseif nofregisteredkerns > 0 then
1782        if trace_injections then
1783            report_injections("injection variant %a","kerns")
1784        end
1785        return inject_kerns_only(head,where)
1786    else
1787        return head
1788    end
1789end
1790
1791