Pregunta

Tengo una aplicación que genera metafiles (EMF). Utiliza el dispositivo de referencia (también conocido como la pantalla) para representar estos metafiles, por lo que el DPI del metafile cambia dependiendo de la máquina en la que se ejecute el código.

Digamos que mi código tiene la intención de crear un metafile que sea 8.5 en x 11 pulg. Usando mi estación de trabajo de desarrollo como referencia, termino con un EMF que tiene

  • Un rClframe de {0, 0, 21590, 27940} (dimensiones del metafile, en milésimas de un mm)
  • un SzlDevice de {1440, 900} (dimensiones del dispositivo de referencia, en píxeles)
  • un szlmillímetros de {416, 260} (dimensiones del dispositivo de referencia, en mm)

De acuerdo, el rClframe me dice que el tamaño del EMF debería ser

  • 21590 /2540 = 8.5 en ancho
  • 27940 /2540 = 11 en alto

Tocar el asunto exacto. Usando esta información, también podemos determinar el DPI físico de mi monitor, si mis matemáticas son correctas:

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

El problema

Cualquier cosa que reproduzca este metafile, una conversión de EMF a PDF, la página de "resumen" cuando haga clic con el botón derecho en el EMF en Windows Explorer, etc., parece truncar el valor de DPI calculado, que muestra 87 en lugar de 87.9231 (incluso 88 estaría bien).

Esto da como resultado una página que tiene un tamaño físico como 8.48 en x 10.98 en (usando 87 ppp) en lugar de 8.5 en x 11 en (usando 88 dpi) cuando se reproduce el metafile.

  • ¿Es posible cambiar el DPI del dispositivo de referencia para que la información almacenada en el metafile utilizada para calcular el DPI salga a un entero agradable?
  • ¿Puedo crear el contexto de mi propio dispositivo y especificar su DPI? ¿O realmente tengo que usar una impresora para hacer eso?

Gracias por cualquier idea.

¿Fue útil?

Solución

Tengo curiosidad por saber cómo Windows conoce el tamaño físico de su monitor. ¿Debe haber cambiado una configuración en alguna parte? Quizás pueda cambiarlo a valores más convenientes que se dividan bien.

Como lo implica el nombre, se debe conectar un "contexto del dispositivo" a un dispositivo del sistema. Sin embargo, esto no necesita ser un controlador de hardware, podría ser un emulador de dispositivos como un controlador de impresión de escritor PDF. He visto al menos uno que te permite establecer un DPI arbitrario.

Otros consejos

Ahora he aprendido más de lo que me importaba haber sabido sobre metafiles.

1. Algunos de los Metafile Las sobrecargas del constructor de la clase funcionan mal y funcionarán con un valor de DPI truncado.

Considera lo siguiente:

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;
}

Si pasaste en un SizeF de {8.5, 11}, puede esperar obtener un Metafile que tiene un rclFrame de {21590, 27940}. La conversión de pulgadas a milímetros no es difícil, después de todo. Pero probablemente no lo harás. Dependiendo de su resolución, GDI+, al parecer, utilizará un valor de DPI truncado al convertir el parámetro de pulgadas. Para hacerlo bien, tengo que hacerlo yo mismo en centésimas de un milímetro, que GDI+ simplemente pasa, ya que así es como se almacena de forma nativa en el encabezado metafile:

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

Error de redondeo #1 resuelto: el rclFrame de mi metafile ahora es correcto.

2. El DPI en un Graphics grabación de instancia a un Metafile siempre está mal.

Mira eso metafileGraphics variable que configuré llamando Graphics.FromImage() en el metafile? Bueno, parece que que Graphics La instancia siempre tendrá un DPI de 96 DPI. (Si tuviera que adivinar, siempre está listo para el lógico DPI, no el físico una.)

Puedes imaginar esa hilaridad que se produce cuando te basas en un Graphics instancia que está operando bajo 96 dpi y grabando a un Metafile instancia que tiene 87.9231 DPI "registrado" en su encabezado. (Digo "registrado" porque está calculado a partir de los otros valores). píxeles) son más grandes, por lo que maldiciones y murmuras por qué tu llamado para dibujar algo de una pulgada, termina, termina siendo una pulgada de una sola vez de largo.

La solución es escalar el Graphics instancia:


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

¿No es eso un grito? Pero parece funcionar.

Error #2 de "redondeo" resuelto: cuando digo dibujar algo a "1 pulgada" a 88 dpi, ¡ese píxel sería mejor $%$^! Grabado como Pixel #88.

3. szlMillimeters puede variar salvajemente; El escritorio remoto causa mucha diversión.

Entonces, descubrimos (según la respuesta de Mark) que, a veces, Windows consulta el EDID de su monitor y en realidad sabe qué tan grande es físicamente. GDI+ usa de manera útil esto (HORZSIZE etc) al completar el szlMillimeters propiedad.

Ahora imagine que se va a casa para depurar este código de escritorio remoto. Digamos que la computadora de su hogar tiene un monitor de pantalla panorámica de 16: 9.

Obviamente, Windows no puede consultar el EDID de una pantalla remota. Por lo tanto, utiliza el valor predeterminado antiguo de 320 x 240 mm, lo que estaría bien, excepto que resulta ser una relación de aspecto de 4: 3, y ahora el mismo código exacto está generando un metafile en una pantalla que supuestamente no tiene no tener Píxeles físicos cuadrados: el DPI horizontal y el DPI vertical son diferentes, y no recuerdo la última vez que vi que sucedió.

Mi solución para esto por ahora es: "Bueno, no lo ejecute en escritorio remoto".

4. La herramienta EMF-to PDF que estaba usando tenía un error de redondeo al mirar el rclFrame encabezamiento.

Esta fue la causa principal de mi problema que desencadenó esta pregunta. Mi metafile fue "correcto" todo el tiempo (bueno, correcto después de solucionar los dos primeros problemas), y toda esta búsqueda de crear un metafile de "alta resolución" fue un arenque rojo. Es cierto que se pierde cierta fidelidad al grabar el metafile en un dispositivo de visualización de baja resolución; Esto se debe a que los comandos GDI especificados en el MetaFile se especifican en píxeles. No importa que sea un formato vectorial y pueda escalar hacia arriba o hacia abajo, se pierde cierta información. Durante la grabación real Cuando GDI+ decide a qué "píxeles" aprovechar una operación.

Me puse en contacto con el proveedor y me dieron una versión corregida.

Error de redondeo #3 resuelto.

5. El panel 'Resumen' en Windows Explorer simplemente trunca los valores al mostrar el DPI calculado.

Sucede que este valor truncado representaba el mismo valor erróneo que la herramienta EMF a PDF estaba utilizando internamente. Aparte de esto, esta peculiaridad no contribuye con nada significativo a la discusión.

Conclusiones

Dado que mi pregunta era sobre el futuro con DPI en contextos del dispositivo, Mark es una buena respuesta.

Tenga en cuenta que ejecuto con 120 dpi en wxp todo el tiempo (fuentes grandes), lo que significa metafilegraphics.dpix regresará 120.

El archivo EMF no parece registrar cuál era el DPI del contexto de referencia (120 en este caso, 96 para la mayoría de las otras personas).

Para hacer las cosas más interesantes, es posible crear un EMF al dibujar en un mapa de bits en memoria que ha tenido setResolution () establecido en, por ejemplo, 300 ppi. En ese caso, creo que el factor de escala debe ser 300 y no lo que el monitor (86.X) o Windows (120) podría estar usando.

Parece que los valores en la página de resumen están equivocados. Se calculan como:

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

Donde los valores de precisión se calculan sin redondeo o truncamiento.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top