Edición acotada de párrafos
HPDFParagraphReflow selecciona los centros de glifos cargados dentro de un rectángulo de origen explícito y escribe un párrafo recién medido dentro de un rectángulo objetivo explícito en la misma página
El perfil de aceptación cubre Delphi Win32/Win64 y Windows FPC/Lazarus; la generación de fuentes medidas usa el builder de apariencias existente del documento y su soporte de fuentes instaladas
Selección, disposición y preservación
Las coordenadas son puntos de espacio de usuario de página PDF sin rotar, con el eje y hacia arriba; ambos rectángulos deben ser finitos, tener dimensiones positivas de como máximo 14400 puntos y caber en el MediaBox de la página
La selección puede abarcar varias baselines originales y varios streams de contenido de página; los operandos de texto literal, hexadecimal, TJ, comilla simple y comilla doble se reescriben en los bordes de los códigos de carácter seleccionados
Los prefijos y sufijos retenidos, los avances de texto y los ajustes de kerning numéricos originales permanecen en su sitio; el texto de origen se elimina de los operandos reales en lugar de ocultarse con un rectángulo de fondo
Los Form XObjects anidados usan su Matrix y BBox; cada invocación seleccionada y sus recursos ancestro se copian en privado, preservando las demás invocaciones y las otras páginas que comparten los Forms originales
Los centros de glifos fuera del BBox de un Form quedan excluidos; los bounds parciales de un glifo de origen se rechazan por defecto, y RejectPartialSourceGlyphs = False permite explícitamente eliminar un código de carácter entero cuando su centro está seleccionado
Appearance elige la fuente instalada, el estilo, el tamaño, el color, el padding, la alineación y la política de desbordamiento; la anchura objetivo controla el ajuste de línea, y el shrink-to-fit informa del EffectiveFontSize real
La geometría de glifos generada se interpreta de forma independiente y se comprueba contra el rectángulo objetivo; la rotación queda fija en cero y la opacidad en uno para este helper acotado
Un reemplazo vacío elimina el texto de origen seleccionado; usa RequireSourceText = False para insertar un párrafo en una región de origen vacía
Evidencia de colisiones
El texto no seleccionado que no es espacio en blanco, las imágenes XObject e inline, los bounds de trazados pintados y los rectángulos de anotaciones se comparan con los bounds de los glifos de reemplazo generados
Cada colisión identifica su tipo, los bounds en espacio de página, el texto vecino opcional y un índice de glifo de reemplazo; el total se conserva incluso cuando la lista de colisiones devuelta alcanza su tope
Los puntos de control de trazados, los bounds de clip de Forms transformados, la anchura de trazo y los límites de miter producen cajas conservadoras; un shading con extensión de pintado desconocida ocupa de forma conservadora todo el rectángulo objetivo
Esta prueba geométrica puede sobrerreportar imágenes transparentes, áreas de anotaciones sin pintar, curvas, trazados con clip y envolventes de Forms rotados; no prueba una separación de tinta píxel-perfecta ni inspecciona clipping paths arbitrarios, composición de blending ni contornos de fuentes
RejectCollisions = True rechaza los reemplazos que se solapan sin mutar nada; ponerlo en false conserva el informe permitiendo la política de solapamiento deliberada del llamador
Preview, commit y rollback
HPDFPreviewParagraphReflow devuelve un plan sellado propio del llamador, la disposición semántica de reemplazo, fingerprints y evidencia de colisiones, y después hace rollback de los objetos de documento generados
HPDFCommitParagraphReflow repite el análisis acotado y aplica el plan de forma atómica solo cuando el contenido de origen, los bounds de los Forms, la geometría de anotaciones y la geometría de la fuente de reemplazo siguen coincidiendo con el preview
Un plan pertenece a la misma instancia viva del documento; libéralo al terminar, mantén vivo el documento durante todo el proceso y regenera el preview tras cargar otro documento o cambiar el contenido seleccionado
THPDFParagraphReflowSession añade un estado de revisión aplicada: Apply expone el documento editado para extracción o rendering, Commit lo publica, y Rollback o la destrucción restauran el grafo original
Usa las sesiones en serie; el documento debe sobrevivir a la sesión y no deben intercalarse otras transacciones del grafo ni llamadas de edición mientras Pending sea true
El rollback restaura el estado en memoria del documento; los ficheros externos ya guardados siguen siendo responsabilidad del llamador
Trabajo finito y comportamiento ante fallos
Los límites por defecto son 64 MiB de contenido decodificado, 64 MiB de streams generados, 100000000 unidades de trabajo, 100000 glifos, 250000 tokens, 12 niveles de Form, 256 invocaciones de Form, 256 colisiones devueltas y 100000 nodos de recursos clonados
Los bytes de las cadenas codificadas se comprueban antes de interpretar los glifos contra el límite de glifos restante, de modo que las codificaciones multibyte pueden consumir presupuesto de forma conservadora por encima de su número de glifos visibles
Los límites decodificados y generados cubren el contenido que maneja el helper; el tamaño del stream generado se comprueba antes de publicar y no representa un límite de memoria pico del proceso ni acota todas las asignaciones del builder de fuentes instaladas
La cancelación es cooperativa a través de la tokenización del contenido y de los bucles del helper, con comprobaciones alrededor de las llamadas existentes de generación de fuentes; retén el token hasta que la llamada termine
Origen ausente, entrada inválida, colisiones, planes obsoletos, contenido no soportado, presupuestos agotados, cancelación y fallos ordinarios devuelven false con un estado explícito y preservan el grafo de objetos original
El texto vertical o singular seleccionado, los Forms cíclicos o sin resolver, los tipos de XObject pintados no soportados y los documentos con campos de firma se rechazan; la edición incremental que preserva firmas requiere un workflow aparte
Ejemplo
Options := HPDFDefaultParagraphReflowOptions;
Options.Appearance.FontName := 'Arial';
Options.Appearance.FontSize := 11;
Source.Left := 55; Source.Bottom := 659;
Source.Right := 350; Source.Top := 714;
Target := Source;
Plan := nil;
try
if HPDFPreviewParagraphReflow(Document, 0, Source, Target,
'The Supplier must deliver signed monthly reports.', Options,
Plan, Report, CancellationToken) then
if HPDFCommitParagraphReflow(Document, Plan, Report, CancellationToken) then
Document.SaveLoadedDocument('reviewed-contract.pdf');
finally
Plan.Free;
end;
Aceptación y salida guardada
Los runners dedicados de Delphi y FPC usan fixtures reales de PDF cargado para párrafos multibaseline, operandos parciales, Forms anidados compartidos, texto de Form con clip, cambios de anchura y de fuente, clases de colisiones, cancelación y rollback del grafo
Los guardados repetidos file/file y stream/file preservan el grafo editado; los documentos cargados nuevos e intactos conservan sus bytes de origen exactos, y tras un fallo de escritura de salida inyectado puede seguir un reintento con éxito
Tests/Fixtures/ParagraphReflow/loaded-repeat-save.pdf aísla la regresión de guardado sin usar el helper de párrafos: carga, inserta un marcador, guarda dos veces y lee de forma independiente el segundo fichero
Ejecuta Tests/Delphi/Run-ParagraphReflowTests.ps1 -Platform Win64 o Tests/FPC/Run-ParagraphReflowTests.ps1 -Platform Win64, y selecciona Win32 para la otra arquitectura
El verificador Python opcional Tests/Fixtures/ParagraphReflow/verify_output.py usa PyMuPDF para extraer el texto Unicode realmente guardado y renderizar las páginas editadas; todos los runners suprimen el arranque automático del visor