summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/trac-vis.lmt
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/trac-vis.lmt')
-rw-r--r--tex/context/base/mkiv/trac-vis.lmt1700
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