Cajas

Si se pregunta cuál es la idea más fundamental de TeX, la respuesta no son las fuentes, ni las matemáticas, ni el algoritmo de salto de línea: es la caja. Todo lo que LaTeX coloca en una página es una caja, un rectángulo con anchura, altura y profundidad, unido a sus vecinos mediante pegamento (glue) y en cuyo interior ya nadie vuelve a mirar una vez montado. Por eso \mbox impide que una palabra se parta, por eso una línea que se desborda se comunica como un Overfull \hbox, y por eso \parbox y minipage se separan justo donde lo hacen. Esta página recorre los comandos de caja que realmente se escriben —\mbox, \makebox, \framebox, \raisebox, \parbox, minipage, \newsavebox— partiendo de la única idea que hay debajo de todos ellos.

Anchura, altura y profundidad: por qué una caja tiene tres dimensiones

Una caja tiene tres dimensiones, no dos: anchura, altura y profundidad. La altura se mide hacia arriba desde la línea base y la profundidad hacia abajo, de modo que una caja no es un rectángulo trazado alrededor de la tinta sino un rectángulo colgado de la línea sobre la que se apoyan las letras. Los tres valores se imprimen con \the\wd, \the\ht y \the\dp. En Computer Modern de 10pt, la palabra Hx metida en una caja tiene profundidad 0.0pt; con Hxg la profundidad pasa a 1.94444pt, porque la g baja por debajo de la línea base. La anchura también creció, claro está, pero la profundidad creció por un motivo completamente distinto.

Este único hecho resuelve un enigma con el que casi todo el mundo tropieza antes de saber qué es una caja: ¿por qué \fbox{Hx} queda ceñido a las letras mientras que \fbox{Hxg} parece descolgarse? Porque \fbox enmarca la caja y no las letras, y la caja de Hxg es 1,94pt más profunda. Alinee una serie de palabras enmarcadas en una fila de tabla y los marcos suben y bajan al ritmo de los rasgos descendentes. El remedio es un strut: \fbox{\strut Hx} y \fbox{\strut Hxg} salen con exactamente la misma altura y profundidad, porque \strut es una caja invisible de anchura cero que reclama 0,7 veces el \baselineskip actual por encima de la línea base y 0,3 veces por debajo. Más abajo hay una sección dedicada a ellos.

