Boîtes

Si l'on demande quelle est l'idée la plus fondamentale de TeX, la réponse n'est ni les polices, ni les mathématiques, ni l'algorithme de coupure des lignes : c'est la boîte. Tout ce que LaTeX pose sur une page est une boîte, un rectangle doté d'une largeur, d'une hauteur et d'une profondeur, relié à ses voisins par de la colle (glue) et dont on ne regarde plus jamais l'intérieur une fois assemblé. C'est pourquoi \mbox empêche la césure d'un mot, pourquoi une ligne qui déborde est signalée comme un Overfull \hbox, et pourquoi \parbox et minipage se séparent là où ils le font. Cette page parcourt les commandes de boîte que l'on tape réellement — \mbox, \makebox, \framebox, \raisebox, \parbox, minipage, \newsavebox — à partir de cette idée unique qui les sous-tend toutes.

Largeur, hauteur et profondeur : pourquoi une boîte a trois dimensions

Une boîte a trois dimensions, pas deux : largeur, hauteur et profondeur. La hauteur se mesure vers le haut à partir de la ligne de base, la profondeur vers le bas — une boîte n'est donc pas un rectangle tracé autour de l'encre, mais un rectangle suspendu à la ligne sur laquelle reposent les lettres. Les trois valeurs s'affichent avec \the\wd, \the\ht et \the\dp. En Computer Modern 10pt, le mot Hx mis en boîte a une profondeur de 0.0pt ; avec Hxg, elle passe à 1.94444pt, parce que le g descend sous la ligne de base. La largeur a augmenté elle aussi, bien sûr — mais la profondeur a augmenté pour une tout autre raison.

Ce seul fait résout une énigme que presque tout le monde rencontre avant de savoir ce qu'est une boîte : pourquoi \fbox{Hx} colle-t-il aux lettres tandis que \fbox{Hxg} semble s'affaisser ? Parce que \fbox encadre la boîte et non les lettres, et que la boîte de Hxg est plus profonde de 1,94pt. Alignez une série de mots encadrés sur une ligne de tableau : les cadres montent et descendent au rythme des jambages. Le remède est un strut : \fbox{\strut Hx} et \fbox{\strut Hxg} sortent avec exactement la même hauteur et la même profondeur, car \strut est une boîte invisible de largeur nulle qui revendique 0,7 fois le \baselineskip courant au-dessus de la ligne de base et 0,3 fois au-dessous. Une section plus bas lui est consacrée.

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 : empêcher un saut de ligne et une césure

