En HotPDF Component, THotPDF.Resolution define la unidad de dibujo: cada coordenada X e Y, cada margen, el tamaño que se pasa a SetFont, y los resultados de TextWidth y GetWideTextWidth se miden en 1/Resolution de pulgada. THPDFPage.Width y Height no lo siguen y se quedan en puntos, así que los límites de maquetación tienen que salir del UserWidth y el UserHeight de solo lectura. El motivo corriente para tocar Resolution es un porte: un motor de informes que ya piensa en 1/96 o 1/144 de pulgada se traslada más fácil cuando el lado PDF habla la misma unidad que cuando cada sitio de llamada se lleva un factor de conversión. Funciona bien, siempre que sepa qué números se mudaron a la unidad nueva y cuáles se quedaron atrás
¿Qué cambia realmente THotPDF.Resolution?
THotPDF.Resolution cambia solo cómo HotPDF lee los números que usted pasa; el PDF que escribe es el mismo. El setter son dos líneas: SetResolution guarda el valor y pone DocScale := Value / 72. A partir de ahí, XProjection y YProjection dividen cada coordenada por DocScale de camino al content stream, y SetFont divide el tamaño igual antes de anotarlo. El user space de PDF por defecto es 1/72 de pulgada (ISO 32000-1 §8.3.2.3), así que con el Resolution por defecto de 72 la proyección es la identidad y con 144 una unidad de dibujo es medio punto. No se escribe ninguna entrada /UserUnit. Ese atributo de página, añadido en PDF 1.6, es otra cosa que HotPDF expone como THPDFPage.SetUserUnit. Un detalle que atrapa a quien llega de los tutoriales de TextOut: las coordenadas de página corren desde la esquina superior izquierda con la Y creciendo hacia abajo, porque YProjection calcula el top del MediaBox menos la Y escalada, y eso se mantiene en cualquier Resolution
var
Pdf: THotPDF;
Page: THPDFPage;
Margin: Single;
Title: WideString;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice.pdf';
Pdf.Resolution := 144; // 1 unidad de dibujo = 1/144 de pulgada
Pdf.BeginDoc;
Page := Pdf.CurrentPage; // A4: Width = 595, UserWidth = 1190
Margin := 144; // una pulgada en unidades de dibujo
Page.SetFont('Arial', [fsBold], 28); // 28/144 de pulgada, una fuente de 14 pt
Title := 'INVOICE 2026-0417';
// Alinee a la derecha contra el borde de la página medido en la misma unidad
Page.TextOut(Page.UserWidth - Margin - Page.GetWideTextWidth(Title),
Margin, 0, Title);
Page.SetLineWidth(2); // filete de 1 pt
Page.MoveTo(Margin, Margin + 48);
Page.LineTo(Page.UserWidth - Margin, Margin + 48);
Page.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
¿Por qué Page.Width discrepa de mis coordenadas a Resolution 144?
THPDFPage.Width y Height reportan la página en puntos, sea cual sea el Resolution del documento, mientras que sus coordenadas están en 1/Resolution de pulgada, así que a 144 la página parece la mitad de ancha de lo que es. Una página A4 lee Width = 595 y Height = 842 a Resolution 72 y sigue leyendo 595 y 842 a 144, donde el borde derecho está de verdad en X = 1190. UserWidth y UserHeight, añadidos en v2.766.0, devuelven Width * DocScale, que es el tamaño de página en la unidad con la que usted dibuja. Antes de que existieran, la library mezclaba las dos cosas por dentro, y los síntomas a Resolution 144 eran espectaculares: los párrafos partían tras cada carácter, THPDFTable.Render empujaba cada fila a una página nueva, y tanto el importador HTML como el aplanador XFA dibujaban su contenido a mitad de tamaño, con el formulario aplanado apretujado en la esquina superior izquierda. La maquetación de párrafos, el render de tablas, la importación HTML, el centrado de EMF, el clip de página WMF y los diagnósticos de maquetación leen ahora todos el tamaño en unidades de usuario. Su propio código de maquetación debería hacerlo también: todo lo que compare contra una coordenada de dibujo (un margen derecho, un test de salto de página, un cálculo de centrado) pertenece a UserWidth y UserHeight, jamás a Width y Height
Trampa uno: asignar Width o Height pasa la página a puntos
Poner Page.Width o Page.Height cambia la página en silencio a UserDefined, y una página UserDefined ignora DocScale por completo, así que todo lo que dibuje en ella después va en puntos, no en 1/Resolution de pulgada. El setter es viejo y toma puntos por diseño, razón por la que su significado se dejó como estaba. La proyección de una página UserDefined es un simple X + MinX, y SetFont guarda el tamaño sin tocar. A Resolution 144 el resultado es una página cuyo contenido sale de repente el doble de grande que el de la página anterior. La library cometió exactamente este error ella misma: las páginas de continuación de párrafo solían copiar el tamaño de la página anterior por Width, y cada página de desbordamiento pasaba a puntos. Esas páginas copian ahora Size, Orientation y el Resolution de página en su lugar, y solo recurren a Width y Height cuando la página original ya era UserDefined
Dos salidas, según lo que necesite. Si una hoja estándar sirve, ponga Page.Size y Page.Orientation y siga dibujando en su unidad de Resolution. Si de verdad necesita un tamaño de página a medida, acepte que es una página en puntos y dibuje en puntos; ahí UserWidth iguala a Width, así que el código de maquetación que siempre lee UserWidth sigue funcionando en ambos tipos de página. El test unitario lo fija: a Resolution 144 una página A4 reporta un UserWidth de 1190, pero tras Width := 500 y Height := 400 reporta 500 y 400. Las páginas cargadas se comportan igual, porque una página reconstruida de un PDF existente solo conoce su MediaBox en puntos y dibuja en puntos. Las páginas que este documento creó conservan sus propias unidades cuando usted cambia de página y vuelve por CurrentPageNumber, que es así desde v2.766.26
Trampa dos: ¿por qué los tamaños de fuente salen a la mitad?
Un tamaño de fuente que nació como puntos sale a la mitad a Resolution 144 porque SetFont trata su argumento de tamaño como unidades de dibujo y lo convierte a puntos antes de guardarlo. Por dentro, SetFont guarda ASize / DocScale * DPI en el objeto de fuente actual, así que el valor guardado son siempre puntos. La library tropezó dos veces con esto: el fallback de fuentes en WideTextOutBoxEx y la página de continuación de párrafo le entregaban ambos ese valor en puntos guardado a SetFont, que lo escalaba una segunda vez y dejaba el texto a la mitad. Su código no puede leer el tamaño guardado, pero el mismo bug asoma cada vez que un valor en puntos de otro sitio llega a SetFont: un TFont.Size de un formulario VCL, un tamaño en una definición de informe, una longitud CSS en pt. Conviértalo primero, e incluya en el factor el Resolution propio de la página y el caso UserDefined, como hace la reproducción de metafiles cuando reproduce el Canvas de la página (véase cómo importa HotPDF gráficos vectoriales EMF y WMF para esa vía):
// Unidades de dibujo por punto en la página actual. Espeja la proyección
// que HotPDF usa: 1 en una página dimensionada por Width/Height, si no
// (Resolution del documento / 72) * (Resolution de la página / 72)
function UnitsPerPoint(Pdf: THotPDF): Single;
begin
if Pdf.CurrentPage.Size = UserDefined then
Result := 1
else
Result := (Pdf.Resolution / 72) * (Pdf.CurrentPage.Resolution / 72);
end;
procedure SetFontFromVcl(Pdf: THotPDF; Font: TFont);
begin
// TFont.Size está en puntos; SetFont espera unidades de dibujo
Pdf.CurrentPage.SetFont(AnsiString(Font.Name), Font.Style,
Font.Size * UnitsPerPoint(Pdf));
end;
La library aplica la misma regla a sus propias constantes en puntos. La fuente de 12 puntos con la que empieza toda página nueva se multiplica ahora por el factor interno de unidades por punto, así que son 12 puntos a cualquier Resolution. DrawChart, cuyos márgenes, tamaños de etiqueta y anchos de línea son todos puntos incrustados en el código, corre ahora con la escala fijada temporalmente en 1. Lo que se queda en unidades de dibujo, a propósito, son los valores por defecto de parámetros públicos como el tamaño de módulo de DrawQRCode y el tamaño de fuente por defecto de las tablas: forman parte del contrato de la API, así que a Resolution 144 significan la mitad de lo que significan a 72. Si dimensiona informes desde una plantilla, la guía de salida de informes con fuentes e imágenes en HotPDF cubre de dónde suelen salir esos valores
¿Cómo verifica que una maquetación es independiente del Resolution?
La comprobación más fiable es una comparación de bytes: renderice la misma página a Resolution 72 y otra vez a 144 con cada coordenada y tamaño doblados, y los content streams sin comprimir tienen que ser idénticos. Las dos ejecuciones aterrizan en los mismos valores en puntos tras la proyección, así que cualquier diferencia es un valor que se saltó la conversión. Así comprueba la suite de tests de HotPDF párrafos, tablas, importación HTML, aplanado XFA, arcos, metafiles e imágenes. La misma técnica le sirve a su propio código de informes con casi ningún arnés:
procedure RenderPage(const FileName: string; Res: Integer; K: Single);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.Compression := cmNone; // content streams legibles
Pdf.FileName := FileName;
Pdf.Resolution := Res;
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 10 * K);
Pdf.CurrentPage.TextOut(36 * K, 36 * K, 0, 'Line 1');
Pdf.CurrentPage.Rectangle(36 * K, 60 * K, 200 * K, 40 * K);
Pdf.CurrentPage.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
// RenderPage('r72.pdf', 72, 1) y RenderPage('r144.pdf', 144, 2)
// deben producir content streams de página idénticos byte a byte
Revise los operadores que llevan números: Td, Tm, Tf, re, w y los arrays TJ. Los bytes a nivel de archivo seguirán difiriendo en la fecha de creación y el /ID, así que compare los streams, no los archivos enteros. Un desajuste casi siempre apunta a una de las dos trampas de arriba: una página redimensionada por Width, o un valor en puntos metido directo en SetFont. Si usted es nuevo en las llamadas de dibujo en sí, empiece por el recorrido TextOut de HotPDF para tamaño, estilo y rotación, y vuelva después para cambiar el Resolution cuando su maquetación lea UserWidth. Los detalles completos de la API y las descargas de prueba están en la página del componente PDF HotPDF para Delphi