Une page PDF ne stocke pas de pixels, et elle ne stocke pas non plus un arbre d'objets de forme comme SVG. Elle stocke un programme. Chaque ligne, courbe, remplissage et image placée sur la page est le résultat de l'exécution, de haut en bas, d'une séquence d'opérateurs dans le flux de contenu, sous l'état graphique en cours. Dès lors que l'on comprend ce fait, la majeure partie du comportement du format n'est plus une surprise : pourquoi l'opération de remplissage nécessite un opérateur de tracé distinct après la construction du chemin, pourquoi les couleurs et les épaisseurs de ligne débordent d'une forme à l'autre si vous ne les encapsulez pas, et pourquoi le même code de dessin atterrit à un endroit complètement différent après une transformation de coordonnées. C'est un voyage à travers le modèle d'exécution défini dans la norme ISO 32000 : les opérateurs que vous rencontrez en ouvrant un flux de contenu, et les règles qui déterminent ce qui s'affiche sur la page
Le flux de contenu est un bytecode suffixe
Le flux de contenu est une séquence d'octets plate composée d'opérandes suivis d'opérateurs. L'opérande vient en premier, suivi de l'opérateur qui le consomme, ce qui est l'inverse d'un appel de fonction et correspond parfaitement à une machine à pile : empiler les nombres, puis émettre le verbe. Il n'y a ni imbrication, ni syntaxe d'expression, ni variables ici. Un contour de triangle correspond à ces cinq lignes de code :
100 100 m % moveto: 在 (100, 100) 处开始一个新的子路径
200 200 l % lineto: 添加一条线段到 (200, 200)
300 100 l % lineto: 添加一条线段到 (300, 100)
h % closepath: 连接回起点
S % stroke: 绘制路径轮廓
Les opérateurs sont délibérément conçus pour être concis. Une page réelle comporte des milliers de ces opérateurs, généralement compressés avec FlateDecode. Le prix de cette compacité est que le flux de contenu n'a pas de structure que vous puissiez interroger : le visualiseur ne peut pas demander "où se trouve le titre de cette page", il doit exécuter le programme et voir où l'encre atterrit. C'est la cause fondamentale pour laquelle il est difficile d'extraire du texte de n'importe quel PDF
L'origine est dans le coin inférieur gauche, l'axe Y augmentant vers le haut
Avant que les coordonnées n'aient un sens, vous devez savoir où se trouve (0, 0). Le PDF place l'origine dans le coin inférieur gauche de la page, l'axe X augmentant vers la droite et l'axe Y augmentant vers le haut, mesurés en points, à raison de 72 points par pouce (ISO 32000-2 §8.3.2). Sur une page US Letter, le bord supérieur se situe à y = 792, et non à y = 0. Quiconque est habitué aux graphismes sur écran (origine en haut à gauche, Y augmentant vers le bas) inversera les choses au premier essai, traçant la première ligne au-delà du bas de la page. Cette unité est indépendante du support : que la page soit rendue sur un écran de téléphone ou sur une imageuse, 72 unités représentent un pouce
La plupart des bibliothèques de dessin de pages héritent directement de cette convention. Par exemple, dans HotPDF, TextOut et les appels de chemin sont tous mesurés en points à partir du coin inférieur gauche, de sorte qu'une valeur proche de la hauteur de la page placera le contenu en haut :
// HotPDF, Delphi: y 轴从底部边缘向上测量,单位为点
Pdf.CurrentPage.SetLineWidth(2.0);
Pdf.CurrentPage.MoveTo(100, 700); // 靠近页面顶部
Pdf.CurrentPage.LineTo(300, 700);
Pdf.CurrentPage.Stroke; // 发出 moveto/lineto/stroke 操作符
Cette séquence d'appels se compile exactement sous la forme des opérateurs m, l et S ci-dessus. La bibliothèque n'est que la dactylo du flux de contenu, rien de plus ; comprendre les opérateurs qu'elle émet vous permet de déduire la cause de l'emplacement d'une forme si elle apparaît là où elle n'est pas attendue
Construire le tracé, puis le dessiner
Le PDF sépare la construction du tracé de son dessin, et cette distinction n'est pas académique. Vous décrivez d'abord une forme à l'aide d'opérateurs de construction sans ajouter de contenu visible, puis vous émettez un unique opérateur de tracé pour décider de ce que vous faites du tracé accumulé. Le même triangle peut être un contour, un remplissage de couleur unie, ou les deux, selon le verbe par lequel vous terminez
Les opérateurs de construction sont peu nombreux. m démarre un nouveau sous-tracé à un point donné. l ajoute un segment de ligne droite. c ajoute une courbe de Bézier cubique à partir de six opérandes (deux points de contrôle et un point final). re est un raccourci pour ajouter un rectangle complet à partir d'un quadruplet x, y, largeur, hauteur. h ferme le sous-tracé actuel vers son point de départ. Aucun d'eux ne laisse d'encre sur la page ; ils ne font qu'accumuler de la géométrie
200 250 m % 开始子路径
300 350 400 450 500 250 c % 三次贝塞尔曲线:两个控制点,然后是终点
150 200 re % 一个 150 x 200 的矩形,作为它自己的子路径添加
h % 闭合
L'exemple d'origine utilisait la variante <code>y de l'opérateur de courbe, désormais dépréciée ; la forme c avec trois points explicites est celle que vous verrez en pratique et que vous devez utiliser. Une fois le tracé existant, un opérateur de tracé permet de le terminer. Le vocabulaire est restreint et mérite d'être mémorisé, car chaque forme sur chaque page se termine par l'un de ces éléments :
- RtLTextOut gère le réordonnancement bidirectionnel et les ligatures contextuelles arabes, ce qui couvre la grande majorité des tâches de reporting et de documentation de droite à gauche.
Ses limites — le besoin de scripts allant au-delà du réordonnancement et des ligatures, comme les langues indiennes, et les fonctionnalités OpenType optionnelles implémentées via la substitution de glyphes individuels — ainsi que les détails de couverture des glyphes et de mise en forme sont détaillés dans l'article connexe Gestion de l'arabe et de la mise en forme du texte RTL avec HotPDF ; les points pratiques ici sont plus restreints. Utilisez RtLTextOut pour les blocs de texte de droite à gauche, TextOut pour tout le reste, et ne laissez jamais l'un empiéter sur le chemin de l'autre - XMP est un document XML autonome rédigé au
format RDF et stocké sous forme de flux. Le catalogue y accède via /Metadata, marqué comme /Type /Metadata /Subtype /XML. Contrairement au dictionnaire Info enfoui profondément dans la structure d'objets du PDF, le paquet de données XMP est conçu pour être extrait et analysé indépendamment par des outils ne connaissant rien au PDF. Voici un paquet typique : - f remplit l'intérieur en utilisant la couleur de remplissage actuelle et la règle de remplissage non nul
Bremplit puis trace le contour du chemin en une seule opération ;bferme d'abord le chemin- Le composa
nt PDFium Component de loslab.com regroupe les API de création de documents, de rendu et de texte utilisées dans cette série
La règle de contournement est l'endroit où les gens se trompent souvent. La règle non nulle (f, B) calcule le nombre de croisements signés d'un rayon à partir du point de test, et remplit les zones où le compte n'est pas nul, de sorte qu'un trou dans un sous-tracé ne reste vide que si sa direction d'enroulement est opposée à celle du tracé extérieur. La règle pair-impair (f*, B*) bascule à chaque croisement, quelle que soit la direction. Si un "beignet" s'affiche plein, c'est que le tracé intérieur s'enroule dans le même sens que le tracé extérieur, et vous devez l'inverser ou basculer vers la règle pair-impair
La couleur est un mode, pas un paramètre
Les couleurs dans le flux de contenu sont persistantes. Une fois que vous avez configuré une couleur, elle reste active jusqu'à ce que vous configuriez une autre couleur ou que vous restauriez un état antérieur, c'est pourquoi un changement de couleur non encapsulé appliquera silencieusement sa teinte à tout ce qui est dessiné après lui. Le PDF conserve également la couleur de remplissage et la couleur de contour comme deux paramètres indépendants, les opérateurs en minuscules étant utilisés pour le remplissage et les majuscules pour le contour. Chaque espace colorimétrique de périphérique possède son propre raccourci :
0.5 g % DeviceGray 填充,中度灰(0 = 黑,1 = 白)
0.2 0.6 0.8 rg % DeviceRGB 填充
0.8 0.2 0.1 RG % DeviceRGB 描边(大写 = 描边)
0.2 0.8 0.0 0.1 k % DeviceCMYK 填充
DeviceRGB convient à l'affichage à l'écran, DeviceCMYK est attendu pour l'impression de production et DeviceGray est le choix minimal pour le contenu monochrome. Les espaces de périphériques sont pratiques mais non étalonnés : le même triplet RGB peut s'afficher différemment sur deux moniteurs, ce qui est le problème que les espaces colorimétriques basés sur ICC et les intentions de rendu PDF/A tentent de résoudre. Pour les travaux où la couleur est critique, vous utilisez cs et CS pour sélectionner un espace étalonné et sc et scn pour configurer le composant, mais pour les documents ordinaires, les raccourcis de périphériques font le gros du travail. Une bibliothèque encapsulera ces appels typés. Par exemple, HotPDF accepte un seul TColor et émet les opérateurs correspondants :
Pdf.CurrentPage.SetRGBFillColor(clRed);
Pdf.CurrentPage.Rectangle(100, 100, 200, 150); // x, y, 宽度, 高度
Pdf.CurrentPage.Fill;
Pdf.CurrentPage.SetRGBFillColor(RGB(0, 255, 0));
Pdf.CurrentPage.Circle(150, 400, 50); // x, y, 半径
Pdf.CurrentPage.Fill;
État graphique et pile q/Q
Tout le reste en dehors du tracé lui-même réside dans l'état graphiue : la matrice de transformation actuelle, les couleurs de remplissage et de contour, l'épaisseur de ligne, le motif de tirets, la zone de détourage, l'alpha. L'état est global et mutable, de sorte que la seule méthode sûre pour effectuer des modifications locales consiste à enregistrer l'état complet, le modifier, dessiner, puis effectuer un retour arrière. C'est ce que font q et q. q pousse une copie de l'état actuel sur la pile ; Q la dépouille de la pile, abandonnant chaque modification effectuée depuis le q correspondantQ
q % 保存整个图形状态
2 0 0 2 100 100 cm % 级联一个变换:放大 2 倍,平移到 (100,100)
0.8 g % 灰色填充,作用域限定于此块
% ... 绘制缩放的、灰色的内容 ...
Q % 恢复:变换和颜色还原
Charger le document sans faire confiance aux données d'entrée
La CTM convertit chaque coordonnée
La matrice de transformation courante (CTM) se situe entre les opérandes et la page réelle. Avant de dessiner quoi que ce soit, chaque coordonnée est multipliée par la CTM, ainsi modifier la matrice change l'emplacement et la manière dont tous les dessins ultérieurs apparaissent, sans toucher aux coordonnées individuelles des tracés. L'opérateur cm concatène une nouvelle matrice à la matrice courante, acceptant six opérandes mappés sur une matrice affine [a b c d e f] :
1 0 0 1 100 50 cm % 平移 (100, 50):e 和 f 承载偏移
2 0 0 1.5 0 0 cm % x 放大 2 倍,y 放大 1.5 倍:a 和 d 是缩放因子
0.707 0.707 -0.707 0.707 0 0 cm % 旋转 45 度(在 a、b、c、d 中的 cos/sin)
Deux aspects peuvent dérouter. Premièrement, cm est une composition et non un remplacement, les transformations sont donc cumulatives et l'ordre importe : mettre à l'échelle puis translater diffère de translater puis mettre à l'échelle. Deuxièmement, les rotations et les mises à l'échelle s'effectuent autour de l'origine actuelle, et non du centre de votre forme, donc pour faire pivoter un objet sur place, vous le translatez vers l'origine, effectuez la rotation, puis le translatez en sens inverse, le tout encapsulé dans q/Q. La même matrice est utilisée pour placer les images, ce qui constitue la dernière partie à examiner
Les images et les contenus réutilisables sont des XObjects
Les images trame ne sont pas intégrées dans le flux de contenu. Elles sont stockées en tant que XObjects d'images, qui sont des objets externes avec leur propre dictionnaire (décrivant la largeur, la hauteur, la profondeur de bits, l'espace colorimétrique et le filtre de compression), le flux de contenu se contentant de les référencer. Une photo s'appuyant sur un JPEG se déclare ainsi :
/Photo <<
/Type /XObject
/Subtype /Image
/Width 640
/Height 480
/BitsPerComponent 8
/ColorSpace /DeviceRGB
/Filter /DCTDecode % 图像数据是 JPEG 流
>>
Un exemple minimal de seulement trois pages peut rendre cela plus concret :
q
640 0 0 480 50 300 cm % 将单位正方形映射到 (50, 300) 处的一个 640x480 的框
/Photo Do % 绘制图像 XObject
Q
Le même mécanisme Do pilote les XObjects de formulaires (Form XObject), qui conservent sous la forme de leur propre flux de contenu avec boîte englobante une portion réutilisable de graphiques, logo ou tampon répétitif. Définissez-le une fois, appelez-le plusieurs fois avec des CTM différentes, et ces octets n'apparaissent qu'une seule fois dans le fichier. La plupart des bibliothèques masquent cela derrière un seul appel de placement : HotPDF utilise AddImage pour enregistrer le bitmap et ShowImage pour le placer, acceptant des valeurs explicites de x, y, largeur et hauteur au lieu de vous demander de construire manuellement la matrice :
var
Bmp: TBitmap;
ImgIndex: Integer;
begin
Bmp := TBitmap.Create;
try
Bmp.LoadFromFile('logo.bmp');
ImgIndex := Pdf.AddImage(Bmp, icFlate);
// x, y (左下角), 宽度, 高度, 旋转角度
Pdf.CurrentPage.ShowImage(ImgIndex, 50, 300, 200, 150, 0);
finally
Bmp.Free;
end;
end;
Dans ce cas, remplacez-le par du Pascal ordinaire :
Où cela mène-t-il
Une table qui déborde d'une page et continue sur la page suivante sans étiquette de colonne est illisible. La solution consiste à traiter l'en-tête comme un contenu à redessiner, plutôt que comme un contenu à dessiner une seule fois. Placez les titres de colonnes et les lignes horizontales qui les entourent dans une seule routine, et appelez cette routine au début et chaque fois qu'une nouvelle page est ouverte. Étant donné que l'en-tête et le corps partagent les mêmes constantes de colonne, ils s'alignent par construction
Les appels de dessin Delphi MoveTo, LineTo, Stroke, Rectangle, Fill, SetRGBFillColor, AddImage et ShowImage présentés ici font tous partie du composant HotPDF pour Delphi et C++Builder, qui émet ces opérateurs de flux de contenu pour vous