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