Abra un PDF de 200 páginas desde un enlace y el navegador a menudo se queda en blanco hasta que llega la mayor parte del archivo. Fast Web View es el nombre que Adobe Acrobat usa para un PDF linealizado (Anexo F de ISO 32000-1): la página uno y un índice pequeño van al frente, así que el visor pinta la página uno temprano y trae las páginas siguientes por peticiones HTTP Range. HotPDF y PDFium Component pueden escribir archivos linealizados desde Delphi
Nada de esto es un formato de archivo aparte. Un PDF linealizado es un PDF ordinario que cualquier lector conforme abre sin manejo especial; la diferencia está en el orden de sus bytes y en dos estructuras extra, el diccionario de parámetros de linealización y el hint stream. Dos condiciones deciden si un lector llega a notar el beneficio. El archivo tiene que estar linealizado y seguir así, cosa que una edición adjuntada deshace en silencio, y el servidor web tiene que responder a las peticiones de rango de bytes con 206 Partial Content. En un disco local, donde saltar al final del archivo no cuesta nada, un PDF linealizado se comporta exactamente como cualquier otro, así que Fast Web View es puramente una optimización de red que rinde para documentos grandes que se abren directamente desde una URL
¿Qué reorganiza un PDF linealizado?
Un PDF linealizado reúne todo lo que la página uno necesita en un bloque contiguo cerca del inicio del archivo, por delante de todas las demás páginas. En un PDF ordinario los objetos pueden quedar en cualquier orden, porque la tabla de referencias cruzadas del final mapea cada número de objeto a su offset de bytes: el lector salta a la cola, lee el puntero startxref, carga la tabla y con eso puede encontrar cualquier cosa (la visión general de la estructura de archivos PDF recorre ese esquema de encabezado, cuerpo, xref y trailer). El diseño es excelente en disco y pobre en red, donde la cola es justamente la parte que llega al final. Para dibujar la página uno, un lector convencional necesita el objeto de página, su flujo de contenido y las fuentes e imágenes que usa, y en un archivo sin ordenar cualquiera de esas piezas puede quedar en el último megabyte
La linealización arregla el orden. Después del encabezado vienen el diccionario de linealización y una pequeña sección de referencias cruzadas de la primera página, luego los objetos a nivel de documento como el catálogo, el hint stream primario y la primera página con todos sus recursos. Las páginas restantes siguen una por una, después los objetos compartidos entre páginas posteriores y al final la tabla de referencias cruzadas principal. Un lector que ignora la optimización igualmente empieza por el final y encuentra cada objeto a través de la tabla principal, razón por la que un archivo linealizado sigue siendo totalmente compatible con software antiguo. La numeración de objetos se ve al revés en un editor hexadecimal como resultado: el bloque del frente lleva los números más altos, mientras que todo lo que va detrás se numera desde 1
El diccionario de linealización y las tablas de hint
El primer objeto después del encabezado %PDF es el diccionario de parámetros de linealización, y el Anexo F exige que quede dentro de los primeros 1024 bytes para que un lector en streaming lo encuentre en el paquete inicial. La tabla F.1 enumera sus entradas: /L es la longitud del archivo completo en bytes, /H el offset y la longitud del hint stream primario, /O el número de objeto de la primera página, /E el offset donde termina la primera página, /N el conteo de páginas y /T el offset de la primera entrada de la tabla de referencias cruzadas principal. La entrada /L pesa más de lo que parece. Cuando no coincide con el tamaño real del archivo, la tabla F.1 le dice al lector que trate el documento como un PDF ordinario y ignore todas las hints que contiene
El hint stream es lo que abarata un salto a la página 40. Contiene una tabla de hint de offsets de página, que registra para cada página dónde empiezan sus objetos y cuántos bytes cubren, y una tabla de hint de objetos compartidos que hace lo mismo para fuentes, logos y otros recursos usados en varias páginas. Un lector que quiere la página 40 consulta su rango de bytes, pide exactamente ese fragmento y renderiza en cuanto llega, trayendo de la misma forma cualquier objeto compartido que aún no tenga. Dos detalles hacen tropezar a los linearizadores caseros. Los offsets de las hints son lógicos, calculados como si el hint stream no existiera, así que un writer que guarda offsets físicos manda cada petición de rango posterior desviada por el tamaño del objeto de hints. Y un writer que sigue el enlace /Parent de una página mientras recolecta sus recursos baja por el árbol de páginas y arrastra el documento completo a la sección de la primera página. Ambos archivos siguen declarándose linealizados. Ninguno carga más rápido
¿Por qué Fast Web View necesita la ayuda del servidor?
Fast Web View funciona solo cuando el servidor entrega fragmentos arbitrarios del archivo a pedido. El lector envía peticiones HTTP con un encabezado Range y espera recibir 206 Partial Content; si el servidor nunca anuncia Accept-Ranges: bytes, o un proxy o CDN que tiene delante convierte las peticiones de rango en transferencias completas, el lector no puede traer la página 40 por su cuenta y descarga el archivo entero. La estructura interna del PDF queda entonces correcta y totalmente desperdiciada, y esta es la falla que con más frecuencia se diagnostica erróneamente como linealización rota. Antes de reconstruir un documento, pida el primer kilobyte de la URL exacta que abre el visor, por ejemplo con curl -r 0-1023, y confirme que el estado es 206. Los hosts de archivos estáticos suelen hacerlo bien por defecto, mientras que los endpoints de aplicaciones que transmiten PDFs a través de su propio handler frecuentemente no
¿Cómo comprobar si un PDF tiene Fast Web View?
Compruebe los bytes en lugar de confiar en la herramienta que produjo el archivo, y verifique las dos mitades de la condición: el archivo se declara linealizado y esa declaración todavía coincide con el archivo en disco. Tres comprobaciones cubren casi todos los casos, desde una mirada rápida en el escritorio hasta una puerta automatizada en un pipeline de publicación:
- Adobe Acrobat o Reader: abra File > Properties; la pestaña Description muestra
Fast Web View: YesoNoen su esquina inferior derecha. Acrobat reporta Yes solo cuando la estructura está presente y vigente, así que un archivo editado después de la linealización muestra No - qpdf en la línea de comandos:
qpdf --check-linearizationvalida las tablas de hint contra las posiciones reales de los objetos, yqpdf --show-linearizationvuelca el diccionario y cada entrada de las tablas de hint - El primer kilobyte del archivo: ábralo en un editor de texto o hexadecimal y busque un objeto que contenga
/Linearized 1dentro de los primeros 1024 bytes; si está, compare su valor/Lcon el tamaño de archivo que reporta el sistema operativo
# Valida el diccionario de linealización y las tablas de hint contra el layout
qpdf --check-linearization report-web.pdf
# Vuelca /L, /H, /O, /E, /N, /T y cada entrada de las tablas de hint
qpdf --show-linearization report-web.pdf
La comprobación con qpdf es la que vale la pena conectar a un pipeline, porque reconcilia las tablas de hint con las posiciones reales de los objetos; una comprobación que solo confirme que el diccionario existe bendecirá un archivo cuyos offsets apuntan a los bytes equivocados. Desde Delphi, THotPDF.IsLoadedLinearized de HotPDF informa si un documento cargado se declara linealizado, escaneando los primeros 1024 bytes buscando /Linearized antes de parsear. PDFium Component responde la misma pregunta mediante la función de clase TPdf.InspectAvailability, cuyo campo Linearization devuelve plnLinearized, plnNotLinearized o plnUnknown sin cargar el documento. Ambas banderas reportan lo que el archivo declara; la comparación de /L le dice si esa declaración todavía coincide con los bytes en disco:
uses
System.SysUtils, System.Classes, System.Math, System.StrUtils, PDFium;
type
TFastWebView = (fwvNo, fwvStale, fwvYes);
// Su propio helper: PDFium decide si el archivo está linealizado,
// y la comparación de /L detecta ediciones adjuntadas después
function FastWebViewState(const FileName: string): TFastWebView;
var
Stream: TFileStream;
Head: AnsiString;
Text: string;
P, Q: Integer;
begin
Result := fwvNo;
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
if TPdf.InspectAvailability(Stream).Linearization <> plnLinearized then
Exit;
SetLength(Head, Min(Stream.Size, 1024)); // Anexo F: primeros 1024 bytes
Stream.Position := 0;
if Length(Head) > 0 then
Stream.ReadBuffer(Head[1], Length(Head));
Text := string(Head);
P := Pos('/Linearized', Text);
if P = 0 then
Exit;
P := PosEx('/L ', Text, P + Length('/Linearized'));
if P = 0 then
Exit;
Inc(P, 3);
Q := P;
while (Q <= Length(Text)) and CharInSet(Text[Q], ['0'..'9']) do
Inc(Q);
if StrToInt64Def(Copy(Text, P, Q - P), -1) = Stream.Size then
Result := fwvYes
else
Result := fwvStale; // linealizado una vez, luego modificado
finally
Stream.Free;
end;
end;
Activar Fast Web View en Acrobat, qpdf y Delphi
Cualquier herramienta que reescriba el archivo completo en el orden del Anexo F puede activar Fast Web View, y lo que más importa es que corra al final. En Acrobat, active Save As optimizes for Fast Web View bajo Preferences > Documents y después use File > Save As en lugar de Save, porque un Save simple puede adjuntar una actualización incremental. En un servidor o en un script de build, qpdf --linearize in.pdf out.pdf reescribe cualquier PDF a forma linealizada y deja la entrada intacta. El código Delphi tiene dos rutas, según si usted genera el documento o lo recibe. HotPDF escribe salida linealizada directamente cuando THotPDF.LinearizeOutput es True; actívela junto con las demás opciones de salida antes de BeginDoc, y el layout del Anexo F se calcula y se escribe dentro de EndDoc:
qpdf --linearize report-final.pdf report-web.pdf
var
PDF: THotPDF;
begin
PDF := THotPDF.Create(nil);
try
PDF.AutoLaunch := False; // no abre un visor después de EndDoc
PDF.FileName := 'catalog-web.pdf';
PDF.LinearizeOutput := True; // se activa antes de BeginDoc
PDF.BeginDoc; // la página 1 existe después de BeginDoc
PDF.CurrentPage.TextOut(72, 72, 0, 'Spring catalog');
PDF.AddPage;
PDF.CurrentPage.TextOut(72, 72, 0, 'Price list');
PDF.EndDoc; // el layout linealizado se escribe aquí
finally
PDF.Free;
end;
end;
Para archivos que ya existen, PDFium Component ofrece TPdf.SaveAsLinearized. Ejecuta el guardado ordinario de PDFium hacia un almacén intermedio, de modo que las ediciones de páginas y los valores de formulario pasan por la misma ruta que cualquier otro guardado, y luego reestructura esa salida según el Anexo F antes de escribir su destino. El método toma los mismos parámetros TSaveOption y TPdfVersion que SaveAs, rechaza saIncremental y cualquier versión anterior a PDF 1.2, y su sobrecarga con nombre de archivo escribe a un archivo temporal que reemplaza el destino solo después de que el guardado tiene éxito. Como el writer reconstruye el archivo desde la revisión actual, también es la forma de restaurar Fast Web View en un archivo que acumuló ediciones adjuntadas. La entrada cifrada necesita saRemoveSecurity: renumerar objetos invalida las llaves por objeto del security handler estándar (ISO 32000-1 §7.6.2), así que la copia web se escribe sin cifrado
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'manual-rev3.pdf'; // trae ediciones adjuntadas
Pdf.Active := True;
if not Pdf.SaveAsLinearized('manual-web.pdf', saNoIncremental) then
raise Exception.Create('Linearized save failed');
finally
Pdf.Free;
end;
end;
Las actualizaciones incrementales apagan Fast Web View en silencio
Una actualización incremental es la razón más común por la que un archivo correctamente linealizado pierde Fast Web View más adelante. Cuando una herramienta agrega una firma, llena un campo de formulario o adjunta una nota adhesiva mediante un guardado incremental, deja los bytes originales intactos y añade al final los objetos modificados, una nueva sección de referencias cruzadas y un nuevo trailer. El diccionario de linealización del frente todavía describe el layout viejo, su /L ya no coincide con el tamaño del archivo, y un lector conforme retrocede a tratar el documento como un PDF ordinario, aunque la estructura linealizada sigue intacta en la primera parte del archivo. HotPDF adjunta exactamente este tipo de revisión mediante BeginIncrementalUpdate y SaveIncrementalUpdate, que es la herramienta correcta mientras el documento sigue cambiando y el paso final equivocado para un archivo destinado a un servidor web:
// Adjunta una revisión: rápido, mantiene intactos los bytes previos,
// y deja el resultado sin Fast Web View
PDF.BeginIncrementalUpdate('report.pdf');
PDF.AddPage;
PDF.CurrentPage.TextOut(72, 760, 0, 'Addendum');
PDF.SaveIncrementalUpdate('report-rev2.pdf');
La regla practicable es editar de forma incremental mientras el documento está en cambio, linealizar una sola vez como el último paso, y dejar Fast Web View desactivado para los archivos que se envían por correo o se descargan antes de que alguien los abra, porque no ganan nada con el índice más grande cargado al frente. Los documentos firmados fuerzan una decisión real: una reescritura completa cambia los bytes que el /ByteRange de cada firma cubre, y firmar después adjunta una actualización que trae de vuelta el desajuste de /L, así que para contratos conserve la firma y acepte descargas completas. El lado de escritura del Anexo F, incluido cómo convergen los offsets antes de escribir el primer byte y por qué la salida linealizada renuncia a los object streams, se cubre en escritura de PDFs linealizados con tablas de hint de HotPDF y linealización Fast Web View con PDFium Component en Delphi. LinearizeOutput e IsLoadedLinearized vienen con el HotPDF Delphi Component, mientras que SaveAsLinearized e InspectAvailability son parte de PDFium Component para Delphi, C++Builder y Lazarus