Transformer du texte

On prend souvent graphicx pour un paquet d’images, mais le x de son nom signifie « étendu », non « graphiques seulement ». Ses commandes \rotatebox, \scalebox, \resizebox et \reflectbox font tourner, étirer et refléter tout ce qui tient dans une boîte : un mot, une formule, un tabular entier. Les dimensions de la boîte, en revanche, ne se comportent pas comme on l’imagine. À la mesure, un Xg de 10pt basculé vers la droite par \rotatebox{-90} donne une boîte de hauteur 0pt et de profondeur 12,50pt : le tout pend sous la ligne de base. C’est précisément pour cela que l’interligne d’un tableau s’effondre dès qu’on dresse un en-tête à la verticale. Cette page parcourt les commandes LaTeX de rotation et de mise à l’échelle du texte, avec les deux pièges qui se referment sur les utilisateurs : les dimensions et le pilote.

Pas seulement pour les images : les quatre transformations de boîte de graphicx

Une seule ligne, \usepackage{graphicx}, met les quatre transformations à disposition. Elles se répartissent en deux groupes : celles qui se donnent par facteur (\scalebox, \reflectbox), celle qui se donne par taille cible (\resizebox), et \rotatebox, qui se donne par un angle. \scalebox{0.8}{…} signifie « 0,8 fois l’original » ; \resizebox{5cm}{!}{…} signifie « fais-lui 5 cm de large ». Le choix dépend de la nature du nombre dont vous disposez : un rapport ou une mesure. Ajuster une figure à la largeur du texte appelle le second ; agrandir légèrement un mot appelle le premier.

CommandeEffetForme
\rotateboxFait tourner le contenu dans le sens antihoraire\rotatebox[origin=c]{45}{...} ; en degrés, négatif pour le sens horaire
\scaleboxMet à l’échelle par un facteur\scalebox{2}[1.5]{...} ; le second argument optionnel est le facteur vertical
\reflectboxRéfléchit de gauche à droite\reflectbox{...} ; identique en interne à \scalebox{-1}[1]
\resizeboxMet à une taille finale donnée\resizebox{largeur}{hauteur}{...} ; un ! dans un des champs conserve les proportions

\rotatebox : des degrés, positif pour le sens antihoraire

\rotatebox{angle}{contenu} place le contenu dans une boîte et le fait tourner dans le sens antihoraire de l’angle indiqué, en degrés. Un angle positif tourne à gauche, un angle négatif à droite : \rotatebox{90}{...} redresse le texte, \rotatebox{-90} le bascule de l’autre côté. Pour écrire les angles dans une autre unité, la clé optionnelle units= attend le nombre correspondant à un tour complet : units=-360 donne des degrés horaires, units=6.283185 des radians antihoraires. Si la convention de signe vous fait trébucher, l’emballer une fois dans une macro du préambule règle la question.

latex
This is \rotatebox{45}{slanted text} in a line of prose.

% clockwise degrees instead of the default anticlockwise:
\rotatebox[units=-360]{90}{tipped to the right}

Venons-en aux dimensions, là où la plupart trébuchent. La largeur, la hauteur et la profondeur d’une boîte tournée sont recalculées à partir du contour du résultat tourné. Mesurez un Xg de 10pt (largeur 12,50pt, hauteur 6,83pt, profondeur 1,94pt) : \rotatebox{90} renvoie largeur 8,78pt, hauteur 12,50pt, profondeur 0pt — l’ancienne hauteur plus profondeur est devenue la nouvelle largeur, et l’ancienne largeur la nouvelle hauteur. \rotatebox{-90} donne la même largeur mais hauteur 0pt et profondeur 12,50pt : toute la boîte s’enfonce sous la ligne de base. Quant à \rotatebox{45}, il revient à 15,05pt de large et 13,67pt de haut, plus grand que l’original dans les deux sens. Une rotation change la hauteur de la ligne : sachant cela, l’interligne qui s’élargit brusquement juste après une rotation cesse d’être un mystère.

origin= : dès qu’on écrit les crochets, le défaut change

Le centre de rotation se fixe par l’argument optionnel [origin=…]. Ses valeurs sont une ou deux lettres parmi l (gauche), r (droite), t (haut), b (bas), B (ligne de base) et c (centre) : origin=lb pivote sur le coin inférieur gauche, origin=c sur le milieu de la boîte. Et voici la subtilité qui déroute quiconque ne la connaît pas. En lisant graphicx.sty, on découvre que si l’argument optionnel est absent, le centre vaut (0,0) — bord gauche, sur la ligne de base, soit l’équivalent d’origin=lB — alors que dès qu’une clé quelconque est écrite, le défaut bascule sur le centre de la boîte, c. La mesure le confirme : \rotatebox{90}{Xg} et \rotatebox[origin=lB]{90}{Xg} coïncident exactement à 8,78pt de large, 12,50pt de haut et 0pt de profondeur, tandis que \rotatebox[origin=c]{45}, au même angle de 45 degrés, se répartit en 9,97pt de hauteur et 5,08pt de profondeur, à cheval sur la ligne de base. « J’ai seulement ajouté units= et tout a bougé » : c’est presque toujours cela.

