Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

This question led to a new package:
lua-visual-debug

Some of you may know the Web Developer Toolbar for Firefox that can outline the block level elements of a page like this:alt text

Is there a way to do something similar with TeX boxes for a complete document?

share|improve this question
1  
Fantastic idea! Sort of like putting a framebox around every box. I suppose that one wouldn't want quite every box. –  Andrew Stacey Aug 20 '10 at 15:09
    
@Andrew or at least one'd want to be able to parametrize the amount of boxes you'd display... –  Mikael Vejdemo-Johansson Aug 20 '10 at 15:20
    
@Mikael: I guess some sort of "depth parameter" would make sense. –  Andrew Stacey Aug 20 '10 at 15:26
add comment

4 Answers

up vote 29 down vote accepted

Another approach, usable with LuaLaTeX. The following sample document was made with the files below.

sample of the to-be-released-in-some-future lvdebug LaTeX package

The TeX input file

\documentclass{article}
\directlua{ require("drawboxes")}
\usepackage{graphicx,atbegshi}

\AtBeginShipout {\directlua{drawboxes.visual_debug()}}
\begin{document}
\hsize 3in
A wonderful serenity has taken possession of my entire soul, like these sweet
mornings of spring which I enjoy with my whole heart.

$$ e=mc^2 $$

\includegraphics[width=4cm]{cow} % from context distribution
\end{document}

and the Lua file (drawboxes.lua):

module(...,package.seeall)

local factor = 65782  -- PDF points vs. TeX points

-- The argument must be a box (hbox or vbox)
local function draw_elements(box)
  local parent = box
  local head   = box.list
  -- We are only interested in the contents of the box (box.list). But we
  -- keep the reference to the box (parent), so we know if we are in horizontal
  -- or vertical mode.

  -- head is a pointer to a node, which is a fundamental data structure in TeX
  -- for example: a node with id 1 denotes a \vbox with height, depth, and
  -- everything we know from TeX. A node with id 10 is a "glue" with the
  -- plus and minus and "1fill" values in a sub node (glue_spec)

  -- The contents of a box is a node list, connected by pointers in the attribute
  -- "next" that point to the next element in the list or nil, if there is no next
  -- element (= the last item in the box).
  while head do
    if head.id == 0 or head.id == 1 then
      -- we are in an hbox or in a vbox. Since we want to debug the contents
      -- of the box, we need to recursively call this function with the
      -- contents of the box. We supply the parent (= the current box), so
      -- we know if we are in vertical or horizontal mode and we know about the
      -- caluclated glue ratio (and sign)
      draw_elements(head)

      -- now that the contents of the current box is handled, we only need to draw
      -- a box around the contents of the box, which is stored in head.list. So we
      -- create a "pdf literal" node and insert it at the head of the list
      -- (and -- we must not forget that -- change the pointer to the contents
      -- of the list to the new "pdf literal", otherwise it exists but is not
      -- part of the box and therefore not inserted into the pdf).

      -- The dimensions of the box are stored in the attributes width, height and depth.
      local wd = head.width                  / factor
      local ht = (head.height + head.depth)  / factor
      local dp = head.depth                  / factor

      local pdfliteral = node.new("whatsit","pdf_literal")

      if head.id == 0 then -- hbox
        -- Wow, this looks complicated. It isn't. This instruction is a PDF instruction
        -- to draw a box (<ll_x> <ll_y> <ur_x> <ur_y> re s) with a 50% grey (0.5 g) and
        -- a rule width of 0.1 (0.1 w). This is enclosed in q .. Q so that the color
        -- change does not affect the next graphics operation in the PDF file.
        pdfliteral.data = string.format("q 0.5 G 0.1 w 0 %g %g %g re s Q", -dp, wd, ht)
      else
        -- a vbox is downwards, so the height must be negative
        pdfliteral.data = string.format("q 0.1 G 0.1 w 0 %g %g %g re s Q", 0, wd, -ht)
      end

      -- node.insert_before( head_of_list, current_node, node_to_insert)
      -- inserts the new node (pdfliteral) before the the first entry of the box
      -- and returns the new head of the list (which is identical to the pdf literal)
      head.list = node.insert_before(head.list,head.list,pdfliteral)

    elseif head.id == 10 then -- glue
      local wd = head.spec.width -- the natural width of the glue
      local color = "0.5 G"
      -- The entries such as "plus 1fil" only take effect when the maximum glue order
      -- of the parent box has the same number of "l"s. If there is a glue with
      -- "0pt plus 1fil" and another one with "0pt plus 1 fill", the former has no effect.
      -- The glue_sign gives the "direction" (shrink/stretch) and the stretch_order and
      -- shrink_order give the maximum number of "l" of the fill commands. Only apply
      -- if they match. We also change the color of the markers to differantiate between
      -- no stretch/shrink (gray), stretch (blue) and shrink (magenta).
      if parent.glue_sign == 1 and parent.glue_order == head.spec.stretch_order then
        wd = wd + parent.glue_set * head.spec.stretch
        color = "0 0 1 RG"
      elseif parent.glue_sign == 2 and parent.glue_order == head.spec.shrink_order then
        wd = wd - parent.glue_set * head.spec.shrink
        color = "1 0 1 RG"
      end

      pdfliteral = node.new("whatsit","pdf_literal")

      -- The parent.id tells us if the glue is horizontal or vertical
      if parent.id == 0 then --hlist
        -- The horizontal glue is drawn with a dash pattern of [0.2] 0 for small dots
        pdfliteral.data = string.format("q %s [0.2] 0 d  0.5 w 0 0  m %g 0 l s Q",color,wd / factor)
      else -- vlist
        -- The vertical glue is drawn with tiny marks at the beginning and the end
        -- and also a small dash pattern. Therefore the PDF string is rahter long.
        pdfliteral.data = string.format("q 0.1 G 0.1 w -0.5 0 m 0.5 0 l -0.5 %g m 0.5 %g l s [0.2] 0 d  0.5 w 0.25 0  m 0.25 %g l s Q",-wd / factor,-wd / factor,-wd / factor)
      end
      node.insert_before(parent.list,head,pdfliteral)
    else
      -- Any other node (for example a glyph node - a character). Probably not interesting.
    end
    -- next node in our list. If the list is at the end, head becomes nil and
    -- the loop ends.
    head = head.next
  end
