Coloque 「コード」 descendo a página desenhando um caractere por vez, cada chamada um em mais abaixo que a anterior, e três dos cinco glifos saem errados. Os dois colchetes de canto ficam na coluna como ficam em uma linha horizontal, abrindo de lado, e a marca de vogal longa ー é um traço reto cortando a coluna onde um leitor japonês espera um pequeno traço vertical. Só コ e ド, os dois katakana comuns, ficam como um diagramador os composaria
Rotacionar a linha inteira em noventa graus conserta os colchetes e a marca de vogal longa mais ou menos por acidente e quebra todo o resto: cada kanji e kana agora está deitado de lado. A escrita vertical faz três coisas ao mesmo tempo. Os caracteres ficam em pé, o avanço corre para baixo na página em vez de atravessá-la, e um punhado de caracteres troca para desenhos diferentes. Acertar as três exige uma fonte que traga formas verticais e métricas verticais, e uma fonte PDF configurada para usá-las
Ativando a escrita vertical no PDF Library for Delphi
O PDF Library for Delphi trata a escrita vertical como um modo de desenho. SetVerticalWritingMode(1) liga o modo, toda chamada DrawText depois disso corre para baixo na página a partir do ponto que você der, e SetVerticalWritingMode(0) traz o texto horizontal de volta. O setter retorna o modo que vigorava antes da chamada e o GetVerticalWritingMode lê o atual, então uma rotina auxiliar pode trocar o modo e depois repor exatamente o que encontrou
As duas coordenadas significam algo ligeiramente diferente do texto horizontal. O valor x nomeia a linha central da coluna: cada glifo pende com o seu meio horizontal sobre essa linha, então uma marca de pontuação estreita e um kanji de largura total ficam alinhados no mesmo eixo. O valor y é o topo da primeira célula de caractere, e a coluna cresce para baixo a partir dali. Com SetOrigin(1), que põe y = 0 na borda superior da página, a aritmética se lê como você deitaria a página no papel
var
Lib: TPDFlib;
H: Double;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.AddTrueTypeFont('MS Mincho', 1); // 1 = incorpora a face
Lib.SetTextSize(12);
Lib.SetVerticalWritingMode(1); // o DrawText comum agora corre para baixo
H := Lib.GetVerticalTextHeight('MS Mincho', 12, '第三章 保守点検');
Lib.DrawText(480, 72, '第三章 保守点検'); // a coluna começa em y = 72
Lib.SetVerticalWritingMode(0); // volta ao horizontal
// Origin 1 põe y = 0 no topo, então 72 + H é o pé da coluna;
// a legenda horizontal fica uma linha abaixo dela
Lib.DrawText(456, 72 + H + 18, 'Chapter 3');
Lib.SaveToFile('manual-ja.pdf');
finally
Lib.Free;
end;
end;
O GetVerticalTextHeight recebe o nome da face, o tamanho e o texto, e retorna o comprimento da coluna em pontos. No exemplo, 72 + H é o pé da coluna e a legenda em inglês vai 18 pontos abaixo dele, começando 24 pontos à esquerda da linha central da coluna para que a linha latina curta fique aproximadamente sob a coluna. A função retorna 0 quando não consegue abrir uma face com esse nome, e um layout construído sobre altura zero põe a legenda em cima da coluna, então teste antes de usar o valor
Por que os colchetes ficam de lado em uma coluna vertical?
Porque a fonte guarda dois desenhos de cada caractere desse tipo e o horizontal é o padrão. Faces OpenType listam as alternativas em uma feature GSUB chamada vert, ou vrt2 em faces que vêm com glifos já rotacionados para diagramação vertical. Dar shape a uma linha de texto retorna os glifos horizontais, e algo precisa aplicar a feature vertical depois, um glifo por um glifo. Colchetes de canto, parênteses e a marca de vogal longa viram para correr ao longo da coluna; kana pequenos e pontuação como 、 e 。 se movem para um canto diferente do seu quadrado
O PDF Library for Delphi lê essa feature da face com que está desenhando e troca cada glifo pela forma vertical que a fonte nomeia. Quando uma face carrega as duas features, o vrt2 vence e o vert é ignorado. A especificação pretende que o vrt2 substitua o vert, e ler ambos pode entregar a um caractere dois glifos verticais diferentes dependendo de qual lookup responde primeiro. Uma face que não tem nenhuma das features mantém os seus glifos comuns. Preferimos esse desfecho a uma tabela de substituição embutida: uma tabela chutada acerta os caracteres comuns e sobrescreve silenciosamente as faces cujos designers trataram um caractere de forma diferente
Despeje as tabelas GSUB das cópias que vêm com o Windows e as contagens ficam bem distantes: a feature vertical do MS Gothic mapeia 979 glifos, a do Yu Gothic 2.491, a do SimSun 72, e a do Arial nenhuma (os números exatos mudam com as versões da fonte). Uma face latina que por acaso contenha alguns kana vai desenhar uma coluna em pé com todo colchete e marca de vogal longa na forma horizontal, porque não há nada na fonte para substituir
A substituição também muda a medição. Uma forma vertical pode ter o seu próprio avanço, diferente do glifo horizontal que substitui, então o GetVerticalTextHeight aplica a mesma substituição antes de somar os avanços. Meça as formas horizontais e desenhe as verticais, e a coluna ultrapassa a caixa que você reservou para ela em alguns pontos por colchete
Nada disso toca o texto em si. Copie a coluna de um visualizador e você recebe 「コード」 de volta, porque cada glifo vertical mapeia para o caractere que você passou. Busca e extração de texto veem a sua string de origem, nunca as formas de apresentação rotacionadas
Como as métricas verticais chegam ao PDF
Cada caractere em uma execução vertical avança pela distância que as próprias métricas verticais da fonte lhe dão. Abra a saída em um inspetor de PDF e você vai encontrar uma fonte Type0 com codificação Identity-V cujo CIDFont carrega uma entrada W2 para cada glifo usado: o avanço vertical da tabela vmtx da face, metade da largura do glifo como offset para a linha central da coluna, e a origem vertical. A ISO 32000-1 §9.7.4.3 define esses três números. Para uma face sem métricas verticais não há array W2, e o padrão DW2 avança cada caractere em exatamente um em
A origem vertical é onde fontes de terceiros discordam entre si. A tabela vhea tem campos ascender e descender que parecem a fonte óbvia, mas a convenção de sinal do descender não é compartilhada entre faces. Leia o campo das cópias do Windows com qualquer visualizador de tabelas de fonte e ele sai +140 no MS Gothic e +120 no Yu Gothic, contra −144 no SimSun e −500 no MingLiU, e os valores podem mudar com as versões da fonte. Qualquer posição derivada desse campo deriva por quantidades diferentes para cada face. O PDF Library for Delphi pega a origem do ascender vertical, que as faces CJK nomeadas acima reportam de forma consistente, e cai para 880 unidades — o padrão que a própria especificação PDF dá ao DW2 — quando uma face não reporta nada plausível
Posicionar glifos verticais à mão tem a sua própria armadilha. Texto horizontal frequentemente ajusta espaçamento com números dentro de um array TJ. Sob um modo de escrita vertical, a ISO 32000-1 §9.4.4 aplica esses números ao longo da coluna, então uma correção calculada para espaçamento horizontal empurra cada caractere mais para baixo na página. É por isso que uma execução vertical no content stream é uma string simples de códigos de glifo sem ajuste nenhum, e cada bit de espaçamento vem do W2
Ao conferir a saída, olhe a tinta em vez dos dicionários. Encontrar /Identity-V na fonte prova que a codificação foi declarada; não diz nada sobre para onde o texto realmente corre. Renderizar a página e medir o bounding box dos pixels desenhados diz: uma execução de sete caracteres deve sair com cerca de um caractere de largura e sete de altura
Uma execução vertical em uma página horizontal
O DrawVerticalText desenha uma única execução vertical e deixa o modo de escrita em paz. Ele recebe a posição, o nome da fonte, o tamanho e o texto, então a seleção de fonte atual também fica intocada. Etiquetas de lombada, selos, uma única coluna de nomes ao lado de uma tabela: essas são as exceções verticais dentro de um documento horizontal, e ligar e desligar um modo global ao redor de cada uma é mais estado do que o trabalho merece
// Uma execução vertical dentro de uma página que de resto é horizontal
if Lib.DrawVerticalText(520, 96, 'MS Mincho', 14, '保守点検記録') = 0 then
begin
// A face não pôde ser aberta ou não pôde definir esse texto.
// LogLayoutProblem é rotina sua, não faz parte da biblioteca
LogLayoutProblem('vertical label on page ' + IntToStr(Lib.SelectedPage));
end;
// O texto horizontal ao redor não é afetado
Lib.DrawText(72, 96, 'Maintenance inspection record');
Uma página que mistura as duas direções carrega a face duas vezes nos seus recursos, uma com codificação horizontal e outra com Identity-V, construídas do mesmo programa de fonte. Cada direção mantém as próprias métricas, então uma página de livro japonês com corpo vertical e títulos correntes horizontais, ou um certificado chinês com título vertical sobre linhas de detalhes horizontais, sai com as duas direções espaçadas corretamente
O valor de retorno é a razão de recorrermos ao DrawVerticalText mesmo em documentos que são majoritariamente verticais. No desenho baseado em modo, uma execução que não pode ser posta verticalmente é desenhada da forma horizontal comum em vez de ser descartada, e o DrawText reporta sucesso nos dois casos. Para corpo de texto, esse é o melhor padrão, porque uma linha horizontal perdida é fácil de avistar na prova e um parágrafo ausente não é. Para uma etiqueta cuja direção é todo o ponto, use o DrawVerticalText e trate um 0 como falha de layout
Diagramando colunas da direita para a esquerda
A ordem das colunas é você quem decide. Texto vertical em japonês e chinês tradicional se lê coluna a coluna da direita para a esquerda, então uma página começa na margem direita e cada nova coluna fica à esquerda da anterior. Nenhuma configuração de modo de escrita infere isso a partir do texto, e o mesmo vale para a ordem das páginas em um livro encadernado à direita e para onde pertencem furigana, notas de rodapé e legendas de figuras
A rotina abaixo quebra uma string em colunas que cabem em uma altura fixa, medindo com o GetVerticalTextHeight conforme avança e andando para a esquerda um em e meio por coluna:
procedure DrawColumns(Lib: TPDFlib; const Face: WideString; Size: Double;
const Text: WideString; Right, Top, BoxH: Double);
var
Col, Trial: WideString;
I: Integer;
X: Double;
begin
// Guarda sugerida, não é checagem da biblioteca: altura zero significa que a face
// não pôde ser aberta, e o loop abaixo nunca quebraria uma coluna
if Lib.GetVerticalTextHeight(Face, Size, Text) <= 0 then
Exit;
X := Right; // primeira coluna na margem direita
Col := '';
for I := 1 to Length(Text) do // assume que não há surrogate pairs
begin
Trial := Col + Text[I];
if (Col <> '') and (Lib.GetVerticalTextHeight(Face, Size, Trial) > BoxH) then
begin
Lib.DrawVerticalText(X, Top, Face, Size, Col);
X := X - Size * 1.5; // próxima coluna à esquerda
Col := Text[I];
end
else
Col := Trial;
end;
if Col <> '' then
Lib.DrawVerticalText(X, Top, Face, Size, Col);
end;
Remedir a string de teste a cada caractere é mais lento que somar avanços, mas mede exatamente o que será desenhado, substituições incluídas. O que o loop não faz é aplicar regras de kinsoku. Um colchete de fechamento ou 。 no topo de uma coluna, ou um colchete de abertura abandonado no pé de uma, é um erro de diagramação para um leitor japonês, e corrigir significa puxar ou empurrar um caractere através da quebra antes de desenhar. Surrogate pairs pedem o mesmo cuidado: indexar Text[I] parte um caractere fora do Basic Multilingual Plane ao meio, então avance por code point se o seu texto pode conter um
Para documentos montados a partir de dados, o passo a passo de busca de texto e enumeração de elementos de página mostra como ler de volta o que caiu em cada página, o que é um jeito rápido de confirmar que toda coluna carrega os caracteres que você pretendia
Qual fonte um documento vertical deve usar?
Uma face CJK com métricas verticais próprias e uma feature vert ou vrt2. Sem elas você ganha caracteres em pé avançando um em por vez e nenhuma mudança de forma. Quando o texto vertical parece sutilmente errado, examine a fonte antes de examinar o código. Um visualizador de tabelas que liste as features GSUB de uma face e as tabelas vhea e vmtx dela diz em um minuto se a face pode fazer o trabalho
O DrawVerticalText e o GetVerticalTextHeight abrem uma face instalada pelo nome via Windows. Em builds Lazarus para outras plataformas, a escrita vertical passa pelo SetVerticalWritingMode e pelo DrawText, que trabalham a partir do programa da fonte que você carregou e selecionou, então as mesmas formas verticais e métricas se aplicam contanto que essa fonte seja a face CJK que você pretendia usar
A incorporação segue as regras e os custos de sempre. Uma face CJK completa vai a muitos megabytes, então subsetting é o padrão sensato para qualquer documento que deixa a sua máquina. As notas sobre otimização de tamanho de arquivo PDF e subsetting de fontes cobrem o que esperar, e o passo a passo de incorporar fontes ausentes a um PDF existente cobre o caso de reparo em que um documento vertical chega sem as suas fontes
A escrita CJK vertical vive na mesma API de desenho que o restante das funções de texto do PDF Library for Delphi, uma biblioteca PDF Pascal nativa para Delphi, C++Builder e Lazarus. A página do produto PDF Library for Delphi lista o conjunto completo de recursos de fonte e texto ao lado dela