typo-pnc.lmt /size: 7341 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['typo-pnc'] = {
2    version   = 1.001,
3    comment   = "companion to typo-pnc.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local nodes           = nodes
10local fonts           = fonts
11
12local enableaction    = nodes.tasks.enableaction
13
14local nuts            = nodes.nuts
15local tonut           = nodes.tonut
16
17local new_kern        = nuts.pool.kern
18local insertafter     = nuts.insertafter
19
20local nextglyph       = nuts.traversers.glyph
21
22local getchar         = nuts.getchar
23local getfont         = nuts.getfont
24local getboth         = nuts.getboth
25local getnext         = nuts.getnext
26local getattr         = nuts.getattr
27local getid           = nuts.getid
28local getsubtype      = nuts.getsubtype
29local getwidth        = nuts.getwidth
30local setwidth        = nuts.setwidth
31local findattribute   = nuts.findattribute
32
33local glyph_code      <const> = nodes.nodecodes.glyph
34local glue_code       <const> = nodes.nodecodes.glue
35local spaceskip_code  <const> = nodes.gluecodes.spaceskip
36
37local parameters      = fonts.hashes.parameters
38local categories      = characters.categories
39
40local texsetattribute = tex.setattribute
41
42local unsetvalue      <const> = attributes.unsetvalue
43
44local period          <const> = 0x2E
45
46local factor          = 0.5
47
48-- alternative: tex.getlccode and tex.getuccode
49
50typesetters             = typesetters or { }
51local typesetters       = typesetters
52
53local periodkerns       = typesetters.periodkerns or { }
54typesetters.periodkerns = periodkerns
55
56local report            = logs.reporter("period kerns")
57local trace             = false
58
59trackers.register("typesetters.periodkerns",function(v) trace = v end)
60
61periodkerns.mapping     = periodkerns.mapping or { }
62periodkerns.factors     = periodkerns.factors or { }
63
64local a_periodkern      <const> = attributes.private("periodkern")
65
66storage.register("typesetters/periodkerns/mapping", periodkerns.mapping, "typesetters.periodkerns.mapping")
67storage.register("typesetters/periodkerns/factors", periodkerns.factors, "typesetters.periodkerns.factors")
68
69local mapping = periodkerns.mapping
70local factors = periodkerns.factors
71
72function periodkerns.handler(head)
73    local _, start = findattribute(head,a_periodkern,glyph_code)
74    if start then
75        for current, char, font in nextglyph, start do
76            if char == period then
77                local a = getattr(current,a_periodkern)
78                if a then
79                    local factor = mapping[a]
80                    if factor then
81                        local prev, next = getboth(current)
82                        if prev and next and getid(prev) == glyph_code and getid(next) == glyph_code then
83                            local pchar = getchar(prev)
84                            local pcode = categories[pchar]
85                            if pcode == "lu" or pcode == "ll" then
86                                local nchar = getchar(next)
87                                local ncode = categories[nchar]
88                                if ncode == "lu" or ncode == "ll" then
89                                    local next2 = getnext(next)
90                                    if next2 and getid(next2) == glyph_code and getchar(next2) == period then
91                                        -- A.B.
92                                        local fontspace, inserted
93                                        if factor ~= 0 then
94                                            fontspace = parameters[getfont(current)].space -- can be sped up
95                                            inserted  = factor * fontspace
96                                            insertafter(head,current,new_kern(inserted))
97                                            if trace then
98                                                report("inserting space at %C . [%p] %C .",pchar,inserted,nchar)
99                                            end
100                                        end
101                                        local next3 = getnext(next2)
102                                        if next3 and getid(next3) == glue_code and getsubtype(next3) == spaceskip_code then
103                                            local width = getwidth(next3)
104                                            local space = fontspace or parameters[getfont(current)].space -- can be sped up
105                                            if width > space then -- space + extraspace
106                                                local next4 = getnext(next3)
107                                                if next4 and getid(next4) == glyph_code then
108                                                    local fchar = getchar(next4)
109                                                    if categories[fchar] ~= "lu" then
110                                                        -- A.B.<glue>X
111                                                        if trace then
112                                                            if inserted then
113                                                                report("reverting space at %C . [%p] %C . [%p->%p] %C",pchar,inserted,nchar,width,space,fchar)
114                                                            else
115                                                                report("reverting space at %C . %C . [%p->%p] %C",pchar,nchar,width,space,fchar)
116                                                            end
117                                                        end
118                                                        setwidth(next3,space)
119                                                    else
120                                                        if trace then
121                                                            if inserted then
122                                                                report("keeping space at %C . [%p] %C . [%p] %C",pchar,inserted,nchar,width,fchar)
123                                                            else
124                                                                report("keeping space at %C . %C . [%p] %C",pchar,nchar,width,fchar)
125                                                            end
126                                                        end
127                                                    end
128                                                end
129                                            end
130                                        end
131                                    end
132                                end
133                            end
134                        end
135                    end
136                end
137            end
138        end
139    end
140    return head
141end
142
143local enabled = false
144
145function periodkerns.set(factor)
146    factor = tonumber(factor) or 0
147    if not enabled then
148        enableaction("processors","typesetters.periodkerns.handler")
149        enabled = true
150    end
151    local a = factors[factor]
152    if not a then
153        a = #mapping + 1
154        factors[factors], mapping[a] = a, factor
155    end
156    factor = a
157    texsetattribute(a_periodkern,factor)
158    return factor
159end
160
161-- interface
162
163interfaces.implement {
164    name      = "setperiodkerning",
165    actions   = periodkerns.set,
166    arguments = "string"
167}
168
169
170