Указание DPI контекста устройства GDI
-
20-09-2019 - |
Вопрос
У меня есть приложение, которое генерирует метафилы (EMF). Он использует эталонное устройство (он же экран) для отображения этих метафилей, поэтому DPI метафильного изменяется в зависимости от того, на какой машине работает код.
Скажем так
- rclframe {0, 0, 21590, 27940} (измерения метафила, в тысятах мм)
- S -Sldevice {1440, 900} (размеры эталонного устройства, в пикселях)
- Szlmillimeters {416, 260} (размеры эталонного устройства, в мм)
Итак, Rclframe говорит мне, что размер ЭДС должен быть
- 21590/2540 = 8,5 в широком
- 27940 /2540 = 11 в высоких
Право на. Используя эту информацию, мы также можем определить физический DPI моего монитора, если моя математика верна:
- (1440 * 25,4) / 416 = 87,9231 Горизонтальный DPI
- (900 * 25,4) / 260 = 87,9231 Вертикальный DPI
Проблема
Все, что воспроизводит этот метафил-преобразование EMF в PDF, страница «Сводка», когда щелкните правой кнопкой мыши на EMF в Windows Explorer и т. Д.-Стремительно усечь рассчитанное значение DPI, отображая 87 вместо 87,9231 (даже 88 будет хорошо).
Это приводит к странице, которая имеет физический размер как 8,48 в x 10,98 в (с использованием 87 dpi) вместо 8,5 в x 11 в (с использованием 88 dpi), когда метафил воспроизводится.
- Можно ли изменить DPI эталонного устройства, чтобы информация, хранящаяся в метафиле, используемой для расчета DPI, выходила на хорошее целое число?
- Могу ли я создать контекст моего собственного устройства и указать его DPI? Или мне действительно нужно использовать принтер для этого?
Спасибо за какое -либо понимание.
Решение
Мне любопытно, как Windows знает физический размер вашего монитора. Вы, должно быть, где -нибудь изменили конфигурацию? Возможно, вы можете изменить его на более удобные ценности, которые хорошо разделились.
Как подразумевается имя, «контекст устройства» должен быть подключен к системному устройству. Однако это не обязательно должен быть аппаратный драйвер, это может быть эмулятор устройства, такой как драйвер печати писателя PDF. Я видел хотя бы один, который позволяет вам установить произвольный DPI.
Другие советы
Теперь я узнал больше, чем я заботился о метафилях.
1. Некоторые из Metafile
Перегрузки конструктора класса работают плохо и будут работать на усеченном значении DPI.
Рассмотрим следующее:
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;
}
Если вы прошли в SizeF
{8.5, 11}, вы можете ожидать Metafile
это имеет rclFrame
{21590, 27940}. В конце концов, преобразование дюймов в миллиметры не сложно. Но вы, вероятно, не будете. В зависимости от вашего разрешения, GDI+, похоже, будет использовать усеченное значение DPI при преобразовании параметра дюймов. Чтобы сделать это правильно, я должен сделать это сам в сотнях миллиметра, который GDI+ проходит, так как это то, как он изначально хранится в заголовке метафилов:
this.currentMetafile = new Metafile(
this.currentStream,
deviceContextHandle,
new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
MetafileFrameUnit.GdiCompatible,
EmfType.EmfOnly);
Ошибка округления № 1 решена- rclFrame
моего метафиля теперь верна.
2. DPI на Graphics
Запись экземпляра в Metafile
всегда неправильно.
Видеть, что metafileGraphics
переменная, которую я установил, позвонив Graphics.FromImage()
на метафиле? Ну, кажется, что Graphics
Пример всегда будет иметь DPI 96 DPI. (Если бы мне пришлось догадаться, это всегда установлено логичный DPI, а не физический один.)
Вы можете представить, что веселье, которое следует, когда вы рисуете Graphics
экземпляр, который работает под 96 DPI и записывает в Metafile
экземпляр, который имеет 87,9231 DPI «записан» в его заголовке. (Я говорю «записано», потому что он рассчитывается по другим значениям.) «Пиксели» метафила (помните, команды GDI, хранящиеся в метафиле, указаны в пиксели) больше, и поэтому вы проклинаете и бормотите, почему ваш призыв нарисовать что-то в длину один дюйм в конечном итоге оказался в дюймах в одном и с чем-то.
Решение состоит в том, чтобы уменьшить Graphics
пример:
metafileGraphics = Graphics.FromImage(this.currentMetafile);
metafileHeader = this.currentMetafile.GetMetafileHeader();
metafileGraphics.ScaleTransform(
metafileHeader.DpiX / metafileGraphics.DpiX,
metafileHeader.DpiY / metafileGraphics.DpiY);
Разве это не крик? Но, кажется, работает.
«Закругление» ошибка № 2 Решена-Когда я говорю, нарисуйте что-то в «1 дюйм» на 88 dpi, что пиксель лучше быть $%$^! Записано как Pixel #88.
3. szlMillimeters
может отличаться дико; Удаленный рабочий стол приносит много удовольствия.
Итак, мы обнаружили (по ответу Марка), что иногда Windows запрашивает edid вашего монитора и на самом деле знает, насколько он велик физически. GDI+ полезно использует это (HORZSIZE
и т.д.) при заполнении в szlMillimeters
имущество.
А теперь представьте, что вы идете домой, чтобы отлаживать этот код удаленного рабочего стола. Допустим, у вашего домашнего компьютера есть широкоэкранный монитор 16: 9.
Очевидно, что Windows не может запрашивать edid удаленного дисплея. Таким образом, он использует вековой дефолт 320 x 240 мм, что было бы хорошо, за исключением того, что это является соотношение сторон 4: 3, и теперь тот же код генерирует метафил на дисплее, на котором предположительно не есть не Квадратные физические пиксели: горизонтальный DPI и вертикальный DPI разные, и я не могу вспомнить в последний раз, когда я видел это.
На данный момент мой обходной путь: «Ну, не запускайте его под удаленным рабочим столом».
4. Инструмент EMF-TO-PDF, который я использовал, имел ошибку округления при просмотре на rclFrame
заголовок.
Это была основная причина моей проблемы, которая вызвала этот вопрос. Моя метафиль была «правильной» на все время (ну, правильно после того, как я исправил первые два выпуска), и весь этот поиск для создания метафил «высокого разрешения» была красной сельдью. Это правда, что некоторая верность теряется при записи метафил на дисплеевом устройстве с низким разрешением; Это потому, что команды GDI, указанные в метафиле, указаны в пикселях. Неважно, что это векторный формат и может масштабировать или вниз, некоторая информация теряется Во время фактической записи Когда GDI+ решает, какой «пиксель» привязать операцию.
Я связался с продавцом, и они дали мне исправленную версию.
Ошибка округления № 3 решена.
5. Панель «Сводка» в Windows Explorer просто так, как и усеть значения при отображении рассчитанного DPI.
Так получилось, что это усеченное значение представляло такое же ошибочное значение, которое использовал инструмент EMF-TO-PDF. Кроме того, эта причуда не вносит ничего значимого для обсуждения.
Выводы
Поскольку мой вопрос был о Futzing с DPI в контекстах устройства, Mark's - хороший ответ.
Обратите внимание, что я работаю с 120 DPI на WXP все время (большие шрифты), что означает, что метафилеграфика. DPIX будет возвращать 120.
Файл EMF, по -видимому, не записывает то, что DPI был справочным контекстом (120 в данном случае, 96 для большинства других людей).
Чтобы сделать вещи более интересными, можно создать ЭДС с помощью рисунка на растровом карте в памяти, на котором было установлено SetResolution (), скажем, 300DPI. В этом случае я считаю, что коэффициент масштабирования должен быть 300, а не то, что может использовать монитор (86.x) или Windows (120).
Похоже, что значения на Сводной странице неверны. Они рассчитаны как:
Size = round(precize_size)+1
Resolution = trunc(precize_resolution)
Где точные значения рассчитываются без округления или усечения.