end

-- The box "AtBeginShipoutBox" holds the page contents.
function visual_debug()
  draw_elements(tex.box["AtBeginShipoutBox"])
end
share|improve this answer
    
Patrick this is a now piece of code. Can you please put some commentary, especially the pdfliteral part interaction? –  Yiannis Lazarides Feb 22 '12 at 17:11
    
@YiannisLazarides I have tried to be a bit more verbose. BTW this package (with some enhancements) is uploaded to CTAN. –  topskip Feb 22 '12 at 19:14
    
Thanks very much appreciated. This is more than adequate, will have a look at CTAN (still propagating). –  Yiannis Lazarides Feb 22 '12 at 19:52
add comment

Hans Hagen has written a "package" for that. See http://pragma-ade.com/articles/art-visi.pdf

This is what the introduction looks like:

visual debugging introduction

share|improve this answer
    
+1 for the pointer, Patrick. Please pass it on to Hans whenever you get the chance :)) –  Geoffrey Jones Aug 20 '10 at 22:08
    
I will, in September: meeting.contextgarden.net :) –  topskip Aug 21 '10 at 8:02
add comment

I believe I once came across a tool that parsed synctex files (produced by running pdftex with the --synctex=-1 option) to produce this kind of display, but I can not find it at the moment.

Edit: I found it. Jerome Laurens demonstrates the tool in this presentation, starting at about 6:30. I am not sure whether this is a publicly available tool.

share|improve this answer
add comment

You could probably write an output routine that does this, possibly by using \vsplit and \unvbox or something. Seems like it'd be tricky.

It is possible to get TeX to write the contents of every box it outputs to the log (also to standard out, but that's probably overkill).

\documentclass{article}
\usepackage{lipsum}
\newtoks\realoutput
\realoutput\output
\output{%
        \batchmode
        \showboxbreadth\maxdimen
        \showboxdepth\maxdimen
        \showbox255
        \the\realoutput
}
\begin{document}
\lipsum
\end{document}

This saves the current output routine into a new token register \realoutput and then when the output routine is called from the page builder (I think), it dumps the contents of \box255 which contains the page. Of course, the output routine need not actually use all of \box255 so the log can contain extra information.

Of course, the output is, uh, verbose.

> \box255=
\vbox(550.0+1.94444)x345.0
.\write-{}
.\glue(\topskip) 3.05556
.\hbox(6.94444+1.94444)x345.0, glue set 0.85849
..\hbox(0.0+0.0)x15.0
..\OT1/cmr/m/n/10 L
..\OT1/cmr/m/n/10 o
..\OT1/cmr/m/n/10 r
..\OT1/cmr/m/n/10 e
..\OT1/cmr/m/n/10 m
..\glue 3.33333 plus 1.66666 minus 1.11111
..\OT1/cmr/m/n/10 i
..\OT1/cmr/m/n/10 p
..\discretionary
...\OT1/cmr/m/n/10 -
..\OT1/cmr/m/n/10 s
..\OT1/cmr/m/n/10 u
..\OT1/cmr/m/n/10 m

You can see the "Lorem ipsum" there as well as glue for spaces and basically every bit of information you'd want to know about a box.

Unfortunately, it's not exactly a visual output.

share|improve this answer
    
You could use the atbegshi package to grab the box that is really shipped out. Or \tracingoutput=1 IIRC. –  Bruno Le Floch Feb 22 '12 at 19:39
1  
You don't need to change the output routine here, just use the standard LaTeX \showoutput command –  David Carlisle May 11 '12 at 12:57
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.