Artículo técnico

PDF a Markdown y DOCX en Delphi con PDFlibPas

PDFlibPas convierte contenido PDF en dos formatos editables sin automatización de Office. ExportPageMarkdown y ExportDocumentMarkdown devuelven Markdown semántico con encabezados inferidos, listas ordenadas y no ordenadas, y tablas con formato de tuberías, mientras que SaveDOCXToFile y SaveDOCXToStream escriben un paquete WordprocessingML que contiene párrafos, encabezados, numeración de listas nativa, tablas detectadas, estilos de fuente, saltos de página e imágenes PNG posicionadas

Ambas se ejecutan enteramente en Pascal, en un servidor, sin Word instalado y sin COM. Esa restricción es la razón por la que la función existe en una biblioteca de PDF y no en una herramienta de escritorio

¿Por qué "PDF a Word" es realmente difícil?

Porque una página PDF no contiene párrafos. Contiene operadores de despliegue de texto que colocan corridas de glifos en coordenadas, en el orden que sea que el productor las haya emitido, sin ninguna obligación de indicar que dos corridas pertenecen a la misma oración, y mucho menos al mismo elemento de lista. El formato fue diseñado para describir exactamente una página impresa, y lo logra descartando la estructura que produjo esa página

Así que cada conversor tiene que reconstruir lo que el generador desechó. La agrupación de líneas proviene del espaciado vertical y la alineación de la línea base. Los límites de párrafo provienen de cambios de espaciado e indentación. Un encabezado es una línea cuya fuente es más grande o más gruesa que el cuerpo del texto y que se destaca de lo que sigue. Una lista es una serie de párrafos que comienzan con un carácter de viñeta o un patrón de numeración. Una tabla es una cuadrícula de bloques de texto cuyos bordes se alinean entre filas y columnas. Cada una de esas es una inferencia, y una inferencia significa un buen resultado en documentos que siguen convenciones tipográficas ordinarias, y uno mediocre en documentos que no lo hacen

Los PDF etiquetados son la excepción, y una excepción importante. Cuando el documento lleva un árbol de estructura, los roles de párrafo, encabezado, lista y tabla quedan registrados en lugar de adivinados, razón por la cual el trabajo de accesibilidad descrito en estructura de accesibilidad de PDF etiquetado también rinde frutos en la calidad de conversión. Si usted controla al productor, etiquetar su salida es lo más rentable que puede hacer por cualquiera que después tenga que convertirla

Exportación a Markdown, una página a la vez

La ruta de Markdown es la que conviene usar cuando el destino es un pipeline de texto: un sitio de documentación, un índice de búsqueda, un corpus de recuperación para un asistente. Las opciones son una máscara de bits: PDF_MARKDOWN_INCLUDE_PAGE_MARKERS, PDF_MARKDOWN_DETECT_HEADINGS, PDF_MARKDOWN_PRESERVE_STYLES, con PDF_MARKDOWN_DEFAULT combinando las tres

var
  Pdf: TPDFlib;
  Md: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.LoadFromFile('handbook.pdf', '');

    // Una página, como cadena
    Md := Pdf.ExportPageMarkdown(1, PDF_MARKDOWN_DEFAULT);

    // Un rango de páginas, transmitido a disco como UTF-8 sin BOM
    Pdf.SaveMarkdownToFile('1-40',
      PDF_MARKDOWN_DETECT_HEADINGS or PDF_MARKDOWN_PRESERVE_STYLES,
      'handbook.md');
  finally
    Pdf.Free;
  end;
end;

Los marcadores de página se ganan su lugar en trabajos de recuperación de información. Un fragmento de texto que lleva la página de la que proviene puede citarse con precisión, y un lector que sigue la cita llega exactamente donde está la afirmación. Desactívelos cuando el Markdown esté destinado a lectura humana, donde los límites de página del diseño original son solo ruido

Los puntos de entrada en streaming importan para documentos grandes. SaveMarkdownToStream y SaveMarkdownToFile escriben UTF-8 una página a la vez y no almacenan en búfer la salida completa, así que un manual de 900 páginas no se convierte primero en una cadena de 900 páginas en memoria. La ausencia de una marca de orden de bytes también es deliberada: un BOM en un archivo Markdown confunde a una cantidad sorprendente de generadores de sitios estáticos y herramientas de diff

