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 nexthlist = nuts.traversers.hlist
55local nextvlist = nuts.traversers.vlist
56
57local nodecodes = nodes.nodecodes
58local glyph_code = nodecodes.glyph
59local disc_code = nodecodes.disc
60local kern_code = nodecodes.kern
61local glue_code = nodecodes.glue
62local penalty_code = nodecodes.penalty
63local hlist_code = nodecodes.hlist
64local vlist_code = nodecodes.vlist
65local par_code = nodecodes.par
66local dir_code = nodecodes.dir
67
68local kerncodes = nodes.kerncodes
69local fontkern_code = kerncodes.font
70
71local nodepool = nuts.pool
72local new_kern = nodepool.kern
73
74local setprop = nuts.setprop
75local getprop = nuts.getprop
76
77local enableaction = nodes.tasks.enableaction
78
79local nofrubies = 0
80local rubylist = { }
81
82local a_ruby = attributes.private("ruby")
83
84local rubies = { }
85typesetters.rubies = rubies
86
87local trace_rubies = false trackers.register("typesetters.rubies",function(v) trace_rubies = v end)
88local report_rubies = logs.reporter("rubies")
89
90do
91
92 local shared = nil
93 local splitter = lpeg.tsplitat("|")
94
95 local function enable()
96 enableaction("processors","typesetters.rubies.check")
97 enableaction("shipouts", "typesetters.rubies.attach")
98 enable = false
99 end
100
101 local ctx_setruby = context.core.setruby
102
103 local function ruby(settings)
104 local base = settings.base
105 local comment = settings.comment
106 shared = settings
107 local c = lpegmatch(splitter,comment)
108 if #c == 1 then
109 ctx_setruby(base,comment)
110 if trace_rubies then
111 report_rubies("- %s -> %s",base,comment)
112 end
113 else
114 local i = 0
115 for b in utfcharacters(base) do
116 i = i + 1
117 local r = c[i]
118 if r then
119 ctx_setruby(b,r)
120 if trace_rubies then
121 report_rubies("%i: %s -> %s",i,b,r)
122 end
123 else
124 ctx_setruby(b,"")
125 if trace_rubies then
126 report_rubies("%i: %s",i,b)
127 end
128 end
129 end
130 end
131 if enable then
132 enable()
133 end
134 end
135
136 local function startruby(settings)
137 shared = settings
138 if enable then
139 enable()
140 end
141 end
142
143 implement {
144 name = "ruby",
145 actions = ruby,
146 arguments = {
147 {
148 { "align" },
149 { "stretch" },
150 { "hoffset", "dimension" },
151 { "voffset", "dimension" },
152 { "comment" },
153 { "base" },
154 }
155 },
156 }
157
158 implement {
159 name = "startruby",
160 actions = startruby,
161 arguments = {
162 {
163 { "align" },
164 { "stretch" },
165 { "hoffset", "dimension" },
166 { "voffset", "dimension" },
167 }
168 },
169 }
170
171 local function setruby(n,m)
172 nofrubies = nofrubies + 1
173 local r = takebox(n)
174 rubylist[nofrubies] = setmetatableindex({
175 text = r,
176 width = getwidth(r),
177 basewidth = 0,
178 start = false,
179 stop = false,
180 }, shared)
181 texsetattribute(a_ruby,nofrubies)
182 end
183
184 implement {
185 name = "setruby",
186 actions = setruby,
187 arguments = "integer",
188 }
189
190end
191
192function rubies.check(head)
193 local current = head
194 local start = nil
195 local stop = nil
196 local found = nil
197
198 local function flush(where)
199 local r = rubylist[found]
200 if r then
201 local prev = getprev(start)
202 local next = getnext(stop)
203 setprev(start)
204 setnext(stop)
205 local h = hpack(start)
206 if start == head then
207 head = h
208 else
209 setlink(prev,h)
210 end
211 setlink(h,next)
212 local bwidth = getwidth(h)
213 local rwidth = r.width
214 r.basewidth = bwidth
215 r.start = start
216 r.stop = stop
217 setprop(h,"ruby",found)
218 if rwidth > bwidth then
219
220 setwidth(h,rwidth)
221 end
222 end
223 end
224
225 while current do
226 local nx = getnext(current)
227 local id = getid(current)
228 if id == glyph_code then
229 local a = getattr(current,a_ruby)
230 if not a then
231 if found then
232 flush("flush 1")
233 found = nil
234 end
235 elseif a == found then
236 stop = current
237 else
238 if found then
239 flush("flush 2")
240 end
241 found = a
242 start = current
243 stop = current
244 end
245
246 elseif id == kern_code and getsubtype(current,fontkern_code) then
247
248 elseif found and id == disc_code then
249
250 elseif found then
251 flush("flush 3")
252 found = nil
253 end
254 current = nx
255 end
256 if found then
257 flush("flush 4")
258 end
259 return head, true
260end
261
262local attach
263
264local function whatever(current)
265 local a = getprop(current,"ruby")
266 if a then
267 local ruby = rubylist[a]
268 local align = ruby.align or v_middle
269 local stretch = ruby.stretch or v_no
270 local hoffset = ruby.hoffset or 0
271 local voffset = ruby.voffset or 0
272 local start = ruby.start
273 local stop = ruby.stop
274 local text = ruby.text
275 local rwidth = ruby.width
276 local bwidth = ruby.basewidth
277 local delta = rwidth - bwidth
278 setwidth(text,0)
279 if voffset ~= 0 then
280 setshift(text,voffset)
281 end
282
283 if delta > 0 then
284
285 if stretch == v_yes then
286 setlink(text,start)
287 while start and start ~= stop do
288 local s = nodepool.stretch()
289 local n = getnext(start)
290 setlink(start,s,n)
291 start = n
292 end
293 text = hpack(text,rwidth,"exactly")
294 else
295 local left = new_kern(delta/2)
296 local right = new_kern(delta/2)
297 setlink(text,left,start)
298 setlink(stop,right)
299 end
300 setlist(current,text)
301 elseif delta < 0 then
302
303 if align == v_auto then
304 local l = true
305 local c = getprev(current)
306 while c do
307 local id = getid(c)
308 if id == glue_code or id == penalty_code or id == kern_code then
309
310 elseif id == hlist_code and getwidth(c) == 0 then
311
312 elseif id == whatsit_code or id == par_code or id == dir_code then
313
314 else
315 l = false
316 break
317 end
318 c = getprev(c)
319 end
320 local r = true
321 local c = getnext(current)
322 while c do
323 local id = getid(c)
324 if id == glue_code or id == penalty_code or id == kern_code then
325
326 elseif id == hlist_code and getwidth(c) == 0 then
327
328 else
329 r = false
330 break
331 end
332 c = getnext(c)
333 end
334 if l and not r then
335 align = v_flushleft
336 elseif r and not l then
337 align = v_flushright
338 else
339 align = v_middle
340 end
341 end
342 if align == v_flushleft then
343 setlink(text,start)
344 setlist(current,text)
345 elseif align == v_flushright then
346 local left = new_kern(-delta)
347 local right = new_kern(delta)
348 setlink(left,text,right,start)
349 setlist(current,left)
350 else
351 local left = new_kern(-delta/2)
352 local right = new_kern(delta/2)
353 setlink(left,text,right,start)
354 setlist(current,left)
355 end
356 else
357 setlink(text,start)
358 setlist(current,text)
359 end
360 setprop(current,"ruby",false)
361 rubylist[a] = nil
362 else
363 local list = getlist(current)
364 if list then
365 attach(list)
366 end
367 end
368end
369
370attach = function(head)
371 for current in nexthlist, head do
372 whatever(current)
373 end
374 for current in nextvlist, head do
375 whatever(current)
376 end
377 return head
378end
379
380rubies.attach = attach
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402 |