\mbox{...} maintient son contenu sur une seule ligne et y désactive la césure. La raison tient à la définition elle-même : LaTeX déclare \mbox comme rien de plus que \leavevmode\hbox{#1}. Une \hbox est une boîte déjà assemblée, et TeX ne revient jamais dans une boîte terminée pour y chercher un point de coupure ou une position de césure. \mbox{antidisestablishmentarianism} n'est donc pas poliment prié de ne pas se couper : il ne peut pas se couper. Une boîte de ce type, qui fait défiler son contenu de gauche à droite sans jamais le replier, s'appelle dans la terminologie LaTeX une boîte LR (son intérieur est en mode LR) ; \mbox, \makebox, \fbox et \framebox forment cette famille, opposée aux boîtes de paragraphe \parbox et minipage, qui fixent une largeur et replient le texte. Toute la vision du monde de TeX — composer, c'est assembler des boîtes avec de la colle — tient dans cette seule commande.

Le prix à payer : une boîte insécable peut être trop large pour la ligne, et TeX proteste. Le message mérite une lecture littérale : Overfull \hbox (29.62221pt too wide) in paragraph at lines 5--5. Il nomme une \hbox et non une ligne parce que chaque ligne produite par TeX est elle-même une hbox : l'algorithme de paragraphe empile mots et colle extensible dans une hbox par ligne, le modèle publié par Donald Knuth et Michael Plass en 1981 sous le titre « Breaking paragraphs into lines ». Avec l'option de classe draft, LaTeX règle \overfullrule à 5pt et imprime une barre noire dans la marge en regard de chaque ligne fautive ; cette barre n'est elle-même qu'une boîte pleine. \mbox s'emploie pour ce qui ne doit vraiment pas se couper — un nom de produit, un numéro de téléphone, une valeur et son unité — et non comme remède universel aux mauvaises coupures.

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 : choisir une largeur et un alignement, et la boîte de largeur nulle

\makebox[width][position]{text} est un \mbox dont le choix de la largeur est retiré à TeX pour être confié à l'auteur. [position] indique où le contenu se place dans la largeur demandée : l à gauche, r à droite, c centré (valeur par défaut), et s étire la colle interlettre entre les mots pour que le contenu remplisse exactement la largeur — \makebox[3cm][s]{a b c d} fait bel et bien 3cm de large. Toute autre lettre est acceptée elle aussi, mais LaTeX émet l'avertissement Unexpected alignment et retombe sur c.

Dans l'argument de largeur, quatre noms désignent la taille naturelle de ce que l'on met en boîte : \width, \height, \depth et \totalheight (hauteur plus profondeur). Ils sont fixés d'après le contenu avant que la largeur ne soit évaluée : \makebox[2\width]{word} mesure donc word à 21,4167pt puis construit une boîte de 42,8334pt. C'est la façon élégante de dire « deux fois plus large que ceci » sans avoir à décider ce que ceci sera. Ces quatre mêmes noms fonctionnent dans les arguments de \framebox, \savebox et \raisebox.

La largeur la plus utile de toutes est 0pt. Une boîte de largeur nulle est composée mais n'avance pas la position courante : elle s'imprime donc par-dessus ce qui suit. \makebox[0pt][l]{...} fait déborder le contenu à droite du point courant, \makebox[0pt][r]{...} à gauche, et [c] l'étale de part et d'autre. Ces deux-là portent des noms courts — \rlap et \llap — et LaTeX les définit précisément ainsi, comme \hbox to 0pt{#1\hss} et \hbox to 0pt{\hss#1}. Surimpressions, ponctuation suspendue, marque dans la marge et la plupart des macros de barré maison ne sont que cette seule astuce.

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 et \framebox : la différence, et les deux longueurs qui font un cadre

La différence entre \fbox{text} et \framebox[width][position]{text} est exactement celle entre \mbox et \makebox : \fbox encadre le contenu à sa largeur naturelle, \framebox l'encadre dans une largeur choisie, avec les mêmes positions l/c/r/s. Les deux tracent un filet sur les quatre côtés de la boîte, et l'aspect dépend de deux longueurs : l'épaisseur du filet \fboxrule vaut 0,4pt, et l'espace entre filet et contenu \fboxsep vaut 3pt. Le calcul tombe juste : Hx mesure 12,77782pt de large et \fbox{Hx} en fait 19,5778pt — l'écart est exactement le double de 3,4pt. Avec \setlength{\fboxsep}{0pt}, le cadre épouse les lettres.

\fbox est une boîte LR : elle encadre une seule ligne. Pour encadrer un paragraphe, on encadre une boîte de paragraphe\fbox{\parbox{6cm}{...}}. La boîte extérieure ne se soucie pas de ce qu'elle contient, seulement de sa taille. Cette composabilité est tout l'intérêt du modèle de boîte, et elle explique aussi pourquoi un cadre tracé autour d'une \parbox hérite du problème de jambage vu plus haut ; un \strut au début et à la fin du texte encadré maintient une pile de cadres au même niveau. Dès qu'il faut des coins arrondis, une couleur de fond ou un cadre qui se coupe entre deux pages, on entre dans le domaine de packages comme framed et tcolorbox, traités sur la page des cadres.

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 différence entre \parbox et minipage

Les deux fixent une largeur et laissent le texte se replier à l'intérieur. La différence n'est pas, contrairement à ce qu'on répète, que \parbox ne contiendrait qu'un seul paragraphe. Elle en contient autant qu'on veut, et des listes aussi — \parbox{4cm}{First.\par Second.} produit bel et bien deux paragraphes. La vraie différence est que minipage est un environnement et \parbox une commande ; c'est cela qui détermine ce qui peut légalement figurer à l'intérieur. Les signatures se correspondent par ailleurs : \parbox[position][height][inner-pos]{width}{text} et \begin{minipage}[position][height][inner-pos]{width}. Les deux remettent \parindent et \parskip à zéro et fixent \linewidth à la largeur de la boîte, ce qui explique que \includegraphics[width=\linewidth]{...} s'y comporte correctement.

[position] décide de la manière dont la boîte rencontre la ligne environnante, et c'est l'argument que l'on comprend le plus souvent de travers. [t] n'aligne pas le sommet de la boîte : il pose la ligne de base de la première ligne de la boîte sur la ligne de base environnante. Mesurez une \parbox[t] de trois lignes : sa hauteur est de 4,30554pt — la hauteur d'un o minuscule, pas celle de la boîte — tandis que sa profondeur est de 24pt, soit deux lignes entières suspendues sous la ligne de base. [b] fait l'image miroir avec la dernière ligne, et [c], la valeur par défaut, centre la boîte sur la ligne. Deux minipages en [t] s'alignent donc sur leur première ligne de base ; si l'une commence par une image ou un corps plus grand, les sommets resteront décalés. Le remède est un \strut au début de chacune, ou un \raisebox sur l'une des deux. La forme à quatre arguments \parbox[position][height][inner-pos]{width}{text} impose en outre une hauteur, et [inner-pos] indique alors si le contenu se place en haut (t), au milieu (c) ou en bas (b) de cette hauteur, ou s'il est étiré pour la remplir (s).

C'est sur ce qu'on peut y mettre que les chemins divergent vraiment. Le texte d'une \parbox est lu comme un argument de macro, les codes de catégorie étant déjà figés : \parbox{5cm}{\begin{verbatim}...\end{verbatim}} échoue donc net avec ! Argument of \@xverbatim has an extra }. — le même verbatim fonctionne dans une minipage. Les notes de bas de page sont pires qu'une erreur : une \footnote dans une \parbox imprime son appel puis perd entièrement le texte de la note, l'insertion restant prisonnière de la boîte sans jamais atteindre la page. Une minipage traite le cas volontairement et place la note juste au-dessous d'elle-même, avec une lettre minuscule en italique issue de son propre compteur mpfootnote. Les flottants sont interdits dans les deux : un figure dans une minipage s'arrête sur ! LaTeX Error: Not in outer par mode. Et ni l'une ni l'autre ne se coupe entre deux pages, car une boîte est par définition posée d'un seul tenant.

Contenu placé\parboxminipage
\parplusieurs paragraphes possiblesplusieurs paragraphes possibles
itemizefonctionnefonctionne (la profondeur de liste est réinitialisée)
verbatim! Argument of \@xverbatim has an extra }.fonctionne
\footnotel'appel s'imprime, le texte est perduapparaît sous la boîte, lettre italique
figurenon autorisé! LaTeX Error: Not in outer par mode.
\parindentremis à 0remis à 0

L'usage le plus courant d'une minipage est une paire de colonnes côte à côte : deux boîtes d'environ 0,48 de \textwidth, séparées par un \hfill. Le total doit rester inférieur à un, sinon la seconde ne tient pas. Et voici le piège qui coûte une demi-journée à tout le monde une fois : le saut de ligne du fichier source entre \end{minipage} et \begin{minipage} est une espace ordinaire, si bien que les deux boîtes sont écartées par une espace-mot que personne n'a tapée. Terminez la ligne par un % et l'espace disparaît. Dès qu'il faut aligner les colonnes en hauteur ou faire couler le texte autour de la boîte, la suite se trouve sur la page des flottants et du placement.

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 : décaler une boîte, et mentir sur sa taille

\raisebox{lift}{text} élève son contenu de lift, ou l'abaisse si la valeur est négative ; c'est l'outil habituel pour aligner sur une même ligne de base deux éléments de tailles différentes. L'intéressant tient aux deux arguments optionnels : \raisebox{lift}[height][depth]{text} fait déclarer à LaTeX cette hauteur et cette profondeur vers l'extérieur, quel que soit ce qui est réellement dessiné. \raisebox{0pt}[0pt][0pt]{\Huge Ag} occupe toujours ses 28,66008pt de large, mais se mesure à 0pt de haut et 0pt de profondeur (en réalité 16,99782pt et 4,83937pt) : l'interligne alentour est donc calculé comme s'il n'y avait rien. C'est ainsi qu'un ornement ou une glyphe surdimensionnée s'insère dans le texte courant sans ouvrir de trou, et les quatre raccourcis \width, \height, \depth et \totalheight sont également disponibles ici. Une déclaration qui contredit l'encre produit évidemment un chevauchement : ne truquez les dimensions que lorsque le chevauchement est justement le but recherché.

\rule et le strut : une boîte invisible qui réserve de la hauteur

\rule[lift]{width}{height} dessine un rectangle noir plein — à peu près la seule commande LaTeX qui pose de l'encre sur la page sous la forme d'une boîte elle-même. \rule{0.5\linewidth}{0.4pt} donne un filet fin de la moitié de la justification. L'argument optionnel [lift] décale le rectangle par rapport à la ligne de base sans changer sa taille : \rule[-2pt]{10pt}{8pt} mesure 10pt de large, 6pt de haut et 2pt de profondeur — sur les 8pt du rectangle, 2pt pendent sous la ligne.

Mettez la largeur à zéro : le filet cesse d'imprimer mais continue de réserver de la place — c'est un strut. \rule{0pt}{2.6ex} est invisible, large de 0pt et haut de 11,19443pt ; placé en début de ligne de tableau, il garantit à cette ligne au moins cette hauteur. Le \strut propre à LaTeX est un modèle tout fait, calé sur l'interligne courant : pour un \baselineskip de 12pt, il mesure 8,39996pt de haut et 3,60004pt de profondeur — une répartition de la ligne en 0,7 et 0,3. Voilà pourquoi \fbox{\strut Hx} et \fbox{\strut Hxg} sont sortis identiques : le strut est plus haut et plus profond que l'un et l'autre mot, si bien que le jambage ne décide plus des dimensions de la boîte. C'est le remède classique quand le texte serre un \hline, et l'outil qui maintient une rangée de boîtes sur un même rythme 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

Enregistrer une boîte avec \newsavebox, \sbox et \usebox

Un contenu qui revient plus d'une fois peut être composé une seule fois puis rejoué. \newsavebox{\name} réserve un registre de boîte, \sbox{\name}{text} le remplit — ou \savebox{\name}[width][position]{text} pour la forme à la manière de \makebox — et \usebox{\name} le restitue. Le contenu n'est réellement composé qu'au moment du \sbox, jamais ensuite : un long tableau ou une figure compliquée employée à trois endroits ne coûte qu'une passe de composition, et les trois copies sont identiques au point près.

Les registres de boîte sont une ressource finie, et pendant une vingtaine d'années ils furent une ressource rare. Le TeX original en comptait exactement 256, numérotés de 0 à 255, et un document qui en réservait trop s'arrêtait sur ! No room for a new \box. eTeX a porté le plafond à 32768 et LuaTeX à 65536, si bien que plus personne ne l'atteint : LaTeX fixe la limite d'après le moteur au moment de construire le format, son \e@alloc@top interne valant 255, 32767 ou 65535. Deux remarques sur l'interface : une boîte de stockage est une boîte LR, un contenu sur plusieurs lignes passe donc d'abord par une \parbox ou une minipage, et la forme en environnement \begin{lrbox}{\name} … \end{lrbox} s'impose lorsque le contenu comprend du verbatim. Les références anciennes avertissent que \savebox est fragile ; ce n'est plus le cas dans le LaTeX actuel : \mbox, \makebox, \fbox, \parbox, \raisebox, \sbox et \savebox sont tous déclarés robustes.

Les compagnons de \usebox sont \settowidth{\len}{text}, \settoheight et \settodepth : ils composent le texte de façon invisible et rangent l'une des dimensions du résultat dans une longueur. Ils existent pour dessiner quelque chose exactement à la taille d'autre chose — mesurer une ligne de signature, puis la souligner avec \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}

Quelle commande de boîte employer, et quand

CommandeTypeÀ utiliser quand
\mboxLRcette expression ne doit jamais être coupée
\makeboxLRchoisir soi-même la largeur, ou surimprimer avec [0pt]
\rlapLRraccourci de \makebox[0pt][l] (\llap déborde à gauche)
\fboxLRun cadre à la largeur naturelle
\frameboxLRun cadre à largeur choisie (régler \fboxsep, \fboxrule)
\parboxparagraphereplier du texte à largeur fixe, dans une cellule par exemple
minipageparagraphebesoin de verbatim, de notes ou de colonnes côte à côte
\raiseboxtransformationdécaler vers le haut ou le bas, ou déclarer hauteur et profondeur nulles
\ruleboîte pleinetracer un trait ; largeur nulle pour un strut invisible
\strutstrutégaliser la hauteur des lignes ou des cellules
\useboxstockagerestituer une boîte construite avec \newsavebox + \sbox
\settowidthmesureranger la largeur (aussi \settoheight, \settodepth) dans une longueur