¿Cómo calcula Excel la resolución de los metafiles que genera al copiar un rango "como se muestra en la pantalla"?
Pregunta
Tengo algún código C# del que obtuve http://bytes.com/topic/c-starp/answers/572657-net-clipboard-metafiles que copia un rango de celda en las siguientes dos configuraciones:
- Como se muestra en la pantalla,
- Como se muestra cuando se imprime.
Cuando miro la resolución de MetaFile resultante (que se documenta como Gets the resolution, in pixels-per-inch, of this Image object
), Obtengo diferentes valores según el método de copia.
Con el Como se muestra cuando se imprime Opción, la resolución es 600, que creo que corresponde a la configuración DPI que tengo en Excel.
Con el Como se muestra en la pantalla configuración, escupe algo como
Metafile.VerticalResolution = 72.08107
yMetafile.HorizontalResolution = 71.95952
. En otras máquinas, he visto que este valor varía mucho (valores alrededor de 111, 130, etc.).
El nivel de zoom no parece afectar esto. Por lo que he observado, los valores se mantienen consistentes en una sola máquina, pero pueden diferir de una máquina a otra.
¿Alguien puede explicar la lógica que Excel sigue al calcular la resolución del metafile en Como se muestra en la pantalla ¿modo?
Después de cambiar la resolución de Windows y medir las resoluciones de metafile, aquí está la tabla que generé (espero que parezca formateado correctamente):
Width Height HorizontalResolution VerticalResolution
1680 1050 71.95952 72.08107
1600 1024 72.05672 72.04874
1600 900 72.05672 71.88678
1360 768 71.96666 71.98228
1280 1024 71.9292 72.04874
1280 960 71.9292 71.9292
1280 800 71.9292 72.05672
1280 768 71.9292 71.98228
1280 720 71.9292 71.99999
1152 864 72.07093 71.95278
1088 612 71.96666 71.96666
1024 768 72.04874 71.98228
960 600 71.9292 71.88678
800 600 72.05672 71.88678
Después de ejecutar un procedimiento similar en una máquina virtual (misma máquina física), estos son los resultados. Un poco más volátil que la máquina física misma. Es posible que estos datos no fueran útiles, pero pensé que los proporcionaría de todos modos.
Width Height HorizontalResolution VerticalResolution
1680 1050 133.35 111.125
1280 800 101.6 84.66666
1024 768 81.27999 81.27999
800 600 63.5 63.5
Solución
Mi hipótesis
Creo que esto tiene que ver con ejecutar una resolución no nativa en un monitor LCD. En "The Old Days", los CRT no tenían una resolución nativa per se. Por lo tanto, la computadora desconocía ninguna resolución preferida para un tamaño de monitor o relación de aspecto dada. Con las pantallas digitales más nuevas (LCD), la computadora ahora conoce la resolución preferida y la relación de aspecto para su pantalla si se instala correctamente. Mi máquina de Windows 7 muestra "recomendado" al lado de la resolución nativa de mi LCD y luego muestra las otras 2 resoluciones de relación de aspecto igual en negro, con los "desajustes" restantes que no están marcados pero seleccionables (lo que resulta en el aspecto aplastado o estirado que odio ver ver ¡En las computadoras de otras personas!).
Los DPI predeterminados de 96 y 120 en Windows se establecieron en los días de CRT. Mi máquina de Windows 7 ya ni siquiera dice DPI, solo dice 'más pequeño', 'medio', 'más grande'.
De cualquier manera, cuando compra un monitor LCD que, por ejemplo, 1920x1080 o 1920x1200, establece la resolución de la pantalla en algo más pequeño, resulta en un factor de conversión. En el caso de las resoluciones horizontales y verticales que no coinciden cerca de 72, su resolución de visualización no nativa puede no ser exactamente el mismo factor de escala verticalmente, ya que horizontalmente da como resultado esta pequeña discrepancia.
Cómo probar mi hipótesis
En cada una de sus máquinas de prueba registra la resolución configurada de sistemas operativos y muestra la resolución nativa. Vea si la relación entre estos dos está cerca de la relación entre su metafile 'como en la pantalla' frente a 96 o 120dpi. Preferiría que realice esta prueba en máquinas físicas para simplemente descartar la posibilidad de una mayor escala con los controladores de escritorio remoto o máquinas virtuales.
Si la solución no es aparente de inmediato, dé un paso más y registre la configuración del panel de operación y control para DPI o 'más pequeño', 'medio' y 'más grande'. Windows XP puede comportarse de manera diferente a Windows Vista/Windows 7.
También puede volver a ejecutar la prueba en la misma máquina física varias veces, ajustar la resolución de visualización configurada entre las pruebas y observar cualquier cambio. Si mi hipótesis es correcta, debería ver una resolución de metafile diferente para cada resolución de visualización configurada en la misma combinación de máquina/visualización física y este resultado debe ser predecible y repetible (volver a la primera resolución debería volver a la misma resolución de metafile)
Editar #1
Encontré un excelente artículo que discute DPI físico frente a DPI lógico. Dale una leida a esto: ¿De dónde proviene 96 DPI en Windows?
¡Así que ahora la próxima prueba que recomendaría es cambiar las pantallas! ¿Tiene un monitor LCD de marca/tamaño/resolución diferente disponible para las pruebas? No necesita tantas líneas como su primera prueba anterior, ya que hemos establecido las diversas resoluciones tienden a producir un DPI muy similar para la misma pantalla. Simplemente pruebe un par de resoluciones comunes que incluyen la resolución nativa de la pantalla y 1024x768 como "línea de base" para comparar.
Además, mientras se asomaba en Windows 7, encontré el enlace "Establecer un tamaño de texto personalizado (DPI)" en el panel de control-> Pantalla que incluía una opción "Usar escala DPI de estilo Windows XP". Si bien no creo que esta sea la principal preocupación, la curiosidad me interesa en su efecto, así que pensé en mencionarlo.
Editar #2 - ¡Resuelto!
La resolución que está viendo en sus metafiles es el DPI físico de su monitor. Publicaré algún código C# aquí para que te pruebes:
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public enum DeviceCap
{
/// <summary>
/// Device driver version
/// </summary>
DRIVERVERSION = 0,
/// <summary>
/// Device classification
/// </summary>
TECHNOLOGY = 2,
/// <summary>
/// Horizontal size in millimeters
/// </summary>
HORZSIZE = 4,
/// <summary>
/// Vertical size in millimeters
/// </summary>
VERTSIZE = 6,
/// <summary>
/// Horizontal width in pixels
/// </summary>
HORZRES = 8,
/// <summary>
/// Vertical height in pixels
/// </summary>
VERTRES = 10,
/// <summary>
/// Number of bits per pixel
/// </summary>
BITSPIXEL = 12,
/// <summary>
/// Number of planes
/// </summary>
PLANES = 14,
/// <summary>
/// Number of brushes the device has
/// </summary>
NUMBRUSHES = 16,
/// <summary>
/// Number of pens the device has
/// </summary>
NUMPENS = 18,
/// <summary>
/// Number of markers the device has
/// </summary>
NUMMARKERS = 20,
/// <summary>
/// Number of fonts the device has
/// </summary>
NUMFONTS = 22,
/// <summary>
/// Number of colors the device supports
/// </summary>
NUMCOLORS = 24,
/// <summary>
/// Size required for device descriptor
/// </summary>
PDEVICESIZE = 26,
/// <summary>
/// Curve capabilities
/// </summary>
CURVECAPS = 28,
/// <summary>
/// Line capabilities
/// </summary>
LINECAPS = 30,
/// <summary>
/// Polygonal capabilities
/// </summary>
POLYGONALCAPS = 32,
/// <summary>
/// Text capabilities
/// </summary>
TEXTCAPS = 34,
/// <summary>
/// Clipping capabilities
/// </summary>
CLIPCAPS = 36,
/// <summary>
/// Bitblt capabilities
/// </summary>
RASTERCAPS = 38,
/// <summary>
/// Length of the X leg
/// </summary>
ASPECTX = 40,
/// <summary>
/// Length of the Y leg
/// </summary>
ASPECTY = 42,
/// <summary>
/// Length of the hypotenuse
/// </summary>
ASPECTXY = 44,
/// <summary>
/// Shading and Blending caps
/// </summary>
SHADEBLENDCAPS = 45,
/// <summary>
/// Logical pixels inch in X
/// </summary>
LOGPIXELSX = 88,
/// <summary>
/// Logical pixels inch in Y
/// </summary>
LOGPIXELSY = 90,
/// <summary>
/// Number of entries in physical palette
/// </summary>
SIZEPALETTE = 104,
/// <summary>
/// Number of reserved entries in palette
/// </summary>
NUMRESERVED = 106,
/// <summary>
/// Actual color resolution
/// </summary>
COLORRES = 108,
// Printing related DeviceCaps. These replace the appropriate Escapes
/// <summary>
/// Physical Width in device units
/// </summary>
PHYSICALWIDTH = 110,
/// <summary>
/// Physical Height in device units
/// </summary>
PHYSICALHEIGHT = 111,
/// <summary>
/// Physical Printable Area x margin
/// </summary>
PHYSICALOFFSETX = 112,
/// <summary>
/// Physical Printable Area y margin
/// </summary>
PHYSICALOFFSETY = 113,
/// <summary>
/// Scaling factor x
/// </summary>
SCALINGFACTORX = 114,
/// <summary>
/// Scaling factor y
/// </summary>
SCALINGFACTORY = 115,
/// <summary>
/// Current vertical refresh rate of the display device (for displays only) in Hz
/// </summary>
VREFRESH = 116,
/// <summary>
/// Horizontal width of entire desktop in pixels
/// </summary>
DESKTOPVERTRES = 117,
/// <summary>
/// Vertical height of entire desktop in pixels
/// </summary>
DESKTOPHORZRES = 118,
/// <summary>
/// Preferred blt alignment
/// </summary>
BLTALIGNMENT = 119
}
private void GetScreenInfo()
{
IntPtr sdc = IntPtr.Zero;
try
{
//Get the Screen Device Context
sdc = GetDC(IntPtr.Zero);
// Get the Screen Devive Context Capabilities Information
Console.WriteLine(string.Format("Size: {0} mm X {1} mm", GetDeviceCaps(sdc, (int)DeviceCap.HORZSIZE), GetDeviceCaps(sdc, (int)DeviceCap.VERTSIZE)));
Console.WriteLine(string.Format("Desktop Resolution: {0}x{1}", GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPHORZRES), GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPVERTRES)));
Console.WriteLine(string.Format("Logical DPI: {0}x{1}", GetDeviceCaps(sdc, (int)DeviceCap.LOGPIXELSX), GetDeviceCaps(sdc, (int)DeviceCap.LOGPIXELSY)));
//Remember: Convert Millimeters to Inches 25.4mm = 1 inch
double PhsyicalDPI_X = GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPHORZRES) * 25.4 / GetDeviceCaps(sdc, (int)DeviceCap.HORZSIZE);
double PhsyicalDPI_Y = GetDeviceCaps(sdc, (int)DeviceCap.DESKTOPVERTRES) * 25.4 / GetDeviceCaps(sdc, (int)DeviceCap.VERTSIZE);
Console.WriteLine(string.Format("Physical DPI: {0}x{1}", PhsyicalDPI_X, PhsyicalDPI_Y));
}
finally
{
ReleaseDC(IntPtr.Zero, sdc);
}
}
Este código en mi pantalla sale lo siguiente:
- Tamaño: 677 mm x 381 mm
- Resolución de escritorio: 1920x1080
- DPI lógico: 96x96
- DPI físico: 72.0354505169867x72
¿Notes tanto el DPI lógico y físico? ¿Ese DPI físico parece familiar? Todo tiene sentido después de leer ese artículo sobre 72dpi que refleja 1pt = 1px. ¡Pruebe este código en sus diversas máquinas de prueba y hágame saber cómo va! (FYI, ejecuté este código en una aplicación Winforms de C#, una aplicación de consola debería poder obtener el contexto del dispositivo de pantalla, pero tal vez no ...)