O PDFlibPas distribui uma passagem de texto retido por uma a 64 colunas de largura igual com DrawTextFlowColumns, e quebra as suas linhas com hifenização limitada e sensível ao idioma assim que se chama SetTextFlowLanguage e SetTextFlowHyphenation. São suportados nove idiomas, e o idioma pode ser herdado do valor /Lang do Catalog do documento em vez de ser definido por cada fluxo
Ambas as funcionalidades existem pela mesma razão: uma coluna estreita é o ponto em que a quebra de linha ingénua deixa de parecer composição tipográfica e começa a parecer um relatório de erro
Porque se desfaz o texto justificado em colunas estreitas?
Porque a justificação distribui o espaço sobrante pelos espaços entre palavras de uma linha, e a quantidade sobrante depende do que cabe. Numa medida larga, o sobrante é pequeno e o olho nunca dá por isso. Reduza a largura a metade e uma única palavra longa que não caiba é empurrada para a linha seguinte, deixando as anteriores a absorver todo esse espaço. Três linhas seguidas assim produzem os canais brancos verticais a que os tipógrafos chamam rios, e que os leitores experimentam como texto difícil de seguir sem saberem porquê
A hifenização corrige a causa e não o sintoma, ao permitir uma quebra dentro da palavra. Os compostos do alemão e do neerlandês tornam isto inegociável: um substantivo de 24 caracteres numa coluna de 60 milímetros não tem bom resultado sem um ponto de quebra. O inglês tolera melhor a ausência, motivo pelo qual produtos pensados primeiro para o inglês tantas vezes distribuem código de esquema que falha na primeira vez que um cliente alemão o utiliza
Que idiomas, e de onde vem o idioma?
A hifenização abrange inglês, alemão, neerlandês, francês, espanhol, italiano, português, russo e turco. Defina-a explicitamente por fluxo com SetTextFlowLanguage, ou deixe-a ser herdada da entrada /Lang do Catalog do documento, o valor que um documento etiquetado e acessível já transporta
Vale a pena usar essa herança em vez de a substituir. Um documento que declara o seu idioma no Catalog está a comunicar o mesmo facto, a partir de um único local, a leitores de ecrã, indexadores de pesquisa e à hifenização, e um único local é onde um facto deve residir. Se já produz saída etiquetada conforme descrito em etiquetagem automática para PDFs acessíveis, a entrada de idioma já está definida e o fluxo pode simplesmente segui-la
var
Lib: TPDFlib;
Flow, Drawn: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.AddTrueTypeFont('Georgia', 1);
Lib.SetTextSize(10.5);
Flow := Lib.NewTextFlow(ArticleBody);
try
Lib.SetTextFlowLanguage(Flow, 'de');
// Ativar, pelo menos 3 caracteres antes da quebra, 3 depois
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
Lib.SetTextFlowMinLines(Flow, 2); // nunca deixar uma única linha isolada
repeat
// Três colunas numa região de 480 pt, calhas de 18 pt, equilibradas
Drawn := Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);
if (Drawn = 0) or (Lib.TextFlowFinished(Flow) = 1) then
Break;
Lib.NewPage;
until False;
finally
Lib.ReleaseTextFlow(Flow);
end;
Lib.SaveToFile('newsletter.pdf');
finally
Lib.Free;
end;
end;
MinPrefix e MinSuffix são tipografia, não validação
Os dois inteiros a seguir à flag de ativação definem o número mínimo de caracteres que têm de permanecer antes e depois de uma quebra. Três e três é uma predefinição conservadora que a maioria dos manuais de estilo aceita. Dois e dois produz mais oportunidades de quebra e resultados visivelmente mais feios, porque um fragmento de duas letras a pender no fim de uma linha lê-se como um erro tipográfico
Aumente os mínimos quando o corpo de letra é grande, em que cada fragmento se torna visualmente proeminente, e reduza-os apenas quando a coluna é genuinamente estreita e decidiu que uma medida apertada importa mais do que uma limpa. Esta é uma decisão de estilo editorial e não uma decisão técnica, o que explica exatamente por que motivo é um parâmetro em vez de uma constante
O que significa "equilibrado" neste contexto?
O parâmetro Balance só altera o comportamento no final de uma passagem. Com o equilíbrio ativo, as colunas são encurtadas para um número exato e igual de linhas quando o restante cabe todo dentro da região, o que evita que uma página final mostre duas colunas completas e uma terceira com uma única linha solitária. Quando a passagem não cabe, todas as colunas mantêm a sua altura total, para que a página transporte o máximo de texto possível, continuando o restante na página seguinte
Essa assimetria é a predefinição correta para documentos contínuos. Equilibrar a meio de um artigo em fluxo desperdiçaria espaço vertical em cada página por um efeito cosmético que ninguém vê, uma vez que as colunas já estão cheias. Equilibrar no final é onde o olho realmente repara, e é exatamente aí que a funcionalidade se aplica
A quebra de linha mede palavras inteiras
O algoritmo de quebra mede palavras completas em vez de acumular larguras de caracteres, e reserva uma pesquisa limitada para tokens de dimensão excessiva que não cabem de todo numa linha, como um URL ou um número de conta. Isso mantém o caso comum rápido e o caso patológico limitado, em vez do contrário
Os hífenes opcionais e os hífenes automáticos só são desenhados quando a quebra que assinalam é efetivamente a quebra escolhida. Isto parece óbvio e é um defeito clássico: uma implementação ingénua escreve o carácter de hífen durante a medição e, se a quebra mudar de posição, o hífen fica esquecido no meio de uma linha. Nada denuncia mais um motor de texto avariado do que um hífen perdido dentro de uma palavra
var
Lib: TPDFlib;
Flow, Needed: Integer;
begin
// Decidir o esquema antes de desenhar seja o que for
Flow := Lib.NewTextFlow(ArticleBody);
try
Lib.SetTextFlowLanguage(Flow, 'fr');
Lib.SetTextFlowHyphenation(Flow, 1, 3, 3);
// Linhas de que o resto da passagem precisa numa largura de coluna
Needed := Lib.MeasureTextFlow(Flow, 148);
if Needed > 3 * LinesPerColumn then
UseTwoPageSpread
else
UseSinglePage;
Lib.DrawTextFlowColumns(Flow, 72, 720, 480, 620, 3, 18, 1);
if Lib.TextFlowFinished(Flow) <> 1 then
CarryOver(Lib.GetTextFlowRemaining(Flow));
finally
Lib.ReleaseTextFlow(Flow);
end;
end;
Mantenha as definições de tipo de letra idênticas entre caixas
Uma regra rege todos os esquemas baseados em fluxo, e vale a pena afirmá-la sem rodeios: DrawTextFlow, DrawTextFlowColumns e MeasureTextFlow quebram todas as linhas usando o tipo de letra selecionado no momento em que são chamadas. Se mudar o tipo de letra ou o tamanho entre duas caixas do mesmo fluxo, ou iniciar uma nova página sem voltar a selecionar um, a segunda caixa quebra de forma diferente daquela que a primeira mediu
O sintoma é irritante precisamente porque parece intermitente: texto que cabe na página um transborda na página dois, ou uma contagem de linhas medida discorda do que foi efetivamente desenhado. Selecione o tipo de letra uma vez antes do ciclo, volte a selecioná-lo após cada NewPage, e o fluxo comporta-se corretamente. Quando surgem sistemas de escrita mistos na mesma passagem, a resolução descrita em substituição automática de tipo de letra para texto CJK e emoji aplica-se tanto à medição como ao desenho, pelo que as larguras permanecem consistentes também nos segmentos de substituição
Para esquemas de relatório em que o fluxo é um elemento entre cabeçalhos, rodapés e blocos gerados por dados, os padrões de composição em o motor de relatórios de conjuntos de dados combinam-se bem com fluxos de colunas: meça primeiro, coloque os elementos fixos e depois entregue ao fluxo a região que sobrar
O PDFlibPas é uma biblioteca PDF para Delphi, C++Builder e Lazarus, e todo o ciclo de vida do TextFlow, criação, desenho, medição, inspeção, retrocesso e libertação, é igualmente exposto através das interfaces DLL e ActiveX. A documentação completa encontra-se na página da biblioteca PDF PDFlibPas para Delphi