Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Есть CDC для рисования в CWnd. Как этот же CDC вывести на печать? 
V
    Опции темы
Dreamer_0x01
Дата 24.10.2010, 22:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Здравствуйте. 

Есть объект CDC, созданный при рисовании содержимого CWnd.

В обработчике WM_PAINT содержимое контекста выводится в окно с помощью BitBlt().

Хочу теперь вывести это же содержимое контекста на принтер, причем так, чтобы выводилось на весь лист бумаги.

Скопировал такой пример из инета:

Код

// get the default printer
CPrintDialog dlg(FALSE);
dlg.GetDefaults();

// is a default printer set up?
HDC hdcPrinter = dlg.GetPrinterDC();
if (hdcPrinter == NULL)
{
   MessageBox(_T("No printer!"));
}
else
{
   // create a CDC and attach it to the default printer
   CDC dcPrinter;
   dcPrinter.Attach(hdcPrinter);

   // call StartDoc() to begin printing
   DOCINFO docinfo;
   memset(&docinfo, 0, sizeof(docinfo));
   docinfo.cbSize = sizeof(docinfo);
   docinfo.lpszDocName = _T("CDC::StartDoc() Code Fragment");

   // if it fails, complain and exit gracefully
   if (dcPrinter.StartDoc(&docinfo) < 0)
   {
      MessageBox(_T("Printer wouldn't initalize"));
   }
   else
   {
      // start a page
      if (dcPrinter.StartPage() < 0)
      {
         MessageBox(_T("Could not start page"));
         dcPrinter.AbortDoc();
      }
      else
      {
         // actually do some printing
         CGdiObject* pOldFont = dcPrinter.SelectStockObject(SYSTEM_FONT);
    CRect rCl;
    GetClientRect(rCl);
    CLosaDlg * ldlg = (CLosaDlg *)AfxGetMainWnd();

        dcPrinter.BitBlt(rCl.left, rCl.top, rCl.Width(), rCl.Height(), &dcMem,0, 0, SRCCOPY);

         dcPrinter.EndPage();
         dcPrinter.EndDoc();
         dcPrinter.SelectObject(pOldFont);
      }
   }
}


dcMem - мой контекст, объевлен глобально, заполняется содержимом в процессе работы программы.
Инициализируется  так :

CDC * pDCDest=GetDC();
dcMem.CreateCompatibleDC(pDCDest);
BmpNewMem.CreateCompatibleBitmap(pDCDest, rCl.Width(), rCl.Height());//BmpNewMem - объект CBitmap
dcMem.SelectObject(&BmpNewMem);


Данный код выводит содержимое окна на печать, но напечатанное изображение получается очень мелким.
Как мне сделать так, чтобы изображение масштабировалось на весь лист?

Либо подскажите дргой метод, как сделать распечатку содержимого окна CWnd.

Спасибо.


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 25.10.2010, 08:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Если у тебя обычное рисование происходит в режиме MM_TEXT (по-умолчанию), то так и будет. Пикселы у принтера очень маленькие.
Придется самому пересчитывать координаты. И выполнять не BitBlt, а StrechBlt. Но тогда сам понимаешь, тонкие линии превратятся в бревна, причем ступенчатые. Первое, возможно, и неплохо, а вот второе... Более правильно всю графику выводить векторно в контекст принтера, но с пересчетом координат. Или изначально работать в каком-то размерном пространстве типа MM_LOMETRIC. Тогда логические координаты при выводе на принтер пересчитает сам контекст.


--------------------
...
PM   Вверх
Dreamer_0x01
Дата 25.10.2010, 11:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Earnest, 
Я не могу понять следующее - для корректного масштабирования мне нужно сделать разные функции прорисовки для контекста принтера и контекста окна?

Цитата

Или изначально работать в каком-то размерном пространстве типа MM_LOMETRIC


У меня по техзаданию важна пиксельная точность прорисовки именно на экране -  внешнее устройство за одну итерацию выдает информацию ровно о 512 измерениях, которые должны соответствовать определенным 512 пикселам на мониторе, поэтому размерность обязательно должна быть в пикселах.

Вот на принтер большой точности вывода не требуется - по принципу "лишь бы можно было прочитать ранее сохраненный результат".
Но надо печатать на весь лист.


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 25.10.2010, 13:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Цитата(Dreamer_0x01 @  25.10.2010,  12:47 Найти цитируемый пост)
Я не могу понять следующее - для корректного масштабирования мне нужно сделать разные функции прорисовки для контекста принтера и контекста окна?

Можно, конечно, и так, но криво. Лучше сделать разные функции установки системы координат. Но для этого функции рисования должны рисовать не в пикселах, а логических координатах. И вовсе не обязательно использовать логические координаты устройства (т.е. ф-и CDC). Например, я делаю так: вся графика у меня в действительных координатах (субпиксельная), а при выводе в зависимости от масштаба и позиции экрана трансформируется в экранные пикселы. На печати, соответственно, в твипсы. Если у тебя нет задачи масштабировать-панорамировать вывод, то можно для экрана сделать пустое (единичное) преобразование. 



