Artículo técnico

Anotaciones PDF en Delphi con HotPDF: Tipos y Rects

Una anotación no es parte del contenido de la página. Cuando usted llama a TextOut o dibuja un rectángulo, las marcas se convierten en parte del flujo de contenido de la página, incorporadas en los bytes que un renderizador pinta. Una anotación es un diccionario separado que cuelga de la página a través de su matriz /Annots, con su propio rectángulo, su propia apariencia y su propio ciclo de vida. Un lector puede abrirla, moverla, ocultarla o eliminarla sin tocar un solo glifo de la página subyacente. Esa separación es la razón principal por la que existen las anotaciones, y también es la fuente de las dos cosas que más sorprenden al principio: dónde aterriza una anotación y cómo se ve una vez que un visor en particular la maneja

HotPDF expone los subtipos de anotación de la norma ISO 32000 a través de una familia de llamadas AddXxxAnnotation en el objeto página. Todas comparten la misma forma: un rectángulo que fija la anotación en la página en el espacio de usuario del PDF, alguna carga útil (texto, un nombre de sello, un par de puntos) y un color. Haga el rectángulo correctamente y la mayor parte del trabajo estará hecho. El resto es saber qué subtipos tienen su propia apariencia y cuáles dependen de que el visor los dibuje

Una página PDF producida por HotPDF que muestra iconos de notas de texto, cuadros de texto libre, marcas cuadradas y de línea, y sellos de aprobación colocados a lo largo de la página
Una página que contiene varios subtipos de anotación a la vez: notas de texto, texto libre, marcas geométricas y sellos

El rectángulo es la anotación, no el texto

Cada llamada de anotación toma un TRect, y ese rectángulo significa algo diferente de las coordenadas que usted pasa a TextOut. Para una nota de texto es la zona activa (hotspot) en la que se puede hacer clic, la pequeña región donde se encuentra el icono de la nota y donde un clic abre el comentario. Para un cuadrado o cuadro de texto libre, es la extensión visible de la marca. Para un sello, es el cuadro en el que se escala el arte del sello. Los números son puntos del espacio de usuario del PDF, medidos desde la esquina inferior izquierda de la página con el eje Y incrementando hacia arriba, la misma convención que usa el resto de HotPDF

Una nota de texto es el subtipo más ligero. Se le da el cuerpo del texto, un rectángulo para el icono, una bandera (flag) que indica si se abre de forma predeterminada, un nombre de icono y un color

Pdf.CurrentPage.AddTextAnnotation(
  'Reviewer: confirm the totals on this line before sign-off.',
  Rect(120, 700, 140, 720),   // zona activa del icono, cuadrado de ~20pt
  False,                      // cerrada hasta que el lector hace clic en ella
  taComment,                  // icono de burbuja
  clBlue);

El rectángulo aquí es deliberadamente pequeño, de unos veinte puntos por lado, porque una nota de texto es solo un icono hasta que alguien hace clic en ella. Si hace el rectángulo grande, no obtiene una nota grande; obtiene un área de clic excesivamente grande con el icono anclado en una esquina. La bandera Open controla si la ventana emergente se muestra cuando se carga el documento. Si configura un grupo de notas en True, se apilarán unas sobre otras y sobre el contenido, así que reserve esa opción para la única nota que realmente desea que el lector vea de inmediato

El nombre del icono proviene de THPDFTextAnnotationType, que se asigna a los iconos de nota estándar: taComment, taKey, taNote, taHelp, taParagraph, taNewParagraph y taInsert. El icono es lo único que cambia este tipo. No altera el comportamiento, y vale la pena saber que no todos los visores dibujan los siete; los más seguros tanto en lectores antiguos como nuevos son taComment, taNote y taHelp

El texto libre escribe en la página, pero sigue siendo una anotación

Una anotación de texto libre (free text) parece contenido porque el texto es visible sin hacer clic, ubicado en su rectángulo como una leyenda. Sin embargo, sigue siendo una anotación, con toda la separación que eso implica, lo cual es exactamente lo que usted quiere para un sello de revisión o una etiqueta de borrador que alguien debería poder eliminar más tarde. La firma (signature) intercambia el icono y la bandera de apertura por un valor de justificación

Pdf.CurrentPage.AddFreeTextAnnotation(
  'DRAFT - not for distribution',
  Rect(200, 210, 400, 235),   // el cuadro donde se coloca el texto
  ftCenter,                   // ftLeftJust / ftCenter / ftRightJust
  clRed);

Aquí el rectángulo importa más que en el caso de una nota de texto, porque el texto se ajusta (wraps) y se alinea dentro de él. Si ajusta el tamaño del cuadro demasiado corto, el texto se recorta en el borde inferior; demasiado angosto y se ajustará en lugares que usted no pretendía. La justificación proviene de THPDFFreeTextAnnotationJust y solo tiene los tres valores. Debido a que el texto libre es una anotación de marcado (markup), un lector que abre el archivo en un editor puede seleccionarlo, moverlo o eliminarlo como una unidad, que es la diferencia que decide si usted usa texto libre o simplemente dibuja las palabras con TextOut. Si la etiqueta debe ser permanente, dibújela. Si es editorial y está destinada a ser removida, hágala una anotación

Marcas geométricas y líneas para señalar elementos

