Edición acotada de párrafos
HPDFParagraphReflow selecciona los centros de glifos cargados dentro de un rectángulo fuente 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, layout y preservación
Las coordenadas son puntos de user space de página PDF sin rotar, con eje y hacia arriba; ambos rectángulos deben ser finitos, tener dimensiones positivas de 14400 puntos como máximo y caber en el MediaBox de la página
La selección puede abarcar múltiples líneas base originales y múltiples content streams de página; los operandos de texto literal, hexadecimal, TJ, comilla simple y comilla doble se reescriben en los límites de código de carácter seleccionados
Los prefijos y sufijos retenidos, los advances de texto y los ajustes originales de kerning numérico permanecen en su lugar; el texto fuente 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 páginas que comparten los Forms originales
Los centros de glifos fuera del BBox de un Form se excluyen; los bounds parciales de glifos fuente rechazan por defecto, y RejectPartialSourceGlyphs = False permite explícitamente eliminar un código de carácter completo cuando su centro está seleccionado
Appearance elige la fuente instalada, estilo, tamaño, color, padding, alineación y política de overflow; el ancho objetivo controla el ajuste de línea, y shrink-to-fit reporta el EffectiveFontSize real
La geometría de glifos generada se interpreta y comprueba de forma independiente 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 fuente seleccionado; usa RequireSourceText = False para insertar un párrafo en una región fuente vacía
Evidencia de colisiones
El texto no seleccionado que no es whitespace, las imágenes XObject e inline, los bounds de paths 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 conteo total se retiene incluso cuando la lista de colisiones devuelta alcanza su tope
Los puntos de control de paths, los bounds de clip de Forms transformados, el ancho de trazo y los límites de miter producen boxes conservadores; el shading con extensión de pintura desconocida ocupa conservadoramente todo el rectángulo objetivo
Esta prueba de geometría puede sobreportar imágenes transparentes, áreas de anotaciones sin pintar, curvas, paths recortados y envelopes de Forms rotados; no prueba separación de tinta píxel a píxel ni inspecciona clipping paths arbitrarios, composición de blends ni outlines de fuentes
RejectCollisions = True rechaza los reemplazos que se solapan sin mutar; ponerlo en false conserva el reporte mientras permite la política deliberada de solapamiento del caller
Preview, commit y rollback
HPDFPreviewParagraphReflow devuelve un plan sellado propio del caller, el layout semántico de reemplazo, fingerprints y evidencia de colisiones, y luego hace rollback de los objetos de documento generados
HPDFCommitParagraphReflow repite el análisis acotado y aplica el plan atómicamente solo cuando el contenido fuente, los bounds de Forms, la geometría de anotaciones y la geometría de fuentes de reemplazo siguen coincidiendo con el preview
Un plan pertenece a la misma instancia viva de documento; libéralo al terminar, mantén vivo el documento durante todo el proceso y regenera el preview después de cargar otro documento o de cambiar el contenido seleccionado
THPDFParagraphReflowSession agrega 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 graph original
Usa las sesiones en serie; el documento debe sobrevivir a la sesión, y otras transacciones de graph o llamadas de edición no deben intercalarse mientras Pending sea true
El rollback restaura el estado en memoria del documento; los archivos externos ya guardados siguen siendo responsabilidad del caller
Trabajo finito y comportamiento de fallo
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 cadenas codificadas se comprueban antes de interpretar glifos contra el límite de glifos restante, de modo que las codificaciones multibyte pueden consumir conservadoramente más presupuesto que su conteo visible de glifos
Los límites decodificados y generados cubren el contenido manejado por el helper; el tamaño del stream generado se comprueba antes de la publicación 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 de contenido y los loops del helper, con comprobaciones alrededor de las llamadas existentes de generación de fuentes; retén el token hasta que la llamada complete
Fuente ausente, entrada inválida, colisiones, planes obsoletos, contenido no soportado, presupuestos agotados, cancelación y fallos ordinarios devuelven false con un status explícito y preservan el object graph original
El texto vertical o singular seleccionado, los Forms cíclicos o no resueltos, los tipos de XObject pintados no soportados y los documentos con campos de firma se rechazan; la edición incremental que preserva firmas exige un workflow separado
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 PDF cargados reales para párrafos multilínea, operandos parciales, Forms anidados compartidos, texto de Forms recortado, cambios de ancho y fuente, tipos de colisiones, cancelación y rollback de graph
Los guardados repetidos file/file y stream/file preservan el graph editado; los documentos cargados nuevos y sin cambios conservan sus bytes fuente exactos, y un fallo inyectado de escritura de salida puede ir seguido de un reintento exitoso
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 independientemente el segundo archivo
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 opcional de Python Tests/Fixtures/ParagraphReflow/verify_output.py usa PyMuPDF para extraer el texto Unicode realmente guardado y renderizar las páginas editadas; cada runner suprime el lanzamiento automático del visor