Edição delimitada de parágrafos
HPDFParagraphReflow seleciona centros de glyphs carregados dentro de um retângulo fonte explícito e escreve um parágrafo recém-medido dentro de um retângulo alvo explícito na mesma página
O perfil de aceitação cobre Delphi Win32/Win64 e Windows FPC/Lazarus; a geração de tipos de letra medidos usa o construtor existente de aparências de documentos e o suporte dele de tipos de letra instalados
Seleção, layout e preservação
As coordenadas são pontos de espaço de utilizador de páginas PDF sem rotação, com eixo y para cima; ambos os retângulos têm de ser finitos, ter dimensões positivas não superiores a 14400 pontos e caber no MediaBox da página
A seleção pode atravessar múltiplas linhas de base originais e múltiplas streams de conteúdo de páginas; operandos de texto literal, hexadecimal, TJ, de plica e de aspa dupla são reescritos nas fronteiras selecionadas de códigos de carateres
Prefixos, sufixos, avanços de texto e ajustes originais de kerning numérico retidos permanecem no lugar; o texto fonte é removido dos operandos reais em vez de ser escondido por um retângulo de fundo
Form XObjects aninhados usam o Matrix e o BBox deles; cada invocação selecionada e os recursos ancestrais dela são copiados em privado, preservando outras invocações e outras páginas que partilhem os Forms originais
Centros de glyphs fora de um BBox de Form são excluídos; bounds parciais de glyphs da fonte são rejeitados por defeito, e RejectPartialSourceGlyphs = False permite explicitamente a remoção de um código de caráter inteiro quando o centro dele está selecionado
Appearance escolhe o tipo de letra instalado, o estilo, o tamanho, a cor, o padding, o alinhamento e a política de transbordo; a largura do alvo controla o wrap, e o ajuste por redução reporta o EffectiveFontSize real
A geometria gerada de glyphs é interpretada de forma independente e verificada contra o retângulo alvo; a rotação é fixada em zero e a opacidade em um para este helper delimitado
Uma substituição vazia elimina o texto fonte selecionado; defina RequireSourceText = False para inserir um parágrafo numa região fonte vazia
Evidência de colisões
Texto não selecionado que não seja whitespace, imagens XObject e inline, bounds de caminhos pintados e retângulos de anotações são comparados com os bounds gerados de glyphs de substituição
Cada colisão identifica o tipo dela, os bounds em espaço de página, o texto vizinho opcional e um índice de glyph de substituição; a contagem total é retida mesmo quando a lista devolvida de colisões atinge o teto dela
Pontos de controlo de caminhos, bounds de clips de Forms transformados, larguras de strokes e limites de miter produzem caixas conservadoras; um shading com extensão de pintura desconhecida ocupa conservadoramente todo o retângulo alvo
Este teste de geometria pode reportar em excesso imagens transparentes, áreas de anotações não pintadas, curvas, caminhos cortados e envelopes de Forms rodados; não prova separação de tinta perfeita ao píxel nem inspeciona caminhos de clipping arbitrários, composição de misturas ou contornos de tipos de letra
RejectCollisions = True rejeita substituições sobrepostas sem mutação; defini-lo como false mantém o relatório permitindo a política deliberada de sobreposições de quem chama
Pré-visualização, commit e rollback
HPDFPreviewParagraphReflow devolve um plano selado próprio de quem chama, layout semântico de substituição, fingerprints e evidência de colisões, e depois reverte os objetos de documentos gerados
HPDFCommitParagraphReflow repete a análise delimitada e aplica atomicamente o plano apenas quando o conteúdo da fonte, os bounds de Forms, a geometria de anotações e a geometria do tipo de letra de substituição ainda correspondem à pré-visualização
Um plano pertence à mesma instância viva de documento; liberte-o quando terminar, mantenha o documento vivo ao longo de todo o processo e regenere a pré-visualização depois de carregar outro documento ou alterar o conteúdo selecionado
THPDFParagraphReflowSession acrescenta um estado de revisão aplicada: Apply expõe o documento editado para extração ou renderização, Commit publica-o, e Rollback ou a destruição restaura o grafo original
Use sessões em série; o documento tem de sobreviver à sessão, e outras transações de grafos ou chamadas de edição não devem ser intercaladas enquanto Pending estiver true
O rollback restaura o estado do documento em memória; ficheiros externos já guardados continuam a ser responsabilidade de quem chama
Trabalho finito e comportamento de falhas
Os limites por defeito são 64 MiB de conteúdo descodificado, 64 MiB de streams geradas, 100000000 unidades de trabalho, 100000 glyphs, 250000 tokens, 12 níveis de Forms, 256 invocações de Forms, 256 colisões devolvidas e 100000 nós de recursos clonados
Os bytes de strings codificadas são verificados antes da interpretação de glyphs contra o limite restante de glyphs, pelo que encodings de carateres multibyte podem consumir conservadoramente mais orçamento do que a contagem visível de glyphs deles
Os limites descodificados e gerados cobrem o conteúdo tratado pelo helper; o tamanho de streams geradas é verificado antes da publicação, e não representa um limite de memória de pico do processo nem limita todas as alocações do construtor de tipos de letra instalados
O cancelamento é cooperativo através da tokenização de conteúdo e dos ciclos do helper, com verificações em torno das chamadas existentes de geração de tipos de letra; retenha o token até a chamada terminar
Fonte em falta, entrada inválida, colisões, planos obsoletos, conteúdo não suportado, orçamentos esgotados, cancelamento e falhas ordinárias devolvem false com um status explícito e preservam o grafo original de objetos
Texto vertical ou singular selecionado, Forms cíclicos ou não resolvidos, tipos de XObjects pintados não suportados e documentos com campos de assinatura são rejeitados; edição incremental que preserve assinaturas exige um workflow separado
Exemplo
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;
Aceitação e saída guardada
Os runners dedicados de Delphi e de FPC usam fixtures reais de PDFs carregados para parágrafos multilinha, operandos parciais, Forms aninhados partilhados, texto de Forms cortado, alterações de largura e de tipos de letra, tipos de colisões, cancelamento e rollback de grafos
Gravações repetidas de ficheiro/ficheiro e de stream/ficheiro preservam o grafo editado; documentos carregados frescos e inalterados mantêm os bytes exatos da fonte deles, e uma falha injetada de escrita de saída pode ser seguida de uma repetição bem-sucedida
Tests/Fixtures/ParagraphReflow/loaded-repeat-save.pdf isola a regressão de gravação sem usar o helper de parágrafos: carregue, insira um marcador, grave duas vezes e leia de forma independente o segundo ficheiro
Corra Tests/Delphi/Run-ParagraphReflowTests.ps1 -Platform Win64 ou Tests/FPC/Run-ParagraphReflowTests.ps1 -Platform Win64, e selecione Win32 para a outra arquitetura
O verificador Python opcional Tests/Fixtures/ParagraphReflow/verify_output.py usa PyMuPDF para extrair o texto Unicode realmente guardado e renderizar páginas editadas; todos os runners suprimem o lançamento automático do leitor