Article technique

Gérer les annotations PDF à l'aide de HotPDF dans Delphi : types et rectangles

Les annotations ne sont pas du contenu de page. Lorsque vous appelez TextOut ou dessinez un rectangle, ces marquages font partie du flux de contenu de la page, intégrés dans les octets dessinés par le moteur de rendu. Une annotation est un dictionnaire indépendant, rattaché à la page via le tableau /Annots de la page, possédant son propre rectangle, sa propre apparence et son propre cycle de vie. Le lecteur peut l'ouvrir, la déplacer, la masquer ou la supprimer sans altérer le moindre glyphe de la page sous-jacente. Cette séparation est la raison d'être des annotations et la source des deux premières surprises : l'endroit où l'annotation se place, et son aspect une fois qu'un lecteur spécifique prend le relais

HotPDF expose les sous-types d'annotations ISO 32000 via une famille d'appels AddXxxAnnotation sur l'objet de page. Ils partagent tous la même forme : un rectangle qui fixe l'annotation dans l'espace utilisateur PDF de la page, des données utiles (texte, nom du tampon, paire de points) et une couleur. Ajustez correctement le rectangle, et la majeure partie du travail est faite. Le reste consiste à savoir quels sous-types intègrent leur propre apparence et lesquels dépendent du lecteur pour leur rendu

一个由 HotPDF 生成的 PDF 页面,展示分布在页面各处的文本批注图标、自由文本框、方框与直线标记,以及审批图章
一个页面同时承载多种注释子类型:文本批注、自由文本、几何标记和图章

C'est le rectangle qui est l'annotation, pas le texte

Chaque appel d'annotation accepte un TRect, et la signification de ce rectangle diffère des coordonnées que vous transmettez à TextOut. Pour une annotation de texte, c'est la zone cliquable active, la petite zone où se trouve l'icône de la note et où le clic fait apparaître le commentaire. Pour un rectangle ou une zone de texte libre, c'est la limite visible du marquage. Pour un tampon, c'est la boîte dans laquelle le motif du tampon est mis à l'échelle. Ces valeurs sont des points de l'espace utilisateur PDF, mesurés depuis le coin inférieur gauche de la page, avec Y augmentant vers le haut, conformément aux conventions utilisées dans le reste de HotPDF

L'annotation de texte est le sous-type le plus léger. Vous lui fournissez le texte de corps, un rectangle pour l'icône, un drapeau indiquant s'il est ouvert par défaut, un nom d'icône et une couleur

Pdf.CurrentPage.AddTextAnnotation(
  'Reviewer: confirm the totals on this line before sign-off.',
  Rect(120, 700, 140, 720),   // 图标热区,约 20pt 见方
  False,                      // 默认关闭,直到读者点击它
  taComment,                  // 气泡图标
  clBlue);

Le rectangle est délibérément configuré très petit, d'environ vingt points de côté, car l'annotation de texte n'est qu'une icône jusqu'à ce que quelqu'un clique dessus. Agrandir le rectangle ne produit pas une grande note ; vous obtenez simplement une cible de clic surdimensionnée avec l'icône ancrée dans un coin. Le drapeau Open contrôle si le pop-up s'affiche lors du chargement du document. Activer ce drapeau sur plusieurs notes les empilera les unes sur les autres et par-dessus le contenu, laissez-le donc pour la note que vous souhaitez réellement afficher immédiatement au lecteur

Les noms d'icônes proviennent de THPDFTextAnnotationType, qui correspond aux icônes de commentaires standard : taComment, taKey, taNote, taHelp, taParagraph, taNewParagraph et taInsert. Le type ne change que l'icône. Il ne modifie pas le comportement, et il convient de savoir que tous les lecteurs ne dessinent pas les sept ; les plus sûrs à utiliser entre les anciens et les nouveaux lecteurs sont taComment, taNote et taHelp

Le texte libre est écrit sur la page, mais reste une annotation

Une annotation de texte libre ressemble à du contenu car le texte est visible sans cliquer, assis dans son rectangle comme une légende. Il s'agit pourtant bien d'une annotation, avec toute la séparabilité que ce statut implique, ce qui est précisément recherché pour un tampon de révision ou une marque de brouillon devant pouvoir être retirée ultérieurement. Sa signature remplace l'icône et le drapeau d'ouverture par une valeur d'alignement

