1if not modules then modules = { } end modules ['typo-sus'] = {
2 version = 1.001,
3 comment = "companion to typo-sus.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 next = next
10
11local punctuation = {
12 po = true,
13}
14
15local openquote = {
16 ps = true,
17 pi = true,
18}
19
20local closequote = {
21 pe = true,
22 pf = true,
23}
24
25local weird = {
26 lm = true,
27 no = true,
28}
29
30local categories = characters.categories
31
32local nodecodes = nodes.nodecodes
33
34local glyph_code <const> = nodecodes.glyph
35local kern_code <const> = nodecodes.kern
36local penalty_code <const> = nodecodes.penalty
37local glue_code <const> = nodecodes.glue
38local math_code <const> = nodecodes.math
39local hlist_code <const> = nodecodes.hlist
40local vlist_code <const> = nodecodes.vlist
41
42local nuts = nodes.nuts
43
44local nextnode = nuts.traversers.node
45
46local getid = nuts.getid
47local getprev = nuts.getprev
48local getnext = nuts.getnext
49local getattr = nuts.getattr
50local getfont = nuts.getfont
51local getchar = nuts.getchar
52local getlist = nuts.getlist
53local getkern = nuts.getkern
54local getpenalty = nuts.getpenalty
55local getwidth = nuts.getwidth
56local getwhd = nuts.getwhd
57local isglyph = nuts.isglyph
58
59local setattr = nuts.setattr
60local setlist = nuts.setlist
61
62local setcolor = nodes.tracers.colors.set
63local insertbefore = nuts.insertbefore
64local insertafter = nuts.insertafter
65local endofmath = nuts.endofmath
66
67local nodepool = nuts.pool
68
69local new_rule = nodepool.rule
70local new_kern = nodepool.kern
71local new_hlist = nodepool.hlist
72
73
74local a_characters <const> = attributes.private("characters")
75local a_suspecting <const> = attributes.private('suspecting')
76local a_suspect <const> = attributes.private('suspect')
77local a_invisibles <const> = attributes.private('invisibles')
78
79local unsetvalue <const> = attributes.unsetvalue
80
81local texsetattribute = tex.setattribute
82local enabled = false
83
84local enableaction = nodes.tasks.enableaction
85
86local threshold = 65536 / 4
87
88local function special(n)
89 if n then
90 local id = getid(n)
91 if id == kern_code then
92 return getkern(n) < threshold
93 elseif id == penalty_code then
94 return true
95 elseif id == glue_code then
96 return getwidth(n) < threshold
97 elseif id == hlist_code then
98 return getwidth(n) < threshold
99 end
100 else
101 return false
102 end
103end
104
105local function goback(current)
106 local prev = getprev(current)
107 while prev and special(prev) do
108 prev = getprev(prev)
109 end
110 if prev then
111 return prev, getid(prev)
112 end
113end
114
115local function goforward(current)
116 local next = getnext(current)
117 while next and special(next) do
118 next = getnext(next)
119 end
120 if next then
121 return next, getid(next)
122 end
123end
124
125
126
127local function mark(head,current,id,color)
128 if id == glue_code then
129
130
131 local width = getwidth(current)
132 local rule = new_rule(width)
133 local kern = new_kern(-width)
134 head = insertbefore(head,current,rule)
135 head = insertbefore(head,current,kern)
136 setcolor(rule,color)
137
138
139
140
141
142
143
144 else
145 local width, height, depth = getwhd(current)
146 local extra = fonts.hashes.xheights[getfont(current)] / 2
147 local rule = new_rule(width,height+extra,depth+extra)
148 local hlist = new_hlist(rule)
149 head = insertbefore(head,current,hlist)
150 setcolor(rule,color)
151 setcolor(current,"white")
152 end
153 return head, current
154end
155
156
157
158
159
160local colors = {
161 "darkred",
162 "darkgreen",
163 "darkblue",
164 "darkcyan",
165 "darkmagenta",
166 "darkyellow",
167 "darkgray",
168 "orange",
169}
170
171local found = 0
172
173function typesetters.marksuspects(head)
174 local current = head
175 local lastdone = nil
176 while current do
177 if getattr(current,a_suspecting) then
178 local char, id = isglyph(current)
179 if char then
180 local code = categories[char]
181 local done = false
182 if punctuation[code] then
183 local prev, pid = goback(current)
184 if prev and pid == glue_code then
185 done = 3
186 elseif prev and pid == math_code then
187 done = 3
188 else
189 local next, nid = goforward(current)
190 if next and nid ~= glue_code then
191 done = 3
192 end
193 end
194 elseif openquote[code] then
195 local next, nid = goforward(current)
196 if next and nid == glue_code then
197 done = 1
198 end
199 elseif closequote[code] then
200 local prev, pid = goback(current)
201 if prev and pid == glue_code then
202 done = 1
203 end
204 elseif weird[code] then
205 done = 2
206 else
207 local prev, pid = goback(current)
208 if prev then
209 if pid == math_code then
210 done = 7
211 elseif pid == glyph_code and getfont(current) ~= getfont(prev) then
212 if lastdone ~= prev then
213 done = 2
214 end
215 end
216 end
217 if not done then
218 local next, nid = goforward(current)
219 if next then
220 if nid == math_code then
221 done = 7
222 elseif nid == glyph_code and getfont(current) ~= getfont(next) then
223 if lastdone ~= prev then
224 done = 2
225 end
226 end
227 end
228 end
229 end
230 if done then
231 setattr(current,a_suspect,done)
232 lastdone = current
233 found = found + 1
234 end
235 current = getnext(current)
236 elseif id == math_code then
237 current = getnext(endofmath(current))
238 elseif id == glue_code then
239 local a = getattr(current,a_characters)
240 if a then
241 local prev = getprev(current)
242 local prid = prev and getid(prev)
243 local done = false
244 if prid == penalty_code and getpenalty(prev) == 10000 then
245 done = 8
246 else
247 done = 5
248 end
249 if done then
250 setattr(current,a_suspect,done)
251
252 found = found + 1
253 end
254 end
255 current = getnext(current)
256 else
257 current = getnext(current)
258 end
259 else
260 current = getnext(current)
261 end
262 end
263 return head
264end
265
266local function showsuspects(head)
267
268 local pickup = false
269 for current, id, subtype in nextnode, head do
270 if current == pickup then
271 pickup = false
272 elseif id == glyph_code then
273 local a = getattr(current,a_suspect)
274 if a then
275 head = mark(head,current,id,colors[a])
276 end
277 elseif id == glue_code then
278 local a = getattr(current,a_suspect)
279 if a then
280 head = mark(head,current,id,colors[a])
281 end
282 elseif id == math_code then
283 pickup = endofmath(current)
284 if pickup == current then
285 pickup = false
286 end
287 elseif id == hlist_code or id == vlist_code then
288 local list = getlist(current)
289 if list then
290 local l = showsuspects(list)
291 if l ~= list then
292 setlist(current,l)
293 end
294 end
295 end
296 end
297 return head
298end
299
300function typesetters.showsuspects(head)
301 if found > 0 then
302 return showsuspects(head)
303 else
304 return head
305 end
306end
307
308
309
310trackers.register("typesetters.suspects",function(v)
311 texsetattribute(a_suspecting,v and 1 or unsetvalue)
312 if v and not enabled then
313 enableaction("processors","typesetters.marksuspects")
314 enableaction("shipouts", "typesetters.showsuspects")
315 enabled = true
316 end
317end)
318
319local enabled = false
320local invisibles = false
321
322local invisible = {
323 [0x200B] = "zwsp",
324 [0x200C] = "zwnj",
325 [0x200D] = "zwj",
326 [0x2061] = "apply",
327 [0x2062] = "times",
328 [0x2063] = "separator",
329 [0x2064] = "plus",
330}
331
332local function showinvisibles(head)
333
334 for current, id, subtype in nextnode, head do
335
336 if id == glyph_code then
337
338
339 local c = getchar(current)
340 local i = invisible[c]
341 if i then
342 if not invisibles then
343 invisibles = nodes.visualizers.register("invisible")
344 end
345 local info = invisibles(i)
346 head = insertbefore(head,current,info)
347 end
348
349 elseif id == hlist_code or id == vlist_code then
350 local list = getlist(current)
351 if list then
352 local l = showinvisibles(list)
353 if l ~= list then
354 setlist(current,l)
355 end
356 end
357 end
358 end
359 return head
360end
361
362function typesetters.showinvisibles(head)
363 return showinvisibles(head)
364end
365
366trackers.register("typesetters.invisibles",function(v)
367
368 if v and not enabled then
369 enableaction("shipouts","typesetters.showinvisibles")
370 enabled = true
371 end
372end)
373 |