--------------------
...
PM   Вверх
Dreamer_0x01
Дата 25.10.2010, 16:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Цитата(Earnest @  25.10.2010,  13:13 Найти цитируемый пост)
И вовсе не обязательно использовать логические координаты устройства (т.е. ф-и CDC).


У меня функция рисования - достаточно большой код, все сделано функциями CDC и более того, привязана к абсолютным значениям в пикселах. Переделывать ее будет трудно, писать вторую, делающую то же самое - тоже не выход.

Разьъясни, пожалуйста, по шагам, что мне нужно сделать, чтбы получить печать с минимальными потерями.
Примем это во внимание:
Цитата

Если у тебя нет задачи масштабировать-панорамировать вывод, то можно для экрана сделать пустое (единичное) преобразование.

Ничего масштабировать не надо - вывод на экран всегда в область определенного размера, все размерности меряются в пикселах.
Опять же, если печать будет не точь в точь на полный лист, а плюс-минус пару сантиметров - тоже не страшно.


 


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 25.10.2010, 17:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Разъясняю.
Все координаты перед выводом на экран пропускаешь через некоторую функцию (объект) преобразователь.
Что-то типа TransformPoints (CPoint* pp, size_t np);
Для вывода на экран используешь пустую функцию (которая не меняет координаты).
Для вывод на принтер устанавливаешь такие параметры, чтобы все растянуть на лист. Преобразование линейное (масштаб + возможно, сдвиг).
Параметры уж сам как-нибудь вычисли, не маленький. Например из расчета отобразить прямоугольник экрана в прямоугольник на листе.
Не хочешь в даблах - делай в интах, только поаккуратнее с вычислениями.

Другой способ (вернее тот же самый, но с подходом сбоку).
Делаем обертку вокруг CDC, которая сама будет вычислить (преобразовывать) координаты при всех нужных функциях рисования.

Цитата(Dreamer_0x01 @  25.10.2010,  17:15 Найти цитируемый пост)
У меня функция рисования - достаточно большой код, все сделано функциями CDC

Все что надо - заменить CDC на MYSuperDC. Естественно, реализовать все нужные функции по мере воплей компилятора.
Это лучше в смысле инкапсуляции - код рисования не загромождается, а все преобразования координат запихиваются в контекст. Собственно, примерно то же самое делает и Windows, если режим не MM_TEXT.
Если решишь делать так, совет: не наследуй CDC, а сделай именно обертку - так более управляемо в смысле сопровождения и вообще более гибко.
В конце концов, если где-то нужен "чистый" контекст (и не важны координаты), можно сделать get-метод (всякие SelectObject).

Но можно и дальше пойти - скажем, у меня на таком супер-контексте висит слежение за устанавливаемыми в контекст перьями и прочей фигней, чтобы, во-первых, ресурсы не уплывали, а во-вторых, оптимизация (если просят то же самое перо, что уже стоит, ничего не делаем). 
В общем, есть простор для выдумывания лучшего, которое враг хорошего...



