driv-shp.lmt /size: 53 Kb    last modification: 2021-10-28 13:51
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 type, next = type, next
11
12local setmetatableindex = table.setmetatableindex
13local formatters        = string.formatters
14local concat            = table.concat
15local keys              = table.keys
16local sortedhash        = table.sortedhash
17local find              = string.find
18local stripstring       = string.strip
19local sequenced         = table.sequenced
20local round             = math.round
21local nuts              = nodes.nuts
22
23local tonut             = nodes.tonut
24local tonode            = nodes.tonode
25
26local getdirection      = nuts.getdirection
27local getlist           = nuts.getlist
28local getoffsets        = nuts.getoffsets
29local getorientation    = nuts.getorientation
30local getwhd            = nuts.getwhd
31local getkern           = nuts.getkern
32local getheight         = nuts.getheight
33local getdepth          = nuts.getdepth
34----- getwidth          = nuts.getwidth
35local getnext           = nuts.getnext
36local getsubtype        = nuts.getsubtype
37local getid             = nuts.getid
38local getleader         = nuts.getleader
39----- getglue           = nuts.getglue
40local getshift          = nuts.getshift
41local getreplace        = nuts.getreplace
42local setreplace        = nuts.setreplace
43local getfont           = nuts.getfont
44local getkerndimension  = nuts.getkerndimension
45
46local setdirection      = nuts.setdirection
47local setlink           = nuts.setlink
48
49local isglyph           = nuts.isglyph
50----- nextdir           = nuts.traversers.dir
51local nextnode          = nuts.traversers.node
52
53local effectiveglue     = nuts.effectiveglue
54local dirdimensions     = nuts.dirdimensions
55
56local fonthashes        = fonts.hashes
57local fontdata          = fonthashes.identifiers
58local characters        = fonthashes.characters
59local parameters        = fonthashes.parameters
60
61local nodecodes         = nodes.nodecodes
62local whatsitcodes      = nodes.whatsitcodes
63local gluecodes         = nodes.gluecodes
64local dirvalues         = nodes.dirvalues
65local subtypes          = nodes.subtypes
66
67local lefttoright_code  = dirvalues.lefttoright
68local righttoleft_code  = dirvalues.righttoleft
69
70local glyph_code        = nodecodes.glyph
71local kern_code         = nodecodes.kern
72local glue_code         = nodecodes.glue
73local hlist_code        = nodecodes.hlist
74local vlist_code        = nodecodes.vlist
75local dir_code          = nodecodes.dir
76local disc_code         = nodecodes.disc
77local math_code         = nodecodes.math
78local rule_code         = nodecodes.rule
79local whatsit_code      = nodecodes.whatsit
80
81local leaders_code      = gluecodes.leaders
82local cleaders_code     = gluecodes.cleaders
83local xleaders_code     = gluecodes.xleaders
84local gleaders_code     = gluecodes.gleaders
85
86local spaceskip_code    = gluecodes.spaceskip
87
88local getpagedimensions = layouts.getpagedimensions
89
90local drivers           = drivers
91
92local report            = logs.reporter("drivers")
93
94---------------------------------------------------------------------------------------
95
96local lastfont       = nil
97local fontcharacters = nil
98
99local magicconstants = tex.magicconstants
100local maxdimen       = magicconstants.maxdimen
101local runningrule    = magicconstants.runningrule
102
103local pos_h          = 0
104local pos_v          = 0
105local pos_r          = lefttoright_code
106local shippingmode   = "none"
107
108local shipbox_h      = 0
109local shipbox_v      = 0
110local page_size_h    = 0
111local page_size_v    = 0
112
113local initialize
114local finalize
115local updatefontstate
116local pushorientation
117local poporientation
118local flushcharacter
119local flushfontchar
120local flushrule
121local flushliteral
122local flushwhatsit
123
124-- make local
125
126function drivers.getpos () return round(pos_h), round(pos_v) end
127function drivers.getrpos() return round(pos_h), round(pos_v), pos_r end
128function drivers.gethpos() return round(pos_h) end
129function drivers.getvpos() return round(pos_v) end
130
131-- characters
132
133local flush_character do
134
135    local stack   = setmetatableindex("table")
136    local level   = 0
137    local nesting = 0
138    local main    = 0
139
140    -- experiment (smaller page stream but might be fragile)
141
142    local tospace = false  directives.register("backends.spaces", function(v) tospace = v end)
143
144    -- todo: cache streams
145
146    local default = 16384 -- * number.dimenfactors.bp -- 65536 // 4
147
148    local vfinjectors = fonts.helpers.vfinjectors
149
150    -- current can go
151
152    local function flush_vf_packet(current,pos_h,pos_v,pos_r,font,char,data,factor,vfcommands,sx,sy)
153        if nesting > 100 then
154            return
155        elseif nesting == 0 then
156            main = font
157        end
158
159        nesting = nesting + 1
160
161        local saved_h = pos_h
162        local saved_v = pos_v
163        local saved_r = pos_r
164                pos_r = lefttoright_code
165
166        local data  = fontdata[font] -- hm, so why pass data
167        local fnt   = font
168        local fonts = data.fonts
169        local siz   = (data.parameters.factor or 1)/65536
170
171        -- An alternative where we (here) locally define handlers like this:
172        --
173        -- if not vfinjectors then
174        --     function vfinjectors.char(hpos,vpos,packet)
175        --         -- .... access: font, char, factor, sx, xy
176        --     end
177        -- end
178        --
179        -- doesn't work because accessing the parameters passed to the outer function doesn't
180        -- work as expected (so we end up in a nesting loop). I remember hitting this somewhat
181        -- unexpected feature before.
182
183        -- we assume resolved fonts: id mandate but maybe also size
184
185        local function flushchar(fnt,chr,f,e) -- can't be moved out of the function due to binding locals
186            if fnt then                       -- to the function variables etc etc ... kind of messy
187                local nest = char ~= chr or font ~= fnt
188                if fnt == 0 then
189                    fnt = main
190                end
191                -- here no current!
192                return flush_character(false,fnt,chr,factor,nest,pos_h,pos_v,pos_r,f,e)
193            else
194                return 0
195            end
196        end
197
198        local refactored = 1000000
199        for i=1,#vfcommands do
200            local packet  = vfcommands[i]
201            local command = packet[1]
202            if command == "char" then
203                local chr = packet[2]
204                local f   = packet[3]
205                local e   = packet[4]
206                pos_h = pos_h + flushchar(fnt,chr,f,e)
207            elseif command == "slot" then
208                local index = packet[2]
209                local chr   = packet[3]
210                local f     = packet[4]
211                local e     = packet[5]
212                if index == 0 then
213                    pos_h = pos_h + flushchar(font,chr,f,e)
214                else
215                    local okay = fonts and fonts[index]
216                    if okay then
217                        local fnt = okay.id
218                        if fnt then
219                            pos_h = pos_h + flushchar(fnt,chr,f,e)
220                        end
221                    else
222                        -- safeguard, we assume the font itself (often index 1)
223                        pos_h = pos_h + flushchar(font,chr,f,e)
224                    end
225                end
226            elseif command == "use" then
227                local index = packet[2]
228                if index then
229                    local fnt
230                    if index == 0 then
231                        fnt = font
232                    else
233                        local okay = fonts and fonts[index]
234                        if okay then
235                            fnt = okay.id
236                        end
237                    end
238                    if fnt then
239                        -- not efficient but ok for now as experiment
240                        local d = characters[fnt]
241                        if d then
242                            for i=3,#packet do
243                                local chr = packet[i]
244                                local dat = d[chr]
245                                if dat then
246                                    flushfontchar(fnt,chr,dat)
247                                end
248                            end
249                        end
250                    end
251                end
252            elseif command == "right" then
253                local h = packet[2] -- already scaled
254                if h ~= 0 then
255                    if factor ~= 0 then
256                        h = h + h * factor / refactored -- expansion
257                    end
258                    pos_h = pos_h + h * sx
259                end
260            elseif command == "left" then
261                local h = packet[2] -- already scaled
262                if h ~= 0 then
263                    if factor ~= 0 then
264                        h = h + h * factor / refactored -- expansion
265                    end
266                    pos_h = pos_h - h * sx
267                end
268            elseif command == "down" then
269                local v = packet[2] -- already scaled
270                if v and v ~= 0 then
271                    pos_v = pos_v - v * sy
272                end
273            elseif command == "up" then
274                local v = packet[2] -- already scaled
275                if v and v ~= 0 then
276                    pos_v = pos_v + v * sy
277                end
278            elseif command == "offset" then
279                local ph = pos_h
280                local pv = pos_v
281                local h = packet[2] or 0
282                local v = packet[3] or 0
283                local c = packet[4]
284                if h ~= 0 then
285                    if factor ~= 0 then
286                        h = h + h * factor / refactored -- expansion
287                    end
288                    pos_h = pos_h + h * sx
289                end
290                if v and v ~= 0 then
291                    pos_v = pos_v + v * sy
292                end
293                if c then
294                    flushchar(fnt,c)
295                    pos_h = ph
296                    pos_v = pv
297                end
298            elseif command == "compose" then -- for now idem
299                local ph = pos_h
300                local pv = pos_v
301                local h = packet[2] or 0
302                local v = packet[3] or 0
303                local c = packet[4]
304                if h ~= 0 then
305                    if factor ~= 0 then
306                        h = h + h * factor / refactored -- expansion
307                    end
308                    pos_h = pos_h + h * sx
309                end
310                if v and v ~= 0 then
311                    pos_v = pos_v + v * sy
312                end
313                if c then
314                    flushchar(fnt,c)
315                    pos_h = ph
316                    pos_v = pv
317                end
318            elseif command == "push" then
319                level = level + 1
320                local s = stack[level]
321                s[1] = pos_h
322                s[2] = pos_v
323            elseif command == "pop" then
324                if level > 0 then
325                    local s = stack[level]
326                    pos_h = s[1]
327                    pos_v = s[2]
328                    level = level - 1
329                end
330            elseif command == "frame" then
331                -- d:width d:height d:depth d:rulethickness b:outline b:advance b:baseline s:color
332                local width  = packet[2]
333                local height = packet[3]
334                local depth  = packet[4]
335                local wd, ht, dp
336                if width == true or height == true or depth == true then
337                    wd, ht, dp = getwhd(current,true)
338                end
339                if width == true then
340                    width = wd
341                elseif not width then
342                    width = 0
343                end
344                if height == true then
345                    height = ht
346                elseif not height then
347                    height = 0
348                end
349                if depth == true then
350                    depth = dp
351                elseif not depth then
352                    depth = 0
353                end
354                local total = height + depth
355                if width > 0 and total > 0 then
356                    if factor ~= 0 then
357                        width = width + width * factor / refactored
358                    end
359                    if width > 0 then
360                        local line    = packet[5] or default
361                        local outline = packet[6]
362                        local advance = packet[7]
363                        if outline == nil then
364                            outline = true
365                        end
366                        if advance == nil then
367                            advance = true
368                        end
369                        local baseline = outline and packet[8]
370                        local color    = packet[9] -- no longer needed probably
371                        if color then
372                            vfinjectors.startcolor(pos_h,pos_v,color) -- takes packet or string
373                        end
374                        width  = width  * sx
375                        height = height * sy
376                        depth  = depth  * sy
377                        flushspecialrule(pos_h,pos_v,pos_r,width,height,depth,line,outline,baseline)
378                        if color then
379                            vfinjectors.stopcolor()
380                        end
381                        if advance then
382                            pos_h = pos_h + width
383                        end
384                    end
385                end
386            elseif command == "rule" then
387                local size_v = packet[2]
388                local size_h = packet[3]
389                if size_h > 0 and size_v > 0 then
390                    if factor ~= 0 then
391                        size_h = size_h + size_h * factor / refactored
392                    end
393                    if size_h > 0 then
394                        size_h = size_h * sx
395                        size_v = size_v * sy
396                        flushsimplerule(pos_h,pos_v,pos_r,size_h,size_v)
397                        pos_h = pos_h + size_h
398                    end
399                end
400            elseif command == "line" then
401                local wd = packet[2] or 0
402                local ht = packet[3] or 0
403                local dp = packet[4] or 0
404                if wd > 0 and ht ~= 0 and dp ~= 0 then
405                    if factor ~= 0 then
406                        wd = wd + wd * factor / refactored
407                    end
408                    if wd > 0 then
409                        wd = wd * sx
410                        ht = ht * sy
411                        dp = dp * sy
412                        local color = packet[5] -- no longer needed probably
413                        if color then
414                            vfinjectors.startcolor(pos_h,pos_v,color) -- takes packet or string
415                        end
416                        flushsimplerule(pos_h,pos_v-dp,pos_r,wd,ht+dp)
417                        if color then
418                            vfinjectors.stopcolor()
419                        end
420                        pos_h = pos_h + wd
421                    end
422                end
423            elseif command == "font" then
424                local index = packet[2]
425                local okay  = fonts and fonts[index]
426                if okay then
427                    fnt = okay.id or fnt -- or maybe just return
428                end
429            elseif command == "lua" then
430                local code = packet[2]
431                local kind = type(code)
432                if kind ~= "function" then
433                    code = loadstring(code)
434                    kind = type(code)
435                end
436                if kind == "function" then
437                    code(font,char,pos_h,pos_v,sx,sy)
438                end
439            elseif command == "node" then
440                local h = packet[2]
441                hlist_out(h,getlist(h))
442         -- elseif command == "pdf" then
443                -- unsupported
444         -- elseif command == "pdfmode" then
445                -- unsupported
446         -- elseif command == "special" then
447                -- unsupported
448         -- elseif command == "nop"     then
449                -- just ignored
450         -- elseif command == "image"   then
451                -- unsupported, use "node"
452            else
453                local injector = vfinjectors[command]
454                if injector then
455                    injector(pos_h,pos_v,packet)
456                end
457            end
458        end
459
460        pos_h = saved_h
461        pos_v = saved_v
462        pos_r = saved_r
463
464        nesting = nesting - 1
465    end
466
467    local onetimemessage -- could be defined later (todo: make plug for this)
468
469    local getxyscales = nuts.getxyscales
470
471    flush_character = function(current,font,char,factor,vfcommands,pos_h,pos_v,pos_r,f,e)
472
473        if font ~= lastfont then
474            lastfont       = font
475            fontcharacters = characters[font]
476            updatefontstate(font)
477        end
478
479        local data = fontcharacters[char]
480        if not data then
481            if char > 0 then
482                if not onetimemessage then
483                    onetimemessage = fonts.loggers.onetimemessage
484                end
485                onetimemessage(font,char,"missing")
486            end
487            return 0, 0, 0
488        end
489
490        if vfcommands then
491            vfcommands = data.commands
492        end
493        local width, height, depth, naturalwidth, sx, sy
494        if current then
495            naturalwidth, height, depth, factor = getwhd(current,true) -- also get corrected width
496            sx, sy = getxyscales(current) -- maybe: getwhdfs
497            if factor == 0 then
498                width = naturalwidth
499            else
500             -- width = (1.0 + factor/1000000.0) * naturalwidth
501                width = naturalwidth + naturalwidth * factor/1000000.0
502             -- width = naturalwidth + naturalwidth * 0.000001 * factor
503            end
504        else
505            width  = data.width or 0
506            height = data.height or 0
507            depth  = data.depth or 0
508            naturalwidth = width
509            if not factor then
510                factor = 0
511            end
512            sx = 1
513            sy = 1
514        end
515        if pos_r == righttoleft_code then
516            pos_h = pos_h - width -- here ?
517        end
518        if vfcommands then
519            flush_vf_packet(current,pos_h,pos_v,pos_r,font,char,data,factor,vfcommands,sx,sy) -- also f ?
520        else
521            -- kind of messy that we do orientation here and offsets elsewhere .. this might change
522            local orientation = data.orientation -- 0 (none), 1, 2, 3 or 4 (none)
523            local x = data.xoffset
524            local y = data.yoffset
525            if x then
526                pos_h = pos_h + x * sx
527            end
528            if y then
529                pos_v = pos_v + y * sy
530            end
531            if orientation and (orientation == 1 or orientation == 3) then
532                -- we can get weird charactersbox tracing here
533                pushorientation(orientation,pos_h,pos_v)
534                flushcharacter(current,pos_h,pos_v,pos_r,font,char,data,f,e,factor,sx,sy) -- ,naturalwidth,width)
535                poporientation(orientation,pos_h,pos_v)
536            else
537                flushcharacter(current,pos_h,pos_v,pos_r,font,char,data,f,e,factor,sx,sy) -- ,naturalwidth,width)
538            end
539        end
540
541        return width, height, depth
542    end
543
544end
545
546-- end of characters
547
548local function reset_state()
549    pos_h         = 0
550    pos_v         = 0
551    pos_r         = lefttoright_code
552    shipbox_h     = 0
553    shipbox_v     = 0
554    shippingmode  = "none"
555    page_size_h   = 0
556    page_size_v   = 0
557end
558
559-- local function dirstackentry(t,k)
560--     local v = {
561--         cur_h = 0,
562--         cur_v = 0,
563--         ref_h = 0,
564--         ref_v = 0,
565--     }
566--     t[k] = v
567--     return v
568-- end
569--
570-- local  dirstack = setmetatableindex(dirstackentry)
571--
572-- local function reset_dir_stack()
573--     dirstack = setmetatableindex(dirstackentry)
574-- end
575
576local dirstack = { }
577
578local function reset_dir_stack()
579    dirstack = { }
580end
581
582local hlist_out, vlist_out  do
583
584    local function applyanchor(orientation,x,y,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
585        local ot = (orientation>> 0) & 0x0F
586        local ay = (orientation>> 4) & 0x0F
587        local ax = (orientation>> 8) & 0x0F
588        local of = (orientation>>12) & 0x0F
589        if ot == 4 then
590            ot, ay = 0, 1
591        elseif ot == 5 then
592            ot, ay = 0, 2
593        end
594        if ot == 0 or ot == 2 then
595            if     ax == 1 then x = x - width
596            elseif ax == 2 then x = x + width
597            elseif ax == 3 then x = x - width/2
598            elseif ax == 4 then x = x + width/2
599            end
600            if ot == 2 then
601                doffset, hoffset = hoffset, doffset
602            end
603            if     ay == 1 then y = y - doffset
604            elseif ay == 2 then y = y + hoffset
605            elseif ay == 3 then y = y + (doffset + hoffset)/2 - doffset
606            end
607        elseif ot == 1 or ot == 3 then
608            if     ay == 1 then y = y - height
609            elseif ay == 2 then y = y + height
610            elseif ay == 3 then y = y - height/2
611            end
612            if ot == 1 then
613                doffset, hoffset = hoffset, doffset
614            end
615            if     ax == 1 then x = x - width
616            elseif ax == 2 then x = x + width
617            elseif ax == 3 then x = x - width/2
618            elseif ax == 4 then x = x + width/2
619            elseif ax == 5 then x = x - hoffset
620            elseif ax == 6 then x = x + doffset
621            end
622        end
623        return ot, x + xoffset, y - yoffset
624    end
625
626    drivers.applyanchor = applyanchor
627
628    -- to be checked: begin- or enddir kan nil zijn, weird
629
630 -- local function calculate_width_to_enddir(this_box,begindir) -- can be a helper
631 --     local dir_nest = 1
632 --     local enddir   = begindir
633 --     for current, subtype in nextdir, getnext(begindir) do
634 --         if subtype == normaldir_code then -- todo
635 --             dir_nest = dir_nest + 1
636 --         else
637 --             dir_nest = dir_nest - 1
638 --         end
639 --         if dir_nest == 0 then -- does the type matter
640 --             enddir = current
641 --             local width = rangedimensions(this_box,begindir,enddir)
642 --             return enddir, width
643 --         end
644 --     end
645 --     if enddir == begindir then
646 --         local width = rangedimensions(this_box,begindir) -- ,enddir)
647 --         return enddir, width
648 --     end
649 --     return enddir, 0
650 -- end
651
652    -- check frequencies of nodes
653
654    hlist_out = function(this_box,current)
655        local ref_h = pos_h
656        local ref_v = pos_v
657        local ref_r = pos_r
658              pos_r = getdirection(this_box)
659        local boxwidth,
660              boxheight,
661              boxdepth = getwhd(this_box)
662
663        local cur_h = 0
664     -- local cur_v = 0
665
666     -- if not current then
667     --     current = getlist(this_box)
668     -- end
669
670        -- we can encounter par, boundary and penalty nodes but a special
671        -- iterator over content nodes won't save much
672        for current, id, subtype in nextnode, current do
673            if id == glyph_code then
674                local char, font = isglyph(current)
675                local x_offset, y_offset, left, right, raise = getoffsets(current)
676                if x_offset ~= 0 or y_offset ~= 0 then
677                    if pos_r == righttoleft_code then
678                        pos_h = ref_h - (cur_h + x_offset)
679                    else
680                        pos_h = ref_h + (cur_h + x_offset)
681                    end
682                 -- pos_v = ref_v - (cur_v - y_offset)
683                    pos_v = ref_v + y_offset
684                    -- synced
685                end
686                pos_v = pos_v + raise
687                pos_h = pos_h - left
688                local wd = flush_character(current,font,char,false,true,pos_h,pos_v,pos_r)
689--                 cur_h = cur_h + wd - right -- hm, no left here?
690cur_h = cur_h + wd -- see new tabulate alignment code
691            elseif id == glue_code then
692                local gluewidth = effectiveglue(current,this_box)
693                if gluewidth ~= 0 then
694                    if subtype >= leaders_code then
695                        local leader = getleader(current)
696                        if leader then
697                            local width, height, depth = getwhd(leader)
698                            if getid(leader) == rule_code then
699                                if gluewidth > 0 then
700                                    if height == runningrule then
701                                        height = boxheight
702                                    end
703                                    if depth == runningrule then
704                                        depth = boxdepth
705                                    end
706                                    local total = height + depth
707                                    if total > 0 then
708                                        if pos_r == righttoleft_code then
709                                            pos_h = pos_h - gluewidth
710                                        end
711                                        pos_v = pos_v - depth
712                                        flushrule(leader,pos_h,pos_v,pos_r,gluewidth,total,getsubtype(leader))
713                                    end
714                                    cur_h = cur_h + gluewidth
715                                end
716                            elseif width > 0 and gluewidth > 0 then
717                                local boxdir = getdirection(leader) or lefttoright_code
718                                gluewidth = gluewidth + 10
719                                local edge = cur_h + gluewidth
720                                local lx = 0
721                                if subtype == gleaders_code then
722                                    local save_h = cur_h
723                                    if pos_r == righttoleft_code then
724                                        cur_h = ref_h - shipbox_h + cur_h
725                                        cur_h = width * (cur_h // width)
726                                        cur_h = ref_h - shipbox_h - cur_h
727                                    else
728                                        cur_h = ref_h - shipbox_h - cur_h
729                                        cur_h = width * (cur_h // width)
730                                        cur_h = ref_h - shipbox_h - cur_h
731                                    end
732                                    if cur_h < save_h then
733                                        cur_h = cur_h + width
734                                    end
735                                elseif subtype == leaders_code then
736                                    local save_h = cur_h
737                                    cur_h = width * (cur_h // width)
738                                    if cur_h < save_h then
739                                        cur_h = cur_h + width
740                                    end
741                                else
742                                    lq = gluewidth / width
743                                    lr = gluewidth % width
744                                    if subtype == cleaders_code then
745                                        cur_h = cur_h + lr // 2
746                                    else
747                                        lx = lr // (lq + 1)
748                                        cur_h = cur_h + (lr - (lq - 1) * lx) // 2
749                                    end
750                                end
751                                local shift = getshift(leader)
752                                pushleaderlevel()
753                                while cur_h + width <= edge do
754                                    local basepoint_h = 0
755                                 -- local basepoint_v = shift
756                                    if boxdir ~= pos_r then
757                                        basepoint_h = boxwidth
758                                    end
759                                    -- synch_pos_with_cur(ref_h,ref_v,cur_h + basepoint_h,shift)
760                                    if pos_r == righttoleft_code then
761                                        pos_h = ref_h - (cur_h + basepoint_h)
762                                    else
763                                        pos_h = ref_h + (cur_h + basepoint_h)
764                                    end
765                                    pos_v = ref_v - shift
766                                    -- synced
767                                    if getid(leader) == vlist_code then
768                                        vlist_out(leader,getlist(leader))
769                                    else
770                                        hlist_out(leader,getlist(leader))
771                                    end
772                                    cur_h = cur_h + width + lx
773                                end
774                                popleaderlevel()
775                                cur_h = edge - 10
776                            else
777                                cur_h = cur_h + gluewidth
778                            end
779                        else
780                            cur_h = cur_h + gluewidth
781                        end
782                    else
783                        if tospace and subtype == spaceskip_code then
784                            -- todo: flush_space
785                            flush_character(false,getfont(current),32,false,true,pos_h,pos_v,pos_r) -- we need tp pass current for scale?
786                        end
787                        cur_h = cur_h + gluewidth
788                    end
789                end
790            elseif id == hlist_code or id == vlist_code then
791                local width, height, depth = getwhd(current)
792                local list = getlist(current)
793                if list then
794                    local boxdir = getdirection(current) or lefttoright_code
795                    local shift, orientation = getshift(current)
796                    if not orientation then
797                        local basepoint_h = boxdir ~= pos_r and width or 0
798                     -- local basepoint_v = shift
799                        if pos_r == righttoleft_code then
800                            pos_h = ref_h - (cur_h + basepoint_h)
801                        else
802                            pos_h = ref_h + (cur_h + basepoint_h)
803                        end
804                        pos_v = ref_v - shift
805                        -- synced
806                        if id == vlist_code then
807                            vlist_out(current,list)
808                        else
809                            hlist_out(current,list)
810                        end
811                    elseif orientation == 0x1000 then
812                        local orientation, xoffset, yoffset = getorientation(current)
813                        local basepoint_h = boxdir ~= pos_r and width or 0
814                     -- local basepoint_v = shift
815                        if pos_r == righttoleft_code then
816                            pos_h = ref_h - (cur_h + basepoint_h + xoffset)
817                        else
818                            pos_h = ref_h + (cur_h + basepoint_h + xoffset)
819                        end
820                        pos_v = ref_v - (shift - yoffset)
821                        -- synced
822                        if id == vlist_code then
823                            vlist_out(current,list)
824                        else
825                            hlist_out(current,list)
826                        end
827                    else
828                        local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
829                        local orientation, basepoint_h, basepoint_v = applyanchor(orientation,0,shift,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
830                        if orientation == 1 then
831                            basepoint_h = basepoint_h + doffset
832                            if boxdir == pos_r then
833                                basepoint_v = basepoint_v - height
834                            end
835                        elseif orientation == 2 then
836                            if boxdir == pos_r then
837                                basepoint_h = basepoint_h + width
838                            end
839                        elseif orientation == 3 then
840                            basepoint_h = basepoint_h + hoffset
841                            if boxdir ~= pos_r then
842                                basepoint_v = basepoint_v - height
843                            end
844                        end
845                        if pos_r == righttoleft_code then
846                            pos_h = ref_h - (cur_h + basepoint_h)
847                        else
848                            pos_h = ref_h + (cur_h + basepoint_h)
849                        end
850                     -- pos_v = ref_v - (cur_v + basepoint_v)
851                        pos_v = ref_v - basepoint_v
852                        -- synced
853                        pushorientation(orientation,pos_h,pos_v,pos_r)
854                        if id == vlist_code then
855                            vlist_out(current,list)
856                        else
857                            hlist_out(current,list)
858                        end
859                        poporientation(orientation,pos_h,pos_v,pos_r)
860                    end
861                end
862                cur_h = cur_h + width
863            elseif id == kern_code then
864                -- we can use getkerndimension(current) but then we get rounded values so for
865                -- now we calculate ourselves
866                local kern, factor = getkern(current,true)
867                if kern ~= 0 then
868                    if factor ~= 0 then
869                        cur_h = cur_h + (1.0 + factor/1000000.0) * kern
870                    else
871                        cur_h = cur_h + kern
872                    end
873                end
874            elseif id == rule_code then
875                local width, height, depth = getwhd(current)
876                if width > 0 then
877                    if height == runningrule then
878                        height = boxheight
879                    end
880                    if depth == runningrule then
881                        depth = boxdepth
882                    end
883                    local total = height + depth
884                    if total > 0 then
885                        local xoffset, yoffset, left, right = getoffsets(current) -- top bottom
886                        if left ~= 0 then
887                            pos_v = pos_v + left
888                            total = total - left
889                        end
890                        if right ~= 0 then
891                            depth = depth - right
892                            total = total - right
893                        end
894                        if pos_r == righttoleft_code then
895                            pos_h   = pos_h - width
896                            xoffset = - xoffset
897                        end
898                        pos_v = pos_v - depth
899                        flushrule(current,pos_h + xoffset,pos_v + yoffset,pos_r,width,total,subtype)
900                    end
901                end
902                -- move into above if
903                cur_h = cur_h + width
904            elseif id == math_code then
905             -- local kern = getkern(current)
906             -- if kern ~= 0 then
907             --     cur_h = cur_h + kern
908             -- else
909                  cur_h = cur_h + effectiveglue(current,this_box)
910             -- end
911            elseif id == dir_code then
912             -- We normally have proper begin-end pairs. A begin without end is (silently) handled
913             -- and an end without a begin will be (silently) skipped we only need to move forward
914             -- so we then have a faster calculation.
915                local dir, cancel = getdirection(current)
916                if cancel then
917                    local ds = dirstack[current]
918                    if ds then
919                        ref_h = ds.ref_h
920                        ref_v = ds.ref_v
921                        cur_h = ds.cur_h
922                     -- cur_v = ds.cur_v
923                    else
924                        -- pardir
925                    end
926                    pos_r = dir
927                else
928                    local width, enddir = dirdimensions(this_box,current)
929                    local new_h = cur_h + width
930                    if dir ~= pos_r then
931                        cur_h = new_h
932                    end
933                    if enddir ~= current then
934                        dirstack[enddir] = {
935                            cur_h = new_h,
936                         -- cur_v = cur_v,
937                            ref_h = ref_h,
938                            ref_v = ref_v,
939                        }
940                        setdirection(enddir,pos_r)
941                    end
942                    if pos_r == righttoleft_code then
943                        pos_h = ref_h - cur_h
944                    else
945                        pos_h = ref_h + cur_h
946                    end
947                 -- pos_v = ref_v - cur_v
948                    pos_v = ref_v
949                    -- synced
950                    ref_h = pos_h
951                    ref_v = pos_v
952                    cur_h = 0
953                 -- cur_v = 0
954                    pos_r = dir
955                    goto synced
956                end
957            elseif id == whatsit_code then
958                flushwhatsit[subtype](current,pos_h,pos_v)
959            elseif id == disc_code then
960                local replace, tail = getreplace(current)
961                -- actually, we no longer have these select discs in the packaged list ... it's about
962                -- time to clean up the disc code a bit further
963                if replace then -- and subtype ~= select_disc_code then
964                    setlink(tail,getnext(current))
965                    setlink(current,replace)
966                    setreplace(current)
967                end
968         -- elseif id == par_code and startofpar(current) then
969         --     local pardir = getdirection(current) or lefttoright_code
970         --     if pardir == righttoleft_code then
971         --     end
972         -- end
973            else
974                -- penalty, boundary ... no dimensions
975                goto synced
976            end
977            -- There is no gain in skipping over this when we have zero progression
978            -- and such.
979            if pos_r == righttoleft_code then
980                pos_h = ref_h - cur_h
981            else
982                pos_h = ref_h + cur_h
983            end
984         -- pos_v = ref_v - cur_v
985            pos_v = ref_v
986            ::synced::
987        end
988        pos_h = ref_h
989        pos_v = ref_v
990        pos_r = ref_r
991    end
992
993    vlist_out = function(this_box,current)
994        local ref_h = pos_h
995        local ref_v = pos_v
996        local ref_r = pos_r
997              pos_r = getdirection(this_box)
998
999        local boxwidth,
1000              boxheight,
1001              boxdepth = getwhd(this_box)
1002
1003        local cur_h    = 0 -- needs checking .. needed ?
1004        local cur_v    = - boxheight
1005        local top_edge = cur_v
1006
1007     -- if pos_r == righttoleft_code then
1008     --     pos_h = ref_h - cur_h
1009     -- else
1010     --     pos_h = ref_h + cur_h
1011     -- end
1012        pos_h = ref_h
1013        pos_v = ref_v - cur_v
1014        -- synced
1015
1016     -- if not current then
1017     --     current = getlist(this_box)
1018     -- end
1019
1020     -- while current do
1021     --     local id = getid(current)
1022        for current, id, subtype in nextnode, current do
1023            if id == glue_code then
1024                local glueheight = effectiveglue(current,this_box)
1025                if glueheight ~= 0 then
1026                    if subtype >= leaders_code then
1027                        local leader = getleader(current)
1028                        if leader then
1029                            local width, height, depth = getwhd(leader)
1030                            local total = height + depth
1031                            if getid(leader) == rule_code then
1032                                depth = 0 -- hm
1033                                total = glueheight -- forgotten ... needs testing
1034                                if total > 0 then
1035                                    if width == runningrule then
1036                                        width = boxwidth
1037                                    end
1038                                    if width > 0 then
1039                                        if pos_r == righttoleft_code then
1040                                            cur_h = cur_h - width
1041                                        end
1042                                        flushrule(leader,pos_h,pos_v - total,pos_r,width,total,getsubtype(leader))
1043                                    end
1044                                    cur_v = cur_v + total
1045                                end
1046                            elseif total > 0 and glueheight > 0 then
1047                                glueheight = glueheight + 10
1048                                local edge = cur_v + glueheight
1049                                local ly   = 0
1050                                if subtype == gleaders_code then
1051                                    save_v = cur_v
1052                                    cur_v  = ref_v - shipbox_v - cur_v
1053                                    cur_v  = total * (cur_v // total)
1054                                    cur_v  = ref_v - shipbox_v - cur_v
1055                                    if cur_v < save_v then
1056                                        cur_v = cur_v + total
1057                                    end
1058                                elseif subtype == leaders_code then -- aleader
1059                                    save_v = cur_v
1060                                    cur_v = top_edge + total * ((cur_v - top_edge) // total)
1061                                    if cur_v < save_v then
1062                                        cur_v = cur_v + total
1063                                    end
1064                                else
1065                                    lq = glueheight / total
1066                                    lr = glueheight % total
1067                                    if subtype == cleaders_code then
1068                                        cur_v = cur_v + lr // 2
1069                                    else
1070                                        ly = lr // (lq + 1)
1071                                        cur_v = cur_v + (lr - (lq - 1) * ly) // 2
1072                                    end
1073                                end
1074                                local shift = getshift(leader)
1075                                pushleaderlevel()
1076                                while cur_v + total <= edge do -- todo: <= edge - total
1077                                    -- synch_pos_with_cur(ref_h, ref_v, getshift(leader), cur_v + height)
1078                                    if pos_r == righttoleft_code then
1079                                        pos_h = ref_h - shift
1080                                    else
1081                                        pos_h = ref_h + shift
1082                                    end
1083                                    pos_v = ref_v - (cur_v + height)
1084                                    -- synced
1085                                    if getid(leader) == vlist_code then
1086                                        vlist_out(leader,getlist(leader))
1087                                    else
1088                                        hlist_out(leader,getlist(leader))
1089                                    end
1090                                    cur_v = cur_v + total + ly
1091                                end
1092                                popleaderlevel()
1093                                cur_v = edge - 10
1094                            else
1095                                cur_v = cur_v + glueheight
1096                            end
1097                        end
1098                    else
1099                        cur_v = cur_v + glueheight
1100                    end
1101                end
1102            elseif id == hlist_code or id == vlist_code then
1103                local width, height, depth = getwhd(current)
1104                local list = getlist(current)
1105                if list then
1106                    local boxdir = getdirection(current) or lefttoright_code
1107                    local shift, orientation = getshift(current)
1108                    if not orientation then
1109                     -- local basepoint_h = shift
1110                     -- local basepoint_v = height
1111                        if boxdir ~= pos_r then
1112                            shift = shift + width
1113                        end
1114                        if pos_r == righttoleft_code then
1115                            pos_h = ref_h - shift
1116                        else
1117                            pos_h = ref_h + shift
1118                        end
1119                        pos_v = ref_v - (cur_v + height)
1120                        -- synced
1121                        if id == vlist_code then
1122                            vlist_out(current,list)
1123                        else
1124                            hlist_out(current,list)
1125                        end
1126                    elseif orientation == 0x1000 then
1127                        local orientation, xoffset, yoffset = getorientation(current)
1128                     -- local basepoint_h = shift
1129                     -- local basepoint_v = height
1130                        if boxdir ~= pos_r then
1131                            shift = shift + width
1132                        end
1133                        if pos_r == righttoleft_code then
1134                            pos_h = ref_h - (shift + xoffset)
1135                        else
1136                            pos_h = ref_h + (shift + xoffset)
1137                        end
1138                        pos_v = ref_v - (cur_v + height - yoffset)
1139                        -- synced
1140                        if id == vlist_code then
1141                            vlist_out(current,list)
1142                        else
1143                            hlist_out(current,list)
1144                        end
1145                    else
1146                        local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current)
1147                        local orientation, basepoint_h, basepoint_v = applyanchor(orientation,shift,height,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset)
1148                        if orientation == 1 then
1149                            basepoint_h = basepoint_h + width - height
1150                            basepoint_v = basepoint_v - height
1151                        elseif orientation == 2 then
1152                            basepoint_h = basepoint_h + width
1153                            basepoint_v = basepoint_v + depth - height
1154                        elseif orientation == 3 then -- weird
1155                            basepoint_h = basepoint_h + height
1156                        end
1157                        if pos_r == righttoleft_code then
1158                            pos_h = ref_h - basepoint_h
1159                        else
1160                            pos_h = ref_h + basepoint_h
1161                        end
1162                        pos_v = ref_v - (cur_v + basepoint_v)
1163                        -- synced
1164                        pushorientation(orientation,pos_h,pos_v,pos_r)
1165                        if id == vlist_code then
1166                            vlist_out(current,list)
1167                        else
1168                            hlist_out(current,list)
1169                        end
1170                        poporientation(orientation,pos_h,pos_v,pos_r)
1171                    end
1172                end
1173                cur_v = cur_v + height + depth
1174            elseif id == kern_code then
1175                cur_v = cur_v + getkern(current)
1176            elseif id == rule_code then
1177                local width, height, depth = getwhd(current)
1178                local total = height + depth
1179                if total > 0 then
1180                    if width == runningrule then
1181                        width = boxwidth
1182                    end
1183                    if width > 0 then
1184                        local xoffset, yoffset, left, right = getoffsets(current)
1185                        if left ~= 0 then
1186                            width = width - left
1187                            xoffset = left
1188                        end
1189                        if right ~= 0 then
1190                            width = width - right
1191                        end
1192                        if pos_r == righttoleft_code then
1193                            xoffset = - xoffset - width
1194                        end
1195                        flushrule(current,pos_h + xoffset,pos_v - total - yoffset,pos_r,width,total,subtype)
1196                    end
1197                end
1198                cur_v = cur_v + total
1199            elseif id == whatsit_code then
1200                flushwhatsit[subtype](current,pos_h,pos_v)
1201            else
1202                -- penalty
1203                goto synced
1204            end
1205            if pos_r == righttoleft_code then
1206                pos_h = ref_h - cur_h
1207            else
1208                pos_h = ref_h + cur_h
1209            end
1210            pos_v = ref_v - cur_v
1211            ::synced::
1212        end
1213        pos_h = ref_h
1214        pos_v = ref_v
1215        pos_r = ref_r
1216    end
1217
1218end
1219
1220function drivers.converters.lmtx(driver,box,smode,objnum,specification)
1221
1222    if not driver then
1223        report("error in converter, no driver")
1224        return
1225    end
1226
1227    if box then
1228        box = tonut(box)
1229    else
1230        report("error in converter, no box")
1231        return
1232    end
1233
1234    local actions     = driver.actions
1235    local flushers    = driver.flushers
1236
1237    initialize        = actions.initialize
1238    finalize          = actions.finalize
1239
1240    updatefontstate   = flushers.updatefontstate
1241
1242    pushorientation   = flushers.pushorientation
1243    poporientation    = flushers.poporientation
1244
1245    pushleaderlevel   = flushers.pushleaderlevel
1246    popleaderlevel    = flushers.popleaderlevel
1247
1248    flushcharacter    = flushers.character
1249    flushfontchar     = flushers.fontchar
1250    flushrule         = flushers.rule
1251    flushsimplerule   = flushers.simplerule
1252    flushspecialrule  = flushers.specialrule
1253    flushliteral      = flushers.literal
1254    flushwhatsit      = flushers.whatsit
1255
1256    reset_dir_stack()
1257    reset_state()
1258
1259    shippingmode = smode
1260
1261    local details = nil -- must be outside labels
1262
1263    local width, height, depth = getwhd(box)
1264
1265    local total = height + depth
1266
1267    if height > maxdimen or depth > maxdimen or width > maxdimen or total > maxdimen then
1268        goto DONE
1269    end
1270
1271    if shippingmode == "page" then
1272
1273        -- We have zero offsets in ConTeXt.
1274
1275        local pagewidth, pageheight = getpagedimensions()
1276
1277        pos_r = lefttoright_code
1278
1279        if pagewidth > 0 then
1280            page_size_h = pagewidth
1281        else
1282            page_size_h = width
1283        end
1284
1285        if page_size_h == 0 then
1286            page_size_h = width
1287        end
1288
1289        if pageheight > 0 then
1290            page_size_v = pageheight
1291        else
1292            page_size_v = total
1293        end
1294
1295        if page_size_v == 0 then
1296            page_size_v = total
1297        end
1298
1299        local refpoint_h = 0
1300        local refpoint_v = page_size_v
1301
1302        pos_h = refpoint_h
1303        pos_v = refpoint_v - height
1304
1305    else
1306
1307        page_size_h = width
1308        page_size_v = total
1309        pos_r       = getdirection(box)
1310        pos_v       = depth
1311        pos_h       = pos_r == righttoleft_code and width or 0
1312
1313    end
1314
1315    shipbox_ref_h = pos_h
1316    shipbox_ref_v = pos_v
1317
1318    details = {
1319        shippingmode  = smode, -- target
1320        boundingbox   = { 0, 0, page_size_h, page_size_v },
1321        objectnumber  = smode ~= "page" and objnum or nil,
1322        pagenumber    = smode == "page" and objnum or nil,
1323        specification = specification,
1324    }
1325
1326    initialize(driver,details)
1327
1328    lastfont = nil -- this forces a sync each page / object
1329
1330    if getid(box) == vlist_code then
1331        vlist_out(box,getlist(box))
1332    else
1333        hlist_out(box,getlist(box))
1334    end
1335
1336    ::DONE::
1337
1338    finalize(driver,details)
1339
1340    shippingmode = "none"
1341end
1342
1343-- This will move to back-out.lua eventually.
1344
1345do
1346
1347    ----- sortedhash = table.sortedhash
1348
1349    ----- tonut      = nodes.tonut
1350    local properties = nodes.properties.data
1351    local flush      = texio.write
1352    local flushline  = texio.writenl
1353
1354    local periods    = utilities.strings.newrepeater(".")
1355
1356    local f_detail_0 = formatters["%s %s = %s"]
1357    local f_detail_1 = formatters["%i: %s %s = %s"]
1358    local f_detail_2 = formatters["%i:%i: %s %s = %s"]
1359
1360    local function showdetails(n,l,tlp,l1,l2)
1361        local p = properties[tonut(n)]
1362        if p then
1363            for k, v in sortedhash(p) do
1364                local t = type(v)
1365                local p = periods[l+1]
1366                if t == "string" then
1367                    if find(v,"[\n\r]") then
1368                        v =  "\n" .. stripstring(v) .. "\n"
1369                    end
1370                elseif t == "number" or t == "boolean" then
1371                    v = tostring(v)
1372                elseif t == "table" then
1373                    v = sequenced(v)
1374                else
1375                    v = "<" .. tostring(v) .. ">"
1376                end
1377                if tlp == 3 then
1378                    flushline(f_detail_2(l1,l2,p,k,v))
1379                elseif tlp == 2 then
1380                    flushline(f_detail_1(l2,p,k,v))
1381                elseif tlp == 1 then
1382                    flushline(f_detail_1(l1,p,k,v))
1383                else
1384                    flushline(f_detail_0(p,k,v))
1385                end
1386            end
1387        end
1388    end
1389
1390    local whatsittracers = {
1391        latelua = showdetails,
1392        literal = showdetails,
1393    }
1394
1395    callback.register("show_whatsit",function(n,what,l,tlp,l1,l2)
1396        local s = nodes.whatsitcodes[n.subtype]
1397        if what == 1 then
1398            return s or "unknown"
1399     -- elseif what == 2 then
1400        else
1401            local w = whatsittracers[s]
1402            if w then
1403                w(n,l,tlp,l1,l2)
1404            end
1405        end
1406    end)
1407
1408    local names = attributes.names
1409
1410    callback.register("get_attribute",function(k,v)
1411        return attributes.names[k], nil -- we show the name and number
1412    end)
1413
1414end
1415