if not modules then modules = { } end modules ['driv-shp'] = { version = 1.001, optimize = true, comment = "companion to driv-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } local type, next, rawget, rawset = type, next, rawget, rawset local setmetatableindex = table.setmetatableindex local formatters = string.formatters local concat = table.concat local keys = table.keys local insert = table.insert local sortedhash = table.sortedhash local find = string.find local stripstring = string.strip local sequenced = table.sequenced local round = math.round local nuts = nodes.nuts local tonut = nodes.tonut local getdirection = nuts.getdirection local getlist = nuts.getlist local getoffsets = nuts.getoffsets local getorientation = nuts.getorientation local getanchors = nuts.getanchors local getgeometry = nuts.getgeometry local getxyscales = nuts.getxyscales local getwhd = nuts.getwhd local getkern = nuts.getkern local getheight = nuts.getheight local getdepth = nuts.getdepth ----- getwidth = nuts.getwidth local getnext = nuts.getnext local getsubtype = nuts.getsubtype local getid = nuts.getid local getleader = nuts.getleader ----- getglue = nuts.getglue local getshift = nuts.getshift local getreplace = nuts.getreplace local setreplace = nuts.setreplace local getfont = nuts.getfont local getboth = nuts.getboth local getglyphdimensions = nuts.getglyphdimensions local getkerndimension = nuts.getkerndimension local getlistdimensions = nuts.getlistdimensions local getruledimensions = nuts.getruledimensions local setdirection = nuts.setdirection local setlink = nuts.setlink local isglyph = nuts.isglyph ----- nextdir = nuts.traversers.dir local nextnode = nuts.traversers.node local effectiveglue = nuts.effectiveglue local dirdimensions = nuts.dirdimensions local fonthashes = fonts.hashes local fontdata = fonthashes.identifiers local characters = fonthashes.characters local parameters = fonthashes.parameters local nodecodes = nodes.nodecodes local whatsitcodes = nodes.whatsitcodes local gluecodes = nodes.gluecodes local subtypes = nodes.subtypes local lefttoright_code = tex.directioncodes.lefttoright local righttoleft_code = tex.directioncodes.righttoleft local glyph_code = nodecodes.glyph local kern_code = nodecodes.kern local glue_code = nodecodes.glue local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local dir_code = nodecodes.dir local disc_code = nodecodes.disc local math_code = nodecodes.math local rule_code = nodecodes.rule local whatsit_code = nodecodes.whatsit local virtualrule_code = nodes.rulecodes.virtual local leaders_code = gluecodes.leaders local cleaders_code = gluecodes.cleaders local xleaders_code = gluecodes.xleaders local gleaders_code = gluecodes.gleaders local spaceskip_code = gluecodes.spaceskip local xspaceskip_code = gluecodes.xspaceskip local getpagedimensions = layouts.getpagedimensions local drivers = drivers local report = logs.reporter("drivers") --------------------------------------------------------------------------------------- -- For the moment rules need at least some height but maybe some day we let user rules -- fall through or have some way to force a rule via some property. --------------------------------------------------------------------------------------- local lastfont = nil local fontcharacters = nil local maxdimen = tex.magicconstants.maxdimen local runningrule = tex.magicconstants.runningrule local pos_h = 0 local pos_v = 0 local pos_r = lefttoright_code local shippingmode = "none" local shipbox_h = 0 local shipbox_v = 0 local page_size_h = 0 local page_size_v = 0 local initialize local finalize local updatefontstate local pushorientation local poporientation local flushcharacter local flushfontchar local flushrule local flushliteral local flushwhatsit local flushspace local flushsimplerule local flushspecialrule local pushleaderlevel local popleaderlevel local pushgroup local popgroup -- make local function drivers.getpos () return round(pos_h), round(pos_v) end function drivers.gethvrpos() return round(pos_h), round(pos_v), pos_r end function drivers.gethpos () return round(pos_h) end function drivers.getvpos () return round(pos_v) end function drivers.getrpos () return pos_r end -- characters -- experiment (smaller page stream but might be fragile) local tospace = false directives.register("backends.spaces", function(v) tospace = v end) local flush_character, flush_space do local stack = setmetatableindex("table") local level = 0 local nesting = 0 local main = 0 -- todo: cache streams local default = 16384 -- * number.dimenfactors.bp -- 65536 // 4 local refactored = 1000000 -- expansion related local vfinjectors = fonts.helpers.vfinjectors -- current can go -- local alternative = false -- more local, can be an option: vf.commands.local local function flush_vf_packet(current,pos_h,pos_v,pos_r,font,char,data,csx,csy,factor,sx,sy,slant,weight,vfcommands) if nesting > 100 then return elseif nesting == 0 then main = font -- if alternative then -- local s = stack[0] -- s[1] = pos_h -- s[2] = pos_v -- s[3] = pos_r -- end else -- if alternative then -- local s = stack[0] -- pos_h = s[1] -- pos_v = s[2] -- pos_r = s[3] -- end end nesting = nesting + 1 local savedlevel = level local function push() level = level + 1 local s = stack[level] s[1] = pos_h s[2] = pos_v s[3] = pos_r end local function pop() if level > 0 then local s = stack[level] pos_h = s[1] pos_v = s[2] pos_r = s[3] level = level - 1 end end -- push() -- or: local saved_h = pos_h local saved_v = pos_v local saved_r = pos_r pos_r = lefttoright_code local fdata = fontdata[font] -- offsets etc local fnt = font local fonts = fdata.fonts local siz = (fdata.parameters.factor or 1)/65536 -- An alternative where we (here) locally define handlers like this: -- -- if not vfinjectors then -- function vfinjectors.char(hpos,vpos,packet) -- -- .... access: font, char, factor, sx, xy -- end -- end -- -- doesn't work because accessing the parameters passed to the outer function doesn't -- work as expected (so we end up in a nesting loop). I remember hitting this somewhat -- unexpected feature before. local scale = data.scale local xoffset = data.xoffset local yoffset = data.yoffset if scale then sx = scale * sx sy = scale * sy end -- -- example of usage needed (nested vf) if csx then sx = sx * csx csx = 1 end if csy then sy = sy * csy csy = 1 end if xoffset and xoffset ~= 0 then if factor ~= 0 then xoffset = xoffset + xoffset * factor / refactored -- expansion end pos_h = pos_h + xoffset * sx end if yoffset and yoffset ~= 0 then pos_v = pos_v + yoffset * sy end -- we assume resolved fonts: id mandate but maybe also size local function flushchar(fnt,chr,csx,csy,slnt) -- can't be moved out of the function due to binding locals if fnt then -- to the function variables etc etc ... kind of messy local nest = char ~= chr or font ~= fnt if fnt == 0 then fnt = main end if csx then csx = csx * sx else csx = sx end if csy then csy = csy * sy else csy = sy end -- here no current! -- return flushcharacter(false,pos_h,pos_v,pos_r,fnt,chr,nil,csx,csy,factor,sx,sy,slant,weight) return flush_character(false,fnt,chr,factor,nest,pos_h,pos_v,pos_r,csx,csy,slnt or slant,weight) else return 0 end end -- virtual t3 fonts have negative font index for i=1,#vfcommands do local packet = vfcommands[i] if packet then local command = packet[1] if command == "char" then local chr = packet[2] local csx = packet[3] local csy = packet[4] or csx pos_h = pos_h + flushchar(fnt,chr,csx,csy) * sx elseif command == "slot" then local index = packet[2] local chr = packet[3] local csx = packet[4] local csy = packet[5] or csx if index == 0 then pos_h = pos_h + flushchar(font,chr,csx,csy) * sx else local okay = fonts and fonts[index] if okay then local fnt = okay.id if fnt then if fnt == 0 then fnt = font end pos_h = pos_h + flushchar(fnt,chr,csx,csy) * sx end else -- safeguard, we assume the font itself (often index 1) pos_h = pos_h + flushchar(font,chr,csx,csy) * sx end end elseif command == "use" then local index = packet[2] if index then local fnt if index == 0 then fnt = font else local okay = fonts and fonts[index] if okay then fnt = okay.id end end if fnt then -- not efficient but ok for now as experiment local d = characters[fnt] if d then for i=3,#packet do local chr = packet[i] local dat = d[chr] if dat then flushfontchar(fnt,chr,dat) end end end end end elseif command == "right" then local h = packet[2] -- already scaled if h ~= 0 then if factor ~= 0 then h = h + h * factor / refactored -- expansion end pos_h = pos_h + h * sx end elseif command == "left" then local h = packet[2] -- already scaled if h ~= 0 then if factor ~= 0 then h = h + h * factor / refactored -- expansion end pos_h = pos_h - h * sx end elseif command == "down" then local v = packet[2] -- already scaled if v and v ~= 0 then pos_v = pos_v - v * sy end elseif command == "up" then local v = packet[2] -- already scaled if v and v ~= 0 then pos_v = pos_v + v * sy end elseif command == "offset" then local c = packet[4] if c then local ph = pos_h local pv = pos_v local csx = packet[5] local csy = packet[6] or csx local h = packet[2] local v = packet[3] local slnt = packet[7] or false if h and h ~= 0 then if factor ~= 0 then h = h + h * factor / refactored -- expansion end pos_h = pos_h + h * sx end if v and v ~= 0 then pos_v = pos_v + v * sy end flushchar(fnt,c,csx,csy,slnt) pos_h = ph pos_v = pv end elseif command == "stay" then -- we can do a fast one if needed, it's more an experiment push() flushchar(font,packet[2],1,1) pop() elseif command == "compose" then -- for now idem local ph = pos_h local pv = pos_v local h = packet[2] or 0 local v = packet[3] or 0 local c = packet[4] if h ~= 0 then if factor ~= 0 then h = h + h * factor / refactored -- expansion end pos_h = pos_h + h * sx end if v and v ~= 0 then pos_v = pos_v + v * sy end if c then flushchar(fnt,c) pos_h = ph pos_v = pv end elseif command == "push" then push() elseif command == "pop" then pop() elseif command == "frame" then -- d:width d:height d:depth d:rulethickness b:outline b:advance b:baseline s:color local width = packet[2] local height = packet[3] local depth = packet[4] local wd, ht, dp if not current then -- can be a space injected instead of glue elseif width == true or height == true or depth == true then wd, ht, dp = getwhd(current,true) end if width == true then width = wd or 0 elseif not width then width = 0 end if height == true then height = ht or 0 elseif not height then height = 0 end if depth == true then depth = dp or 0 elseif not depth then depth = 0 end local total = height + depth if width > 0 and total > 0 then if factor ~= 0 then width = width + width * factor / refactored end if width > 0 then local line = packet[5] or default local outline = packet[6] local advance = packet[7] if outline == nil then outline = true end if advance == nil then advance = true end local baseline = outline and packet[8] local color = packet[9] -- no longer needed probably if color then vfinjectors.startcolor(pos_h,pos_v,color) -- takes packet or string end width = width * sx height = height * sy depth = depth * sy flushspecialrule(pos_h,pos_v,pos_r,width,height,depth,line,outline,baseline) if color then vfinjectors.stopcolor() end if advance then pos_h = pos_h + width end end end elseif command == "rule" then local size_v = packet[2] local size_h = packet[3] if size_h > 0 and size_v > 0 then if factor ~= 0 then size_h = size_h + size_h * factor / refactored end if size_h > 0 then size_h = size_h * sx size_v = size_v * sy flushsimplerule(pos_h,pos_v,pos_r,size_h,size_v) pos_h = pos_h + size_h end end elseif command == "line" then local wd = packet[2] or 0 local ht = packet[3] or 0 local dp = packet[4] or 0 if wd > 0 and ht ~= 0 and dp ~= 0 then if factor ~= 0 then wd = wd + wd * factor / refactored end if wd > 0 then wd = wd * sx ht = ht * sy dp = dp * sy local color = packet[5] -- no longer needed probably if color then vfinjectors.startcolor(pos_h,pos_v,color) -- takes packet or string end flushsimplerule(pos_h,pos_v-dp,pos_r,wd,ht+dp) if color then vfinjectors.stopcolor() end pos_h = pos_h + wd end end elseif command == "font" then local index = packet[2] local okay = fonts and fonts[index] if okay then fnt = okay.id or fnt -- or maybe just return end elseif command == "lua" then local code = packet[2] local kind = type(code) if kind ~= "function" then code = loadstring(code) kind = type(code) end if kind == "function" then code(font,char,pos_h,pos_v,sx,sy) -- maybe also packet end elseif command == "node" then local h = packet[2] hlist_out(h,getlist(h)) -- elseif command == "pdf" then -- unsupported -- elseif command == "pdfmode" then -- unsupported -- elseif command == "special" then -- unsupported -- elseif command == "nop" then -- just ignored -- elseif command == "image" then -- unsupported, use "node" elseif command == "inspect" then inspect(vfcommands) elseif command == "trace" then report("virtual state: h=%p v=%p d=%i",pos_h,pos_v,pos_r) else local injector = vfinjectors[command] if injector then injector(pos_h,pos_v,packet) -- maybe also sx, sy but then we need to check usage end end end end -- pop() -- or: pos_h = saved_h pos_v = saved_v pos_r = saved_r if savedlevel ~= level then report("") report("virtual state: stack is corrupt") report("") end level = savedlevel nesting = nesting - 1 end local onetimemessage -- could be defined later (todo: make plug for this) flush_character = function(current,font,char,factor,vfcommands,pos_h,pos_v,pos_r,csx,csy,slnt) if font ~= lastfont then lastfont = font fontcharacters = characters[font] updatefontstate(font) -- can move to lpdf-lmt, cleaner end local data = fontcharacters[char] if not data then if char > 0 then if not onetimemessage then onetimemessage = fonts.loggers.onetimemessage end onetimemessage(font,char,"missing") end return 0, 0, 0 end if vfcommands then vfcommands = data.commands end local width, height, depth, naturalwidth, sx, sy, slant, weight if current then width, height, depth, factor, sx, sy, slant, weight = getglyphdimensions(current) else width = data.width or 0 height = data.height or 0 depth = data.depth or 0 naturalwidth = width if not factor then factor = 0 end sx = 1 sy = 1 slant = 0 weight = 0 end if pos_r == righttoleft_code then pos_h = pos_h - width -- here ? end if vfcommands then flush_vf_packet(current,pos_h,pos_v,pos_r,font,char,data,csx,csy,factor,sx,sy,slant,weight,vfcommands) else local orientation = data.orientation -- 0 (none), 1, 2, 3 or 4 (none) if orientation and (orientation == 1 or orientation == 3) then -- we can get weird charactersbox tracing here pushorientation(orientation,pos_h,pos_v) flushcharacter(current,pos_h,pos_v,pos_r,font,char,data,csx,csy,factor,sx,sy,slnt or slant,weight) poporientation(orientation,pos_h,pos_v) else flushcharacter(current,pos_h,pos_v,pos_r,font,char,data,csx,csy,factor,sx,sy,slnt or slant,weight) end end return width, height, depth end flush_space = function(current,pos_h,pos_v,pos_r) local font = getfont(current) if font == lastfont then -- local char = 32 -- local data = fontcharacters[32] -- if not data then -- return -- end -- -- if pos_r == righttoleft_code then -- -- pos_h = pos_h - (data.width or 0) -- here ? -- -- end flushspace(font) end end end -- end of characters local function reset_state() pos_h = 0 pos_v = 0 pos_r = lefttoright_code shipbox_h = 0 shipbox_v = 0 shippingmode = "none" page_size_h = 0 page_size_v = 0 end -- local function dirstackentry(t,k) -- local v = { -- cur_h = 0, -- cur_v = 0, -- ref_h = 0, -- ref_v = 0, -- } -- t[k] = v -- return v -- end -- -- local dirstack = setmetatableindex(dirstackentry) -- -- local function reset_directions() -- dirstack = setmetatableindex(dirstackentry) -- end local dirstack = { } local anchors = { } local befores = setmetatableindex("table") local afters = setmetatableindex("table") local stored = false local function reset_directions() dirstack = { } end local function reset_anchors() anchors = { } end local hlist_out, vlist_out do local finalize = nodes.handlers.finalizelist local flushnode = nuts.flushnode interfaces.implement { name = "registeranchorbox", public = true, protected = true, arguments = { "integer", "integer", "box" }, actions = function(anchor,where,box) box = tonut(box) insert(where < 0 and befores[anchor] or afters[anchor],box) finalize(box,false) stored = true end } local function flushstored(current,source,before) local t = before and befores or afters local s = rawget(t,source) if s then for i=1,#s do local si = s[i] if si then local box = si -- si[1] -- tagging needs it to be earlier -- finalize(box,false) pushgroup() if getid(box) == vlist_code then vlist_out(current,box) else hlist_out(current,box) end popgroup() flushnode(box) s[i] = false end end rawset(t,source,nil) end end local function applyorientation(orientation,x,y,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset) local ot = (orientation >> 0) & 0x0F local ay = (orientation >> 4) & 0x0F local ax = (orientation >> 8) & 0x0F if ot == 4 then ot, ay = 0, 1 elseif ot == 5 then ot, ay = 0, 2 end if ot == 0 or ot == 2 then if ax == 1 then x = x - width elseif ax == 2 then x = x + width elseif ax == 3 then x = x - width/2 elseif ax == 4 then x = x + width/2 end if ot == 2 then doffset, hoffset = hoffset, doffset end if ay == 1 then y = y - doffset elseif ay == 2 then y = y + hoffset elseif ay == 3 then y = y + (doffset + hoffset)/2 - doffset end elseif ot == 1 or ot == 3 then if ay == 1 then y = y - height elseif ay == 2 then y = y + height elseif ay == 3 then y = y - height/2 end if ot == 1 then doffset, hoffset = hoffset, doffset end if ax == 1 then x = x - width elseif ax == 2 then x = x + width elseif ax == 3 then x = x - width/2 elseif ax == 4 then x = x + width/2 elseif ax == 5 then x = x - hoffset elseif ax == 6 then x = x + doffset end end return ot, x + xoffset, y - yoffset end local function applyanchor(anchor,shift,anchor_h,anchor_v,width,height,depth) local h = 0 local v = 0 local a = anchor & 0x00FF local s = anchor & 0x0F00 if a == 0x02 then v = height elseif a == 0x03 then v = - depth elseif a == 0x04 then h = width elseif a == 0x05 then h = width v = height elseif a == 0x06 then h = width v = - depth elseif a == 0x07 then h = width/2 elseif a == 0x08 then h = width/2 v = height elseif a == 0x09 then h = width/2 v = - depth elseif a == 0x0A then -- halfway_total_anchor h = width/2 v = height/2 - depth/2 elseif a == 0x0B then -- halfway_height_anchor h = width/2 v = height/2 elseif a == 0x0C then -- halfway_depth_anchor h = width/2 v = - depth/2 elseif a == 0x0D then -- halfway_left_anchor v = height/2 - depth/2 elseif a == 0x0E then -- halfway_right_anchor h = width v = height/2 - depth/2 end if not shift then h = -h v = -v end if s == 0x100 then h = -h elseif s == 0x200 then v = -v elseif s == 0x300 then h = -h v = -v else end anchor_h = anchor_h + h anchor_v = anchor_v + v return anchor_h, anchor_v end drivers.applyanchor = applyanchor drivers.applyorientation = applyorientation -- to be checked: begin- or enddir kan nil zijn, weird -- check frequencies of nodes local eps = 10 local function handle_except(except) while except do local h = pos_h local v = pos_v local r = pos_r local id = getid(except) -- if id == vlist_code then -- vlist_out(except,except) -- elseif id == hlist_code then hlist_out(except,except) -- end pos_h = h pos_v = v pos_r = r except = getnext(except) end end local hnextnode = nextnode local vnextnode = nextnode experiments.register("optimize.backend.traverse",function() if helper and helper.traversehorizontal then local glyph = nuts.pool.glyph() hnextnode = helper.traversehorizontal(glyph) vnextnode = helper.traversevertical(glyph) nuts.flushnode(glyph) end end) hlist_out = function(this_box,current) local ref_h = pos_h local ref_v = pos_v local ref_r = pos_r pos_r = getdirection(this_box) local boxwidth, boxheight, boxdepth = getwhd(this_box) local cur_h = 0 -- local cur_v = 0 -- if not current then -- current = getlist(this_box) -- end local boxr = pos_r == 1 local boxh = pos_h -- we can encounter par, boundary and penalty nodes but a special -- iterator over content nodes won't save much for current, id, subtype in hnextnode, current do if id == glyph_code then local char, font = isglyph(current) local x_offset, y_offset, left, right, raise = getoffsets(current) if x_offset ~= 0 or y_offset ~= 0 then if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + x_offset) else pos_h = ref_h + (cur_h + x_offset) end -- pos_v = ref_v - (cur_v - y_offset) pos_v = ref_v + y_offset -- synced end pos_v = pos_v + raise pos_h = pos_h - left local wd = flush_character(current,font,char,false,true,pos_h,pos_v,pos_r) -- cur_h = cur_h + wd - right -- hm, no left here? cur_h = cur_h + wd -- see new tabulate alignment code elseif id == glue_code then -- local gluewidth = effectiveglue(current,this_box) local gluewidth = effectiveglue(current,this_box,true) if gluewidth ~= 0 then if subtype >= leaders_code then local leader = getleader(current) -- could also return id if leader then local id = getid(leader) if id == rule_code then if gluewidth > 0 then local width, height, depth = getwhd(leader) local runningheight = height == runningrule local runningdepth = depth == runningrule if runningheight then height = boxheight end if runningdepth then depth = boxdepth end local total = height + depth if total > 0 then local xoffset, yoffset, top, bottom, on, off = getoffsets(leader) -- -- only when we have a use case: -- -- if pos_r == righttoleft_code then -- pos_h = pos_h - gluewidth -- xoffset = - xoffset -- end -- if not virtual then -- if top ~= 0 then -- -- height = height - top -- total = total - top -- end -- if bottom ~= 0 then -- depth = depth - bottom -- total = total - bottom -- end -- end -- pos_v = pos_v - depth -- flushrule(leader,pos_h + xoffset,pos_v + yoffset,pos_r,gluewidth,total,getsubtype(leader)) -- if pos_r == righttoleft_code then pos_h = pos_h - gluewidth end pos_v = pos_v - depth flushrule(leader,pos_h,pos_v,pos_r,gluewidth,total,getsubtype(leader),on,off,runningheight and runningdepth) end cur_h = cur_h + gluewidth end elseif gluewidth > 0 and (id == hlist_code or id == vlist_code or id == glyph_code) then local width, height, depth = getwhd(leader) -- no need for // if width > 0 then gluewidth = gluewidth + eps local edge = cur_h + gluewidth local lx = 0 if subtype == gleaders_code then local save_h = cur_h if pos_r == righttoleft_code then cur_h = ref_h - shipbox_h + cur_h cur_h = width * (cur_h / width) cur_h = ref_h - shipbox_h - cur_h else cur_h = ref_h - shipbox_h - cur_h cur_h = width * (cur_h / width) cur_h = ref_h - shipbox_h - cur_h end if cur_h < save_h then cur_h = cur_h + width end local lr = gluewidth % width cur_h = cur_h + lr / 2 elseif subtype == leaders_code then local save_h = cur_h cur_h = width * (cur_h / width) if cur_h < save_h then cur_h = cur_h + width end else local lq = gluewidth / width local lr = gluewidth % width if subtype == cleaders_code then cur_h = cur_h + lr / 2 else lx = lr / (lq + 1) cur_h = cur_h + (lr - (lq - 1) * lx) / 2 end end if id == glyph_code then local char, font = isglyph(leader) local x_offset, y_offset, left, right, raise = getoffsets(leader) local h = ref_h local v = ref_v if x_offset ~= 0 or y_offset ~= 0 then if pos_r == righttoleft_code then h = h - x_offset else h = h + x_offset end v = v + y_offset end v = v + raise h = h - left local basepoint_h = 0 if boxdir ~= pos_r then basepoint_h = boxwidth end if pos_r == righttoleft_code then pos_h = h - basepoint_h else pos_h = h + basepoint_h end while cur_h + width <= edge do if pos_r == righttoleft_code then pos_h = h - cur_h else pos_h = h + cur_h end pos_v = v flush_character(leader,font,char,false,true,pos_h,pos_v,pos_r) cur_h = cur_h + width + lx end else local shift = isglyph and 0 or getshift(leader) local boxdir = getdirection(leader) or lefttoright_code pushleaderlevel() while cur_h + width <= edge do -- todo: move some out of loop as above local basepoint_h = 0 -- local basepoint_v = shift if boxdir ~= pos_r then basepoint_h = boxwidth end -- synch_pos_with_cur(ref_h,ref_v,cur_h + basepoint_h,shift) if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h) else pos_h = ref_h + (cur_h + basepoint_h) end pos_v = ref_v - shift -- synced if id == vlist_code then vlist_out(leader,getlist(leader)) else hlist_out(leader,getlist(leader)) end cur_h = cur_h + width + lx end popleaderlevel() end cur_h = edge - eps else cur_h = cur_h + gluewidth end else -- maybe some day also glyphs cur_h = cur_h + gluewidth end else cur_h = cur_h + gluewidth end else if tospace and (subtype == spaceskip_code or subtype == xspaceskip_code) then -- kind of tricky because because we can have a different sx sy flush_space(current) end cur_h = cur_h + gluewidth end end elseif id == hlist_code or id == vlist_code then local width, height, depth, shift, list, except = getlistdimensions(current) if list then local geometry, hasoffset, hasorientation, hasanchor, boxdir = getgeometry(current,true) local anchor, source, target, targetdata, s_anchor, t_anchor local anc_h, anc_v local usedorientation = false if hasanchor then anchor, source, target, s_anchor, t_anchor = getanchors(current) end if hasorientation then local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current) local orientation, basepoint_h, basepoint_v = applyorientation(orientation,0,shift,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset) if orientation == 1 then basepoint_h = basepoint_h + doffset if boxdir == pos_r then basepoint_v = basepoint_v - height end usedorientation = orientation elseif orientation == 2 then if boxdir == pos_r then basepoint_h = basepoint_h + width end usedorientation = orientation elseif orientation == 3 then basepoint_h = basepoint_h + hoffset if boxdir ~= pos_r then basepoint_v = basepoint_v - height end usedorientation = orientation end if target then targetdata = anchors[target] if targetdata then anc_h = basepoint_h anc_v = - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h) else pos_h = ref_h + (cur_h + basepoint_h) end -- pos_v = ref_v - (cur_v + basepoint_v) pos_v = ref_v - basepoint_v elseif hasoffset then -- local orientation, xoffset, yoffset = getorientation(current) local xoffset, yoffset = getoffsets(current) local basepoint_h = boxdir ~= pos_r and width or 0 local basepoint_v = shift if target then targetdata = anchors[target] if targetdata then anc_h = xoffset + basepoint_h anc_v = yoffset - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h + xoffset) else pos_h = ref_h + (cur_h + basepoint_h + xoffset) end pos_v = ref_v - (basepoint_v - yoffset) elseif hasanchor then local basepoint_h = boxdir ~= pos_r and width or 0 local basepoint_v = shift if target then targetdata = anchors[target] if targetdata then anc_h = basepoint_h anc_v = - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h) else pos_h = ref_h + (cur_h + basepoint_h) end pos_v = ref_v - basepoint_v else local basepoint_h = boxdir ~= pos_r and width or 0 local basepoint_v = shift if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + basepoint_h) else pos_h = ref_h + (cur_h + basepoint_h) end pos_v = ref_v - basepoint_v end goto process ::posdone:: if pos_r == righttoleft_code then pos_h = targetdata[1] - anc_h else pos_h = targetdata[1] + anc_h end pos_v = targetdata[2] + anc_v if anchor and anchor > 0 then -- pos_h, pos_v = applyanchor(anchor,true,t_anchor,pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5]) -- pos_h, pos_v = applyanchor(anchor,false,s_anchor,pos_h,pos_v,width,height,depth) pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5]) pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth) end ::process:: if source then local anchor_h = pos_h local anchor_v = pos_v if usedorientation then if usedorientation == 1 then anchor_v = anchor_v - (width - height) elseif usedorientation == 2 then anchor_v = anchor_v - (depth - height) elseif usedorientation == 3 then -- weird anchor_v = anchor_v + (height - width) end end -- anchor_v = anchor_v + shift anchors[source] = { anchor_h, anchor_v, width, height, depth } end if usedorientation then pushorientation(usedorientation,pos_h,pos_v,pos_r) end if source and stored then flushstored(current,source,true) end if except then handle_except(except) end if id == vlist_code then vlist_out(current,list) else hlist_out(current,list) end if source and stored then flushstored(current,source,false) end if usedorientation then poporientation(usedorientation,pos_h,pos_v,pos_r) end end cur_h = cur_h + width elseif id == kern_code then -- when we use getkerndimension we get rounded values -- if true then local kern = getkerndimension(current) if kern ~= 0 then cur_h = cur_h + kern end -- else -- local kern, factor = getkern(current,true) -- if kern ~= 0 then -- if factor ~= 0 then -- cur_h = cur_h + (1.0 + factor/1000000.0) * kern -- else -- cur_h = cur_h + kern -- end -- end -- end elseif id == rule_code then local width, height, depth, virtual = getruledimensions(current) if width ~= 0 then local runningheight = height == runningrule local runningdepth = depth == runningrule if runningheight then height = boxheight end if runningdepth then depth = boxdepth end local total = height + depth if total > 0 then local xoffset, yoffset, top, bottom, on, off = getoffsets(current) if pos_r == righttoleft_code then pos_h = pos_h - width xoffset = - xoffset end if not virtual then if top ~= 0 then -- height = height - top total = total - top end if bottom ~= 0 then depth = depth - bottom total = total - bottom end end pos_v = pos_v - depth flushrule(current,pos_h + xoffset,pos_v + yoffset,pos_r,width,total,subtype,on,off,runningheight and runningdepth) end if not virtual then cur_h = cur_h + width end end elseif id == math_code then -- local kern = getkern(current) -- if kern ~= 0 then -- cur_h = cur_h + kern -- else cur_h = cur_h + effectiveglue(current,this_box,true) -- end elseif id == dir_code then -- We normally have proper begin-end pairs. A begin without end is (silently) handled -- and an end without a begin will be (silently) skipped we only need to move forward -- so we then have a faster calculation. local dir, cancel = getdirection(current) if cancel then local ds = dirstack[current] if ds then ref_h = ds.ref_h ref_v = ds.ref_v cur_h = ds.cur_h -- cur_v = ds.cur_v else -- pardir end pos_r = dir else local width, enddir = dirdimensions(this_box,current) local new_h = cur_h + width if dir ~= pos_r then cur_h = new_h end -- kind of weird but indeed we don't adapt here: pos_r = dir if enddir ~= current then dirstack[enddir] = { cur_h = new_h, -- cur_v = cur_v, ref_h = ref_h, ref_v = ref_v, } -- kind of weird but indeed cheat here, so we basically have a bad one now setdirection(enddir,pos_r) end if pos_r == righttoleft_code then pos_h = ref_h - cur_h else pos_h = ref_h + cur_h end -- pos_v = ref_v - cur_v pos_v = ref_v -- synced ref_h = pos_h ref_v = pos_v cur_h = 0 -- cur_v = 0 pos_r = dir goto synced end elseif id == whatsit_code then flushwhatsit[subtype](current,pos_h,pos_v,pos_r) elseif id == disc_code then local replace, tail = getreplace(current) -- actually, we no longer have these select discs in the packaged list ... it's about -- time to clean up the disc code a bit further if replace then -- and subtype ~= select_disc_code then setlink(tail,getnext(current)) setlink(current,replace) setreplace(current) end -- elseif id == par_code and startofpar(current) then -- local pardir = getdirection(current) or lefttoright_code -- if pardir == righttoleft_code then -- end -- end else -- penalty, boundary ... no dimensions goto synced end -- There is no gain in skipping over this when we have zero progression -- and such. if pos_r == righttoleft_code then pos_h = ref_h - cur_h else pos_h = ref_h + cur_h end -- pos_v = ref_v - cur_v pos_v = ref_v ::synced:: end pos_h = ref_h pos_v = ref_v pos_r = ref_r end vlist_out = function(this_box,current) local ref_h = pos_h local ref_v = pos_v local ref_r = pos_r pos_r = getdirection(this_box) local boxwidth, boxheight, boxdepth = getwhd(this_box) local cur_h = 0 -- needs checking .. needed ? local cur_v = - boxheight local top_edge = cur_v -- if pos_r == righttoleft_code then -- pos_h = ref_h - cur_h -- else -- pos_h = ref_h + cur_h -- end pos_h = ref_h pos_v = ref_v - cur_v -- synced -- if not current then -- current = getlist(this_box) -- end -- while current do -- local id = getid(current) for current, id, subtype in vnextnode, current do if id == glue_code then local glueheight = effectiveglue(current,this_box,true) if glueheight ~= 0 then if subtype >= leaders_code then local leader = getleader(current) if leader then local width, height, depth = getwhd(leader) local total = height + depth if getid(leader) == rule_code then depth = 0 -- hm total = glueheight -- forgotten ... needs testing if total > 0 then local runningwidth = width == runningrule if runningwidth then width = boxwidth end if width > 0 then local xoffset, yoffset, left, right, on, off = getoffsets(leader) -- -- only when i have a use case: -- -- if not virtual then -- if left ~= 0 then -- width = width - left -- xoffset = left -- end -- if right ~= 0 then -- width = width - right -- end -- end -- if pos_r == righttoleft_code then -- xoffset = - xoffset - width -- end -- flushrule(leader,pos_h + xoffset,pos_v - total - yoffset,pos_r,width,total,getsubtype(leader)) -- if pos_r == righttoleft_code then cur_h = cur_h - width end flushrule(leader,pos_h,pos_v - total,pos_r,width,total,getsubtype(leader),on,off,runningwidth) end cur_v = cur_v + total end elseif total > 0 and glueheight > 0 then glueheight = glueheight + 10 local edge = cur_v + glueheight local ly = 0 if subtype == gleaders_code then local save_v = cur_v cur_v = ref_v - shipbox_v - cur_v cur_v = total * (cur_v / total) cur_v = ref_v - shipbox_v - cur_v if cur_v < save_v then cur_v = cur_v + total end local lr = glueheight % total cur_v = cur_v + lr / 2 elseif subtype == leaders_code then -- aleader local save_v = cur_v cur_v = top_edge + total * ((cur_v - top_edge) // total) if cur_v < save_v then cur_v = cur_v + total end else local lq = glueheight / total local lr = glueheight % total if subtype == cleaders_code then cur_v = cur_v + lr / 2 else ly = lr // (lq + 1) cur_v = cur_v + (lr - (lq - 1) * ly) / 2 end end local shift = getshift(leader) pushleaderlevel() while cur_v + total <= edge do -- todo: <= edge - total -- synch_pos_with_cur(ref_h, ref_v, getshift(leader), cur_v + height) if pos_r == righttoleft_code then pos_h = ref_h - shift else pos_h = ref_h + shift end pos_v = ref_v - (cur_v + height) -- synced if getid(leader) == vlist_code then vlist_out(leader,getlist(leader)) else hlist_out(leader,getlist(leader)) end cur_v = cur_v + total + ly end popleaderlevel() cur_v = edge - 10 else cur_v = cur_v + glueheight end end else cur_v = cur_v + glueheight end end elseif id == hlist_code or id == vlist_code then local width, height, depth, shift, list, except = getlistdimensions(current) if list then local geometry, hasoffset, hasorientation, hasanchor, boxdir = getgeometry(current,true) local anchor, source, target, targetdata, s_anchor, t_anchor local usedorientation = false if hasanchor then anchor, source, target, s_anchor, t_anchor = getanchors(current) end if hasorientation then local orientation, xoffset, yoffset, woffset, hoffset, doffset = getorientation(current) local orientation, basepoint_h, basepoint_v = applyorientation(orientation,shift,height,width,height,depth,woffset,hoffset,doffset,xoffset,yoffset) if orientation == 1 then basepoint_h = basepoint_h + width - height -- hm basepoint_v = basepoint_v - height usedorientation = orientation elseif orientation == 2 then basepoint_h = basepoint_h + width basepoint_v = basepoint_v + depth - height usedorientation = orientation elseif orientation == 3 then -- weird basepoint_h = basepoint_h + height usedorientation = orientation end if target then targetdata = anchors[target] if targetdata then if pos_r == righttoleft_code then pos_h = targetdata[1] - basepoint_h else pos_h = targetdata[1] + basepoint_h end pos_v = targetdata[2] - basepoint_v goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - basepoint_h else pos_h = ref_h + basepoint_h end pos_v = ref_v - (cur_v + basepoint_v) elseif hasoffset then -- local orientation, xoffset, yoffset = getorientation(current) local xoffset, yoffset = getoffsets(current) -- local basepoint_h = shift -- local basepoint_v = height if boxdir ~= pos_r then shift = shift + width end if target then targetdata = anchors[target] if targetdata then if pos_r == righttoleft_code then pos_h = targetdata[1] - (shift + xoffset) else pos_h = targetdata[1] + (shift + xoffset) end pos_v = targetdata[2] - (height - yoffset) goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - (shift + xoffset) else pos_h = ref_h + (shift + xoffset) end pos_v = ref_v - (cur_v + height - yoffset) elseif hasanchor then -- local basepoint_h = shift -- local basepoint_v = height if boxdir ~= pos_r then shift = shift + width end if target then local a = anchors[target] if a then if pos_r == righttoleft_code then pos_h = targetdata[1] - shift else pos_h = targetdata[1] + shift end pos_v = targetdata[2] - height goto posdone end end if pos_r == righttoleft_code then pos_h = ref_h - shift else pos_h = ref_h + shift end pos_v = ref_v - (cur_v + height) else -- local basepoint_h = shift -- local basepoint_v = height if boxdir ~= pos_r then shift = shift + width end if pos_r == righttoleft_code then pos_h = ref_h - shift else pos_h = ref_h + shift end pos_v = ref_v - (cur_v + height) end goto process ::posdone:: if anchor and anchor > 0 then pos_h, pos_v = applyanchor(t_anchor,true, pos_h,pos_v,targetdata[3],targetdata[4],targetdata[5]) pos_h, pos_v = applyanchor(s_anchor,false,pos_h,pos_v,width,height,depth) end ::process:: if source then -- move this into apply_anchor local anchor_h = pos_h local anchor_v = pos_v if usedorientation then if usedorientation == 1 then anchor_v = anchor_v - (width - height) elseif usedorientation == 2 then anchor_v = anchor_v - (depth - height) elseif usedorientation == 3 then -- weird anchor_v = anchor_v + (height - width) end end anchors[source] = { anchor_h, anchor_v, width, height, depth } end if usedorientation then pushorientation(usedorientation,pos_h,pos_v,pos_r) end if source and stored then flushstored(current,source,true) end if except then handle_except(except) end if id == vlist_code then vlist_out(current,list) else hlist_out(current,list) end if source and stored then flushstored(current,source,false) end if usedorientation then poporientation(usedorientation,pos_h,pos_v,pos_r) end end cur_v = cur_v + height + depth elseif id == kern_code then cur_v = cur_v + getkern(current) elseif id == rule_code then local width, height, depth, virtual = getruledimensions(current) local total = height + depth if total > 0 then local runningwidth = width == runningrule if runningwidth then width = boxwidth end if width ~= 0 then local xoffset, yoffset, left, right, on, off = getoffsets(current) if not virtual then if left ~= 0 then width = width - left xoffset = left end if right ~= 0 then width = width - right end end if pos_r == righttoleft_code then xoffset = - xoffset - width end flushrule(current,pos_h + xoffset,pos_v - total - yoffset,pos_r,width,total,subtype,on,off,runningwidth) end if not virtual then cur_v = cur_v + total end end elseif id == whatsit_code then flushwhatsit[subtype](current,pos_h,pos_v,pos_r) else -- penalty goto synced end if pos_r == righttoleft_code then pos_h = ref_h - cur_h else pos_h = ref_h + cur_h end pos_v = ref_v - cur_v ::synced:: end pos_h = ref_h pos_v = ref_v pos_r = ref_r end end function drivers.converters.lmtx(driver,box,smode,objnum,specification) if not driver then report("error in converter, no driver") return end if box then box = tonut(box) else report("error in converter, no box") return end local actions = driver.actions local flushers = driver.flushers initialize = actions.initialize -- called in convert so timed there finalize = actions.finalize -- called in convert so timed there -- initialize = drivers.initialize -- finalize = drivers.finalize updatefontstate = flushers.updatefontstate pushorientation = flushers.pushorientation poporientation = flushers.poporientation pushleaderlevel = flushers.pushleaderlevel popleaderlevel = flushers.popleaderlevel flushcharacter = flushers.character flushfontchar = flushers.fontchar flushrule = flushers.rule flushsimplerule = flushers.simplerule flushspecialrule = flushers.specialrule flushliteral = flushers.literal flushwhatsit = flushers.whatsit flushspace = flushers.space pushgroup = flushers.pushgroup popgroup = flushers.popgroup reset_directions() reset_anchors() reset_state() shippingmode = smode local details = nil -- must be outside labels local width, height, depth = getwhd(box) local total = height + depth if height > maxdimen or depth > maxdimen or width > maxdimen or total > maxdimen then goto DONE end if shippingmode == "page" then -- We have zero offsets in ConTeXt. local pagewidth, pageheight = getpagedimensions() pos_r = lefttoright_code if pagewidth > 0 then page_size_h = pagewidth else page_size_h = width end if page_size_h == 0 then page_size_h = width end if pageheight > 0 then page_size_v = pageheight else page_size_v = total end if page_size_v == 0 then page_size_v = total end local refpoint_h = 0 local refpoint_v = page_size_v pos_h = refpoint_h pos_v = refpoint_v - height else page_size_h = width page_size_v = total pos_r = getdirection(box) pos_v = depth pos_h = pos_r == righttoleft_code and width or 0 end details = { shippingmode = smode, -- target boundingbox = { 0, 0, page_size_h, page_size_v }, objectnumber = smode ~= "page" and objnum or nil, pagenumber = smode == "page" and objnum or nil, specification = specification, } initialize(driver,details) lastfont = nil -- this forces a sync each page / object if getid(box) == vlist_code then vlist_out(box,getlist(box)) else hlist_out(box,getlist(box)) end ::DONE:: finalize(driver,details) shippingmode = "none" end -- This will move to back-out.lua eventually. do ----- sortedhash = table.sortedhash ----- tonut = nodes.tonut local properties = nodes.properties.data local flush = texio.write local flushline = texio.writenl local periods = utilities.strings.newrepeater(".") local f_detail_0 = formatters["%s %s = %s"] local f_detail_1 = formatters["%i: %s %s = %s"] local f_detail_2 = formatters["%i:%i: %s %s = %s"] local function showdetails(n,l,tlp,l1,l2) local p = properties[n] if p then for k, v in sortedhash(p) do local t = type(v) local p = periods[l+1] if t == "string" then if find(v,"[\n\r]") then v = "\n" .. stripstring(v) .. "\n" end elseif t == "number" or t == "boolean" then v = tostring(v) elseif t == "table" then v = sequenced(v) else v = "<" .. tostring(v) .. ">" end if tlp == 3 then flushline(f_detail_2(l1,l2,p,k,v)) elseif tlp == 2 then flushline(f_detail_1(l2,p,k,v)) elseif tlp == 1 then flushline(f_detail_1(l1,p,k,v)) else flushline(f_detail_0(p,k,v)) end end end end local whatsittracers = { latelua = showdetails, literal = showdetails, } local function show_whatsits_callback(n,what,l,tlp,l1,l2) local s = nodes.whatsitcodes[getsubtype(n)] if what == 1 then return s or "unknown" -- elseif what == 2 then else local w = whatsittracers[s] if w then w(n,l,tlp,l1,l2) end end end callbacks.register { name = "show_whatsit", action = show_whatsits_callback, comment = "provide whatsit details", frozen = true, } local names = attributes.names -- we show the name and number local taglist = structures.tags.taglist local details = false local a_tagged = attributes.private('tagged') trackers.register("attributes.tags",function(v) details = v end) local function get_attribute_callback(k,v) local detail = nil if details and k == a_tagged then detail = taglist[v] if detail then detail = detail.taglist if detail then detail = detail[#detail] end end end return names[k], detail end callbacks.register { name = "get_attribute", action = get_attribute_callback, comment = "provide verbose attribute name", frozen = true, } end