segunda-feira, 22 de outubro de 2012

Aplicando Zoom nas Fontes do Windows

Nessa última atualização do componente (1.5), além das novas funcionalidades que estava desenvolvendo há bastante tempo, me deparei com um problema, que a principio parecia ser simples, com o ajuste de fontes na tela de visualização do relatório e com a diferença na aparência entre a tela e o papel, quando precisava imprimir os textos que tinham configurado a altura automática (TamanhoAuto = True e WordWrap = True). Percebi que os dois problemas estavam relacionados com a diferença de densidade de pontos da tela e da impressora e o sistema de escala de fontes do Windows.

Ocorre que quando o Windows desenha um texto na tela, a fonte tem uma densidade de pontos por polegada diferente quando imprime para a impressora e para tela. Isso faz com que o GDI, que é o engine responsável por desenhar as letras, utilize mais ou menos pixels para um mesmo texto, e isso deixa de ser proporcional. Dessa forma, como o engine do relatório usava a tela como base para medir as larguras e alturas do textos, ocorria que alguns textos aparentemente corretos na tela de visualização, quando enviados para a impressora, as vezes, ocupavam mais espaço do que o "retângulo" que havia sido calculado para ele.
Esse cálculo baseado na tela, é importante, porque define a quantidade de texto que cabe por página e consequentemente, a quantidade de páginas do relatório.

Eu não entendia porque isso não funcionava porque embora estivesse usando a tela como referência, mantinha os valores em Twips, o que garantia a conversão para qualquer Device Context. Eu usava a API DrawText do Windows. Além dela fornecer um meio de calcular a altura que um texto ocupará, ela trabalha o texto com alinhamentos e quebras de linha automáticas. Porém, eu não tinha o controle sobre em qual palavra quebraria o texto de várias linhas. Quando eu passava ela para o Device Context da tela e para o Device Context da impressora, em alguns textos, ela imprimia as quebras de linha de forma diferente, exatamente por causa do problema da diferença entre as densidades dos pixels.

Uma forma de resolver foi deixar de usar essa API e começar a fazer o cálculo das quebras de linha manualmente, calculando, para cada parágrafo, o número de linhas. Para isso usei utilizei a API GetTextExtentExPoint. Depois imprimir, linha a linha, usando a API ExTextOut.
Até esse ponto estava aparentemente tudo resolvido, porque agora eu tinha o controle sobre cada linha do texto e ao enviar tanto para a tela como para a impressora, a quantidade de linhas sempre seria a mesma, de forma que ao fazer um cálculo do número de linhas e páginas não haveria diferenças.

Porém, surgiu outro problema. Durante o zoom no visualizador, para algumas situações, as larguras e as alturas das linhas ficam diferentes do apresentado quando estava em 100%. Mais tarde descobri que esse problema tinha a mesma origem da diferença de densidade de pixels entre a impressora e a tela. Porque na verdade, ao fazer o zoom, internamente o sistema de escalas estava oferecendo uma resolução maior ou menor para o GDI conforme o fator do zoom na tela. Isso porque o engine de visualização usa as APIs de mapeamento e escala do Windows. Depois de tentar entender e procurar alternativas para resolver essa diferença entre as escalas, achei essa discussão no stackoverflow. A discussão estava falando de um problema com uma rotina em Delphi, mas através dela, consegui entender melhor o que estava acontecendo e finalmente resolver o problema.

Para alguns fatores de zoom e também para algumas resoluções de impressão, uma fonte de texto pode ocupar mais ou menos espaço propositalmente. Assim, para o problema na largura dos textos, tive que verificar a diferença de pixels entre o texto que foi calculado para ser impresso numa largura pré-definida e a largura efetiva que o texto precisa para ser impresso na nova resolução ou densidade. A partir desse cálculo, precisava distribuir ou remover os espaços entre os caracteres (caracteres de espaço em branco, se possível) do texto daquela linha. Foi então que percebi porque o Windows colocou um parâmetro chamado "lpDx" na API ExtTextOut. Esse parâmetro serve para fazer exatamente esse "ajuste fino" entre resoluções diferentes. Já o problema da diferença na altura das linhas do texto quando altera o zoom da visualização, resolvi utilizando a altura da fonte quando o fator de zoom está em 100%.

Como toda solução, embora tenha entendido melhor o funcionamento do GDI, acredito que não seja a última e definitiva. :)

Nenhum comentário:

Postar um comentário