Pdf.CurrentPage.AddFreeTextAnnotation(
  'DRAFT - not for distribution',
  Rect(200, 210, 400, 235),   // 文本排入的框
  ftCenter,                   // ftLeftJust / ftCenter / ftRightJust
  clRed);

Le rectangle est plus important ici que pour les annotations de texte, car le texte s'y enveloppera et s'y alignera. Si le cadre est trop bas, le texte sera rogné au bord inférieur ; s'il est trop étroit, il retournera à la ligne là où vous ne l'aviez pas prévu. L'alignement provient de THPDFFreeTextAnnotationJust, avec seulement trois valeurs possibles. La zone de texte libre étant une annotation de marquage, le lecteur ouvrant le fichier dans un éditeur peut la sélectionner, la déplacer ou la supprimer comme un tout, et c'est cette distinction qui détermine si vous devez utiliser du texte libre ou dessiner directement ces caractères avec TextOut. Si l'étiquette doit être permanente, dessinez-la. S'il s'agit d'une édition destinée à être retirée ultérieurement, faites-en une annotation

Repères géométriques et lignes droites pour pointer vers des éléments

Les rectangles, les cercles et les lignes droites sont les marques que vous utilisez pour pointer vers une zone, plutôt que de la décrire avec du texte. AddCircleSquareAnnotation couvre ces deux formes de boîtes via csCircle ou csSquare de THPDFCSAnnotationType, le rectangle fournissant les limites de la forme

// 围绕一个需要关注的图形画一个框
Pdf.CurrentPage.AddCircleSquareAnnotation(
  'Check this region against the source data',
  Rect(50, 300, 120, 360),
  csSquare,
  clGreen);

// 一条直线,给的是两个点而非一个矩形
var
  StartPt, EndPt: THPDFCurrPoint;
begin
  StartPt.X := 130; StartPt.Y := 360;
  EndPt.X   := 250; EndPt.Y   := 320;
  Pdf.CurrentPage.AddLineAnnotation(
    'Points from the note to the figure',
    StartPt, EndPt,
    clBlue);
end;

Notez que les annotations de lignes brisent le modèle du rectangle : elles acceptent deux enregistrements THPDFCurrPoint, un point de départ et un point d'arrivée, car une ligne est définie par ses extrémités et non par une boîte de délimitation. La couleur détermine le contour. Si vous souhaitez des flèches, HotPDF possède une surcharge AddLineAnnotation acceptant des styles d'extrémités de lignes, mais la forme simple à trois paramètres dessine une ligne simple, ce qui est généralement requis pour une annotation

Les sous-types de balisage de texte agissent sur la zone que vous avez déjà mise en page. AddHighlightAnnotation accepte un rectangle, un contenu facultatif et une couleur par défaut jaune, colorant cette zone à la manière d'un surligneur. Elle est destinée à recouvrir du texte réel, de sorte que le rectangle doit correspondre aux limites des mots que vous avez tracés, ce qui signifie que vous le calculez généralement à partir du même ensemble de coordonnées que vous transmettez à TextOut, plutôt que de le deviner

Les tampons dépendent du lecteur pour leur rendu

Les annotations de tampon sont celles qui risquent le plus de différer d'un lecteur à l'autre, et il convient d'en comprendre les raisons. AddStampAnnotation spécifie un tampon standard via THPDFStampAnnotationType, prenant des valeurs telles que satApproved, satConfidential, satFinal, satDraft et satForComment

Pdf.CurrentPage.AddStampAnnotation(
  'Approved for release on review',
  Rect(50, 400, 200, 440),
  satApproved,
  clGreen);

Le nom du tampon est une demande. Le PDF définit un ensemble de noms de tampons standard, mais ne définit pas les graphismes associés, de sorte que chaque lecteur intègre son propre rendu pour "APPROVED" ou "CONFIDENTIAL", et certains lecteurs ne dessinent rien pour les noms qu'ils ne connaissent pas. Le rectangle contrôle la boîte dans laquelle le motif est mis à l'échelle, et la couleur est un indice que le lecteur peut adopter ou non. Si un tampon doit avoir exactement la même apparence partout, la voie fiable n'est pas le tampon standard : dessinez vous-même ce marquage à l'aide de TextOut et d'appels de dessin, ou placez-le sous forme d'annotation de texte libre dont vous contrôlez l'apparence. Utilisez les tampons standard lorsque vous souhaitez retrouver l'aspect familier du lecteur et pouvez tolérer les variations