Outre origin=, il existe x= et y=, qui donnent le pivot sous forme de longueurs explicites. En écrivant \rotatebox[x=1cm,y=1cm]{90}{...}, le centre se place à (1 cm, 1 cm) depuis le coin inférieur gauche de la boîte. C’est l’outil des réglages fins que la poignée de mots-clés ne sait pas exprimer.

latex
% no optional argument: the pivot is the left edge on the baseline (= origin=lB)
\rotatebox{90}{upright}

% ANY key present: the default pivot becomes the box centre (origin=c)
\rotatebox[origin=c]{180}{upside down}
\rotatebox[x=1cm,y=1cm]{90}{pivot given as a length}

Dresser un en-tête de tableau : plus étroit, mais la ligne s’enfonce

L’usage le plus pratique de \rotatebox est de dresser à la verticale des noms de colonnes trop longs pour regagner de la largeur. Cela marche : \begin{tabular}{ll}Header&b\\ c&d\end{tabular} mesurait 60,42pt de large, et faire tourner Header avec \rotatebox{90}{...} l’a ramené à 36,50pt. Il y a toutefois un prix. Dans la même mesure, la profondeur du tableau est passée de 9,50pt à 20,73pt. Les dimensions de la section précédente font exactement ce qu’elles annonçaient : un en-tête dressé grandit vers le bas, dans la ligne. Laissé tel quel, il finit par heurter le texte ou la ligne suivante. Les remèdes sont simples : déplacer le pivot avec \rotatebox[origin=c]{90}{...}, soulever la cellule avec \raisebox, ou réserver la hauteur par un étai tel que \rule{0pt}{hauteur}. Faire tenir le tableau lui-même sur la page — pages en paysage, sidewaystable, colonnes de largeur déclarée — relève des pages consacrées aux tableaux ; voyez-les.

latex
\begin{tabular}{lrr}
  % rotating the pivot to the centre keeps the row from sinking
  & \rotatebox[origin=c]{90}{Measured} & \rotatebox[origin=c]{90}{Predicted} \\
  \hline
  Sample A & 12.4 & 12.1 \\
  Sample B & 9.8  & 10.0 \\
\end{tabular}

Mise à l’échelle par facteur et miroir : \scalebox et \reflectbox

\scalebox{facteur}{contenu} met à l’échelle les deux directions de la même façon et conserve donc la forme. \scalebox{2}{...} double, \scalebox{0.5}{...} divise par deux. Pour étirer les deux directions différemment, on passe un facteur vertical en second argument optionnel : \scalebox{horizontal}[vertical]{contenu}. À la mesure, \scalebox{2} a doublé largeur, hauteur et profondeur, tandis que \scalebox{1}[2] a laissé la largeur à 12,50pt en ne doublant que hauteur et profondeur. C’est ainsi qu’on condense ou aplatit des caractères — mais rappelez-vous que les graisses de la fonte se déforment aussi, d’où un usage parcimonieux dans le texte courant.

Un facteur peut aussi être négatif, ce qui retourne selon cet axe. \scalebox{-1}[1]{...}, c’est un facteur horizontal de −1 et un facteur vertical de 1 : un miroir gauche-droite. Le cas est assez fréquent pour avoir son abréviation, et dans graphics.sty \reflectbox est défini comme exactement \Gscale@box-1[1]. La mesure concorde : les deux boîtes font 12,50pt de large, 6,83pt de haut et 1,94pt de profondeur. \reflectbox n’est donc qu’un alias de \scalebox{-1}[1]. Réfléchissez la moitié du palindrome « Able was I ere I saw Elba » et le comportement de la commande saute aux yeux.

latex
\scalebox{2}{bigger}\quad\scalebox{0.5}{smaller}\quad\scalebox{1}[2]{stretched}

% \reflectbox is literally \scalebox{-1}[1]
Able was I \reflectbox{Able was I}

Mettre à une taille cible : \resizebox et \resizebox* étoilé

\resizebox{largeur}{hauteur}{contenu} met le contenu à une taille finale donnée. Un point d’exclamation ! dans l’un des champs reprend pour cette direction le facteur déduit de l’autre : les proportions sont conservées. \resizebox{\textwidth}{!}{...} est la forme la plus courante. Dans les arguments de dimension, on peut employer \width, \height, \totalheight et \depth, qui désignent la taille propre du contenu. À la mesure, \resizebox{2\width}{!}{Xg} s’est révélé identique à \scalebox{2}{Xg} au dernier chiffre près : 25,00pt de large, 13,67pt de haut, 3,89pt de profondeur.

Un point qui échappe à la plupart : l’argument de hauteur de \resizebox désigne la hauteur, non la hauteur plus la profondeur. Mesurez \resizebox{!}{1cm}{Xg} : la hauteur vaut 28,46pt — soit à peu près 1 cm —, mais 8,10pt de profondeur pendent en dessous, si bien que la boîte entière dépasse 1 cm. Quand on vise la hauteur totale, il faut le \resizebox* étoilé. À la mesure, \resizebox*{!}{1cm}{Xg} a donné 22,15pt de hauteur et 6,30pt de profondeur, soit 28,45pt en tout : exactement 1 cm. Si quelque chose « réduit à 1 cm » déborde encore de sa ligne, soupçonnez l’étoile manquante.

