diff options
Diffstat (limited to 'tex/context/base/mkiv/trac-vis.lmt')
-rw-r--r-- | tex/context/base/mkiv/trac-vis.lmt | 1700 |
1 files changed, 0 insertions, 1700 deletions
diff --git a/tex/context/base/mkiv/trac-vis.lmt b/tex/context/base/mkiv/trac-vis.lmt deleted file mode 100644 index 7ac5964da..000000000 --- a/tex/context/base/mkiv/trac-vis.lmt +++ /dev/null @@ -1,1700 +0,0 @@ -if not modules then modules = { } end modules ['trac-vis'] = { - version = 1.001, - optimize = true, - comment = "companion to trac-vis.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local node, nodes, attributes, tex = node, nodes, attributes, tex -local type, tonumber, next, rawget = type, tonumber, next, rawget -local gmatch = string.gmatch -local formatters = string.formatters -local round = math.round - --- This module started out in the early days of mkiv and luatex with visualizing --- kerns related to fonts. In the process of cleaning up the visual debugger code it --- made sense to integrate some other code that I had laying around and replace the --- old supp-vis debugging code. As only a subset of the old visual debugger makes --- sense it has become a different implementation. Soms of the m-visual --- functionality will also be ported. The code is rather trivial. The caching is not --- really needed but saves upto 50% of the time needed to add visualization. Of --- course the overall runtime is larger because of color and layer processing in the --- backend (can be times as much) so the runtime is somewhat larger with full --- visualization enabled. In practice this will never happen unless one is demoing. - --- todo: global switch (so no attributes) --- todo: maybe also xoffset, yoffset of glyph --- todo: inline concat (more efficient) --- todo: tags can also be numbers (just add to hash) --- todo: make a lmtx variant (a few more efficient fetchers) - -local nodecodes = nodes.nodecodes - -local nuts = nodes.nuts -local tonut = nuts.tonut - -local setboth = nuts.setboth -local setlink = nuts.setlink -local setdisc = nuts.setdisc -local setlist = nuts.setlist -local setleader = nuts.setleader -local setsubtype = nuts.setsubtype -local setattr = nuts.setattr -local setwidth = nuts.setwidth -local setshift = nuts.setshift - -local getid = nuts.getid -local getfont = nuts.getfont -local getattr = nuts.getattr -local getsubtype = nuts.getsubtype -local getbox = nuts.getbox -local getlist = nuts.getlist -local getleader = nuts.getleader -local getnext = nuts.getnext -local getboth = nuts.getboth -local getdisc = nuts.getdisc -local getwhd = nuts.getwhd -local getkern = nuts.getkern -local getpenalty = nuts.getpenalty -local getwidth = nuts.getwidth -local getdepth = nuts.getdepth -local getshift = nuts.getshift -local getexpansion = nuts.getexpansion -local getdirection = nuts.getdirection -local getstate = nuts.getstate - -local isglyph = nuts.isglyph - -local hpack_nodes = nuts.hpack -local vpack_nodes = nuts.vpack -local copy_list = nuts.copy_list -local copy_node = nuts.copy_node -local flush_node_list = nuts.flush_list -local insert_node_before = nuts.insert_before -local insert_node_after = nuts.insert_after -local apply_to_nodes = nuts.apply -local effectiveglue = nuts.effective_glue - -local hpack_string = nuts.typesetters.tohpack - -local texgetattribute = tex.getattribute -local texsetattribute = tex.setattribute - -local setmetatableindex = table.setmetatableindex - -local unsetvalue = attributes.unsetvalue - -local current_font = font.current - -local fonthashes = fonts.hashes -local chardata = fonthashes.characters -local exheights = fonthashes.exheights -local emwidths = fonthashes.emwidths -local pt_factor = number.dimenfactors.pt - -local nodepool = nuts.pool -local new_rule = nodepool.rule -local new_kern = nodepool.kern -local new_glue = nodepool.glue -local new_hlist = nodepool.hlist -local new_vlist = nodepool.vlist - -local tracers = nodes.tracers -local visualizers = nodes.visualizers - -local setcolor = tracers.colors.set -local setlistcolor = tracers.colors.setlist -local settransparency = tracers.transparencies.set -local setlisttransparency = tracers.transparencies.setlist - -local starttiming = statistics.starttiming -local stoptiming = statistics.stoptiming - -local a_visual = attributes.private("visual") -local a_layer = attributes.private("viewerlayer") - -local band = bit32.band -local bor = bit32.bor - -local enableaction = nodes.tasks.enableaction - --- local trace_hbox --- local trace_vbox --- local trace_vtop --- local trace_kern --- local trace_glue --- local trace_penalty --- local trace_fontkern --- local trace_strut --- local trace_whatsit --- local trace_user --- local trace_math --- local trace_italic --- local trace_discretionary --- local trace_expansion --- local trace_line --- local trace_space - -local report_visualize = logs.reporter("visualize") - -local modes = { - hbox = 0x000001, - vbox = 0x000002, - vtop = 0x000004, - kern = 0x000008, - glue = 0x000010, - penalty = 0x000020, - fontkern = 0x000040, - strut = 0x000080, - whatsit = 0x000100, - glyph = 0x000200, - simple = 0x000400, - simplehbox = 0x000401, - simplevbox = 0x000402, - simplevtop = 0x000404, - user = 0x000800, - math = 0x001000, - italic = 0x002000, - origin = 0x004000, - discretionary = 0x008000, - expansion = 0x010000, - line = 0x020000, - space = 0x040000, - depth = 0x080000, - marginkern = 0x100000, - mathlistkern = 0x200000, - dir = 0x400000, - par = 0x800000, -} - -local usedfont, exheight, emwidth -local l_penalty, l_glue, l_kern, l_fontkern, l_hbox, l_vbox, l_vtop, l_strut, l_whatsit, l_glyph, l_user, l_math, l_marginkern, l_mathlistkern, l_italic, l_origin, l_discretionary, l_expansion, l_line, l_space, l_depth, - l_dir, l_whatsit - -local enabled = false -local layers = { } - -local preset_boxes = modes.hbox + modes.vbox + modes.vtop + modes.origin -local preset_makeup = preset_boxes - + modes.kern + modes.glue + modes.penalty -local preset_all = preset_makeup - + modes.fontkern + modes.marginkern + modes.mathlistkern - + modes.whatsit + modes.glyph + modes.user + modes.math - + modes.dir + modes.whatsit - -function visualizers.setfont(id) - usedfont = id or current_font() - exheight = exheights[usedfont] - emwidth = emwidths[usedfont] -end - --- we can preset a bunch of bits - -local userrule -- bah, not yet defined: todo, delayed(nuts.rules,"userrule") -local outlinerule -- bah, not yet defined: todo, delayed(nuts.rules,"userrule") - -local function initialize() - -- - if not usedfont then - -- we use a narrow monospaced font -- infofont ? - visualizers.setfont(fonts.definers.define { name = "lmmonoltcond10regular", size = tex.sp("4pt") }) - end - -- - for mode, value in next, modes do - local tag = formatters["v_%s"](mode) - attributes.viewerlayers.define { - tag = tag, - title = formatters["visualizer %s"](mode), - visible = "start", - editable = "yes", - printable = "yes" - } - layers[mode] = attributes.viewerlayers.register(tag,true) - end - l_hbox = layers.hbox - l_vbox = layers.vbox - l_vtop = layers.vtop - l_glue = layers.glue - l_kern = layers.kern - l_penalty = layers.penalty - l_fontkern = layers.fontkern - l_strut = layers.strut - l_whatsit = layers.whatsit - l_glyph = layers.glyph - l_user = layers.user - l_math = layers.math - l_italic = layers.italic - l_marginkern = layers.marginkern - l_mathlistkern = layers.mathlistkern - l_origin = layers.origin - l_discretionary = layers.discretionary - l_expansion = layers.expansion - l_line = layers.line - l_space = layers.space - l_depth = layers.depth - l_dir = layers.dir - l_par = layers.par - -- - if not userrule then - userrule = nuts.rules.userrule - end - -- - if not outlinerule then - outlinerule = nuts.pool.outlinerule - end - initialize = false -end - -local function enable() - if initialize then - initialize() - end - enableaction("shipouts","nodes.visualizers.handler") - report_visualize("enabled") - enabled = true - tex.setcount("global","c_syst_visualizers_state",1) -- so that we can optimize at the tex end -end - -local function setvisual(n,a,what,list) -- this will become more efficient when we have the bit lib linked in - if not n or n == "reset" then - return unsetvalue - elseif n == true or n == "makeup" then - if not a or a == 0 or a == unsetvalue then - a = preset_makeup - else - a = bor(a,preset_makeup) - end - elseif n == "boxes" then - if not a or a == 0 or a == unsetvalue then - a = preset_boxes - else - a = bor(a,preset_boxes) - end - elseif n == "all" then - if what == false then - return unsetvalue - elseif not a or a == 0 or a == unsetvalue then - a = preset_all - else - a = bor(a,preset_all) - end - else - for s in gmatch(n,"[a-z]+") do - local m = modes[s] - if not m then - -- go on - elseif not a or a == 0 or a == unsetvalue then - a = m - else - a = bor(a,m) - end - end - end - if not a or a == 0 or a == unsetvalue then - return unsetvalue - elseif not enabled then -- must happen at runtime (as we don't store layers yet) - enable() - end - return a -end - -function nuts.setvisual(n,mode) - setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true)) -end - -function nuts.setvisuals(n,mode) -- currently the same - setattr(n,a_visual,setvisual(mode,getattr(n,a_visual),true,true)) -end - --- fast setters - -do - - local cached = setmetatableindex(function(t,k) - if k == true then - return texgetattribute(a_visual) - elseif not k then - t[k] = unsetvalue - return unsetvalue - else - local v = setvisual(k) - t[k] = v - return v - end - end) - - -- local function applyvisuals(n,mode) - -- local a = cached[mode] - -- apply_to_nodes(n,function(n) setattr(n,a_visual,a) end) - -- end - - local a = unsetvalue - - local f = function(n) setattr(n,a_visual,a) end - - local function applyvisuals(n,mode) - a = cached[mode] - apply_to_nodes(n,f) - end - - nuts.applyvisuals = applyvisuals - - function nodes.applyvisuals(n,mode) - applyvisuals(tonut(n),mode) - end - - function visualizers.attribute(mode) - return cached[mode] - end - - visualizers.attributes = cached - -end - -function nuts.copyvisual(n,m) - setattr(n,a_visual,getattr(m,a_visual)) -end - -function visualizers.setvisual(n) - texsetattribute(a_visual,setvisual(n,texgetattribute(a_visual))) -end - -function visualizers.setlayer(n) - texsetattribute(a_layer,layers[n] or unsetvalue) -end - -local function set(mode,v) - texsetattribute(a_visual,setvisual(mode,texgetattribute(a_visual),v)) -end - -for mode, value in next, modes do - trackers.register(formatters["visualizers.%s"](mode), function(v) set(mode,v) end) -end - -local fraction = 10 - -trackers .register("visualizers.reset", function(v) set("reset", v) end) -trackers .register("visualizers.all", function(v) set("all", v) end) -trackers .register("visualizers.makeup", function(v) set("makeup",v) end) -trackers .register("visualizers.boxes", function(v) set("boxes", v) end) -directives.register("visualizers.fraction", function(v) fraction = (v and tonumber(v)) or (v == "more" and 5) or 10 end) - -local c_positive = "trace:b" -local c_negative = "trace:r" -local c_zero = "trace:g" -local c_text = "trace:s" -local c_space = "trace:y" -local c_space_x = "trace:m" -local c_skip_a = "trace:c" -local c_skip_b = "trace:m" -local c_glyph = "trace:o" -local c_ligature = "trace:s" -local c_white = "trace:w" ------ c_math = "trace:s" ------ c_origin = "trace:o" ------ c_discretionary = "trace:d" ------ c_expansion = "trace:o" -local c_depth = "trace:o" -local c_indent = "trace:s" - -local c_positive_d = "trace:db" -local c_negative_d = "trace:dr" -local c_zero_d = "trace:dg" -local c_text_d = "trace:ds" -local c_space_d = "trace:dy" -local c_space_x_d = "trace:dm" -local c_skip_a_d = "trace:dc" -local c_skip_b_d = "trace:dm" -local c_glyph_d = "trace:do" -local c_ligature_d = "trace:ds" -local c_white_d = "trace:dw" -local c_math_d = "trace:dr" -local c_origin_d = "trace:do" -local c_discretionary_d = "trace:dd" ------ c_expansion_d = "trace:do" ------ c_depth_d = "trace:do" ------ c_indent_d = "trace:ds" - -local function sometext(str,layer,color,textcolor,lap) -- we can just paste verbatim together .. no typesteting needed - local text = hpack_string(str,usedfont) - local size = getwidth(text) - local rule = new_rule(size,2*exheight,exheight/2) - local kern = new_kern(-size) - if color then - setcolor(rule,color) - end - if textcolor then - setlistcolor(getlist(text),textcolor) - end - local info = setlink(rule,kern,text) - setlisttransparency(info,c_zero) - info = hpack_nodes(info) - local width = getwidth(info) - if lap then - info = new_hlist(setlink(new_kern(-width),info)) - else - info = new_hlist(info) -- a bit overkill: double wrapped - end - if layer then - setattr(info,a_layer,layer) - end - return info, width -end - -local function someblob(str,layer,color,textcolor,width) - local text = hpack_string(str,usedfont) - local size = getwidth(text) - local rule = new_rule(width,2*exheight,exheight/2) - local kern = new_kern(-width + (width-size)/2) - if color then - setcolor(rule,color) - end - if textcolor then - setlistcolor(getlist(text),textcolor) - end - local info = setlink(rule,kern,text) - setlisttransparency(info,c_zero) - info = hpack_nodes(info) - local width = getwidth(info) - info = new_hlist(info) - if layer then - setattr(info,a_layer,layer) - end - return info, width -end - -local caches = setmetatableindex("table") - -local fontkern, italickern, marginkern, mathlistkern do - - local f_cache = caches["fontkern"] - local i_cache = caches["italickern"] - local m_cache = caches["marginkern"] - local l_cache = caches["mathlistkern"] - - local function somekern(head,current,cache,color,layer) - local width = getkern(current) - local extra = getexpansion(current) - local kern = width + extra - local info = cache[kern] - if not info then - local text = hpack_string(formatters[" %0.3f"](kern*pt_factor),usedfont) - local rule = new_rule(emwidth/fraction,6*exheight,2*exheight) - local list = getlist(text) - if kern > 0 then - setlistcolor(list,c_positive_d) - elseif kern < 0 then - setlistcolor(list,c_negative_d) - else - setlistcolor(list,c_zero_d) - end - setlisttransparency(list,color) - setcolor(rule,color) - settransparency(rule,color) - setshift(text,-5 * exheight) - info = new_hlist(setlink(rule,text)) - setattr(info,a_layer,layer) - f_cache[kern] = info - end - head = insert_node_before(head,current,copy_list(info)) - return head, current - end - - fontkern = function(head,current) - return somekern(head,current,f_cache,c_text_d,l_fontkern) - end - - italickern = function(head,current) - return somekern(head,current,i_cache,c_glyph_d,l_italic) - end - - marginkern = function(head,current) - return somekern(head,current,m_cache,c_glyph_d,l_marginkern) - end - - mathlistkern = function(head,current) - return somekern(head,current,l_cache,c_glyph_d,l_mathlistkern) - end - -end - -local glyphexpansion do - - local f_cache = caches["glyphexpansion"] - - glyphexpansion = function(head,current) - local extra = getexpansion(current) - if extra and extra ~= 0 then - extra = extra / 1000 - local info = f_cache[extra] - if not info then - local text = hpack_string(round(extra),usedfont) - local rule = new_rule(emwidth/fraction,exheight,2*exheight) - local list = getlist(text) - if extra > 0 then - setlistcolor(list,c_positive_d) - elseif extra < 0 then - setlistcolor(list,c_negative_d) - end - setlisttransparency(list,c_text_d) - setcolor(rule,c_text_d) - settransparency(rule,c_text_d) - setshift(text,1.5 * exheight) - info = new_hlist(setlink(rule,text)) - setattr(info,a_layer,l_expansion) - f_cache[extra] = info - end - head = insert_node_before(head,current,copy_list(info)) - return head, current - end - return head, current - end - -end - -local kernexpansion do - - local f_cache = caches["kernexpansion"] - - -- in mkiv we actually need to reconstruct but let's not do that now - - kernexpansion = function(head,current) - local extra = getexpansion(current) - if extra ~= 0 then - extra = extra / 1000 - local info = f_cache[extra] - if not info then - local text = hpack_string(round(extra),usedfont) - local rule = new_rule(emwidth/fraction,exheight,4*exheight) - local list = getlist(text) - if extra > 0 then - setlistcolor(list,c_positive_d) - elseif extra < 0 then - setlistcolor(list,c_negative_d) - end - setlisttransparency(list,c_text_d) - setcolor(rule,c_text_d) - settransparency(rule,c_text_d) - setshift(text,3.5 * exheight) - info = new_hlist(setlink(rule,text)) - setattr(info,a_layer,l_expansion) - f_cache[extra] = info - end - head = insert_node_before(head,current,copy_list(info)) - return head, current - end - return head, current - end - -end - -local whatsit do - - local whatsitcodes = nodes.whatsitcodes - local w_cache = caches["whatsit"] - - local tags = { - open = "OPN", - write = "WRI", - close = "CLS", - special = "SPE", - latelua = "LUA", - savepos = "POS", - userdefined = "USR", - literal = "LIT", - setmatrix = "MAT", - save = "SAV", - restore = "RES", - } - - whatsit = function(head,current) - local what = getsubtype(current) - local info = w_cache[what] - if info then - -- print("hit whatsit") - else - info = sometext(formatters["W:%s"](what),usedfont,nil,c_white) - setattr(info,a_layer,l_whatsit) - w_cache[what] = info - end - head, current = insert_node_after(head,current,copy_list(info)) - return head, current - end - -end - -local dir, par do - - local dircodes = nodes.dircodes - local dirvalues = nodes.dirvalues - - local cancel_code = dircodes.cancel - local l2r_code = dirvalues.l2r - local r2l_code = dirvalues.r2l - - local d_cache = caches["dir"] - - local tags = { - l2r = "L2R", - r2l = "R2L", - cancel = "CAN", - par = "PAR", - } - - par = function(head,current) - local what = "par" -- getsubtype(current) - local info = d_cache[what] - if info then - -- print("hit par") - else - info = sometext(formatters["L:%s"](what),usedfont,nil,c_white) - setattr(info,a_layer,l_dir) - d_cache[what] = info - end - return head, current - end - - dir = function(head,current) - local what = getsubtype(current) - if what == cancelcode then - what = "cancel" - elseif getdirection(current) == r2l_code then - what = "r2l" - else - what = "l2r" - end - local info = d_cache[what] - if info then - -- print("hit dir") - else - info = sometext(formatters["D:%s"](what),usedfont,nil,c_white) - setattr(info,a_layer,l_dir) - d_cache[what] = info - end - return head, current - end - -end - -local user do - - local u_cache = caches["user"] - - user = function(head,current) - local what = getsubtype(current) - local info = u_cache[what] - if info then - -- print("hit user") - else - info = sometext(formatters["U:%s"](what),usedfont) - setattr(info,a_layer,l_user) - u_cache[what] = info - end - head, current = insert_node_after(head,current,copy_list(info)) - return head, current - end - -end - -local math do - - local mathcodes = nodes.mathcodes - local m_cache = { - beginmath = caches["bmath"], - endmath = caches["emath"], - } - local tags = { - beginmath = "B", - endmath = "E", - } - - math = function(head,current) - local what = getsubtype(current) - local tag = mathcodes[what] - local skip = getkern(current) + getwidth(current) -- surround - local info = m_cache[tag][skip] - if info then - -- print("hit math") - else - local text, width = sometext(formatters["M:%s"](tag and tags[tag] or what),usedfont,nil,c_math_d) - local rule = new_rule(skip,-655360/fraction,2*655360/fraction) - setcolor(rule,c_math_d) - settransparency(rule,c_math_d) - setattr(rule,a_layer,l_math) - if tag == "beginmath" then - info = new_hlist(setlink(new_glue(-skip),rule,new_glue(-width),text)) - else - info = new_hlist(setlink(new_glue(-skip),rule,new_glue(-skip),text)) - end - setattr(info,a_layer,l_math) - m_cache[tag][skip] = info - end - head, current = insert_node_after(head,current,copy_list(info)) - return head, current - end - -end - -local ruleddepth do - - ruleddepth = function(current,wd,ht,dp) - local wd, ht, dp = getwhd(current) - if dp ~= 0 then - local rule = new_rule(wd,0,dp) - setcolor(rule,c_depth) - settransparency(rule,c_zero) - setattr(rule,a_layer,l_depth) - setlist(current,setlink(rule,new_kern(-wd),getlist(current))) - end - end - -end - -local ruledbox do - - local b_cache = caches["box"] - local o_cache = caches["origin"] - - setmetatableindex(o_cache,function(t,size) - local rule = new_rule(2*size,size,size) - local origin = hpack_nodes(rule) - setcolor(rule,c_origin_d) - settransparency(rule,c_origin_d) - setattr(rule,a_layer,l_origin) - t[size] = origin - return origin - end) - - ruledbox = function(head,current,vertical,layer,what,simple,previous,trace_origin,parent) - local wd, ht, dp = getwhd(current) - if wd ~= 0 then - local shift = getshift(current) - local next = getnext(current) - local prev = previous - setboth(current) - local linewidth = emwidth/fraction - local size = 2*linewidth - local this - if not simple then - this = b_cache[what] - if not this then - local text = hpack_string(what,usedfont) - this = setlink(new_kern(-getwidth(text)),text) - setlisttransparency(this,c_text) - this = new_hlist(this) - b_cache[what] = this - end - end - -- we need to trigger the right mode (else sometimes no whatits) - local info = setlink( - this and copy_list(this) or nil, - (dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth)) or userrule { - width = wd, - height = ht, - depth = dp, - line = linewidth, - type = "box", - dashed = 3*size, - } - ) - -- - setlisttransparency(info,c_text) - info = new_hlist(info) -- important - -- - setattr(info,a_layer,layer) - if vertical then - if shift == 0 then - info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info) - elseif trace_origin then - local size = 2*size - local origin = o_cache[size] - origin = copy_list(origin) - if getid(parent) == vlist_code then - setshift(origin,-shift) - info = setlink(current,new_kern(-size),origin,new_kern(-size-dp),info) - else - -- todo .. i need an example - info = setlink(current,dp ~= 0 and new_kern(-dp) or nil,info) - end - setshift(current,0) - else - info = setlink(current,new_dp ~= 0 and new_kern(-dp) or nil,info) - setshift(current,0) - end - info = new_vlist(info,wd,ht,dp,shift) - else - if shift == 0 then - info = setlink(current,new_kern(-wd),info) - elseif trace_origin then - local size = 2*size - local origin = o_cache[size] - origin = copy_list(origin) - if getid(parent) == vlist_code then - info = setlink(current,new_kern(-wd-size-shift),origin,new_kern(-size+shift),info) - else - setshift(origin,-shift) - info = setlink(current,new_kern(-wd-size),origin,new_kern(-size),info) - end - setshift(current,0) - else - info = setlink(current,new_kern(-wd),info) - setshift(current,0) - end - info = new_hlist(info,wd,ht,dp,shift) - end - if next then - setlink(info,next) - end - if prev and prev > 0 then - setlink(prev,info) - end - if head == current then - return info, info - else - return head, info - end - else - return head, current - end - end - -end - -local ruledglyph do - - -- see boundingbox feature .. maybe a pdf stream is more efficient, after all we - -- have a frozen color anyway or i need a more detailed cache .. below is a more - -- texie approach - - ruledglyph = function(head,current,previous) -- wrong for vertical glyphs - local wd = getwidth(current) - if wd ~= 0 then - local wd, ht, dp = getwhd(current) - local next = getnext(current) - local prev = previous - setboth(current) - local linewidth = emwidth/(2*fraction) - local info - -- - info = setlink( - (dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth)) or userrule { - width = wd, - height = ht, - depth = dp, - line = linewidth, - type = "box", - }, - new_kern(-wd) - ) - -- - local c, f = isglyph(current) - local char = chardata[f][c] - if char and type(char.unicode) == "table" then -- hackery test - setlistcolor(info,c_ligature) - setlisttransparency(info,c_ligature_d) - else - setlistcolor(info,c_glyph) - setlisttransparency(info,c_glyph_d) - end - info = new_hlist(info) - setattr(info,a_layer,l_glyph) - local info = setlink(current,new_kern(-wd),info) - info = hpack_nodes(info) - setwidth(info,wd) - if next then - setlink(info,next) - end - if prev then - setlink(prev,info) - end - if head == current then - return info, info - else - return head, info - end - else - return head, current - end - end - - function visualizers.setruledglyph(f) - ruledglyph = f or ruledglyph - end - -end - -local ruledglue do - - local gluecodes = nodes.gluecodes - - local userskip_code = gluecodes.userskip - local spaceskip_code = gluecodes.spaceskip - local xspaceskip_code = gluecodes.xspaceskip - local zerospaceskip_code = gluecodes.zerospaceskip or gluecodes.userskip - -- local keepskip_code = gluecodes.keepskip or gluecodes.userskip - local leftskip_code = gluecodes.leftskip - local rightskip_code = gluecodes.rightskip - local parfillleftskip_code = gluecodes.parfillleftskip or parfillskip_code - local parfillrightskip_code = gluecodes.parfillrightskip or parfillskip_code - local indentskip_code = gluecodes.indentskip - local correctionskip_code = gluecodes.correctionskip - - local g_cache_v = caches["vglue"] - local g_cache_h = caches["hglue"] - - local tags = { - -- [userskip_code] = "US", - [gluecodes.lineskip] = "LI", - [gluecodes.baselineskip] = "BS", - [gluecodes.parskip] = "PS", - [gluecodes.abovedisplayskip] = "DA", - [gluecodes.belowdisplayskip] = "DB", - [gluecodes.abovedisplayshortskip] = "SA", - [gluecodes.belowdisplayshortskip] = "SB", - [gluecodes.topskip] = "TS", - [gluecodes.splittopskip] = "ST", - [gluecodes.tabskip] = "AS", - [gluecodes.lefthangskip] = "LH", - [gluecodes.righthangskip] = "RH", - [gluecodes.thinmuskip] = "MS", - [gluecodes.medmuskip] = "MM", - [gluecodes.thickmuskip] = "ML", - [gluecodes.intermathskip] = "IM", - [gluecodes.keepskip or 99] = "KS", - [gluecodes.mathskip] = "MT", - [gluecodes.leaders] = "NL", - [gluecodes.cleaders] = "CL", - [gluecodes.xleaders] = "XL", - [gluecodes.gleaders] = "GL", - -- true = "VS", - -- false = "HS", - [leftskip_code] = "LS", - [rightskip_code] = "RS", - [spaceskip_code] = "SP", - [xspaceskip_code] = "XS", - [zerospaceskip_code] = "ZS", - [parfillleftskip_code] = "PL", - [parfillrightskip_code] = "PR", - [indentskip_code] = "IN", - [correctionskip_code] = "CS", - } - - -- we sometimes pass previous as we can have issues in math (not watertight for all) - - ruledglue = function(head,current,vertical,parent) - local subtype = getsubtype(current) - local width = effectiveglue(current,parent) - local amount = formatters["%s:%0.3f"](tags[subtype] or (vertical and "VS") or "HS",width*pt_factor) - local info = (vertical and g_cache_v or g_cache_h)[amount] - if info then - -- print("glue hit") - else - if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then - info = sometext(amount,l_glue,c_space) - elseif subtype == leftskip_code or subtype == rightskip_code then - info = sometext(amount,l_glue,c_skip_a) - elseif subtype == parfillleftskip_code or subtype == parfillrightskip_code or subtype == indentskip_code or subtype == correctionskip_code then - info = sometext(amount,l_glue,c_indent) - elseif subtype == userskip_code then - if width > 0 then - info = sometext(amount,l_glue,c_positive) - elseif width < 0 then - info = sometext(amount,l_glue,c_negative) - else - info = sometext(amount,l_glue,c_zero) - end - else - info = sometext(amount,l_glue,c_skip_b) - end - (vertical and g_cache_v or g_cache_h)[amount] = info - end - info = copy_list(info) - if vertical then - info = vpack_nodes(info) - end - head, current = insert_node_before(head,current,info) - return head, getnext(current) - end - - -- ruledspace = function(head,current,parent) - -- local subtype = getsubtype(current) - -- if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then - -- local width = effectiveglue(current,parent) - -- local amount = formatters["%s:%0.3f"](tags[subtype] or "HS",width*pt_factor) - -- local info = g_cache_h[amount] - -- if info then - -- -- print("space hit") - -- else - -- info = sometext(amount,l_glue,c_space) - -- g_cache_h[amount] = info - -- end - -- info = copy_list(info) - -- head, current = insert_node_before(head,current,info) - -- return head, getnext(current) - -- else - -- return head, current - -- end - -- end - - local g_cache_s = caches["space"] - local g_cache_x = caches["xspace"] - - ruledspace = function(head,current,parent) - local subtype = getsubtype(current) - if subtype == spaceskip_code or subtype == xspaceskip_code or subtype == zerospaceskip_code then -- not yet all space - local width = effectiveglue(current,parent) - local info - if subtype == spaceskip_code then - info = g_cache_s[width] - if not info then - info = someblob("SP",l_glue,c_space,nil,width) - g_cache_s[width] = info - end - else - info = g_cache_x[width] - if not info then - info = someblob("XS",l_glue,c_space_x,nil,width) - g_cache_x[width] = info - end - end - info = copy_list(info) - head, current = insert_node_before(head,current,info) - return head, getnext(current) - else - return head, current - end - end - -end - -local ruledkern do - - local k_cache_v = caches["vkern"] - local k_cache_h = caches["hkern"] - - ruledkern = function(head,current,vertical,mk) - local kern = getkern(current) - local cache = vertical and k_cache_v or k_cache_h - local info = cache[kern] - if not info then - local amount = formatters["%s:%0.3f"](vertical and "VK" or (mk and "MK") or "HK",kern*pt_factor) - if kern > 0 then - info = sometext(amount,l_kern,c_positive) - elseif kern < 0 then - info = sometext(amount,l_kern,c_negative) - else - info = sometext(amount,l_kern,c_zero) - end - cache[kern] = info - end - info = copy_list(info) - if vertical then - info = vpack_nodes(info) - end - head, current = insert_node_before(head,current,info) - return head, getnext(current) - end - -end - -local ruleditalic do - - local i_cache = caches["italic"] - - ruleditalic = function(head,current) - local kern = getkern(current) - local info = i_cache[kern] - if not info then - local amount = formatters["%s:%0.3f"]("IC",kern*pt_factor) - if kern > 0 then - info = sometext(amount,l_kern,c_positive) - elseif kern < 0 then - info = sometext(amount,l_kern,c_negative) - else - info = sometext(amount,l_kern,c_zero) - end - i_cache[kern] = info - end - info = copy_list(info) - head, current = insert_node_before(head,current,info) - return head, getnext(current) - end - -end - -local ruledmarginkern do - - local m_cache = caches["marginkern"] - - ruledmarginkern = function(head,current) - local kern = getkern(current) - local info = m_cache[kern] - if not info then - local amount = formatters["%s:%0.3f"]("MK",kern*pt_factor) - if kern > 0 then - info = sometext(amount,l_marginkern,c_positive) - elseif kern < 0 then - info = sometext(amount,l_marginkern,c_negative) - else - info = sometext(amount,l_marginkern,c_zero) - end - m_cache[kern] = info - end - info = copy_list(info) - head, current = insert_node_before(head,current,info) - return head, getnext(current) - end - -end - -local ruledmathlistkern do - - local l_cache = caches["mathlistkern"] - - ruledmathlistkern = function(head,current) - local kern = getkern(current) - local info = l_cache[kern] - if not info then - local amount = formatters["%s:%0.3f"]("LK",kern*pt_factor) - if kern > 0 then - info = sometext(amount,l_mathlistkern,c_positive) - elseif kern < 0 then - info = sometext(amount,l_mathlistkern,c_negative) - else - info = sometext(amount,l_mathlistkern,c_zero) - end - l_cache[kern] = info - end - info = copy_list(info) - head, current = insert_node_before(head,current,info) - return head, getnext(current) - end - -end - -local ruleddiscretionary do - - local d_cache = caches["discretionary"] - - ruleddiscretionary = function(head,current) - local d = d_cache[true] - if not the_discretionary then - local rule = new_rule(4*emwidth/fraction,4*exheight,exheight) - local kern = new_kern(-2*emwidth/fraction) - setlink(kern,rule) - setcolor(rule,c_discretionary_d) - settransparency(rule,c_discretionary_d) - setattr(rule,a_layer,l_discretionary) - d = new_hlist(kern) - d_cache[true] = d - end - insert_node_after(head,current,copy_list(d)) - return head, current - end - -end - -local ruledpenalty do - - local p_cache_v = caches["vpenalty"] - local p_cache_h = caches["hpenalty"] - - local raisepenalties = false - - directives.register("visualizers.raisepenalties",function(v) raisepenalties = v end) - - ruledpenalty = function(head,current,vertical) - local penalty = getpenalty(current) - local info = (vertical and p_cache_v or p_cache_h)[penalty] - if info then - -- print("penalty hit") - else - local amount = formatters["%s:%s"](vertical and "VP" or "HP",penalty) - if penalty > 0 then - info = sometext(amount,l_penalty,c_positive) - elseif penalty < 0 then - info = sometext(amount,l_penalty,c_negative) - else - info = sometext(amount,l_penalty,c_zero) - end - (vertical and p_cache_v or p_cache_h)[penalty] = info - end - info = copy_list(info) - if vertical then - info = vpack_nodes(info) - elseif raisepenalties then - setshift(info,-65536*4) - end - head, current = insert_node_before(head,current,info) - return head, getnext(current) - end - -end - -do - - local disc_code = nodecodes.disc - local kern_code = nodecodes.kern - local glyph_code = nodecodes.glyph - local glue_code = nodecodes.glue - local penalty_code = nodecodes.penalty - local whatsit_code = nodecodes.whatsit - local user_code = nodecodes.user - local math_code = nodecodes.math - local hlist_code = nodecodes.hlist - local vlist_code = nodecodes.vlist - local marginkern_code = nodecodes.marginkern - local mathlistkern_code = nodecodes.mathlistkern - local dir_code = nodecodes.dir - local par_code = nodecodes.par - - local kerncodes = nodes.kerncodes - local fontkern_code = kerncodes.fontkern - local italickern_code = kerncodes.italiccorrection - local leftmarginkern_code = kerncodes.leftmarginkern - local rightmarginkern_code = kerncodes.rightmarginkern - local mathlistkern_code = kerncodes.mathlistkern - ----- userkern_code = kerncodes.userkern - - local listcodes = nodes.listcodes - local linelist_code = listcodes.line - - local vtop_package_state = 3 -- todo: symbolic - - local cache - - local function visualize(head,vertical,forced,parent) - local trace_hbox = false - local trace_vbox = false - local trace_vtop = false - local trace_kern = false - local trace_glue = false - local trace_penalty = false - local trace_fontkern = false - local trace_strut = false - local trace_whatsit = false - local trace_glyph = false - local trace_simple = false - local trace_user = false - local trace_math = false - local trace_italic = false - local trace_origin = false - local trace_discretionary = false - local trace_expansion = false - local trace_line = false - local trace_space = false - local trace_depth = false - local trace_dir = false - local trace_par = false - local current = head - local previous = nil - local attr = unsetvalue - local prev_trace_fontkern = nil - local prev_trace_italic = nil - local prev_trace_marginkern = nil --- local prev_trace_mathlist = nil - local prev_trace_expansion = nil - - while current do - local id = getid(current) - local a = forced or getattr(current,a_visual) or unsetvalue - local subtype - if a ~= attr then - prev_trace_fontkern = trace_fontkern - prev_trace_italic = trace_italic - prev_trace_marginkern = trace_marginkern --- prev_trace_mathlistkern = trace_mathlistkern - prev_trace_expansion = trace_expansion - attr = a - if a == unsetvalue then - trace_hbox = false - trace_vbox = false - trace_vtop = false - trace_kern = false - trace_glue = false - trace_penalty = false - trace_fontkern = false - trace_strut = false - trace_whatsit = false - trace_glyph = false - trace_simple = false - trace_user = false - trace_math = false - trace_italic = false - trace_origin = false - trace_discretionary = false - trace_expansion = false - trace_line = false - trace_space = false - trace_depth = false - trace_marginkern = false - trace_mathlistkern = false - trace_dir = false - trace_par = false - if id == kern_code then - goto kern - else - goto list - end - else -- dead slow: - -- cache[a]() - trace_hbox = band(a,0x000001) ~= 0 - trace_vbox = band(a,0x000002) ~= 0 - trace_vtop = band(a,0x000004) ~= 0 - trace_kern = band(a,0x000008) ~= 0 - trace_glue = band(a,0x000010) ~= 0 - trace_penalty = band(a,0x000020) ~= 0 - trace_fontkern = band(a,0x000040) ~= 0 - trace_strut = band(a,0x000080) ~= 0 - trace_whatsit = band(a,0x000100) ~= 0 - trace_glyph = band(a,0x000200) ~= 0 - trace_simple = band(a,0x000400) ~= 0 - trace_user = band(a,0x000800) ~= 0 - trace_math = band(a,0x001000) ~= 0 - trace_italic = band(a,0x002000) ~= 0 - trace_origin = band(a,0x004000) ~= 0 - trace_discretionary = band(a,0x008000) ~= 0 - trace_expansion = band(a,0x010000) ~= 0 - trace_line = band(a,0x020000) ~= 0 - trace_space = band(a,0x040000) ~= 0 - trace_depth = band(a,0x080000) ~= 0 - trace_marginkern = band(a,0x100000) ~= 0 - trace_mathlistkern = band(a,0x200000) ~= 0 - trace_dir = band(a,0x400000) ~= 0 - trace_whatsit = band(a,0x800000) ~= 0 - end - elseif a == unsetvalue then - goto list - end - if trace_strut then - setattr(current,a_layer,l_strut) - elseif id == glyph_code then - if trace_glyph then - head, current = ruledglyph(head,current,previous) - end - if trace_expansion then - head, current = glyphexpansion(head,current) - end - elseif id == disc_code then - if trace_discretionary then - head, current = ruleddiscretionary(head,current) - end - local pre, post, replace = getdisc(current) - if pre then - pre = visualize(pre,false,a,parent) - end - if post then - post = visualize(post,false,a,parent) - end - if replace then - replace = visualize(replace,false,a,parent) - end - setdisc(current,pre,post,replace) - elseif id == kern_code then - goto kern - elseif id == glue_code then - local content = getleader(current) - if content then - setleader(current,visualize(content,false,nil,parent)) - elseif trace_glue then - head, current = ruledglue(head,current,vertical,parent) - elseif trace_space then - head, current = ruledspace(head,current,parent) - end - elseif id == penalty_code then - if trace_penalty then - head, current = ruledpenalty(head,current,vertical) - end - elseif id == hlist_code or id == vlist_code then - goto list - elseif id == whatsit_code then - if trace_whatsit then - head, current = whatsit(head,current) - end - elseif id == user_code then - if trace_user then - head, current = user(head,current) - end - elseif id == math_code then - if trace_math then - head, current = math(head,current) - end - elseif id == marginkern_code then - if trace_kern then - head, current = ruledkern(head,current,vertical,true) - end - elseif id == dir_code then - if trace_dir then - head, current = dir(head,current) - end - elseif id == par_code then - if trace_par then - head, current = par(head,current) - end - end - goto next - ::kern:: - subtype = getsubtype(current) - if subtype == fontkern_code then - if trace_fontkern or prev_trace_fontkern then - head, current = fontkern(head,current) - end - if trace_expansion or prev_trace_expansion then - head, current = kernexpansion(head,current) - end - elseif subtype == italickern_code then - if trace_italic or prev_trace_italic then - head, current = italickern(head,current) - elseif trace_kern then - head, current = ruleditalic(head,current) - end - elseif subtype == leftmarginkern_code or subtype == rightmarginkern_code then - if trace_marginkern or prev_trace_marginkern then - head, current = marginkern(head,current) - elseif trace_kern then - head, current = ruledmarginkern(head,current) - end - elseif subtype == mathlistkern_code then - if trace_mathlist then -- or prev_trace_mathlist then - head, current = mathlistkern(head,current) - elseif trace_kern then - head, current = ruledmathlistkern(head,current) - end - else - if trace_kern then - head, current = ruledkern(head,current,vertical) - end - end - goto next; - ::list:: - if id == hlist_code then - local content = getlist(current) - if content then - setlist(current,visualize(content,false,nil,current)) - end - if trace_depth then - ruleddepth(current) - end - if trace_line and getsubtype(current) == linelist_code then - head, current = ruledbox(head,current,false,l_line,"L__",trace_simple,previous,trace_origin,parent) - elseif trace_hbox then - head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent) - end - elseif id == vlist_code then - local content = getlist(current) - local isvtop = getstate(current) == vtop_package_state - local tag = nil - local layer = nil - if content then - setlist(current,visualize(content,true,nil,current)) - end - if trace_vtop then - if isvtop then - tag = "_T_" - layer = l_vtop - elseif trace_vbox then - tag = "__V" - layer = l_vbox - end - elseif trace_vbox then - if not isvtop then - tag = "__V" - layer = l_vbox - end - end - if tag then - head, current = ruledbox(head,current,true,layer,tag,trace_simple,previous,trace_origin,parent) - end - end - ::next:: - previous = current - current = getnext(current) - end - return head - end - - local function cleanup() - for tag, cache in next, caches do - for k, v in next, cache do - flush_node_list(v) - end - end - cleanup = function() - report_visualize("error, duplicate cleanup") - end - end - - luatex.registerstopactions(cleanup) - - function visualizers.handler(head) - if usedfont then - starttiming(visualizers) - head = visualize(head,true) - stoptiming(visualizers) - return head, true - else - return head, false - end - end - - function visualizers.box(n) - if usedfont then - starttiming(visualizers) - local box = getbox(n) - if box then - setlist(box,visualize(getlist(box),getid(box) == vlist_code)) - end - stoptiming(visualizers) - return head, true - else - return head, false - end - end - -end - -do - - local hlist_code = nodecodes.hlist - local vlist_code = nodecodes.vlist - local nextnode = nuts.traversers.node - - local last = nil - local used = nil - - local mark = { - "trace:1", "trace:2", "trace:3", - "trace:4", "trace:5", "trace:6", - "trace:7", - } - - local function markfonts(list) - for n, id in nextnode, list do - if id == glyph_code then - local font = getfont(n) - local okay = used[font] - if not okay then - last = last + 1 - okay = mark[last] - used[font] = okay - end - setcolor(n,okay) - elseif id == hlist_code or id == vlist_code then - markfonts(getlist(n)) - end - end - end - - function visualizers.markfonts(list) - last, used = 0, { } - markfonts(type(n) == "number" and getlist(getbox(n)) or n) - end - -end - -statistics.register("visualization time",function() - if enabled then - -- cleanup() -- in case we don't don't do it each time - return formatters["%s seconds"](statistics.elapsedtime(visualizers)) - end -end) - --- interface - -do - - local implement = interfaces.implement - - implement { - name = "setvisual", - arguments = "string", - actions = visualizers.setvisual - } - - implement { - name = "setvisuals", - arguments = "string", - actions = visualizers.setvisual - } - - implement { - name = "getvisual", - arguments = "string", - actions = { setvisual, context } - } - - implement { - name = "setvisuallayer", - arguments = "string", - actions = visualizers.setlayer - } - - implement { - name = "markvisualfonts", - arguments = "integer", - actions = visualizers.markfonts - } - - implement { - name = "setvisualfont", - arguments = "integer", - actions = visualizers.setfont - } - -end - --- Here for now: - -do - - local function make(str,forecolor,rulecolor,layer) - if initialize then - initialize() - end - local rule = new_rule(emwidth/fraction,exheight,4*exheight) - setcolor(rule,rulecolor) - settransparency(rule,rulecolor) - local info - if str == "" then - info = new_hlist(rule) - else - local text = hpack_string(str,usedfont) - local list = getlist(text) - setlistcolor(list,textcolor) - setlisttransparency(list,textcolor) - setshift(text,3.5 * exheight) - info = new_hlist(setlink(rule,text)) - end - setattr(info,a_layer,layer) - return info - end - - function visualizers.register(name,textcolor,rulecolor) - if rawget(layers,name) then - -- message - return - end - local cache = caches[name] - local layer = layers[name] - if not textcolor then - textcolor = c_text_d - end - if not rulecolor then - rulecolor = c_origin_d - end - return function(str) - if not str then - str = "" - end - local info = cache[str] - if not info then - info = make(str,textcolor,rulecolor,layer) - cache[str] = info - end - return copy_node(info) - end - end - -end |