Les pièces jointes de fichiers suivent la même structure de "rectangle chargeant des données". AddFileAttachmentAnnotation accepte une description, le chemin du fichier à intégrer, un rectangle pour l'icône de trombone et une couleur. Le fichier est inclus dans le PDF, et l'icône sert de poignée au lecteur pour l'extraire

En quoi les annotations diffèrent des champs AcroForm

L'erreur la plus coûteuse en temps consiste à traiter les annotations comme des champs de formulaire. Les deux sont rattachés à la page via /Annots, et les champs de formulaire sont en réalité un sous-type d'annotation spécifique (widget), ce qui explique leur parenté visuelle. Ils ne sont pas interchangeables. Un champ de formulaire détient une valeur, possède un nom, participe à l'ordre des tabulations et peut être soumis, réinitialisé ou scripté ; vous les créez à l'aide d'appels AddTextField, AddCheckBox et AddPushButton, et non avec les appels d'annotations décrits sur cette page. Une annotation de balisage contient un commentaire ou une forme, n'a pas de valeur soumise, et s'avère être le mauvais outil si vous devez collecter des données saisies

La distinction pratique est simple. Si un utilisateur doit saisir, sélectionner ou cliquer, et que le document doit le mémoriser, c'est d'un champ AcroForm dont vous avez besoin. Si vous laissez une note, marquez une zone ou apposez un tampon d'état qui circule avec le fichier sans être une donnée, vous avez besoin d'une annotation. Les confondre produira un document qui semble correct mais se comporte mal : un "champ" que personne ne peut remplir, ou un commentaire qui disparaît lors de la réinitialisation du formulaire. L'aspect aspect interactif, y compris les types de champs, les validations et les actions de soumission, est un sujet à part entière traité dans le détail des champs et actions AcroForm

Assembler une page

Ces composants s'assemblent comme le reste de HotPDF. Définissez les propriétés du document, appelez BeginDoc, dessinez tout le contenu de page requis par des appels de texte et de graphismes, ajoutez les annotations, puis terminez par EndDoc. Les annotations sont rattachées à la page courante (CurrentPage), ainsi après un AddPage elles se retrouveront sur la nouvelle page ; si vous ajoutez après la pagination une note que vous destiniez à la première page, elle apparaîtra silencieusement sur la deuxième

Pdf := THotPDF.Create(nil);
try
  Pdf.FileName := 'annotated.pdf';
  Pdf.Compression := cmFlateDecode;
  Pdf.FontEmbedding := True;
  Pdf.BeginDoc;

  Pdf.CurrentPage.SetFont('Arial', [], 11);
  Pdf.CurrentPage.TextOut(50, 740, 0, 'Quarterly figures, draft for review');

  Pdf.CurrentPage.AddTextAnnotation(
    'Confirm the totals before sign-off.',
    Rect(50, 720, 70, 740), False, taComment, clBlue);
  Pdf.CurrentPage.AddFreeTextAnnotation(
    'DRAFT', Rect(450, 720, 540, 745), ftCenter, clRed);
  Pdf.CurrentPage.AddStampAnnotation(
    'For comment', Rect(50, 660, 180, 695), satForComment, clGreen);

  Pdf.EndDoc;
finally
  Pdf.Free;
end;

Lorsque le résultat ne semble pas correct, le dernier réflexe à acquérir est le suivant : ouvrez le fichier dans plusieurs lecteurs avant de juger que votre code est défaillant. Les tampons et les icônes d'annotations plus rares en sont généralement les coupables. Comme une annotation est une demande adressée au lecteur et non des pixels dessinés, la différence entre Acrobat et un lecteur léger s'explique souvent par une spécification fonctionnant comme prévu plutôt qu'un bogue dans vos appels

Les appels d'annotations présentés ici appartiennent au composant HotPDF Component pour Delphi et C++Builder