1if not modules then modules = { } end modules ['typo-rub'] = {
2 version = 1.001,
3 comment = "companion to typo-rub.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
10
11
12
13
14
15
16
17
18local lpegmatch = lpeg.match
19local utfcharacters = utf.characters
20local setmetatableindex = table.setmetatableindex
21
22local variables = interfaces.variables
23local implement = interfaces.implement
24
25local texsetattribute = tex.setattribute
26
27local v_flushleft = variables.flushleft
28local v_middle = variables.middle
29local v_flushright = variables.flushright
30local v_yes = variables.yes
31local v_no = variables.no
32local v_auto = variables.auto
33
34local nuts = nodes.nuts
35
36local getid = nuts.getid
37local getsubtype = nuts.getsubtype
38local getattr = nuts.getattr
39local setattr = nuts.setattr
40local getnext = nuts.getnext
41local setnext = nuts.setnext
42local getprev = nuts.getprev
43local setprev = nuts.setprev
44local setlink = nuts.setlink
45local getlist = nuts.getlist
46local setlist = nuts.setlist
47local setshift = nuts.setshift
48local getwidth = nuts.getwidth
49local setwidth = nuts.setwidth
50
51local hpack = nuts.hpack
52local takebox = nuts.takebox
53
54local nextlist = nuts.traversers.list
55
56local nodecodes = nodes.nodecodes
57local glyph_code = nodecodes.glyph
58local disc_code = nodecodes.disc
59local kern_code = nodecodes.kern
60local glue_code = nodecodes.glue
61local penalty_code = nodecodes.penalty
62local hlist_code = nodecodes.hlist
63local vlist_code = nodecodes.vlist
64local par_code = nodecodes.par
65local dir_code = nodecodes.dir
66
67local kerncodes = nodes.kerncodes
68local fontkern_code = kerncodes.font
69
70local nodepool = nuts.pool
71local new_kern = nodepool.kern
72
73local setprop = nuts.setprop
74local getprop = nuts.getprop
75
76local findattribute = nuts.findattribute
77
78local enableaction = nodes.tasks.enableaction
79
80local nofrubies = 0
81local rubylist = { }
82
83local a_ruby = attributes.private("ruby")
84
85local rubies = { }
86typesetters.rubies = rubies
87
88local trace_rubies = false trackers.register("typesetters.rubies",function(v) trace_rubies = v end)
89local report_rubies = logs.reporter("rubies")
90
91
92
93local registervalue = attributes.registervalue
94local getvalue = attributes.getvalue
95local texsetattribute = tex.setattribute
96
97do
98
99 local shared = nil
100 local splitter = lpeg.tsplitat("|")
101
102 local function enable()
103 enableaction("processors","typesetters.rubies.check")
104 enableaction("shipouts", "typesetters.rubies.attach")
105 enable = false
106 end
107
108 local ctx_setruby = context.core.setruby
109
110 local function ruby(settings)
111 local base = settings.base
112 local comment = settings.comment
113 shared = settings
114 local c = lpegmatch(splitter,comment)
115 if #c == 1 then
116 ctx_setruby(base,comment)
117 if trace_rubies then
118 report_rubies("- %s -> %s",base,comment)
119 end
120 else
121 local i = 0
122 for b in utfcharacters(base) do
123 i = i + 1
124 local r = c[i]
125 if r then
126 ctx_setruby(b,r)
127 if trace_rubies then
128 report_rubies("%i: %s -> %s",i,b,r)
129 end
130 else
131 ctx_setruby(b,"")
132 if trace_rubies then
133 report_rubies("%i: %s",i,b)
134 end
135 end
136 end
137 end
138 if enable then
139 enable()
140 end
141 end
142
143 local function startruby(settings)
144 shared = settings
145 if enable then
146 enable()
147 end
148 end
149
150 implement {
151 name = "ruby",
152 actions = ruby,
153 arguments = {
154 {
155 { "align" },
156 { "stretch" },
157 { "hoffset", "dimension" },
158 { "voffset", "dimension" },
159 { "comment" },
160 { "base" },
161 }
162 },
163 }
164
165 implement {
166 name = "startruby",
167 actions = startruby,
168 arguments = {
169 {
170 { "align" },
171 { "stretch" },
172 { "hoffset", "dimension" },
173 { "voffset", "dimension" },
174 }
175 },
176 }
177
178 local function setruby(n,m)
179 nofrubies = nofrubies + 1
180 local r = takebox(n)
181 local t = {
182 text = r,
183 width = getwidth(r),
184 basewidth = 0,
185 start = false,
186 stop = false,
187 }
188
189
190 texsetattribute(a_ruby,registervalue(a_ruby,setmetatableindex(t,shared)))
191 end
192
193 implement {
194 name = "setruby",
195 actions = setruby,
196 arguments = "integer",
197 }
198
199end
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272function rubies.check(head)
273 local _, current = findattribute(head,a_ruby)
274 if current then
275
276 local start = nil
277 local stop = nil
278 local found = nil
279
280 local function flush(where)
281
282local r = getvalue(a_ruby,found)
283 if r then
284
285 while start ~= stop and getid(start) == glue_code do
286 start = getnext(start)
287 end
288 while stop ~= start and getid(stop) == glue_code do
289 stop = getprev(stop)
290 end
291
292 local prev = getprev(start)
293 local next = getnext(stop)
294 setprev(start)
295 setnext(stop)
296 local h = hpack(start)
297 if start == head then
298 head = h
299 else
300 setlink(prev,h)
301 end
302 setlink(h,next)
303 local bwidth = getwidth(h)
304 local rwidth = r.width
305 r.basewidth = bwidth
306 r.start = start
307 r.stop = stop
308 setprop(h,"ruby",found)
309 if rwidth > bwidth then
310
311 setwidth(h,rwidth)
312 end
313 end
314 end
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339 while current do
340 local nx = getnext(current)
341 local a = getattr(current,a_ruby)
342 if not a then
343 if found then
344 flush("flush 1")
345 found = nil
346 end
347_, current = findattribute(nx,a_ruby)
348 elseif a == found then
349 stop = current
350current = nx
351 else
352 if found then
353 flush("flush 2")
354 end
355 found = a
356 start = current
357 stop = current
358current = nx
359 end
360 end
361
362 end
363
364 if found then
365 flush("flush 4")
366 end
367 return head, true
368end
369
370
371local attach
372
373local function whatever(current,list)
374 local a = getprop(current,"ruby")
375 if a then
376
377local ruby = getvalue(a_ruby,a)
378 local align = ruby.align or v_middle
379 local stretch = ruby.stretch or v_no
380 local hoffset = ruby.hoffset or 0
381 local voffset = ruby.voffset or 0
382 local start = ruby.start
383 local stop = ruby.stop
384 local text = ruby.text
385 local rwidth = ruby.width
386 local bwidth = ruby.basewidth
387 local delta = rwidth - bwidth
388 setwidth(text,0)
389 if voffset ~= 0 then
390 setshift(text,voffset)
391 end
392
393 if delta > 0 then
394
395 if stretch == v_yes then
396 setlink(text,start)
397 while start and start ~= stop do
398 local s = nodepool.stretch()
399 local n = getnext(start)
400 setlink(start,s,n)
401 start = n
402 end
403 text = hpack(text,rwidth,"exactly")
404 else
405 local left = new_kern(delta/2)
406 local right = new_kern(delta/2)
407 setlink(text,left,start)
408 setlink(stop,right)
409 end
410 setlist(current,text)
411 elseif delta < 0 then
412
413 if align == v_auto then
414 local l = true
415 local c = getprev(current)
416 while c do
417 local id = getid(c)
418 if id == glue_code or id == penalty_code or id == kern_code then
419
420 elseif id == hlist_code and getwidth(c) == 0 then
421
422 elseif id == whatsit_code or id == par_code or id == dir_code then
423
424 else
425 l = false
426 break
427 end
428 c = getprev(c)
429 end
430 local r = true
431 local c = getnext(current)
432 while c do
433 local id = getid(c)
434 if id == glue_code or id == penalty_code or id == kern_code then
435
436 elseif id == hlist_code and getwidth(c) == 0 then
437
438 else
439 r = false
440 break
441 end
442 c = getnext(c)
443 end
444 if l and not r then
445 align = v_flushleft
446 elseif r and not l then
447 align = v_flushright
448 else
449 align = v_middle
450 end
451 end
452 if align == v_flushleft then
453 setlink(text,start)
454 setlist(current,text)
455 elseif align == v_flushright then
456 local left = new_kern(-delta)
457 local right = new_kern(delta)
458 setlink(left,text,right,start)
459 setlist(current,left)
460 else
461 local left = new_kern(-delta/2)
462 local right = new_kern(delta/2)
463 setlink(left,text,right,start)
464 setlist(current,left)
465 end
466 else
467 setlink(text,start)
468 setlist(current,text)
469 end
470 setprop(current,"ruby",false)
471
472 elseif list then
473 attach(list)
474 end
475end
476
477attach = function(head)
478 for current, id, subtype, list in nextlist, head do
479 if id == hlist_code or id == vlist_code then
480 whatever(current,list)
481 end
482 end
483 return head
484end
485
486rubies.attach = attach
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508 |