Pergunta

Eu tenho um aplicativo que gera metafilos (EMFs). Ele usa o dispositivo de referência (também conhecido como tela) para renderizar esses metafilos; portanto, o DPI do Metafile muda, dependendo da máquina em que o código está em execução.

Digamos que meu código pretenda criar um metafile que seja 8,5 em x 11 pol. Usando minha estação de trabalho de desenvolvimento como referência, acabo com um EMF que tem

  • um rclframe de {0, 0, 21590, 27940} (dimensões do metafile, em milésimos de um mm)
  • um szldevice de {1440, 900} (dimensões do dispositivo de referência, em pixels)
  • a Szlmillimeters de {416, 260} (dimensões do dispositivo de referência, em mm)

Ok, então o rclframe me diz que o tamanho da EMF deveria ser

  • 21590 /2540 = 8,5 em ampla
  • 27940 /2540 = 11 em altura

Pode apostar. Usando essas informações, também podemos determinar o DPI físico do meu monitor, se minha matemática estiver certa:

  • (1440 * 25.4) / 416 = 87.9231 DPI horizontal
  • (900 * 25.4) / 260 = 87.9231 DPI vertical

O problema

Qualquer coisa que reproduza este metafile-uma conversão EMF-para-PDF, a página de "resumo" ao clicar com o botão direito do mouse no EMF no Windows Explorer, etc.-parece truncar o valor de DPI calculado, exibindo 87 em vez de 87.9231 (mesmo 88 seria bom).

Isso resulta em uma página que é de tamanho fisicamente como 8,48 em x 10,98 pol (usando 87 dpi) em vez de 8,5 em x 11 pol (usando 88 dpi) quando o metafile é reproduzido.

  • É possível alterar o DPI do dispositivo de referência para que as informações armazenadas no metafile usadas para calcular o DPI sejam lançadas para um bom número inteiro?
  • Posso criar meu próprio contexto de dispositivo e especificar seu DPI? Ou eu realmente tenho que usar uma impressora para fazer isso?

Obrigado por qualquer insight.

Foi útil?

Solução

Estou curioso para saber como o Windows conhece o tamanho físico do seu monitor. Você deve ter mudado uma configuração em algum lugar? Talvez você possa alterá -lo para valores mais convenientes que se dividem bem.

Conforme implícito no nome, um "contexto do dispositivo" deve ser conectado a um dispositivo do sistema. No entanto, isso não precisa ser um driver de hardware, pode ser um emulador de dispositivo, como um driver de impressão de escritor em PDF. Eu já vi pelo menos um que permite definir um DPI arbitrário.

Outras dicas

Agora aprendi mais do que me importava em saber sobre metafilos.

1. Alguns dos Metafile As sobrecargas do construtor da classe funcionam mal e funcionarão com um valor DPI truncado.

Considere o seguinte:

protected Graphics GetNextPage(SizeF pageSize)
{
    IntPtr deviceContextHandle;
    Graphics offScreenBufferGraphics;
    Graphics metafileGraphics;
    MetafileHeader metafileHeader;

    this.currentStream = new MemoryStream();
    using (offScreenBufferGraphics = Graphics.FromHwnd(IntPtr.Zero))
    {
        deviceContextHandle = offScreenBufferGraphics.GetHdc();
        this.currentMetafile = new Metafile(
            this.currentStream,
            deviceContextHandle,
            new RectangleF(0, 0, pageSize.Width, pageSize.Height),
            MetafileFrameUnit.Inch,
            EmfType.EmfOnly);

        metafileGraphics = Graphics.FromImage(this.currentMetafile);

        offScreenBufferGraphics.ReleaseHdc();
    }

    return metafileGraphics;
}

Se você passou em um SizeF de {8.5, 11}, você pode esperar obter um Metafile isso tem um rclFrame de {21590, 27940}. Afinal, converter polegadas em milímetros não é difícil. Mas você provavelmente não vai. Dependendo da sua resolução, o GDI+, ao que parece, usará um valor DPI truncado ao converter o parâmetro de polegadas. Para acertar, eu tenho que fazer isso em centésimos de um milímetro, pelo qual o GDI+ passa, pois é assim que é armazenado nativamente no cabeçalho da Metafile:

this.currentMetafile = new Metafile(
    this.currentStream,
    deviceContextHandle,
    new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
    MetafileFrameUnit.GdiCompatible,
    EmfType.EmfOnly);

Erro de arredondamento nº 1 resolvido-o rclFrame do meu metafile agora está correto.

2. O DPI em um Graphics gravação de instância para um Metafile está sempre errado.

