Understanding TeX Boxes with LuaTeX - Advanced Guide
Deep dive into TeX’s box model using LuaTeX. Learn how LaTeX creates pages, debug layout issues, and manipulate boxes programmatically.
Explore the inner workings of TeX’s box model using LuaTeX’s powerful Lua integration. This advanced guide reveals how LaTeX constructs documents internally and provides practical techniques for debugging and manipulating the typesetting process.
Advanced Topic: This guide assumes strong LaTeX knowledge and basic programming experience. For LaTeX basics, start with Creating Your First Document.
In TeX, everything on a page is built from boxes. Think of boxes as rectangular containers that hold content:
Rendered Output
The TeX Box Hierarchy shows the progression from smallest to largest units: Character (single glyph box) flows into hbox (horizontal list of characters/words), which combines into vbox (vertical list of lines/paragraphs), ultimately forming the complete Page output. Each level nests within the next, building the document structure from individual glyphs up to full pages.
\documentclass{article}\usepackage{tikz}\directlua{ function draw_box_structure(n, x, y) local box = tex.box[n] if not box then return end -- Draw box outline tex.print("\string\\draw[red,thick] (" .. x .. "," .. y .. ") rectangle +(" .. box.width/65536 .. "pt," .. box.height/65536 .. "pt);") -- Draw baseline tex.print("\string\\draw[blue,dashed] (" .. x .. "," .. y .. ") -- +(" .. box.width/65536 .. "pt,0);") -- Add dimensions tex.print("\string\\node[above,font=\string\\tiny] at (" .. x + box.width/131072 .. "," .. y + box.height/65536 .. ") {" .. string.format("%.1f", box.width/65536) .. "pt};") end}\begin{document}\setbox0=\hbox{Sample Text}\begin{tikzpicture}\directlua{draw_box_structure(0, 0, 0)}\node at (0,0) {\box0};\end{tikzpicture}\end{document}
Rendered output:
Rendered Output
The visualization displays “Sample Text” enclosed in a red rectangular border representing the hbox boundaries. A blue dashed line runs horizontally through the box indicating the baseline. Above the box, the width measurement “53.1pt” is displayed, showing the precise box dimensions calculated by TeX. This visual debugging technique helps identify box boundaries, baselines, and measurements during document development.
\documentclass{article}\directlua{ function add_color_to_glyphs(head) for n in node.traverse(head) do if n.id == node.id("glyph") then -- Insert color node before glyph local color = node.new(node.id("whatsit"), node.subtype("pdf_colorstack")) color.stack = 0 color.cmd = 1 -- push color.data = "1 0 0 rg" -- red color head = node.insert_before(head, n, color) -- Insert color reset after glyph local reset = node.new(node.id("whatsit"), node.subtype("pdf_colorstack")) reset.stack = 0 reset.cmd = 2 -- pop head = node.insert_after(head, n, reset) end end return head end -- Register callback luatexbase.add_to_callback("pre_linebreak_filter", add_color_to_glyphs, "color glyphs")}\begin{document}This text will have each character colored individually!\end{document}
\documentclass{article}\directlua{ function analyze_paragraph_boxes() local head = tex.lists.page_head if not head then return end local line_count = 0 local total_badness = 0 for n in node.traverse(head) do if n.id == node.id("hlist") then line_count = line_count + 1 -- Check glue settings if n.glue_sign == 1 then -- stretching print("Line " .. line_count .. " stretched by factor " .. n.glue_set) elseif n.glue_sign == 2 then -- shrinking print("Line " .. line_count .. " shrunk by factor " .. n.glue_set) end end end end -- Add to shipout callback luatexbase.add_to_callback("pre_shipout_filter", function(head) analyze_paragraph_boxes() return head end, "analyze paragraphs")}\begin{document}\parbox{3cm}{This is a narrow paragraph that will likely have some badly stretched or compressed lines that we can detect and analyze using our Lua code.}\end{document}
\documentclass{article}\usepackage{xcolor}\directlua{ -- Highlight overfull hboxes function highlight_overfull_boxes(head, groupcode) for n in node.traverse(head) do if n.id == node.id("hlist") and n.width > tex.hsize then -- Create a rule to highlight local rule = node.new(node.id("rule")) rule.width = n.width rule.height = n.height rule.depth = n.depth -- Add color local color = node.new(node.id("whatsit"), node.subtype("pdf_colorstack")) color.stack = 0 color.cmd = 1 color.data = "1 0 0 0.2 k" -- light red n.head = node.insert_before(n.head, n.head, color) n.head = node.insert_before(n.head, n.head, rule) end end return head end luatexbase.add_to_callback("post_linebreak_filter", highlight_overfull_boxes, "highlight overfull")}\begin{document}This line contains a verylongwordthatwillcauseanoverfullhbox in our text.\end{document}
\documentclass{article}\directlua{ function custom_linebreak_filter(head, is_display) -- Get natural breaks local copy = node.copy_list(head) local params = { hsize = tex.hsize, emergencystretch = tex.emergencystretch, pretolerance = tex.pretolerance, tolerance = tex.tolerance } local breaks, info = tex.linebreak(copy, params) -- Analyze break quality if info.prevgraf > 5 then -- For long paragraphs, try different parameters params.tolerance = 2000 params.emergencystretch = tex.sp("3em") local alt_breaks, alt_info = tex.linebreak(head, params) if alt_info.demerits < info.demerits then print("Using alternative line breaking") return alt_breaks end end return breaks end luatexbase.add_to_callback("linebreak_filter", custom_linebreak_filter, "custom linebreak")}\begin{document}\noindent This paragraph demonstrates custom line breaking logic that adjusts parameters based on paragraph length and quality metrics.\end{document}
\documentclass{article}\usepackage{xcolor}\directlua{ local show_boxes = true function visualize_boxes(head, groupcode) if not show_boxes then return head end for n in node.traverse(head) do if n.id == node.id("hlist") or n.id == node.id("vlist") then -- Add colored frame local rule = node.new(node.id("rule")) rule.width = tex.sp("0.1pt") rule.height = n.height + tex.sp("2pt") rule.depth = n.depth + tex.sp("2pt") -- Different colors for different box types local color = n.id == node.id("hlist") and "0 0 1" or "1 0 0" -- blue/red -- Insert visualization -- (simplified for clarity) end end return head end}% Toggle command\newcommand{\showboxes}{\directlua{show_boxes = true}}\newcommand{\hideboxes}{\directlua{show_boxes = false}}\begin{document}\showboxesThis text will show box boundaries.\hideboxesThis text will not.\end{document}
\documentclass{article}\directlua{ local stats = { total_boxes = 0, total_glyphs = 0, total_glue = 0, processing_time = 0 } function profile_document(head) local start_time = os.clock() for n in node.traverse_id(node.id("hlist"), head) do stats.total_boxes = stats.total_boxes + 1 for m in node.traverse(n.head) do if m.id == node.id("glyph") then stats.total_glyphs = stats.total_glyphs + 1 elseif m.id == node.id("glue") then stats.total_glue = stats.total_glue + 1 end end end stats.processing_time = os.clock() - start_time return head end function print_stats() print("\string\nDocument Statistics:") print(" Total boxes: " .. stats.total_boxes) print(" Total glyphs: " .. stats.total_glyphs) print(" Total glue nodes: " .. stats.total_glue) print(" Processing time: " .. string.format("%.3f", stats.processing_time) .. "s") end luatexbase.add_to_callback("post_linebreak_filter", profile_document, "profile")}\AtEndDocument{\directlua{print_stats()}}\begin{document}Your document content here...\end{document}
LuaTeX Box Commands Quick Reference covers three key areas: Box Access functions include tex.box[n] for accessing numbered boxes, node.traverse(head) for iterating node lists, node.traverse_id(id, head) for type-specific traversal, and node.copy_list(head) for duplicating lists. Node Properties include node.id/next/prev for navigation, box.width/height/depth for dimensions, glyph.char/font for character info, and glue.width/stretch for spacing. Common Callbacks include pre_linebreak_filter, post_linebreak_filter, pre_shipout_filter, and buildpage_filter for intercepting TeX processing at different stages.
LaTeX Cloud Studio supports LuaTeX! Enable it in your project settings to use these advanced features. Our platform provides enhanced debugging output and visualization tools.