Los cuadrados, círculos y líneas son el marcado que usted usa para señalar una región en lugar de describirla con palabras. AddCircleSquareAnnotation cubre las dos formas de cuadro a través de un THPDFCSAnnotationType de csCircle o csSquare, donde el rectángulo indica los límites de la forma

// Un cuadro dibujado alrededor de una figura que requiere atención
Pdf.CurrentPage.AddCircleSquareAnnotation(
  'Check this region against the source data',
  Rect(50, 300, 120, 360),
  csSquare,
  clGreen);

// Una línea, dada por dos puntos en lugar de un rectángulo
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;

Observe que la anotación de línea rompe el patrón del rectángulo: toma dos registros THPDFCurrPoint, un inicio y un fin, porque una línea se define por sus extremos, no por un cuadro delimitador. El color establece el trazo. Si desea puntas de flecha, HotPDF tiene sobrecargas de AddLineAnnotation que aceptan estilos de terminación de línea, pero la forma simple de tres argumentos dibuja una línea limpia, que suele ser lo que se busca en una llamada de atención

Los subtipos de marcado de texto operan en una región que usted ya ha diseñado. AddHighlightAnnotation toma un rectángulo, contenido opcional y un color que por defecto es amarillo, y tiñe el área de la manera en que lo haría un rotulador resaltador. Está diseñado para colocarse sobre texto real, por lo que el rectángulo debe coincidir con los límites de las palabras que usted dibujó, lo que significa que generalmente lo computa a partir de las mismas coordenadas que le pasó a TextOut en lugar de adivinar

Los sellos dependen de que el visor los renderice

Una anotación de sello es la que tiene más probabilidades de verse diferente de un lector a otro, y vale la pena comprender la razón. AddStampAnnotation nombra un sello estándar a través de THPDFStampAnnotationType, con valores como satApproved, satConfidential, satFinal, satDraft y satForComment

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

El nombre del sello es una solicitud. El formato PDF define el conjunto de nombres de sellos estándar, pero no el arte visual detrás de ellos, por lo que cada visor incluye su propio renderizado de "APPROVED" (APROBADO) o "CONFIDENTIAL" (CONFIDENCIAL), y algunos no renderizan nada en absoluto para nombres que no reconocen. El rectángulo controla el cuadro en el que se escala el arte, y el color es una sugerencia que el visor puede o no respetar. Si un sello tiene que verse idéntico en todas partes, la ruta confiable no es usar un sello estándar en absoluto: dibuje la marca usted mismo con TextOut y las llamadas de dibujo, o colóquelo como una anotación de texto libre cuya apariencia usted controle. Use el sello estándar cuando desee el aspecto familiar del visor y pueda tolerar la variación

Los archivos adjuntos siguen la misma estructura de rectángulo y carga útil. AddFileAttachmentAnnotation toma la descripción, la ruta del archivo que se va a incrustar, un rectángulo para el icono del clip y un color. El archivo viaja dentro del PDF, y el icono es el asa (handle) que un lector usa para extraerlo

En qué difieren las anotaciones de los campos AcroForm

La confusión que cuesta más tiempo es tratar una anotación como si fuera un campo de formulario. Ambos se adjuntan a la página a través de /Annots, y un campo de formulario es, de hecho, un subtipo especial de anotación (un widget), razón por la cual se ven relacionados. Sin embargo, no son intercambiables. Un campo de formulario contiene un valor, tiene un nombre, participa en el orden de tabulación y puede enviarse (submit), restablecerse o manejarse por scripts; esos se crean con las llamadas AddTextField, AddCheckBox y AddPushButton, no con las llamadas de anotación de esta página. Una anotación de marcado contiene un comentario o una forma, no tiene ningún valor que enviar y es la herramienta incorrecta en el momento en que usted necesita recopilar información (input)

La prueba práctica es simple. Si se espera que un usuario escriba, elija o haga clic y que el documento lo recuerde, usted quiere un campo AcroForm. Si está dejando una nota, marcando una región o sellando un estado que viaja con el archivo pero no es un dato a procesar, usted quiere una anotación. Mezclarlos produce documentos que se ven bien pero se comportan mal: un "campo" que nadie puede rellenar, o un comentario que desaparece cuando se restablece el formulario. El lado interactivo, con tipos de campo, validación y acciones de envío, es su propio tema tratado en el tutorial de acciones y campos AcroForm

Poniendo una página en conjunto

Las piezas se componen de la misma manera que el resto de HotPDF. Establezca las propiedades del documento, llame a BeginDoc, dibuje el contenido de la página que necesite con las llamadas de texto y gráficos, agregue anotaciones en la parte superior y cierre con EndDoc. Las anotaciones se adjuntan a CurrentPage, por lo que después de un AddPage aterrizan en la nueva página, y una nota que usted pretendía para la página uno aparecerá silenciosamente en la página dos si la agrega después del salto

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;

Un último reflejo que vale la pena desarrollar cuando el resultado se ve incorrecto: abra el archivo en más de un visor antes de decidir que el código no funciona. Los sellos y los iconos de nota más raros suelen ser los culpables habituales, y debido a que la anotación es una solicitud al lector en lugar de píxeles pintados, una diferencia entre Acrobat y un visor ligero a menudo significa que la especificación está funcionando como se diseñó, no un error en su llamada

Las llamadas de anotación que se muestran aquí son parte del Componente HotPDF para Delphi y C++Builder