summaryrefslogtreecommitdiff
path: root/tex/context/base/trac-vis.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/trac-vis.lua')
-rw-r--r--tex/context/base/trac-vis.lua1014
1 files changed, 0 insertions, 1014 deletions
diff --git a/tex/context/base/trac-vis.lua b/tex/context/base/trac-vis.lua
deleted file mode 100644
index b54182798..000000000
--- a/tex/context/base/trac-vis.lua
+++ /dev/null
@@ -1,1014 +0,0 @@
-if not modules then modules = { } end modules ['trac-vis'] = {
- version = 1.001,
- 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 string, number, table = string, number, table
-local node, nodes, attributes, fonts, tex = node, nodes, attributes, fonts, tex
-local type = type
-local format = string.format
-local formatters = string.formatters
-
--- 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.
-
--- We could use pdf literals and re stream codes but it's not worth the
--- trouble because we would end up in color etc mess. Maybe one day I'll
--- make a nodeinjection variant.
-
--- 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)
-
-local nodecodes = nodes.nodecodes
-local disc_code = nodecodes.disc
-local kern_code = nodecodes.kern
-local glyph_code = nodecodes.glyph
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-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 gluespec_code = nodecodes.gluespec
-
-local kerncodes = nodes.kerncodes
-local font_kern_code = kerncodes.fontkern
-local user_kern_code = kerncodes.userkern
-
-local gluecodes = nodes.gluecodes
-local cleaders_code = gluecodes.cleaders
-local userskip_code = gluecodes.userskip
-local space_code = gluecodes.space
-local xspace_code = gluecodes.xspace
-local leftskip_code = gluecodes.leftskip
-local rightskip_code = gluecodes.rightskip
-
-local whatsitcodes = nodes.whatsitcodes
-local mathcodes = nodes.mathcodes
-
-local nuts = nodes.nuts
-local tonut = nuts.tonut
-local tonode = nuts.tonode
-
-local getfield = nuts.getfield
-local getnext = nuts.getnext
-local getprev = nuts.getprev
-local getid = nuts.getid
-local setfield = nuts.setfield
-local getattr = nuts.getattr
-local setattr = nuts.setattr
-local getfont = nuts.getfont
-local getsubtype = nuts.getsubtype
-local getchar = nuts.getchar
-local getbox = nuts.getbox
-local getlist = nuts.getlist
-local getleader = nuts.getleader
-
-local hpack_nodes = nuts.hpack
-local vpack_nodes = nuts.vpack
-local copy_node = nuts.copy
-local copy_list = nuts.copy_list
-local free_node = nuts.free
-local free_node_list = nuts.flush_list
-local insert_node_before = nuts.insert_before
-local insert_node_after = nuts.insert_after
-local traverse_nodes = nuts.traverse
-local linked_nodes = nuts.linked
-
-local fast_hpack = nuts.fasthpack
-local fast_hpack_string = nuts.typesetters.fast_hpack
-
-local texgetattribute = tex.getattribute
-local texsetattribute = tex.setattribute
-
-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_penalty = nodepool.penalty
-
-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_fontkern = attributes.private("fontkern")
-local a_layer = attributes.private("viewerlayer")
-
-local hasbit = number.hasbit
-local bit = number.bit
-local setbit = number.setbit
-local clearbit = number.clearbit
-
-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 report_visualize = logs.reporter("visualize")
-
-local modes = {
- hbox = 1,
- vbox = 2,
- vtop = 4,
- kern = 8,
- glue = 16,
- penalty = 32,
- fontkern = 64,
- strut = 128,
- whatsit = 256,
- glyph = 512,
- simple = 1024,
- simplehbox = 1024 + 1,
- simplevbox = 1024 + 2,
- simplevtop = 1024 + 4,
- user = 2048,
- math = 4096,
-}
-
-local modes_makeup = { "hbox", "vbox", "kern", "glue", "penalty" }
-local modes_boxes = { "hbox", "vbox" }
-local modes_all = { "hbox", "vbox", "kern", "glue", "penalty", "fontkern", "whatsit", "glyph", "user", "math" }
-
-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
-
-local enabled = false
-local layers = { }
-
-local preset_boxes = modes.hbox + modes.vbox
-local preset_makeup = preset_boxes + modes.kern + modes.glue + modes.penalty
-local preset_all = preset_makeup + modes.fontkern + modes.whatsit + modes.glyph + modes.user + modes.math
-
-function visualizers.setfont(id)
- usedfont = id or current_font()
- exheight = exheights[usedfont]
- emwidth = emwidths[usedfont]
-end
-
--- we can preset a bunch of bits
-
-local function enable()
- 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
- nodes.tasks.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) -- this will become more efficient when we have the bit lib linked in
- if not n or n == "reset" then
- return unsetvalue
- elseif n == "makeup" then
- if not a or a == 0 or a == unsetvalue then
- a = preset_makeup
- else
- a = setbit(a,preset_makeup)
- end
- elseif n == "boxes" then
- if not a or a == 0 or a == unsetvalue then
- a = preset_boxes
- else
- a = setbit(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 = setbit(a,preset_all)
- end
- else
- local m = modes[n]
- if not m then
- -- go on
- elseif a == unsetvalue then
- if what == false then
- return unsetvalue
- else
- -- a = setbit(0,m)
- a = m
- end
- elseif what == false then
- a = clearbit(a,m)
- elseif not a or a == 0 then
- a = m
- else
- a = setbit(a,m)
- 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 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 = tonumber(v) or fraction 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_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"
-local c_math = "trace:r"
-
-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_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 function sometext(str,layer,color,textcolor,lap) -- we can just paste verbatim together .. no typesteting needed
- local text = fast_hpack_string(str,usedfont)
- local size = getfield(text,"width")
- 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 = linked_nodes(rule,kern,text)
- setlisttransparency(info,c_zero)
- info = fast_hpack(info)
- local width = getfield(info,"width")
- if lap then
- info = fast_hpack(linked_nodes(new_kern(-width),info))
- end
- if layer then
- setattr(info,a_layer,layer)
- end
- setfield(info,"width",0)
- setfield(info,"height",0)
- setfield(info,"depth",0)
- return info, width
-end
-
-local f_cache = { }
-
-local function fontkern(head,current)
- local kern = getfield(current,"kern")
- local info = f_cache[kern]
- if info then
- -- print("hit fontkern")
- else
- local text = fast_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,c_text_d)
- settransparency(rule,c_text_d)
- setfield(text,"shift",-5 * exheight)
- info = fast_hpack(linked_nodes(rule,text))
- setattr(info,a_layer,l_fontkern)
- setfield(info,"width",0)
- setfield(info,"height",0)
- setfield(info,"depth",0)
- f_cache[kern] = info
- end
- head = insert_node_before(head,current,copy_list(info))
- return head, current
-end
-
-local w_cache = { }
-local tags = {
- open = "FIC",
- write = "FIW",
- close = "FIC",
- special = "SPE",
- localpar = "PAR",
- dir = "DIR",
- pdfliteral = "PDF",
- pdfrefobj = "PDF",
- pdfrefxform = "PDF",
- pdfrefximage = "PDF",
- pdfannot = "PDF",
- pdfstartlink = "PDF",
- pdfendlink = "PDF",
- pdfdest = "PDF",
- pdfthread = "PDF",
- pdfstartthread = "PDF",
- pdfendthread = "PDF",
- pdfsavepos = "PDF",
- pdfthreaddata = "PDF",
- pdflinkdata = "PDF",
- pdfcolorstack = "PDF",
- pdfsetmatrix = "PDF",
- pdfsave = "PDF",
- pdfrestore = "PDF",
- latelua = "LUA",
- closelua = "LUA",
- cancelboundary = "CBD",
- userdefined = "USR",
-}
-
-local function whatsit(head,current)
- local what = getsubtype(current)
- local info = w_cache[what]
- if info then
- -- print("hit whatsit")
- else
- local tag = whatsitcodes[what]
- -- maybe different text colors per tag
- info = sometext(formatters["W:%s"](tag and tags[tag] or 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
-
-local u_cache = { }
-
-local function user(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
-
-local m_cache = { }
-local tags = {
- beginmath = "B",
- endmath = "E",
-}
-
-local function math(head,current)
- local what = getsubtype(current)
- local info = m_cache[what]
- if info then
- -- print("hit math")
- else
- local tag = mathcodes[what]
- info = sometext(formatters["M:%s"](tag and tags[tag] or what),usedfont,nil,c_math_d)
- setattr(info,a_layer,l_math)
- m_cache[what] = info
- end
- head, current = insert_node_after(head,current,copy_list(info))
- return head, current
-end
-
-local b_cache = { }
-
-local function ruledbox(head,current,vertical,layer,what,simple,previous)
- local wd = getfield(current,"width")
- if wd ~= 0 then
- local ht = getfield(current,"height")
- local dp = getfield(current,"depth")
- local shift = getfield(current,"shift")
- local next = getnext(current)
- local prev = previous
- -- local prev = getprev(current) -- prev can be wrong in math mode < 0.78.3
- setfield(current,"next",nil)
- setfield(current,"prev",nil)
- local linewidth = emwidth/fraction
- local baseline, baseskip
- if dp ~= 0 and ht ~= 0 then
- if wd > 20*linewidth then
- baseline = b_cache.baseline
- if not baseline then
- -- due to an optimized leader color/transparency we need to set the glue node in order
- -- to trigger this mechanism
- local leader = linked_nodes(new_glue(2*linewidth),new_rule(6*linewidth,linewidth,0),new_glue(2*linewidth))
- -- setlisttransparency(leader,c_text)
- leader = fast_hpack(leader)
- -- setlisttransparency(leader,c_text)
- baseline = new_glue(0)
- setfield(baseline,"leader",leader)
- setfield(baseline,"subtype",cleaders_code)
- local spec = getfield(baseline,"spec")
- setfield(spec,"stretch",65536)
- setfield(spec,"stretch_order",2)
- setlisttransparency(baseline,c_text)
- b_cache.baseline = baseline
- end
- baseline = copy_list(baseline)
- baseline = fast_hpack(baseline,wd-2*linewidth)
- -- or new hpack node, set head and also:
- -- baseline.width = wd
- -- baseline.glue_set = wd/65536
- -- baseline.glue_order = 2
- -- baseline.glue_sign = 1
- baseskip = new_kern(-wd+linewidth)
- else
- baseline = new_rule(wd-2*linewidth,linewidth,0)
- baseskip = new_kern(-wd+2*linewidth)
- end
- end
- local this
- if not simple then
- this = b_cache[what]
- if not this then
- local text = fast_hpack_string(what,usedfont)
- this = linked_nodes(new_kern(-getfield(text,"width")),text)
- setlisttransparency(this,c_text)
- this = fast_hpack(this)
- setfield(this,"width",0)
- setfield(this,"height",0)
- setfield(this,"depth",0)
- b_cache[what] = this
- end
- end
- -- we need to trigger the right mode (else sometimes no whatits)
- local info = linked_nodes(
- this and copy_list(this) or nil,
- new_rule(linewidth,ht,dp),
- new_rule(wd-2*linewidth,-dp+linewidth,dp),
- new_rule(linewidth,ht,dp),
- new_kern(-wd+linewidth),
- new_rule(wd-2*linewidth,ht,-ht+linewidth)
- )
- if baseskip then
- info = linked_nodes(info,baseskip,baseline) -- could be in previous linked
- end
- local shft
- if shift == 0 then
- shift = nil
- else
- local sh = shift > 0 and shift or 0
- local sd = shift < 0 and - shift or 0
- shft = fast_hpack(new_rule(2*emwidth/fraction,sh,sd))
- setfield(shft,"width",0)
- if sh > 0 then
- setfield(shft,"height",0)
- end
- if sd > 0 then
- setfield(shft,"depth",0)
- end
- end
- setlisttransparency(info,c_text)
- info = fast_hpack(info)
- setfield(info,"width",0)
- setfield(info,"height",0)
- setfield(info,"depth",0)
- setattr(info,a_layer,layer)
- local info = linked_nodes(shft,current,new_kern(-wd),info)
- info = fast_hpack(info,wd)
- if vertical then
- info = vpack_nodes(info)
- end
- if shift then
- setfield(current,"shift",0)
- setfield(info,"width",wd)
- setfield(info,"height",ht)
- setfield(info,"depth",dp)
- setfield(info,"shift",shift)
- end
- if next then
- setfield(info,"next",next)
- setfield(next,"prev",info)
- end
- if prev then
- if getid(prev) == gluespec_code then
- report_visualize("ignoring invalid prev")
- -- weird, how can this happen, an inline glue-spec, probably math
- else
- setfield(info,"prev",prev)
- setfield(prev,"next",info)
- end
- end
- if head == current then
- return info, info
- else
- return head, info
- end
- else
- return head, current
- end
-end
-
-local function ruledglyph(head,current,previous)
- local wd = getfield(current,"width")
- -- local wd = chardata[getfield(current,"font")][getfield(current,"char")].width
- if wd ~= 0 then
- local ht = getfield(current,"height")
- local dp = getfield(current,"depth")
- local next = getnext(current)
- local prev = previous
- setfield(current,"next",nil)
- setfield(current,"prev",nil)
- local linewidth = emwidth/(2*fraction)
- local baseline
- -- if dp ~= 0 and ht ~= 0 then
- if (dp >= 0 and ht >= 0) or (dp <= 0 and ht <= 0) then
- baseline = new_rule(wd-2*linewidth,linewidth,0)
- end
- local doublelinewidth = 2*linewidth
- -- could be a pdf rule
- local info = linked_nodes(
- new_rule(linewidth,ht,dp),
- new_rule(wd-doublelinewidth,-dp+linewidth,dp),
- new_rule(linewidth,ht,dp),
- new_kern(-wd+linewidth),
- new_rule(wd-doublelinewidth,ht,-ht+linewidth),
- new_kern(-wd+doublelinewidth),
- baseline
- )
-local char = chardata[getfield(current,"font")][getfield(current,"char")]
-if char and char.tounicode and #char.tounicode > 4 then -- hack test
- setlistcolor(info,c_ligature)
- setlisttransparency(info,c_ligature_d)
-else
- setlistcolor(info,c_glyph)
- setlisttransparency(info,c_glyph_d)
-end
- info = fast_hpack(info)
- setfield(info,"width",0)
- setfield(info,"height",0)
- setfield(info,"depth",0)
- setattr(info,a_layer,l_glyph)
- local info = linked_nodes(current,new_kern(-wd),info)
- info = fast_hpack(info)
- setfield(info,"width",wd)
- if next then
- setfield(info,"next",next)
- setfield(next,"prev",info)
- end
- if prev then
- setfield(info,"prev",prev)
- setfield(prev,"next",info)
- end
- if head == current then
- return info, info
- else
- return head, info
- end
- else
- return head, current
- end
-end
-
-local g_cache = { }
-
-local tags = {
- -- userskip = "US",
- lineskip = "LS",
- baselineskip = "BS",
- parskip = "PS",
- abovedisplayskip = "DA",
- belowdisplayskip = "DB",
- abovedisplayshortskip = "SA",
- belowdisplayshortskip = "SB",
- leftskip = "LS",
- rightskip = "RS",
- topskip = "TS",
- splittopskip = "ST",
- tabskip = "AS",
- spaceskip = "SS",
- xspaceskip = "XS",
- parfillskip = "PF",
- thinmuskip = "MS",
- medmuskip = "MM",
- thickmuskip = "ML",
- leaders = "NL",
- cleaders = "CL",
- xleaders = "XL",
- gleaders = "GL",
- -- true = "VS",
- -- false = "HS",
-}
-
--- we sometimes pass previous as we can have issues in math (not watertight for all)
-
-local function ruledglue(head,current,vertical)
- local spec = getfield(current,"spec")
- local width = getfield(spec,"width")
- local subtype = getsubtype(current)
- local amount = formatters["%s:%0.3f"](tags[subtype] or (vertical and "VS") or "HS",width*pt_factor)
- local info = g_cache[amount]
- if info then
- -- print("glue hit")
- else
- if subtype == space_code or subtype == xspace_code then -- not yet all space
- 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 == 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
- g_cache[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
-
-local k_cache = { }
-
-local function ruledkern(head,current,vertical)
- local kern = getfield(current,"kern")
- local info = k_cache[kern]
- if info then
- -- print("kern hit")
- else
- local amount = formatters["%s:%0.3f"](vertical and "VK" 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
- k_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
-
-local p_cache = { }
-
-local function ruledpenalty(head,current,vertical)
- local penalty = getfield(current,"penalty")
- local info = p_cache[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
- p_cache[penalty] = 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
-
-local function visualize(head,vertical,forced)
- 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 current = head
- local previous = nil
- local attr = unsetvalue
- local prev_trace_fontkern = nil
- while current do
- local id = getid(current)
- local a = forced or getattr(current,a_visual) or unsetvalue
- if a ~= attr then
- prev_trace_fontkern = trace_fontkern
- 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
- else -- dead slow:
- trace_hbox = hasbit(a, 1)
- trace_vbox = hasbit(a, 2)
- trace_vtop = hasbit(a, 4)
- trace_kern = hasbit(a, 8)
- trace_glue = hasbit(a, 16)
- trace_penalty = hasbit(a, 32)
- trace_fontkern = hasbit(a, 64)
- trace_strut = hasbit(a, 128)
- trace_whatsit = hasbit(a, 256)
- trace_glyph = hasbit(a, 512)
- trace_simple = hasbit(a,1024)
- trace_user = hasbit(a,2048)
- trace_math = hasbit(a,4096)
- end
- attr = a
- 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
- elseif id == disc_code then
- local pre = getfield(current,"pre")
- if pre then
- setfield(current,"pre",visualize(pre,false,a))
- end
- local post = getfield(current,"post")
- if post then
- setfield(current,"post",visualize(post,false,a))
- end
- local replace = getfield(current,"replace")
- if replace then
- setfield(current,"replace",visualize(replace,false,a))
- end
- elseif id == kern_code then
- local subtype = getsubtype(current)
- -- tricky ... we don't copy the trace attribute in node-inj (yet)
- if subtype == font_kern_code or getattr(current,a_fontkern) then
- if trace_fontkern or prev_trace_fontkern then
- head, current = fontkern(head,current)
- end
- else -- if subtype == user_kern_code then
- if trace_kern then
- head, current = ruledkern(head,current,vertical)
- end
- end
- elseif id == glue_code then
- local content = getleader(current)
- if content then
- setfield(current,"leader",visualize(content,false))
- elseif trace_glue then
- head, current = ruledglue(head,current,vertical)
- end
- elseif id == penalty_code then
- if trace_penalty then
- head, current = ruledpenalty(head,current,vertical)
- end
- elseif id == hlist_code then
- local content = getlist(current)
- if content then
- setfield(current,"list",visualize(content,false))
- end
- if trace_hbox then
- head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous)
- end
- elseif id == vlist_code then
- local content = getlist(current)
- if content then
- setfield(current,"list",visualize(content,true))
- end
- if trace_vtop then
- head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple,previous)
- elseif trace_vbox then
- head, current = ruledbox(head,current,true,l_vbox,"__V",trace_simple,previous)
- end
- 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
- end
- previous = current
- current = getnext(current)
- end
- return head
-end
-
-local function freed(cache)
- local n = 0
- for k, v in next, cache do
- free_node_list(v)
- n = n + 1
- end
- if n == 0 then
- return 0, cache
- else
- return n, { }
- end
-end
-
-local function cleanup()
- local hf, ng, np, nk, nw
- nf, f_cache = freed(f_cache)
- ng, g_cache = freed(g_cache)
- np, p_cache = freed(p_cache)
- nk, k_cache = freed(k_cache)
- nw, w_cache = freed(w_cache)
- nb, b_cache = freed(b_cache)
- -- report_visualize("cache cleanup: %s fontkerns, %s skips, %s penalties, %s kerns, %s whatsits, %s boxes",nf,ng,np,nk,nw,nb)
-end
-
-local function handler(head)
- if usedfont then
- starttiming(visualizers)
- -- local l = texgetattribute(a_layer)
- -- local v = texgetattribute(a_visual)
- -- texsetattribute(a_layer,unsetvalue)
- -- texsetattribute(a_visual,unsetvalue)
- head = visualize(tonut(head))
- -- texsetattribute(a_layer,l)
- -- texsetattribute(a_visual,v)
- -- -- cleanup()
- stoptiming(visualizers)
- return tonode(head), true
- else
- return head, false
- end
-end
-
-visualizers.handler = handler
-
-function visualizers.box(n)
- if usedfont then
- starttiming(visualizers)
- local box = getbox(n)
- setfield(box,"list",visualize(getlist(box)))
- stoptiming(visualizers)
- return head, true
- else
- return head, false
- end
-end
-
-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 in traverse_nodes(list) do
- local id = getid(n)
- 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
-
-luatex.registerstopactions(cleanup)
-
-statistics.register("visualization time",function()
- if enabled then
- -- cleanup() -- in case we don't don't do it each time
- return format("%s seconds",statistics.elapsedtime(visualizers))
- end
-end)
-
--- interface
-
-local implement = interfaces.implement
-
-implement { name = "setvisual", 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 }
-