Veja isso metafileGraphics variável que eu defini ligando Graphics.FromImage() no metafile? Bem, parece que isso Graphics A instância sempre terá um DPI de 96 DPI. (Se eu tivesse que adivinhar, está sempre definido para o lógico DPI, não o fisica 1.)

Você pode imaginar aquela hilaridade que se segue quando você está desenhando um Graphics instância que está operando sob 96 dpi e gravação para um Metafile instância que possui 87.9231 DPI "gravado" em seu cabeçalho. (Eu digo "gravado" porque é calculado a partir dos outros valores.) Os "pixels" do metafile (lembre -se, os comandos GDI armazenados no metafile são especificados em píxeis) são maiores e, portanto, você amaldiçoa e murmura por que sua chamada para desenhar algo de uma polegada acaba sendo uma polegada de um e algo e algo assim.

A solução é diminuir o Graphics instância:


metafileGraphics = Graphics.FromImage(this.currentMetafile);
metafileHeader = this.currentMetafile.GetMetafileHeader();
metafileGraphics.ScaleTransform(
    metafileHeader.DpiX / metafileGraphics.DpiX,
    metafileHeader.DpiY / metafileGraphics.DpiY);

Não é isso uma piada? Mas parece funcionar.

Erro "arredondado" nº 2 resolvido-quando eu digo que desenhe algo em "1 polegada" a 88 dpi, é melhor que o pixel tenha sido $%$^! Gravado como Pixel #88.

3. szlMillimeters pode variar muito; A área de trabalho remota causa muita diversão.

Então, descobrimos (por resposta de Mark) que, às vezes, o Windows consulta o EDID do seu monitor e, na verdade, sabe o tamanho fisicamente. O GDI+ usa isso útil (HORZSIZE etc) ao preencher o szlMillimeters propriedade.

Agora imagine que você vai para casa para depurar este código de desktop remoto. Digamos que seu computador doméstico tenha um monitor Widescreen de 16: 9.

Obviamente, o Windows não pode consultar o EDID de uma tela remota. Por isso, ele usa o padrão antigo de 320 x 240 mm, o que seria bom, exceto que é uma proporção de 4: 3, e agora o mesmo código está gerando um metafile em uma exibição que supostamente não possui não Pixels físicos quadrados: o DPI horizontal e o DPI vertical são diferentes, e não me lembro da última vez que vi isso acontecer.

Minha solução alternativa para isso por enquanto é: "Bem, não o execute sob a área de trabalho remota".

4. A ferramenta EMF para PDF que eu estava usando teve um erro de arredondamento ao olhar para o rclFrame cabeçalho.

Essa foi a principal causa do meu problema que desencadeou essa questão. Meu metafile estava "correto" o tempo todo (bem, correto depois que eu corrigi os dois primeiros problemas), e toda essa pesquisa por criar um metafile de "alta resolução" foi um arenque vermelho. É verdade que alguma fidelidade é perdida ao gravar o metafile em um dispositivo de exibição de baixa resolução; Isso ocorre porque os comandos GDI especificados no metafile são especificados em pixels. Não importa que seja um formato vetor Durante a gravação real Quando o GDI+ decide qual "pixel" encaixar uma operação.

Entrei em contato com o fornecedor e eles me deram uma versão corrigida.

Erro de arredondamento nº 3 resolvido.

5. O painel 'Resumo' no Windows Explorer, por acaso, truncaram os valores ao exibir o DPI calculado.

Acontece que esse valor truncado representava o mesmo valor errôneo que a ferramenta EMF para PDF estava usando internamente. Além disso, essa peculiaridade não contribui com nada significativo para a discussão.

Conclusões

Como minha pergunta era sobre o Futzing com o DPI nos contextos de dispositivos, o Mark's é uma boa resposta.

Observe que eu corro com 120 dpi no WXP o tempo todo (fontes grandes), o que significa que o metafilegraphics.dpix retornará 120.

O arquivo EMF não parece registrar o que o DPI era do contexto de referência (120 neste caso, 96 para a maioria das outras pessoas).

Para tornar as coisas mais interessantes, é possível criar um EMF através de um desenho em um bitmap na memória que teve setresolution () definido como, digamos, 300dpi. Nesse caso, acredito que o fator de escala deve ser 300 e não o que o monitor (86.x) ou o Windows (120) podem estar usando.

Parece que os valores na página de resumo estão errados. Eles são calculados como:

Size = round(precize_size)+1
Resolution = trunc(precize_resolution)

Onde os valores precisos são calculados sem arredondamento ou truncamento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top