Toute chaîne visible dans un document HotPDF arrive par un seul appel : TextOut(X, Y, angle, Text). L'exemple Hello World l'utilise dans sa forme la plus simple, la police définie une fois et quatre arguments laissés à des valeurs par défaut raisonnables. Au-delà de cette première page, les mêmes quatre arguments portent tout le poids de la mise en page. Le troisième argument fait pivoter la séquence. La police définie juste avant détermine la taille et le style. Et la paire X, Y, mesurée depuis le coin de la page en points, est la seule chose qui sépare un rapport propre d'un texte qui se chevauche, se coupe ou dérive d'une ligne vers le bas sur l'imprimante de quelqu'un d'autre. C'est là que TextOut prouve son utilité, et là où les valeurs par défaut ne suffisent plus.
La signature mérite d'être mémorisée avant tout : X et Y sont des Single en points, angle est un Extended en degrés, et Text est un WideString, de sorte que l'Unicode passe sans appel séparé. Une seconde surcharge prend un PWORD plus une longueur pour les cas où vous tenez déjà des codes de glyphes, mais pour les chaînes ordinaires, c'est la forme WideString qu'on utilise.
La taille et le style viennent de SetFont, pas de TextOut
TextOut n'a pas de paramètre de taille. La taille, le poids, l'inclinaison, tout cela réside dans l'appel SetFont qui précède la séquence, et reste en vigueur jusqu'à ce que le prochain SetFont le remplace. C'est le fait unique qui explique la plupart des confusions du premier jour : une ligne sort en gras parce que trois appels plus tôt quelque chose a défini [fsBold] et rien ne l'a effacé.
Pdf.CurrentPage.SetFont('Times New Roman', [], 24);
Pdf.CurrentPage.TextOut(72, 740, 0, 'Quarterly Report'); // 24pt régulier
Pdf.CurrentPage.SetFont('Times New Roman', [fsBold], 12);
Pdf.CurrentPage.TextOut(72, 712, 0, 'Revenue'); // 12pt gras
Pdf.CurrentPage.SetFont('Times New Roman', [fsItalic], 11);
Pdf.CurrentPage.TextOut(72, 694, 0, 'figures in thousands'); // 11pt italique
Pdf.CurrentPage.SetFont('Courier New', [fsBold, fsItalic], 10);
Pdf.CurrentPage.TextOut(72, 676, 0, ' +18.4% YoY'); // styles combinés
Le second argument est un ensemble TFontStyles, donc [fsBold, fsItalic] donne du gras italique et [] donne du texte simple. La taille est en points, la même unité que les coordonnées, ce qui facilite le raisonnement sur l'espacement vertical : une ligne de 12 points a besoin d'environ 14 à 16 points de pas vertical pour respirer, donc décrémenter Y de 14 par ligne est un interlignage de départ raisonnable. Il n'y a pas d'avance de ligne automatique. Vous calculez chaque ligne de base vous-même, ce qui est fastidieux pour un paragraphe mais précis pour un formulaire, où chaque champ se trouve à une coordonnée fixe.
Deux remarques pratiques sur le nom de police. Il est résolu par rapport aux polices installées sur la machine de compilation, et ce que le système renvoie est ce qui est intégré, donc un nom résolu sur votre bureau et un nom résolu sur un serveur de compilation ne sont pas garantis d'être la même fonte. Et la police doit couvrir les scripts de la chaîne. Une séquence de texte cyrillique ou CJK sous une fonte uniquement latine produit des boîtes de glyphes manquants sans erreur, raison pour laquelle l'exemple Hello World utilise une fonte Unicode large pour les mélanges de langues.
L'argument angle effectue une rotation autour du point d'ancrage
Le troisième argument est celui que la plupart des codes laissent éternellement à zéro. Passez une valeur non nulle et la séquence pivote dans le sens antihoraire autour de son propre point d'ancrage (X, Y), le bas gauche du texte, de ce nombre de degrés. Le point d'ancrage lui-même ne bouge pas, donc les mêmes coordonnées qui placent un libellé horizontal placent son jumeau pivoté ; seule la direction dans laquelle les glyphes progressent change.
Pdf.CurrentPage.SetFont('Arial', [fsBold], 11);
// Un libellé d'axe vertical dans la marge gauche : 90 degrés se lit de bas en haut.
Pdf.CurrentPage.TextOut(40, 300, 90, 'Units sold');
// Un filigrane DRAFT en diagonale sur le corps de la page.
Pdf.CurrentPage.SetFont('Arial', [fsBold], 60);
Pdf.CurrentPage.TextOut(150, 250, 45, 'DRAFT');
// En-têtes de colonnes inclinés à 60 degrés pour que les libellés longs entrent dans une colonne étroite.
Pdf.CurrentPage.SetFont('Arial', [], 9);
Pdf.CurrentPage.TextOut(120, 600, 60, 'Q1 actual');
Pdf.CurrentPage.TextOut(160, 600, 60, 'Q2 actual');
Quatre-vingt-dix degrés est le cas courant, un libellé courant sur le côté d'un graphique ou un titre de colonne vertébrale. Quarante-cinq degrés gère les en-têtes de colonnes inclinés, l'astuce qui permet à un libellé large de s'inscrire au-dessus d'une colonne étroite sans déborder dans ses voisines. La rotation ne change pas la façon dont le point d'ancrage est interprété, ce qui perturbe les gens : une séquence à 90 degrés commence toujours à (X, Y) et monte à partir de là, donc pour centrer un libellé pivoté vous ajustez le point d'ancrage, pas l'angle. Lorsque plusieurs séquences pivotées partagent une ligne de base, donnez-leur le même Y et incrémentez X, exactement comme vous incrémenteriez Y pour des lignes horizontales empilées.
Placer des coordonnées sans deviner
Les coordonnées sont la partie qui survit à la révision ou y échoue discrètement. HotPDF mesure depuis le coin inférieur gauche de la page, Y croissant vers le haut, en points à 72 par pouce. Une page US Letter fait 612 par 792 points ; l'A4 fait 595 par 842. Une marge supérieure d'un pouce sur Letter place donc votre première ligne de base près de Y = 792 moins 72 moins la taille de police, non pas à un petit nombre proche du haut. Quiconque arrive des coordonnées d'écran, où Y croît vers le bas depuis zéro, écrit la première ligne hors du bas de la page et passe dix minutes à se demander où elle est passée.
Traitez la mise en page comme de l'arithmétique sur des ancres nommées plutôt qu'une colonne de nombres magiques. Une marge gauche, une ligne de base courante que vous décrémentez par ligne, et un interlignage fixe transforment un bloc de libellés en une courte boucle plutôt qu'un mur de littéraux :
const
LeftMargin = 72; // 1 pouce à l'intérieur
TopBaseline = 720; // première ligne, ~1 pouce vers le bas sur Letter
Leading = 16; // pas vertical entre les lignes
var
Y: Single;
Line: string;
begin
Pdf.CurrentPage.SetFont('Arial', [], 11);
Y := TopBaseline;
for Line in ReportLines do
begin
Pdf.CurrentPage.TextOut(LeftMargin, Y, 0, Line);
Y := Y - Leading;
if Y < 72 then // marge inférieure atteinte
begin
Pdf.AddPage;
Pdf.CurrentPage.SetFont('Arial', [], 11); // la police se réinitialise sur une nouvelle page
Y := TopBaseline;
end;
end;
end;
Le garde de saut de page est la ligne que tout le monde oublie en premier et que le terrain ressent le plus durement. Il n'y a pas de disposition de flux sous TextOut. Décrémentez en dessous de la marge inférieure et le texte continue de se dessiner dans le gouttière, hors de la page, dans le vide, sans avertissement. Donc vous surveillez Y vous-même, appelez AddPage quand il passe en dessous du plancher, et réinitialisez la ligne de base. Le SetFont après AddPage n'est pas un rembourrage optionnel : la police actuelle ne survit pas à un saut de page, et la première séquence sur la nouvelle page s'affiche dans la fonte par défaut du visualiseur si vous l'omettez.
Espacement des caractères et des mots pour l'ajustement et l'alignement
Parfois une chaîne est correcte mais a une mauvaise largeur : un en-tête qui doit s'étendre sur un filet fixe, un code qui devrait se lire avec des chiffres plus aérés, une colonne dont les valeurs doivent être légèrement décalées pour s'aligner. PDF dispose de deux opérateurs d'état de texte pour cela, l'espacement des caractères (Tc, espace supplémentaire ajouté après chaque glyphe) et l'espacement des mots (Tw, espace supplémentaire ajouté à chaque caractère espace), tous deux exprimés en unités d'espace de texte non mis à l'échelle, effectivement des points à la taille de police actuelle. Ce sont des états, pas des arguments de TextOut, donc vous les définissez, dessinez, et les réinitialisez.
// Espacez un court en-tête pour qu'il s'étende sur un filet.
Pdf.CurrentPage.SetCharacterSpacing(4);
Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(72, 740, 0, 'S U M M A R Y');
Pdf.CurrentPage.SetCharacterSpacing(0); // réinitialiser avant le texte normal
// Élargissez les espaces entre les mots sur une seule ligne large.
Pdf.CurrentPage.SetWordSpacing(6);
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(72, 712, 0, 'Name Department Extension');
Pdf.CurrentPage.SetWordSpacing(0);
L'espacement des mots n'agit que sur le caractère espace (code 32), ce qui a une conséquence importante : il ne fait rien à l'intérieur d'une séquence CJK sans espaces ASCII, et interagit étrangement avec du texte encodé comme des indices de glyphes plutôt que des octets. Pour la sortie tabulaire latine, c'est le moyen économique d'élargir les espaces sans ressaisir la chaîne. L'espacement des caractères est le meilleur outil pour un en-tête qui doit atteindre une largeur cible, car il répartit l'ajustement uniformément sur chaque glyphe au lieu de le concentrer aux espaces.
La réinitialisation est toute la discipline. L'espacement, comme la police, fait partie de l'état de dessin de la page, et l'état persiste jusqu'à ce que vous le changiez. Espacez un en-tête et oubliez de le remettre à zéro, et chaque paragraphe en dessous hérite de l'étirement, ce qui se lit comme une légèreté subtile et difficile à localiser qui survit à une relecture désinvolte et échoue à une relecture attentive. L'habitude fiable est de définir une valeur d'espacement, de dessiner la séquence qui en a besoin, et de la remettre à zéro à la ligne suivante, de sorte qu'aucun code ultérieur n'ait à savoir ce qu'une section précédente a fait.
Vérifier la sortie là où elle échoue vraiment
La mise en page de texte échoue sur la deuxième machine, pas la première, donc les vérifications qui comptent se font loin de votre bureau. Ouvrez le fichier généré sur un système sans votre jeu de polices de développeur installé et confirmez que les fontes intégrées se rendent toujours correctement, y compris le latin accentué, les scripts non latins et la ponctuation, en un seul passage plutôt qu'en vérifiant les caractères faciles séparément. Sélectionnez et copiez quelques lignes pour confirmer que le texte est du vrai texte et non des contours, ce qui compte dès que la recherche ou l'extraction est en jeu. Alimentez la mise en page avec des données représentatives, le libellé allemand le plus long et le nombre le plus large, pas un espace réservé soigné, car la séquence qui déborde d'un champ est toujours celle que vous n'avez pas saisie à la main. Et si la page doit s'inscrire sur un formulaire pré-imprimé, imprimez ou rastérisez un échantillon et posez-le sur l'original ; un décalage de ligne de base d'un quart de millimètre est invisible à l'écran et évident sur papier.
Si vous n'avez pas encore écrit une seule page, commencez par l'exemple HotPDF Hello World, qui configure le document, la police et le système de coordonnées bas-gauche dont tout ce qui précède dépend. Les appels TextOut, SetFont et d'espacement présentés ici font partie du HotPDF Component pour Delphi et C++Builder.