latex
\newsavebox{\probe}
\newcommand{\dims}[1]{\sbox{\probe}{#1}%
  \typeout{[#1] w=\the\wd\probe\space h=\the\ht\probe\space d=\the\dp\probe}}

\dims{Hx}    % w=12.77782pt  h=6.83331pt  d=0.0pt      <- no descender
\dims{Hxg}   % w=17.77783pt  h=6.83331pt  d=1.94444pt  <- the g adds depth

% same words framed: only the second frame sinks below the baseline
\fbox{Hx}\quad\fbox{Hxg}\quad \fbox{\strut Hx}\quad\fbox{\strut Hxg}

\mbox: cómo impedir un salto de línea y la partición de palabras

\mbox{...} mantiene su contenido en una sola línea y desactiva la partición de palabras dentro de él. La razón está en la propia definición: LaTeX declara \mbox como nada más que \leavevmode\hbox{#1}. Una \hbox es una caja ya montada, y TeX nunca vuelve a entrar en una caja terminada para buscar en ella un punto de corte o una posición de partición. Así que a \mbox{antidisestablishmentarianism} no se le pide amablemente que no se parta: sencillamente no puede partirse. A una caja de esta clase, que hace fluir su contenido de izquierda a derecha y nunca lo repliega, LaTeX la llama caja LR (su interior está en modo LR); \mbox, \makebox, \fbox y \framebox forman esa familia, opuesta a las cajas de párrafo \parbox y minipage, que fijan una anchura y repliegan el texto. Toda la visión de TeX —componer es unir cajas con pegamento— queda condensada en este único comando.

El precio es que una caja indivisible puede resultar demasiado ancha para la línea, y entonces TeX protesta. El mensaje merece una lectura literal: Overfull \hbox (29.62221pt too wide) in paragraph at lines 5--5. Nombra una \hbox y no una línea porque cada línea que produce TeX es en sí misma una hbox: el algoritmo de párrafo empaqueta palabras y pegamento elástico en una hbox por línea, el modelo que Donald Knuth y Michael Plass publicaron en 1981 con el título «Breaking paragraphs into lines». Con la opción de clase draft, LaTeX fija \overfullrule en 5pt e imprime una barra negra en el margen junto a cada línea infractora; esa barra no es más que una caja rellena. \mbox se reserva para lo que de verdad no debe partirse —un nombre de producto, un número de teléfono, un valor y su unidad— y no como remedio universal contra los cortes feos.

latex
% never split, never hyphenated
Released for \mbox{Mac OS X} and \mbox{Windows 11}; call \mbox{+81 3-1234-5678}.

% too wide for the measure -> Overfull \hbox (...pt too wide) in the log
\parbox{3.4cm}{aaa \mbox{antidisestablishmentarianism}}

% \documentclass[draft]{article} sets \overfullrule=5pt and marks the line

\makebox: elegir anchura y alineación, y la caja de anchura cero

\makebox[width][position]{text} es un \mbox en el que la elección de la anchura se le quita a TeX y pasa a quien escribe. [position] dice dónde se sitúa el contenido dentro de la anchura solicitada: l a la izquierda, r a la derecha, c centrado (valor predeterminado) y s estira el pegamento entre palabras para que el contenido llene exactamente la anchura: \makebox[3cm][s]{a b c d} sale realmente con 3cm de ancho. Cualquier otra letra también se acepta, pero LaTeX emite el aviso Unexpected alignment y vuelve a c.

Dentro del argumento de anchura, cuatro nombres representan el tamaño natural de aquello que se está encajonando: \width, \height, \depth y \totalheight (altura más profundidad). Se fijan a partir del contenido antes de evaluar la anchura, de modo que \makebox[2\width]{word} mide word en 21,4167pt y luego construye una caja de 42,8334pt. Es la manera limpia de decir «el doble de ancho que esto» sin decidir todavía qué será esto. Esos mismos cuatro nombres funcionan en los argumentos de \framebox, \savebox y \raisebox.

La anchura más útil de todas es 0pt. Una caja de anchura cero se compone pero no hace avanzar la posición actual, de modo que se imprime encima de lo que venga después. \makebox[0pt][l]{...} cuelga el contenido a la derecha del punto actual, \makebox[0pt][r]{...} a la izquierda y [c] lo reparte a ambos lados. Estos dos tienen nombres cortos —\rlap y \llap— y LaTeX los define exactamente así, como \hbox to 0pt{#1\hss} y \hbox to 0pt{\hss#1}. Las sobreimpresiones, la puntuación colgante, una marca en el margen y la mayoría de las macros caseras de tachado son todas este único truco.

latex
\makebox[3cm][l]{left}|\makebox[3cm][r]{right}|
\makebox[3cm][s]{a b c d}|   % interword glue stretched to exactly 3cm
\makebox[2\width]{word}|      % twice the natural width of "word"

% zero width: the slash overprints the space that follows the X
X\rlap{/}\quad Y              % identical to X\makebox[0pt][l]{/}\quad Y

\fbox frente a \framebox, y las dos longitudes que dan forma a un marco

La diferencia entre \fbox{text} y \framebox[width][position]{text} es exactamente la que hay entre \mbox y \makebox: \fbox enmarca el contenido a su anchura natural y \framebox lo enmarca dentro de una anchura elegida, con las mismas posiciones l/c/r/s. Ambos trazan líneas en los cuatro lados de la caja, y el aspecto lo gobiernan dos longitudes: el grosor de la línea \fboxrule vale 0,4pt y la separación entre línea y contenido \fboxsep vale 3pt. La aritmética cuadra: Hx mide 12,77782pt de ancho y \fbox{Hx} sale con 19,5778pt; la diferencia es exactamente el doble de 3,4pt. Con \setlength{\fboxsep}{0pt} el marco se ciñe a las letras.

\fbox es una caja LR, así que enmarca una sola línea. Para enmarcar un párrafo se enmarca una caja de párrafo: \fbox{\parbox{6cm}{...}}. La caja exterior no se preocupa por lo que hay dentro, solo por su tamaño. Esa componibilidad es justamente la gracia del modelo de cajas, y explica además por qué un marco dibujado alrededor de una \parbox hereda el problema de los descendentes de la sección anterior; un \strut al principio y al final del texto enmarcado mantiene nivelada una pila de marcos. En cuanto hagan falta esquinas redondeadas, un color de fondo o un marco que se parta entre páginas, se entra en el terreno de paquetes como framed y tcolorbox, tratados en la página de marcos.

latex
\setlength{\fboxrule}{1pt}   % default 0.4pt
\setlength{\fboxsep}{6pt}    % default 3pt
\fbox{one line}\quad\framebox[4cm][r]{4cm wide, flush right}

% frame a whole paragraph: put a paragraph box inside the frame
\fbox{\parbox{6cm}{\strut A framed paragraph. The frame follows the box,
and the struts keep its height independent of ascenders and descenders.\strut}}

La diferencia entre \parbox y minipage

Ambos fijan una anchura y dejan que el texto se ajuste dentro. La diferencia no es, como suele repetirse, que \parbox solo admita un párrafo. Admite tantos como se quiera, y también listas: \parbox{4cm}{First.\par Second.} produce realmente dos párrafos. La diferencia de fondo es que minipage es un entorno y \parbox es un comando, y eso decide qué puede aparecer legalmente dentro. Por lo demás las firmas coinciden: \parbox[position][height][inner-pos]{width}{text} y \begin{minipage}[position][height][inner-pos]{width}. Ambos reinician \parindent y \parskip a cero y fijan \linewidth a la anchura de la caja, y por eso \includegraphics[width=\linewidth]{...} hace lo correcto dentro de cualquiera de los dos.

[position] decide cómo se encuentra la caja con la línea que la rodea, y es el argumento que más se malinterpreta. [t] no alinea la parte superior de la caja: pone la línea base de la primera línea de la caja sobre la línea base exterior. Mida una \parbox[t] de tres líneas y su altura será 4,30554pt —la altura de una o minúscula, no la de la caja— mientras que su profundidad será de 24pt, dos líneas enteras colgando bajo la línea base. [b] hace lo simétrico con la última línea, y [c], el valor predeterminado, centra la caja sobre la línea. Así, dos minipages con [t] se alinean por su primera línea base, y si una empieza con una imagen o un cuerpo mayor, las partes superiores seguirán desiguales. El remedio es un \strut al principio de cada una, o un \raisebox sobre una de ellas. La forma de cuatro argumentos \parbox[position][height][inner-pos]{width}{text} impone además una altura, y [inner-pos] dice entonces si el contenido queda arriba (t), en el medio (c) o abajo (b) dentro de esa altura, o si se estira para llenarla (s).

Donde de verdad se separan es en qué se puede meter dentro. El texto de una \parbox se lee como argumento de macro, con los códigos de categoría ya fijados, de modo que \parbox{5cm}{\begin{verbatim}...\end{verbatim}} falla de plano con ! Argument of \@xverbatim has an extra }.; ese mismo verbatim funciona dentro de una minipage. Las notas al pie son peores que un error: una \footnote dentro de una \parbox imprime su llamada y luego pierde por completo el texto de la nota, porque la inserción queda atrapada en la caja y nunca llega a la página. Una minipage lo trata a propósito y coloca la nota justo debajo de sí misma, con una letra minúscula en cursiva de su propio contador mpfootnote. Los flotantes están vetados en ambos: un figure dentro de una minipage se detiene con ! LaTeX Error: Not in outer par mode. Y ninguno de los dos se parte entre páginas, porque una caja se coloca por definición entera.

Contenido\parboxminipage
\parvarios párrafos, sin problemavarios párrafos, sin problema
itemizefuncionafunciona (también se reinicia la profundidad de lista)
verbatim! Argument of \@xverbatim has an extra }.funciona
\footnotela llamada se imprime, el texto se pierdeaparece bajo la caja, letra cursiva
figureno permitido! LaTeX Error: Not in outer par mode.
\parindentse reinicia a 0se reinicia a 0

El trabajo más habitual de una minipage es un par de columnas contiguas: dos de ellas a cerca de 0,48 de \textwidth, con un \hfill en medio. El total debe quedar por debajo de uno o la segunda no cabrá. Y aquí está la trampa que a todo el mundo le cuesta media jornada una vez: el salto de línea del fichero fuente entre \end{minipage} y \begin{minipage} es un espacio corriente, así que las dos cajas quedan separadas por un espacio entre palabras que nadie escribió. Termine la línea con un % y el espacio desaparece. Cuando las columnas deban alinearse en altura, o se quiera que el texto fluya alrededor de la caja, la continuación está en la página de flotantes y colocación.

latex
\noindent
\begin{minipage}[t]{0.48\textwidth}
  \strut Left column. Several paragraphs, lists and a local footnote
  are all fine here.\footnote{Printed under this box, marked a, b, \dots}
\end{minipage}\hfill%   <- the % kills the newline; without it the boxes drift apart
\begin{minipage}[t]{0.48\textwidth}
  \strut Right column. [t] aligns the FIRST BASELINES, not the tops --
  the struts are what make the tops line up as well.
\end{minipage}

\raisebox: desplazar una caja y mentir sobre su tamaño

\raisebox{lift}{text} eleva su contenido en lift, o lo baja si el valor es negativo; es la herramienta habitual para alinear sobre una misma línea base dos elementos de tamaños distintos. Lo interesante son los dos argumentos opcionales: \raisebox{lift}[height][depth]{text} hace que LaTeX declare hacia fuera esa altura y esa profundidad, sea lo que sea lo que realmente se dibuje. \raisebox{0pt}[0pt][0pt]{\Huge Ag} sigue ocupando sus 28,66008pt de anchura, pero mide 0pt de alto y 0pt de profundidad (la verdad es 16,99782pt y 4,83937pt), de modo que el interlineado de alrededor se calcula como si allí no hubiera nada. Así es como un adorno o un glifo sobredimensionado cae en el texto corriente sin abrir un hueco, y los cuatro atajos \width, \height, \depth y \totalheight están disponibles también aquí. Una declaración que contradice la tinta produce, claro está, solapamiento: falsee las dimensiones solo cuando el solapamiento sea precisamente lo que se busca.

\rule y el strut: una caja invisible que reserva altura

\rule[lift]{width}{height} dibuja un rectángulo negro macizo: casi el único comando de LaTeX que pone tinta en la página con la forma de una caja en sí. \rule{0.5\linewidth}{0.4pt} es una línea horizontal fina de media medida. El [lift] opcional desplaza el rectángulo respecto a la línea base sin cambiar su tamaño: \rule[-2pt]{10pt}{8pt} mide 10pt de ancho, 6pt de alto y 2pt de profundidad; de los 8pt del rectángulo, 2pt cuelgan bajo la línea.

Ponga la anchura a cero y la línea deja de imprimirse pero sigue reservando espacio: eso es un strut. \rule{0pt}{2.6ex} es invisible, mide 0pt de ancho y 11,19443pt de alto; colocado al principio de una fila de tabla, garantiza que esa fila tendrá al menos esa altura. El \strut propio de LaTeX es uno prefabricado, ajustado al interlineado vigente: con un \baselineskip de 12pt mide 8,39996pt de alto y 3,60004pt de profundidad, un reparto de la línea en 0,7 y 0,3. Por eso \fbox{\strut Hx} y \fbox{\strut Hxg} salieron idénticos: el strut es más alto y más profundo que cualquiera de las dos palabras, de modo que el descendente ya no decide las dimensiones de la caja. Es el remedio clásico cuando el texto se agolpa contra un \hline, y la herramienta para mantener una hilera de cajas en un mismo ritmo vertical.

latex
% a centred horizontal rule
\noindent\hfil\rule{0.5\linewidth}{0.4pt}\hfil

% a strut forces a taller, roomier table row
% (\strut is the ready-made one, sized to the current \baselineskip)
\begin{tabular}{|l|}
  \hline
  \rule{0pt}{2.6ex}Tall, uncramped row \\
  \hline
  \strut Same idea, sized to the current leading \\
  \hline
\end{tabular}

% put two differently sized boxes on one baseline
big \raisebox{-0.4ex}{\Huge A} small

Guardar una caja con \newsavebox, \sbox y \usebox

El material que aparece más de una vez puede componerse una sola vez y luego reproducirse. \newsavebox{\name} reserva un registro de caja, \sbox{\name}{text} lo llena —o \savebox{\name}[width][position]{text} si se quiere la forma al estilo de \makebox— y \usebox{\name} lo emite. El contenido se compone realmente en el momento del \sbox y nunca más, así que una tabla larga o una figura intrincada usada en tres sitios cuesta una sola pasada de composición, y las tres copias son idénticas hasta el punto.

Los registros de caja son un recurso finito, y durante unas dos décadas fueron un recurso escaso. El TeX original tenía exactamente 256, numerados del 0 al 255, y un documento que reservaba demasiados se detenía con ! No room for a new \box. eTeX elevó el techo a 32768 y LuaTeX a 65536, y por eso ya nadie lo alcanza: LaTeX fija el límite según el motor al construir el formato, y su \e@alloc@top interno vale 255, 32767 o 65535. Dos apuntes sobre la interfaz: una caja de almacenamiento es una caja LR, así que el contenido de varias líneas entra envuelto antes en una \parbox o una minipage, y la forma de entorno \begin{lrbox}{\name} … \end{lrbox} es la indicada cuando el contenido incluye verbatim. Las referencias antiguas advierten de que \savebox es frágil; en el LaTeX actual no lo es: \mbox, \makebox, \fbox, \parbox, \raisebox, \sbox y \savebox están todos declarados robustos.

Los compañeros de \usebox son \settowidth{\len}{text}, \settoheight y \settodepth: componen el texto de forma invisible y guardan una de las dimensiones del resultado en una longitud. Existen para dibujar algo exactamente del tamaño de otra cosa: medir una línea de firma y luego subrayarla con \rule.

latex
\newsavebox{\mylogo}
\sbox{\mylogo}{\fbox{\textbf{Draft}}}   % typeset once, here

Header: \usebox{\mylogo} \dots\ Footer: \usebox{\mylogo}

% verbatim inside a save box needs the environment form
\newsavebox{\snippet}
\begin{lrbox}{\snippet}
  \begin{minipage}{6cm}\begin{verbatim}int main(void) { }\end{verbatim}\end{minipage}
\end{lrbox}
\usebox{\snippet}

% measure a box into a length, then rule exactly that wide
\newlength{\siglen}
\settowidth{\siglen}{Signature}
Signature\par\rule{\siglen}{0.4pt}

Qué comando de caja usar y cuándo

ComandoTipoCuándo recurrir a él
\mboxLResta expresión no debe partirse nunca
\makeboxLRse quiere elegir la anchura, o sobreimprimir con [0pt]
\rlapLRatajo de \makebox[0pt][l] (\llap cuelga a la izquierda)
\fboxLRun marco a la anchura natural
\frameboxLRun marco de anchura elegida (ajustar \fboxsep, \fboxrule)
\parboxpárrafoajustar texto a una anchura fija, p. ej. en una celda
minipagepárrafohacen falta verbatim, notas al pie o columnas contiguas
\raiseboxtransformacióndesplazar arriba o abajo, o declarar altura y profundidad cero
\rulecaja rellenatrazar una línea; anchura cero da un strut invisible
\strutstrutigualar la altura de líneas o celdas
\useboxalmacenamientoemitir una caja creada con \newsavebox + \sbox
\settowidthmediciónguardar la anchura (también \settoheight, \settodepth) en una longitud