driv-usr.lmt /size: 21 Kb    last modification: 2023-12-21 09:44
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----- getkerndimension   = nuts.getkerndimension
31----- getglyphdimensions = nuts.getglyphdimensions
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 -- todo: some can be combined
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 -- or id == kern_code
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--             cur_h = cur_h + effectiveglue(current,this_box)
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                 -- pos_v = ref_v - (cur_v + basepoint_v)
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 -- weird
237                            anchor_v = anchor_v + (height - width)
238                        end
239                    end
240                    anchors[source] = { anchor_h, anchor_v, width, height, depth }
241                end
242             -- if usedorientation then
243             --     pushorientation(usedorientation,pos_h,pos_v,pos_r)
244             -- end
245                if id == vlist_code then
246                    vlist_out(current,list)
247                else
248                    hlist_out(current,list)
249                end
250             -- if usedorientation then
251             --     poporientation(usedorientation,pos_h,pos_v,pos_r)
252             -- end
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--             cur_h = cur_h + effectiveglue(current,this_box)
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--             cur_v = cur_v + effectiveglue(current,this_box)
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 -- hm
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 -- weird
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                 -- local basepoint_h = shift
403                 -- local basepoint_v = height
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                 -- local basepoint_h = shift
427                 -- local basepoint_v = height
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                 -- local basepoint_h = shift
451                 -- local basepoint_v = height
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                    -- move this into apply_anchor
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 -- weird
479                            anchor_v = anchor_v + (height - width)
480                        end
481                    end
482                    anchors[source] = { anchor_h, anchor_v, width, height, depth }
483                end
484             -- if usedorientation then
485             --     pushorientation(usedorientation,pos_h,pos_v,pos_r)
486             -- end
487             -- if source then
488             --     flushstored(current,source,true)
489             -- end
490                if id == vlist_code then
491                    vlist_out(current,list)
492                else
493                    hlist_out(current,list)
494                end
495             -- if source then
496             --     flushstored(current,source,false)
497             -- end
498             -- if usedorientation then
499             --     poporientation(usedorientation,pos_h,pos_v,pos_r)
500             -- end
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