Как сделать предварительный просмотр в Win32 C++?
У меня есть функция рисования, которая просто принимает HDC. Но мне нужно показать ТОЧНУЮ масштабированную версию того, что будет напечатано.
Поэтому в настоящее время я использую CreateCompatibleDC() с HDC принтера и CreateCompatibleBitmap() с HDC принтера.
Я так полагаю, что DC будет иметь точную ширину и высоту принтера. И когда я выбираю шрифты в этот HDC, текст будет масштабироваться точно так же, как принтер.
К сожалению, я не могу в StretchBlt() скопировать пиксели этого HDC в HDC элемента управления, так как я думаю, что они имеют разные типы HDC.
Если я создаю "холст памяти" из окна HDC с тем же значением w, h, что и на странице принтера, шрифты получаются ДАЛЕКОМ, так как они масштабируются для экрана, а не для страницы...
Должен ли я CreateCompatibleDC() из DC окна и CreateCompatibleBitmap() из DC принтера или что-то??
Если кто-то может объяснить ПРАВИЛЬНЫЙ способ сделать это. (И все же есть что-то, что выглядит точно так же, как на принтере)...
Ну, я был бы признателен за это!
...Стив
3 ответа
В зависимости от того, насколько точным вы хотите быть, это может быть сложно.
Есть много подходов. Похоже, вы пытаетесь нарисовать растровое изображение размером с принтер, а затем уменьшить его. Шаги, чтобы сделать это:
- Создайте DC (или, еще лучше, IC- информационный контекст) для принтера.
- Запросите данные о контроллере принтера, чтобы узнать разрешение, размер страницы, физические смещения и т. Д.
- Создайте DC для окна / экрана.
- Создайте совместимый DC (память DC).
- Создайте совместимое растровое изображение для окна / экрана, но размер должен соответствовать размеру пикселя страницы принтера. (Проблема этого подхода в том, что это ОГРОМНОЕ растровое изображение, и оно может дать сбой.)
- Выберите совместимое растровое изображение в памяти DC.
- Нарисуйте в память DC, используя те же координаты, которые вы использовали бы при рисовании на реальном принтере. (Когда вы выбираете шрифты, убедитесь, что вы масштабируете их до логического дюйма принтера, а не логического дюйма экрана.)
StretchBlt
память DC до окна, которое будет уменьшать все изображение. Возможно, вы захотите поэкспериментировать с режимом растяжения, чтобы увидеть, что лучше всего подходит для изображения, которое вы собираетесь отображать.- Отпустите все ресурсы.
Но прежде чем идти в этом направлении, рассмотрите альтернативы. Этот подход включает в себя выделение ОГРОМНОГО закадрового изображения. Это может произойти сбой на компьютерах с ограниченными ресурсами. Даже если это не так, вы можете голодать другие приложения.
Подход метафайла, приведенный в другом ответе, является хорошим выбором для многих приложений. Я бы начал с этого.
Другой подход состоит в том, чтобы выяснить все размеры в некоторой вымышленной единице с высоким разрешением. Например, предположим, что все в тысячных дюйма. Тогда ваши процедуры рисования будут масштабировать эту воображаемую единицу к фактическому dpi, используемому целевым устройством.
Проблема этого последнего подхода (и, возможно, метафайла) заключается в том, что шрифты GDI не линейно масштабируются. Ширина отдельных символов настраивается в зависимости от целевого разрешения. На устройстве с высоким разрешением (например, лазерный принтер с разрешением 300+ точек на дюйм) эта настройка минимальна. Но на экране с разрешением 96 точек на дюйм твики могут привести к значительной ошибке по всей длине строки. Таким образом, текст в окне предварительного просмотра может отображаться непропорционально (как правило, шире), чем на распечатанной странице.
Таким образом, хардкорный подход заключается в измерении текста в контексте принтера, повторном измерении в контексте экрана и корректировке на несоответствие. Например (используя вымышленные числа), вы можете измерить ширину некоторого текста в контексте принтера, и она составит 900 пикселей принтера. Предположим, что отношение пикселей принтера к пикселям экрана составляет 3:1. Вы ожидаете, что один и тот же текст на экране будет иметь ширину 300 пикселей. Но вы измеряете в контексте экрана, и вы получаете значение, как 325 пикселей экрана. Когда вы рисуете на экране, вам нужно как-то сузить текст на 25 пикселей. Вы можете объединить символы ближе друг к другу или выбрать немного меньший шрифт, а затем растянуть их.
Хардкорный подход подразумевает большую сложность. Например, вы можете попытаться обнаружить замены шрифтов, сделанные драйвером принтера, и сопоставить их как можно ближе с доступными экранными шрифтами.
Мне повезло с гибридом большого битмапа и хардкорного подхода. Вместо создания гигантского растрового изображения для всей страницы, я создаю одно достаточно большое для строки текста. Затем я рисую на принтере в размер экрана и StretchBlt
это до размера экрана. Это устраняет проблему с несоответствием размера при небольшом ухудшении качества шрифта. Он подходит для предварительного просмотра печати, но вы не захотите создавать такой редактор WYSIWYG. Однострочное растровое изображение достаточно мало, чтобы сделать это практичным.
Хорошей новостью является только текст, это сложно. Все остальные рисунки - это простое масштабирование координат и размеров.
Я не слишком много использовал GDI+, но думаю, что он покончил с нелинейным масштабированием шрифтов. Поэтому, если вы используете GDI+, вам просто нужно масштабировать свои координаты. Недостатком является то, что я не думаю, что качество шрифта в GDI + так хорошо.
И, наконец, если вы являетесь родным приложением в Vista или более поздней версии, убедитесь, что вы пометили процесс как " DPI-осведомленный" . В противном случае, если пользователь находится на экране с высоким разрешением, Windows будет вам лгать и утверждать, что разрешение составляет всего 96 dpi, а затем выполнять нечеткое масштабирование того, что вы рисуете. Это ухудшает визуальное качество и может усложнить отладку предварительного просмотра. Поскольку многие программы плохо адаптируются к экранам с более высоким DPI, Microsoft по умолчанию добавила "масштабирование с высоким DPI", начиная с Vista.
Отредактировано для добавления
Еще одно предостережение: если вы выберете HFONT в DC памяти с помощью растрового изображения размером с принтер, возможно, вы получите другой шрифт, чем тот, который был бы при выборе того же HFONT в реальном DC принтера. Это связано с тем, что некоторые драйверы принтеров заменяют обычные шрифты в памяти. Например, некоторые принтеры PostScript заменят внутренний шрифт PostScript на некоторые распространенные шрифты TrueType.
Вы можете сначала выбрать HFONT в ИС принтера, а затем использовать такие функции GDI, как GetTextFace
, GetTextMetrics
, и возможно GetOutlineTextMetrics
узнать о фактическом выбранном шрифте. Затем вы можете создать новый LOGFONT, чтобы попытаться более точно соответствовать тому, что будет использовать принтер, превратить его в HFONT и выбрать его в свой DC памяти. Это признак действительно хорошей реализации.
Одна вещь, которую стоит попробовать - создать расширенный DC метафайла, нарисовать его как обычно и затем масштабировать этот метафайл с помощью метрик принтера. Этот подход используется в примере WTL BmpView - я не знаю, насколько точным это будет, но на него стоит обратить внимание (должно быть легко перенести соответствующие классы на Win32, но WTL - отличная замена для программирования на Win32, поэтому может быть стоит использовать.)
Ну, это не будет выглядеть одинаково, потому что у вас есть более высокое разрешение в DC принтера, поэтому вам придется написать своего рода функцию преобразования. Я бы пошел с методом, который вы должны работать, но текст был слишком маленьким и просто умножил каждую позицию / размер шрифта на ширину окна принтера и разделил на ширину исходного окна.