1if not modules then modules = { } end modules ['driv-shp'] = {
2 version = 1.001,
3 optimize = true,
4 comment = "companion to driv-ini.mkiv",
5 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6 copyright = "PRAGMA ADE / ConTeXt Development Team",
7 license = "see context related readme files"
8}
9
10local nuts = nodes.nuts
11local tonut = nodes.tonut
12
13local getdirection = nuts.getdirection
14local getlist = nuts.getlist
15local getoffsets = nuts.getoffsets
16local getorientation = nuts.getorientation
17local getanchors = nuts.getanchors
18local getgeometry = nuts.getgeometry
19local getwhd = nuts.getwhd
20local getkern = nuts.getkern
21local getwidth = nuts.getwidth
22local getheight = nuts.getheight
23local getdepth = nuts.getdepth
24local getnext = nuts.getnext
25local getid = nuts.getid
26local getshift = nuts.getshift
27local getprop = nuts.getprop
28local getreplace = nuts.getreplace
29local setreplace = nuts.setreplace
30
31
32
33local setdirection = nuts.setdirection
34local setlink = nuts.setlink
35
36local nextnode = nuts.traversers.node
37
38local effectiveglue = nuts.effectiveglue
39local dirdimensions = nuts.dirdimensions
40
41local nodecodes = nodes.nodecodes
42local whatsitcodes = nodes.whatsitcodes
43local directioncodes = tex.directioncodes
44
45local glyph_code <const> = nodecodes.glyph
46local kern_code <const> = nodecodes.kern
47local glue_code <const> = nodecodes.glue
48local hlist_code <const> = nodecodes.hlist
49local vlist_code <const> = nodecodes.vlist
50local dir_code <const> = nodecodes.dir
51local disc_code <const> = nodecodes.disc
52local math_code <const> = nodecodes.math
53local rule_code <const> = nodecodes.rule
54local whatsit_code <const> = nodecodes.whatsit
55
56local userdefined_code <const> = whatsitcodes.userdefined
57
58local lefttoright_code <const> = directioncodes.lefttoright
59local righttoleft_code <const> = directioncodes.righttoleft
60
61local drivers = drivers
62
63local report = logs.reporter("drivers")
64
65local maxdimen <const> = tex.magicconstants.maxdimen
66
67local pos_h = 0
68local pos_v = 0
69local pos_r = lefttoright_code
70
71local applyorientation = drivers.applyorientation
72local applyanchor = drivers.applyanchor
73
74local initialize
75local finalize
76local userdefined
77
78local function reset_state()
79 pos_h = 0
80 pos_v = 0
81 pos_r = lefttoright_code
82end
83
84local dirstack = { }
85
86local function reset_dir_stack()
87 dirstack = { }
88end
89
90local function handlewhatsit(current,pos_h,pos_v)
91 local action = userdefined[getprop(current,"id")]
92 if action then
93 action(current,pos_h,pos_v)
94 end
95end
96
97local hlist_out, vlist_out
98
99hlist_out = function(this_box,current)
100 local ref_h = pos_h
101 local ref_v = pos_v
102 local ref_r = pos_r
103 pos_r = getdirection(this_box)
104 local cur_h = 0
105 local cur_b
106
107 for current, id, subtype in nextnode, current do
108 if id == glyph_code then
109 local width, factor = getwidth(current,true)
110 if width ~= 0 then
111 if factor ~= 0 then
112 cur_h = cur_h + (1.0 + factor/1000000.0) * width
113 else
114 cur_h = cur_h + width
115 end
116 end
117 elseif id == glue_code then
118
119cur_h = cur_h + effectiveglue(current,this_box,true)
120 elseif id == hlist_code or id == vlist_code then
121 local width, height, depth = getwhd(current)
122 local list = getlist(current)
123 if list then
124 local boxdir = getdirection(current) or lefttoright_code
125 local shift = getshift(current)
126 local geometry, hasoffset, hasorientation, hasanchor = getgeometry(current,true)
127 local anchor, source, target, targetdata, s_anchor, t_anchor
128 local anc_h, anc_v
129 local usedorientation = false
130 if hasanchor then
131 anchor, source, target, s_anchor, t_anchor = getanchors(current)
132 end
133 if hasorientation then
134 local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
135 local orientation, basepoint_h, basepoint_v = applyorientation(orientation,0,shift,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
136 if orientation == 1 then
137 basepoint_h = basepoint_h + doffset
138 if boxdir == pos_r then
139 basepoint_v = basepoint_v - height
140 end
141 usedorientation = orientation
142 elseif orientation == 2 then
143 if boxdir == pos_r then
144 basepoint_h = basepoint_h + width
145 end
146 usedorientation = orientation
147 elseif orientation == 3 then
148 basepoint_h = basepoint_h + hoffset
149 if boxdir ~= pos_r then
150 basepoint_v = basepoint_v - height
151 end
152 usedorientation = orientation
153 end
154 if target then
155 targetdata = anchors[target]
156 if targetdata then
157 anc_h = basepoint_h
158 anc_v = - basepoint_v
159 goto posdone
160 end
161 end
162 if pos_r == righttoleft_code then
163 pos_h = ref_h - (cur_h + basepoint_h)
164 else
165 pos_h = ref_h + (cur_h + basepoint_h)
166 end
167
168 pos_v = ref_v - basepoint_v
169 elseif hasoffset then
170 local orientation, xoffset, yoffset = getorientation(current)
171 local basepoint_h = boxdir ~= pos_r and width or 0
172 local basepoint_v = shift
173 if target then
174 targetdata = anchors[target]
175 if targetdata then
176 anc_h = xoffset + basepoint_h
177 anc_v = yoffset - basepoint_v
178 goto posdone
179 end
180 end
181 if pos_r == righttoleft_code then
182 pos_h = ref_h - (cur_h + basepoint_h + xoffset)
183 else
184 pos_h = ref_h + (cur_h + basepoint_h + xoffset)
185 end
186 pos_v = ref_v - (basepoint_v - yoffset)
187 elseif hasanchor then
188 local basepoint_h = boxdir ~= pos_r and width or 0
189 local basepoint_v = shift
190 if target then
191 targetdata = anchors[target]
192 if targetdata then
193 anc_h = basepoint_h
194 anc_v = - basepoint_v
195 goto posdone
196 end
197 end
198 if pos_r == righttoleft_code then
199 pos_h = ref_h - (cur_h + basepoint_h)
200 else
201 pos_h = ref_h + (cur_h + basepoint_h)
202 end
203 pos_v = ref_v - basepoint_v
204 else
205 local basepoint_h = boxdir ~= pos_r and width or 0
206 local basepoint_v = shift
207 if pos_r == righttoleft_code then
208 pos_h = ref_h - (cur_h + basepoint_h)
209 else
210 pos_h = ref_h + (cur_h + basepoint_h)
211 end
212 pos_v = ref_v - basepoint_v
213 end
214 goto process
215 ::posdone::
216 if pos_r == righttoleft_code then
217 pos_h = targetdata[1] - anc_h
218 else
219 pos_h = targetdata[1] + anc_h
220 end
221 pos_v = targetdata[2] + anc_v
222 if anchor and anchor > 0 then
223 pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5])
224 pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth)
225 end
226 ::process::
227 if source then
228 local anchor_h = pos_h
229 local anchor_v = pos_v
230 if usedorientation then
231 if usedorientation == 1 then
232 anchor_v = anchor_v - (width - height)
233 elseif usedorientation == 2 then
234 anchor_v = anchor_v - (depth - height)
235 elseif usedorientation == 3 then
236 anchor_v = anchor_v + (height - width)
237 end
238 end
239 anchors[source] = { anchor_h, anchor_v, width, height, depth }
240 end
241
242
243
244 if id == vlist_code then
245 vlist_out(current,list)
246 else
247 hlist_out(current,list)
248 end
249
250
251
252 end
253 cur_h = cur_h + width
254
255 elseif id == kern_code then
256 local kern, factor = getkern(current,true)
257 if kern ~= 0 then
258 if factor ~= 0 then
259 cur_h = cur_h + (1.0 + factor/1000000.0) * kern
260 else
261 cur_h = cur_h + kern
262 end
263 end
264 elseif id == rule_code then
265 cur_h = cur_h + getwidth(current)
266 elseif id == math_code then
267
268cur_h = cur_h + effectiveglue(current,this_box,true)
269 elseif id == dir_code then
270 local dir, cancel = getdirection(current)
271 if cancel then
272 local ds = dirstack[current]
273 if ds then
274 ref_h = ds.ref_h
275 ref_v = ds.ref_v
276 cur_h = ds.cur_h
277 end
278 pos_r = dir
279 else
280 local width, enddir = dirdimensions(this_box,current)
281 local new_h = cur_h + width
282 if dir ~= pos_r then
283 cur_h = new_h
284 end
285 if enddir ~= current then
286 dirstack[enddir] = {
287 cur_h = new_h,
288 ref_h = ref_h,
289 ref_v = ref_v,
290 }
291 setdirection(enddir,pos_r)
292 end
293 if pos_r == righttoleft_code then
294 pos_h = ref_h - cur_h
295 else
296 pos_h = ref_h + cur_h
297 end
298 pos_v = ref_v
299 ref_h = pos_h
300 ref_v = pos_v
301 cur_h = 0
302 pos_r = dir
303 goto synced
304 end
305 elseif id == whatsit_code then
306 if subtype == userdefined_code then
307 local action = userdefined[getprop(current,"id")]
308 if action then
309 if not cur_b then
310 local wd, ht, dp = getwhd(this_box)
311 cur_b = { wd, ht, dp, ref_h, ref_v, ref_r }
312 end
313 action(current, pos_h,pos_v, cur_b)
314 end
315 end
316 elseif id == disc_code then
317 local replace, tail = getreplace(current)
318 if replace then
319 setlink(tail,getnext(current))
320 setlink(current,replace)
321 setreplace(current)
322 end
323 else
324 goto synced
325 end
326 if pos_r == righttoleft_code then
327 pos_h = ref_h - cur_h
328 else
329 pos_h = ref_h + cur_h
330 end
331 pos_v = ref_v
332 ::synced::
333 end
334 pos_h = ref_h
335 pos_v = ref_v
336 pos_r = ref_r
337end
338
339vlist_out = function(this_box,current)
340 local ref_h = pos_h
341 local ref_v = pos_v
342 local ref_r = pos_r
343 pos_r = getdirection(this_box)
344 local cur_v = - getheight(this_box)
345 local top_edge = cur_v
346 pos_h = ref_h
347 pos_v = ref_v - cur_v
348 local cur_b
349
350 for current, id, subtype in nextnode, current do
351 if id == glue_code then
352
353cur_v = cur_v + effectiveglue(current,this_box,true)
354 elseif id == hlist_code or id == vlist_code then
355 local width, height, depth = getwhd(current)
356 local list = getlist(current)
357 if list then
358 local boxdir = getdirection(current) or lefttoright_code
359 local shift = getshift(current)
360 local geometry, hasoffset, hasorientation, hasanchor = getgeometry(current,true)
361 local anchor, source, target, targetdata, s_anchor, t_anchor
362 local usedorientation = false
363 if hasanchor then
364 anchor, source, target, s_anchor, t_anchor = getanchors(current)
365 end
366 if hasorientation then
367 local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
368 local orientation, basepoint_h, basepoint_v = applyorientation(orientation,shift,height,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
369 if orientation == 1 then
370 basepoint_h = basepoint_h + width - height
371 basepoint_v = basepoint_v - height
372 usedorientation = orientation
373 elseif orientation == 2 then
374 basepoint_h = basepoint_h + width
375 basepoint_v = basepoint_v + depth - height
376 usedorientation = orientation
377 elseif orientation == 3 then
378 basepoint_h = basepoint_h + height
379 usedorientation = orientation
380 end
381 if target then
382 targetdata = anchors[target]
383 if targetdata then
384 if pos_r == righttoleft_code then
385 pos_h = targetdata[1] - basepoint_h
386 else
387 pos_h = targetdata[1] + basepoint_h
388 end
389 pos_v = targetdata[2] - basepoint_v
390 goto posdone
391 end
392 end
393 if pos_r == righttoleft_code then
394 pos_h = ref_h - basepoint_h
395 else
396 pos_h = ref_h + basepoint_h
397 end
398 pos_v = ref_v - (cur_v + basepoint_v)
399 elseif hasoffset then
400 local orientation, xoffset, yoffset = getorientation(current)
401
402
403 if boxdir ~= pos_r then
404 shift = shift + width
405 end
406 if target then
407 targetdata = anchors[target]
408 if targetdata then
409 if pos_r == righttoleft_code then
410 pos_h = targetdata[1] - (shift + xoffset)
411 else
412 pos_h = targetdata[1] + (shift + xoffset)
413 end
414 pos_v = targetdata[2] - (height - yoffset)
415 goto posdone
416 end
417 end
418 if pos_r == righttoleft_code then
419 pos_h = ref_h - (shift + xoffset)
420 else
421 pos_h = ref_h + (shift + xoffset)
422 end
423 pos_v = ref_v - (cur_v + height - yoffset)
424 elseif hasanchor then
425
426
427 if boxdir ~= pos_r then
428 shift = shift + width
429 end
430 if target then
431 local a = anchors[target]
432 if a then
433 if pos_r == righttoleft_code then
434 pos_h = targetdata[1] - shift
435 else
436 pos_h = targetdata[1] + shift
437 end
438 pos_v = targetdata[2] - height
439 goto posdone
440 end
441 end
442 if pos_r == righttoleft_code then
443 pos_h = ref_h - shift
444 else
445 pos_h = ref_h + shift
446 end
447 pos_v = ref_v - (cur_v + height)
448 else
449
450
451 if boxdir ~= pos_r then
452 shift = shift + width
453 end
454 if pos_r == righttoleft_code then
455 pos_h = ref_h - shift
456 else
457 pos_h = ref_h + shift
458 end
459 pos_v = ref_v - (cur_v + height)
460 end
461 goto process
462 ::posdone::
463 if anchor and anchor > 0 then
464 pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5])
465 pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth)
466 end
467 ::process::
468 if source then
469
470 local anchor_h = pos_h
471 local anchor_v = pos_v
472 if usedorientation then
473 if usedorientation == 1 then
474 anchor_v = anchor_v - (width - height)
475 elseif usedorientation == 2 then
476 anchor_v = anchor_v - (depth - height)
477 elseif usedorientation == 3 then
478 anchor_v = anchor_v + (height - width)
479 end
480 end
481 anchors[source] = { anchor_h, anchor_v, width, height, depth }
482 end
483
484
485
486
487
488
489 if id == vlist_code then
490 vlist_out(current,list)
491 else
492 hlist_out(current,list)
493 end
494
495
496
497
498
499
500 end
501 cur_v = cur_v + height + depth
502
503 elseif id == kern_code then
504 cur_v = cur_v + getkern(current)
505 elseif id == rule_code then
506 local width, height, depth = getwhd(current)
507 cur_v = cur_v + height + depth
508 elseif id == whatsit_code then
509 if subtype == userdefined_code then
510 local action = userdefined[getprop(current,"id")]
511 if action then
512 if not cur_b then
513 local wd, ht, dp = getwhd(this_box)
514 cur_b = { wd, ht, dp, ref_h, ref_v, ref_r }
515 end
516 action(current,pos_h,pos_v)
517 end
518 end
519 else
520 goto synced
521 end
522 pos_h = ref_h
523 ::synced::
524 end
525 pos_h = ref_h
526 pos_v = ref_v
527 pos_r = ref_r
528end
529
530function drivers.converters.analyze(driver,box)
531
532 if not driver then
533 report("error in converter, no driver")
534 return
535 elseif box then
536 box = tonut(box)
537 else
538 report("error in converter, no box")
539 return
540 end
541
542 local width, height, depth = getwhd(box)
543 local total = height + depth
544
545 if height > maxdimen or depth > maxdimen or width > maxdimen or total > maxdimen then
546 report("error in converter, overflow")
547 return
548 end
549
550 local actions = driver.actions
551 local flushers = driver.flushers
552
553 initialize = actions.initialize
554 finalize = actions.finalize
555
556 userdefined = flushers.userdefined
557
558 reset_dir_stack()
559 reset_state()
560
561 pos_r = getdirection(box)
562 pos_v = depth
563 pos_h = pos_r == righttoleft_code and width or 0
564
565 local details = {
566 boundingbox = { 0, 0, width, total },
567 }
568
569 if initialize then
570 initialize(driver,details)
571 end
572
573 if getid(box) == vlist_code then
574 vlist_out(box,getlist(box))
575 else
576 hlist_out(box,getlist(box))
577 end
578
579 if finalize then
580 finalize(driver,details)
581 end
582
583end
584
585 |