--------------------
...
PM   Вверх
Dreamer_0x01
Дата 26.10.2010, 10:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Хорошо, допустим, делаю обертку, в которой поределяю все, что использую - рисование линий, дуг, текста. Все функции преобразовывают координаты в зависимости от настроек принтер-экран.
Но как быть с вывода текста? У меня  в CreateFont передается высота символов в пикселах. Получается, для шрифтов надо тоже обертки сделать?


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 26.10.2010, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Я бы сделала не для самих шрифтов, а для функции SelectObject, которая шрифт устанавливает (в смысле отдельную функцию SelectObject (CFont*).
Она должна, если это контекст печати, создавать новый фонт, пересчитывая размеры переданного. Только эти созданные шрифты где-то хранить надо, т.е. придется завести у себя в обертке CFont. Как-то так.


--------------------
...
PM   Вверх
Dreamer_0x01
Дата 26.10.2010, 15:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Ага, идея ясна. И последний, самый дурацкий вопрос - а как узнать, какому количеству пикселей соответствует размер печатного листа?
Не могу найти нужные функции.




--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 26.10.2010, 15:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



А фиг его знает, если честно. Сто лет этим не занималась. Вот смотри, выдрала из приложения кусок кода, который вроде параметры бумаги устанавливает. Код древний и кривой, но вроде работает, так что разбирайся.
Там вроде все выковыривается то ли в мм, то ли в долях мм, а твипсы (пикселы принтера) получаются согласно определению:
Цитата

MM_TWIPS   Each logical unit is converted to 1/20 of a point. (Because a point is 1/72 inch, a twip is 1/1440 inch.) Positive x is to the right; positive y is up. 

И обрати внимание, по отношению к MM_TEXT это "перевернутая" CК (т.е. у растет вверх); это нужно учесть при преобразовании координат.
Код

//устанавливает размеры бумаги и ориентацию, принятые для принтера по умолчанию
void CPrintingParmsDlg::DefinePaperParms()
{    ms_bPaperParmsAreDefined = TRUE;
    ms_ptPaperSize = CPoint(0,0);
    // получаем параметры принтера
    CPrintDialog dlgPrint(TRUE);
    if( dlgPrint.GetDefaults() )
    {    LPDEVMODE lpDevMode = dlgPrint.GetDevMode();
        if( (lpDevMode->dmFields & DM_PAPERSIZE) && lpDevMode->dmPaperSize)    //размер бумаги - в нумераторе
        {    //определяем количество форматов бумаги, поддерживаемых принтером
            DWORD dwQPaperFormats = DeviceCapabilities (dlgPrint.GetDeviceName(), dlgPrint.GetPortName(), DC_PAPERS, NULL, lpDevMode);
            if( dwQPaperFormats > 0 )
            {    //создаем массив для хранения нумераторов форматов
                WORD *pPaperFormats = new WORD [dwQPaperFormats] ;
                //... и читаем в него нумераторы форматов
                dwQPaperFormats = DeviceCapabilities (dlgPrint.GetDeviceName(), dlgPrint.GetPortName(), DC_PAPERS, (LPTSTR)pPaperFormats, lpDevMode);
                //определяем количество размеров бумаги, поддерживаемых принтером
                DWORD dwQPaperSizes = DeviceCapabilities (dlgPrint.GetDeviceName(), dlgPrint.GetPortName(), DC_PAPERSIZE, NULL, lpDevMode) ;
                if( dwQPaperSizes > 0 )
                {    //создаем массив для хранения размеров бумаги
                    POINT *pPaperSizes = new POINT [dwQPaperSizes] ;
                    //... и читаем в него размеры бумаги
                    dwQPaperSizes = DeviceCapabilities (dlgPrint.GetDeviceName(), dlgPrint.GetPortName(), DC_PAPERSIZE, (LPTSTR)pPaperSizes, lpDevMode) ;
                    //ищем в массиве нумератор lpDevMode->dmPaperSize
                    for (size_t i=0; i < dwQPaperSizes && i < dwQPaperFormats; i++)
                        if( pPaperFormats[i] == lpDevMode->dmPaperSize )
                            { ms_ptPaperSize = pPaperSizes[i];  break; }
                    delete[] pPaperSizes;
                }
                delete[] pPaperFormats ;
            }
        }
        else            //размеры бумаги заданы явно
        {    ms_ptPaperSize.x = lpDevMode->dmPaperWidth;
            ms_ptPaperSize.y = lpDevMode->dmPaperLength;
        }
        ms_wOrientation = lpDevMode->dmOrientation;
        if( ms_ptPaperSize.y == 0 || ms_ptPaperSize.x == 0 )        //где-то что-то не сработало
        {    lpDevMode->dmOrientation = ms_wOrientation = DMORIENT_PORTRAIT; //принудительно устанавливаем А4 
            lpDevMode->dmPaperWidth  = ms_ptPaperSize.x = 2100;
            lpDevMode->dmPaperLength = ms_ptPaperSize.y = 2970;
            lpDevMode->dmPaperSize = DMPAPER_A4;
        }
        ::GlobalUnlock (dlgPrint.m_pd.hDevMode); //обязаны выполнить после dlgPrint.GetDevMode()
    }
}



--------------------
...
PM   Вверх
Dreamer_0x01
Дата 9.11.2010, 12:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



В общем, мучался я очень долго. Не знаю почему, но мне так и не удалось заставить принтер хоть что-либо печатать в том случае, если делал SetMapMode с любым параметром, у которого координата у направлена снизу вверх.

Однако, использование StretchBlt вместо BitBlt уже само по себе позволило растянуть изображение почти на весь лист, но с прикидкой "на глаз".
Код Earnest рабочий, но он видимо дает размеры печатной области соответствующие режима MM_LOMETRIC, с которым как я говорил у меня ни печатать не получилось, ни даже рисовать напрямую тестовые линии/кружочки.

Для листа А4 эти параметры как раз равны 2969 и 2101.
На вид требовалось явно в двое большая размерность. 

Выход нашел такой - не менял никакие режимы вывода, то есть оставил MM_TEXT по умолчанию.
Размеры печатной области в логических координатах получил через функцию контекста GetClipBox.
Теперь печатный рисунок вписывается точь в  точь в размеры листа бумаги, что мне и требовалось сделать.

Что касается этого эффекта:
Цитата(Earnest @  25.10.2010,  08:23 Найти цитируемый пост)
Придется самому пересчитывать координаты. И выполнять не BitBlt, а StrechBlt. Но тогда сам понимаешь, тонкие линии превратятся в бревна, причем ступенчатые. Первое, возможно, и неплохо, а вот второе... 

Показал заказчику - его такое качество изображения удовлетворило вполне, так как ему совершенно не важна "красивость" линий и шрифтов, а важно только, чтобы расстояние на картинке между контрольными точками было пропорционально экранным расстояниям.


Спасибо Earnest за то, что цепочкой рассуждений привела к хорошему решению ;)





--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




[ Время генерации скрипта: 0.0612 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.