DOCX sin Office en la máquina

El escritor de DOCX produce el paquete él mismo: entradas ZIP escritas como Deflate crudo con verificaciones CRC, las partes de WordprocessingML, y las relaciones que las vinculan. Nada llama a Word, lo que significa que la conversión corre en un servidor sin cabeza, dentro de una cuenta de servicio, en un contenedor, en todos los lugares donde la automatización de Office está sin licencia, es inestable o está prohibida

var
  Pdf: TPDFlib;
  Target: TFileStream;
begin
  Pdf := TPDFlib.Create;
  Target := TFileStream.Create('handbook.docx', fmCreate);
  try
    Pdf.LoadFromFile('handbook.pdf', '');
    Pdf.SaveDOCXToStream('1-40',
      PDF_DOCX_INCLUDE_IMAGES or PDF_DOCX_DETECT_HEADINGS or
      PDF_DOCX_PRESERVE_STYLES or PDF_DOCX_PRESERVE_PAGE_BREAKS,
      Target);
  finally
    Target.Free;
    Pdf.Free;
  end;
end;

Los datos de imagen se escriben a medida que se procesa cada página, en lugar de recolectarse y anexarse al final, así que el pico de memoria sigue el ritmo de una página y no de todo el documento. El orden explícito de páginas se preserva, y la página PDF seleccionada se restaura después, lo cual importa cuando la exportación es un paso dentro de un trabajo más largo que tenía una página seleccionada por otras razones

¿Qué le da el empaquetado determinístico?

Reproducibilidad byte a byte. Dos conversiones de la misma entrada con las mismas opciones producen el mismo paquete, lo que significa que usted puede hashear la salida para detectar cambios, comparar dos compilaciones de un documento generado, y almacenar en caché de forma agresiva sin preocuparse de que una entrada idéntica produzca un artefacto distinto

La automatización de Office no puede prometer eso. Incrusta marcas de tiempo, identificadores de revisión y metadatos dependientes de la máquina, así que el mismo documento convertido dos veces difiere de formas que arruinan el hasheo. El mismo razonamiento impulsa los identificadores de archivo determinísticos que se explican en identificadores de PDF determinísticos para compilaciones reproducibles: cuando la salida es reproducible, la verificación se convierte en una comparación en lugar de una inspección

Dónde la salida es buena, y dónde no lo es

Sea honesto con sus usuarios sobre esto, porque la calidad de conversión varía más con la entrada que con el conversor. Los PDF etiquetados y los documentos comerciales generados de forma limpia, facturas, reportes, contratos, convierten bien: los encabezados llegan como encabezados, las tablas sobreviven, las listas se renumeran correctamente en Word. Los diseños académicos de dos columnas convierten de forma aceptable si la geometría de columnas es regular. Las tablas que abarcan saltos de página se reensamblan por inferencia y a veces se dividen. El material de marketing muy diseñado, donde el texto se coloca por efecto visual y no en orden de lectura, convierte mal, y ninguna cantidad de inferencia lo arregla

Los documentos escaneados son un caso completamente distinto. Una página que es una sola imagen grande no contiene objetos de texto, así que no hay nada que exportar hasta que exista una capa de texto; la ruta de OCR que la produce es un prerrequisito, no una opción. Antes de correr un lote grande, tome una muestra de una docena de archivos representativos y observe la salida, y considere enumerar primero los elementos de página, como se describe en búsqueda de texto y enumeración de elementos de página, para ver qué contienen realmente las páginas

Para pipelines de asistentes y recuperación de información, la ruta de Markdown suele ser el mejor destino: los encabezados se convierten en límites de fragmento, las tablas permanecen legibles como tablas de tuberías, y los marcadores de página le dan a cada fragmento una ubicación citable. Para edición humana, DOCX es la respuesta, porque lo que el usuario quiere no es el texto sino la capacidad de cambiarlo

PDFlibPas es una biblioteca de PDF para Delphi, C++Builder y Lazarus con interfaces DLL y ActiveX equivalentes, así que las mismas llamadas de exportación están disponibles desde C#, C++ o entornos de scripting. La documentación completa y una compilación de prueba están en la página de la biblioteca PDFlibPas Delphi