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