typo-wrp.lmt /size: 5392 b    last modification: 2023-12-21 09:44
1if not modules then modules = { } end modules ['typo-wrp'] = {
2    version   = 1.001,
3    comment   = "companion to typo-wrp.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
9-- begin/end par wrapping stuff ... more to come
10
11local boundary_code     = nodes.nodecodes.boundary
12local glyph_code        = nodes.nodecodes.glyph
13local wordboundary_code = nodes.boundarycodes.word
14local userboundary_code = nodes.boundarycodes.user
15
16local nuts              = nodes.nuts
17
18local findtail          = nuts.tail
19local getprev           = nuts.getprev
20local setnext           = nuts.setnext
21local getid             = nuts.getid
22local getdata           = nuts.getdata
23local isglyph           = nuts.isglyph
24local getsubtype        = nuts.getsubtype
25local getattr           = nuts.getattr
26local flushnodelist     = nuts.flushlist
27local traverse_boundary = nuts.traversers.boundary
28local insertnodebefore  = nuts.insertbefore
29local newkern           = nuts.pool.kern
30
31local fontdata          = fonts.hashes.identifiers
32
33local enableaction      = nodes.tasks.enableaction
34
35local implement         = interfaces.implement
36
37local wrappers          = { }
38typesetters.wrappers    = wrappers
39
40local trace_wrappers      trackers.register("typesetters.wrappers",function(v) trace_wrappers = v end)
41local report            = logs.reporter("paragraphs","wrappers")
42
43-- In luametatex we don't have the parfilskip attached yet but we can have final glue
44-- anyway. This check is very bound to the \crlf definition where we get:
45--
46-- ... boundary [strut: hlist] [break: glue penalty] boundary
47
48local a_crlf = attributes.private("crlf")
49
50local function remove_dangling_crlf(head,tail)
51    if head and tail then
52        if getid(tail) == boundary_code and getsubtype(tail) == wordboundary_code then
53            -- findnode could run backwards
54            if getattr(tail,a_crlf) then
55                local t = tail
56                while t do
57                    t = getprev(t)
58                    if not t then
59                        break
60                    elseif getid(t) == boundary_code and getsubtype(t) == wordboundary_code then
61                        if t ~= head then
62                            if trace_wrappers then
63                                report("removing a probably unwanted end-of-par break in line %s (guess)",tex.inputlineno)
64                            end
65                            tail = getprev(t)
66                            setnext(tail)
67                            flushnodelist(t)
68                        end
69                        break
70                    end
71                end
72            end
73        end
74    end
75    return head, tail
76end
77
78implement {
79    name     = "enablecrlf",
80    onlyonce = true,
81    actions  = function()
82        enableaction("processors","typesetters.wrappers.handler")
83    end
84}
85
86-- Here's a solution for a trivial challenge by MS who got it from the internet
87-- (SE). If needed we can make it a bit more granular.
88
89local tighten       = { }
90typesetters.tighten = tighten
91
92local trace_tighten   trackers.register("typesetters.tighten",function(v) trace_tighten = v end)
93local report        = logs.reporter("typesetters","tighten")
94
95local a_tightfit_boundary = tex.boundaries.system("c_tightfit_boundary") -- private, not defined with \defineboundary
96
97local function bbcompensation(font,char)
98    local tfmdata   = fontdata[font]
99    local character = tfmdata.characters[char]
100    if character then
101        local compensation = character.compensation
102        if not compensation then
103            local description = tfmdata.descriptions[char]
104            if description then
105                local boundingbox  = description.boundingbox
106                return boundingbox and (boundingbox[3] - (description.width or 0)) * tfmdata.parameters.hfactor
107            end
108            character.compensation = compensation
109        end
110    end
111    return 0
112end
113
114function tighten.handler(head)
115    for n, subtype in traverse_boundary, head do
116        if subtype == userboundary_code and getdata(n) == a_tightfit_boundary then
117            local prev = getprev(n)
118            if prev then
119                local char, font = isglyph(prev)
120                if char then
121                    local compensation = bbcompensation(font,char)
122                    if compensation > 0 then
123                        if trace_tighten then
124                            report("compensating %p after %C",compensation,char)
125                        end
126                        insertnodebefore(head,n,newkern(compensation))
127                    end
128                end
129            end
130        end
131    end
132    return head
133end
134
135implement {
136    name     = "enabletighten",
137    onlyonce = true,
138    actions  = function()
139        enableaction("processors","typesetters.tighten.handler")
140    end
141}
142
143local dimension_value = tokens.values.dimension
144local texgetnest      = tex.getnest
145
146implement {
147    name      = "tightfitcompensation",
148    public    = true,
149    protected = true,
150    usage     = "value",
151    actions   = function()
152        local list = texgetnest()
153        if list then
154            list = list.tail
155        end
156        return
157            dimension_value,
158            list and list.id == glyph_code and bbcompensation(list.font,list.char) or 0
159    end,
160}
161