Alimenten cuarenta mil PDFs de clientes a un indexador de búsqueda y los fallos se ordenan solos en tres pilas: documentos cuyas palabras se pegan porque se ignoraron umbrales de espaciado, documentos cuyo texto extraído es ilegible porque una fuente subsetted no trae mapa ToUnicode, y documentos donde "el logo" resulta ser nueve image XObjects separados más una soft mask. La extracción no es una llamada de API — es una cadena de decisiones sobre orden, codificación y procedencia, y cada decisión cambia lo que los sistemas posteriores pueden confiar. losLab PDF Library (PDFlibPas) da a aplicaciones Delphi y C++Builder varios niveles de extracción con distintos contratos de fidelidad; elegir entre ellos deliberadamente es la mayor parte del trabajo.
Niveles de extracción de texto y qué promete cada uno
GetPageText toma un valor de opciones de 0 a 8, y el número selecciona un motor, no un formato. Las opciones 0 a 2 ejecutan una pasada ligera adecuada para vistas rápidas. Las opciones 3 a 8 pasan por el motor de extracción consciente de layout, que reconstruye líneas y espaciado desde la geometría de glifos: 4 y 6 además dividen la salida en palabras, 5 y 6 emiten información de ancho, y 7 produce texto plano mientras ignora deliberadamente metadatos de fuente, color y bloque — la opción habitual para alimentar un índice de búsqueda.
Sea cual sea el nivel, la fidelidad está limitada por el propio documento. PDF mapea códigos de carácter a glifos, y solo el ToUnicode CMap de una fuente (ISO 32000-1 §9.10) mapea esos códigos de vuelta a texto. Una fuente subsetted sin ese mapa deja a todo extractor — este, el copiar y pegar de un visor, cualquiera — adivinando a partir de nombres de glifo o rindiéndose. Detectar ese caso importa más que manejarlo: marquen la página como de baja confianza y enruten a OCR en lugar de indexar basura en silencio.
Cuando las opciones planas no encajan — tokenización personalizada, forense de content streams, construir un embudo de texto propio — la capa de clases expone directamente el decodificador: TPDFExtractor se construye sobre el diccionario de recursos de una página y su colección de fuentes, su método ExtractTextW convierte operaciones de texto del content stream en Unicode mediante la misma maquinaria de fuentes, y su evento OnFindObject expone cada objeto mientras pasa por el stream. La mayoría de aplicaciones nunca necesita bajar tanto; las que sí lo hacen agradecen que la capa sea pública.
Bloques posicionados: la unidad de resultados de búsqueda y revisión de redacción
El texto plano responde qué dice la página; la mayoría de productos termina necesitando saber dónde lo dice — para resaltar un resultado de búsqueda, verificar un candidato de redacción, anclar una anotación. ExtractPageTextBlocks devuelve un handle a una lista de corridas de texto, cada una con su texto, bounding box, nombre de fuente y tamaño:
var
Pdf: TPDFlib;
Blocks, I: Integer;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile('contract.pdf', '') <> 1 then
raise Exception.Create('load failed');
Pdf.SelectPage(1);
Blocks := Pdf.ExtractPageTextBlocks(0);
for I := 0 to Pdf.GetTextBlockCount(Blocks) - 1 do
Writeln(Format('%s [%s %.1f pt at %.0f,%.0f]',
[Pdf.GetTextBlockText(Blocks, I),
Pdf.GetTextBlockFontName(Blocks, I),
Pdf.GetTextBlockFontSize(Blocks, I),
Pdf.GetTextBlockBound(Blocks, I, 0),
Pdf.GetTextBlockBound(Blocks, I, 1)]));
Pdf.ReleaseTextBlocks(Blocks);
finally
Pdf.Free;
end;
end;
La trampa de estado en esta área sorprende a equipos durante integración: SetTextExtractionArea, SetTextExtractionWordGap y SetTextExtractionOptions son configuraciones persistentes de nivel documento, no argumentos por llamada. Una restricción de área configurada para una característica — leer solo la franja de encabezado para clasificación, por ejemplo — trunca silenciosamente toda extracción posterior contra el mismo documento, incluidos los niveles de GetPageText conscientes de layout. Restablezcan el estado de extracción entre tareas lógicas, o acoten cada tarea a su propio handle de documento.
El umbral word-gap es la palanca para la pila de palabras pegadas del diagnóstico inicial: SetTextExtractionWordGap indica al motor de layout cuánto espacio horizontal cuenta como separación de palabra, medido contra el espaciado de glifos de la página. Los layouts tabulares apretados necesitan una brecha menor que las páginas de marketing aireadas, y una configuración por clase de documento supera a una constante global — solo recuerden que persiste como el resto del estado de extracción.
Imágenes: streams originales, no capturas
Renderizar la página y recortar es la forma equivocada de sacar imágenes de un PDF: re-muestrea, incorpora rotación y descarta los originales. GetPageImageList enumera los recursos de imagen reales que referencia la página, y cada elemento expone sus propiedades y datos originales:
var
ImgList, I: Integer;
begin
Pdf.SelectPage(1);
ImgList := Pdf.GetPageImageList(0);
for I := 0 to Pdf.GetImageListCount(ImgList) - 1 do
begin
Writeln(Pdf.GetImageListItemFormatDesc(ImgList, I, 0));
Pdf.SaveImageListItemDataToFile(ImgList, I, 0,
Format('page1-img%.2d.bin', [I]));
end;
Pdf.ReleaseImageList(ImgList);
end;
Inspeccionen GetImageListItemFormatDesc antes de suponer nada sobre un elemento. Lo que las páginas referencian rara vez es una imagen limpia por imagen visible: las soft masks llegan como entradas separadas, el mismo XObject se repite entre páginas — dedupliquen por hash de contenido antes de archivar una exportación de "todas las imágenes" — y los JPEG CMYK necesitan gestión de color aguas abajo o se renderizan invertidos en visores ingenuos. Para un inventario de todo el documento en lugar de uno por página, FindImages con SetFindImagesMode escanea todo el archivo.
Un límite que conviene comunicar temprano a stakeholders: la extracción de imágenes devuelve recursos raster. Logos y diagramas dibujados como trazados vectoriales no son imágenes en el sentido de recursos y nunca aparecerán en una lista de imágenes — cuando el requisito es entregar el gráfico como imagen, la implementación honesta renderiza la región de página a un bitmap, y ambos tipos de salida no deberían compartir carpeta de exportación sin etiquetado.
Fuentes: superficie de auditoría, no función de exportación
La API de fuentes responde preguntas sobre fuentes; no entrega archivos de fuente. Después de que FindFonts escanea el documento, la enumeración recorre por ID y las llamadas de propiedades describen la fuente que esté seleccionada actualmente:
var
I: Integer;
begin
Pdf.FindFonts;
for I := 1 to Pdf.FontCount do // font indexes start at 1, not 0
if Pdf.SelectFont(Pdf.GetFontID(I)) = 1 then
Writeln(Format('%s type=%d embedded=%d subset=%d',
[Pdf.FontName, Pdf.FontType,
Pdf.GetFontIsEmbedded, Pdf.GetFontIsSubsetted]));
end;
Noten los límites del bucle: los índices de fuente van de 1 a FontCount, a diferencia de los índices de bloques de texto y listas de imágenes con base cero unas secciones antes — mezclar ambas convenciones produce un off-by-one que salta la primera fuente o lee más allá de la última. Y para precisar el alcance: no hay exportación de fuentes a nivel de bytes en esta API. Ninguna llamada devuelve el programa de fuente incrustado como archivo TTF u OTF; enumeración más inspección de metadatos es el modelo previsto. Para producción, ese modelo cubre lo que realmente importa — detección de subset por patrón de nombre, auditorías de incrustación antes de conversión archivística (una fuente no incrustada bloquea PDF/A, como se cubre en preflight PDF/A y PDF/UA en Delphi) y diagnósticos de codificación cuando baja la confianza de extracción. Los programas de fuente subsetted son material licenciado e incompleto como fuentes instalables de todos modos; tratarlos como metadatos de auditoría en lugar de activos extraíbles es la postura defendible.
La sonda de codificación gana su lugar en canalizaciones de triage: GetFontEncoding en cada fuente, combinado con la bandera subset, predice la calidad de extracción antes de extraer texto alguno — una página cuyas fuentes están todas subsetted con codificaciones no estándar es candidata a OCR solo por inspección.
Extracción a escala sin cargar documentos
Para canalizaciones por lotes, cargar un documento entero para leer una página desperdicia I/O. Las variantes de llamada única — ExtractFilePageText y ExtractFilePageTextBlocks — toman directamente un nombre de archivo, contraseña y número de página. Para archivos de escala gigabyte hay una marcha más baja: la ruta de acceso directo abre el archivo mediante lecturas xref en streaming, de modo que DAOpenFileReadOnly seguido de DAExtractPageText toca solo los objetos que necesita una página. Hay que respetar un cambio de convención: las funciones DA direccionan páginas por PageRef, un handle de referencia de objeto obtenido de DAFindPage, no por número de página — pasar el número donde corresponde el handle opera sobre el objeto equivocado sin lanzar error. El conjunto más amplio de herramientas Direct Access está mapeado en fusión, división y acceso directo de PDFs grandes.
Preguntas comunes de extracción
¿Por qué el texto extraído es distinto de lo que muestra el visor?
Normalmente por codificación: las ligaduras se decodifican como glifos únicos, y fuentes subsetted con mapas ToUnicode incompletos generan sustituciones o huecos. Comparen la confianza de extracción entre páginas y traten las páginas dominadas por glifos no mapeados como candidatas a OCR.
¿Puede PDFlibPas guardar una fuente incrustada como archivo TTF u OTF?
No. La API de fuentes enumera e inspecciona — nombre, familia, tipo, codificación, estado incrustado y subset — y ese es todo su alcance. Planeen flujos de fuente alrededor de preguntas de auditoría, no de exportación de archivos.
¿Cómo extraigo texto de una sola región de la página?
SetTextExtractionArea restringe las extracciones posteriores a un rectángulo. Recuerden que persiste en el documento: restablézcanlo cuando termine la tarea regional, o la siguiente extracción de página completa devolverá silenciosamente solo la región.
Las compilaciones de evaluación, proyectos demo y referencia completa de la API de extracción están en la página del producto losLab PDF Library for Delphi.