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