latex
% twice the original width, aspect ratio kept
\resizebox{2\width}{!}{double width}

% height only vs. height+depth
\resizebox{!}{1cm}{Tall}      % height = 1cm, depth hangs below
\resizebox*{!}{1cm}{Tall}     % height + depth = 1cm

L’usage le plus courant de \resizebox est de réduire un tableau trop large jusqu’à ce qu’il tienne dans la justification. Enveloppez tout le tabular dans \resizebox{\textwidth}{!}{...} et la largeur sera toujours juste. C’est pourtant une arme à double tranchant, car les caractères du tableau rétrécissent avec lui : une forte réduction donne des lettres plus petites que le corps du texte, parfois difficiles à lire, et désaccordées de leur entourage. Redéfinir ensuite une taille de police pour le tableau ne sert à rien, puisque toute la boîte est mise à l’échelle en fin de compte. De combien un tableau rétrécit, et à quel point un tableau trop étroit se trouve gonflé par la même commande, est traité avec des mesures sur la page des tableaux trop larges. Le bon premier réflexe est de faire tenir le tableau lui-même — ajuster les largeurs de colonnes, en supprimer — et de garder \resizebox en dernier recours.

Rien ne tourne : un pilote qui ne correspond pas

\rotatebox ne fait pas dessiner à TeX des lettres tournées. TeX se contente d’enfouir une instruction dans le fichier de sortie — « fais tourner ce qui suit de tel angle » — et la rotation effective revient au pilote en aval. Aussi, désigner le mauvais pilote fait disparaître la rotation sans erreur ni avertissement. Voici la mesure. Compilez avec pdfLaTeX un document portant \usepackage[dvips]{graphicx} : la compilation se termine proprement, zéro erreur — et pourtant le flux de contenu du PDF obtenu ne contient pas une seule matrice de transformation cm. Le texte reste bien droit. Retirez l’option de pilote du même document et le journal indique Package graphics Info: Driver file: pdftex.def, tandis que le PDF gagne .70709 .70709 -.70709 .70709 0 0 cm — le cosinus et le sinus de 45 degrés.

La conclusion est simple : ne nommez pas de pilote. pdfLaTeX, XeLaTeX et LuaLaTeX sont tous détectés automatiquement par graphicx ; écrire [dvips] ou [pdftex] à la main, c’est exactement fabriquer un décalage. La voie DVI fait exception : sous latex, graphicx retient par défaut le pilote dvips et le DVI transporte du PostScript brut (ps: gsave currentpoint currentpoint translate 45 neg rotate …). dvipdfmx comprend assez de ces specials PostScript pour tourner effectivement, mais la voie propre consiste à écrire explicitement \usepackage[dvipdfmx]{graphicx} ; le DVI porte alors une instruction orientée PDF : pdf:btrans rotate 45. Ce qu’un DVI transmet réellement à son pilote est traité en détail sur la page consacrée à la conversion DVI vers PDF.

adjustbox et max width= : réduire seulement, jamais agrandir

Les commandes de graphicx sont simples et puissantes, mais leur combinaison s’imbrique en profondeur. adjustbox, chargé par \usepackage{adjustbox}, est l’alternative de plus haut niveau qui les réunit dans une seule interface clé-valeur à la manière de \includegraphics. \adjustbox{angle=45,scale=0.8}{…} règle d’un coup la rotation (angle=), le facteur (scale=), la taille cible (width=/height=) et le miroir (reflect) ; un environnement adjustbox du même nom existe aussi. À la mesure, la boîte produite par \adjustbox{angle=45}{Xg} coïncidait au dernier chiffre avec \rotatebox{45}{Xg} : la mécanique sous-jacente est la même. Ce qui change, c’est l’écriture — et la notion de plafond décrite ci-après.

La clé vraiment utile est max width=\textwidth (ainsi que max totalheight= et consorts). Le \resizebox{\textwidth}{!}{...} de la section précédente agrandit volontiers un contenu plus étroit que le texte, tandis que max width= est conçu pour ne réduire qu’en cas de débordement et laisser intact ce qui tient déjà. À la mesure : un petit tabular d’une colonne, large de 17,00pt, est ressorti de max width=\textwidth toujours à 17,00pt, alors que le même tableau confié à \resizebox{\textwidth}{!} a été étiré à 345,00pt pleins. Si donc, par habitude d’\includegraphics, vous appliquez \resizebox à tous les tableaux, ce sont les étroits qui deviennent monstrueux. Faites de « réduire au besoin, jamais agrandir » votre réglage par défaut : en pratique, c’est celui qui provoque le moins d’accidents.

latex
\usepackage{adjustbox}
% ...
\adjustbox{angle=45,scale=0.8}{rotate and scale in one go}

% shrinks only when the content overflows; leaves narrow tables alone
\adjustbox{max width=\textwidth}{%
  \begin{tabular}{lrr} ... \end{tabular}%
}