Приветствую Вас на своем сайте!
Главная Форум Полезные файлы Музыка Cофт Профиль Фото галерея Выход Вход
  Менюшка

  Мини-чат

  Cыграйка

  Бонус wmr


Получите WMR-бонус
от 0,01 до 0,20 WMR на свой кошелек!

  Проголосуй

Нравится ли вам школа?
Всего ответов: 26

Главная » 2008 » Сентябрь » 21 » Печать и предварительный просмотр документов(1)
Печать и предварительный просмотр документов(1)
10:00:34

Печать и предварительный просмотр документов


Для взаимодействия приложений с графическими устройствами, такими как экран
дисплея, принтер или плоттер, в Windows используется интерфейс графических
устройств (GDI). И когда приложению
требуется выполнить операцию вывода графического изображения, оно осуществляет
это посредством GDI и работает не с
реальным (физическим), а с логическим устройством. Выполняя запрос приложения,
GDI обращается к драйверу
соответствующего устройства вывода, который уже непосредственно работает с
физическим устройством. Такой подход позволяет приложениям Windows единообразно
работать на любом оборудовании, лишь бы был установлен соответствующий драйвер.
Само приложение обращается к устройству вывода посредством контекста устройства,
который представляет собой структуру данных, содержащую информацию о том, как
нужно выполнять операции вывода на данном устройстве. Эта структура при ее
создании ассоциируется с драйвером конкретного физического устройства,
преобразующего все направляемые ему вызовы в такие, которые им поддерживаются.


Все сказанное в полной мере относится и к выводу на принтер: сначала
необходимо создать контекст отображения, связанный с принтером, куда затем и
направлять весь графический вывод (как и при выводе на экран). Однако отличия
все-таки есть. И связаны они, прежде всего, с тем, что при печати графический
вывод осуществляется постранично, а листы бумаги могут иметь различные размеры
и/или ориентацию (книжную или альбомную). За отслеживание этих параметров
целиком отвечает программист. Есть и некоторые другие отличия.


Следует совершенно четко представлять, что вывод на принтер осуществляется
исключительно через буфер, который, однако, служит не только для временного
хранения данных. Все несколько сложнее. Дело в том, что при вызове функций
рисования для контекста принтера эти команды GDI выполняются не сразу, а
накапливаются в специальном метафайле. И только после того, как приложение
завершит рисование одной страницы документа, созданный метафайл "проигрывается"
в контексте принтера. Печать происходит именно в этот момент. Сам механизм скрыт
от приложения, и единственное, что требуется, — это сообщить
face="Courier New" size=2>GDI
о начале и завершении процесса печати
листа.


Вторым важным моментом является то, что печать в системе Windows организована
через специальную очередь, которая формируется приложением Print Manager
(Диспетчер печати). Для печати документа приложения помещают в эту очередь свои
данные (задания на печать), выводимые на принтер в фоновом режиме, в порядке
поступления.


Для печати документов Win32 API предоставляет следующие функции: 



  •  StartDoc — формирует задание
    на печать нового документа;
  •  StartPage — подготавливает
    контекст устройства вывода для печати новой страницы — готовит метафайл;
    необходимо вызвать эту функцию перед выводом в контекст устройства;
  •  EndPage — завершает
    программный процесс печати одной страницы — формирование метафайла, после чего
    он выводится непосредственно на принтер;
  •  EndDoc — завершает процесс
    печати документа;
  •  AbortDoc — служит для
    принудительного завершения процесса печати;
  •  SetAbortProc — используется
    для обеспечения возможности фоновой печати и принудительного завершения
    процесса печати;
  •  ResetDC — позволяет настроить
    индивидуальные параметры печати отдельных листов документа.

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


1. Прежде всего формируется контекст отображения для принтера.


2. Затем обеспечивается возможность принудительного (аварийного) завершения
печати, заполняется структура DOCINFO и
вызывается функция StartDoc.


3. После завершения подготовительных действий организуется цикл печати
страниц документа, где должны быть проверены специально созданные флаги отмены и
завершения печати. Вывод на принтер в Windows организуется постранично, поэтому
перед началом печати каждой страницы необходимо вызвать функцию StartPage,
которая устанавливает параметры специального метафайла, куда будет направляться
вывод для всей страницы.


4. После того как вся страница записана в метафайл, следует закрыть его и
отправить непосредственно на принтер. Для этой цели используется функция
face="Courier New" size=2>EndPage
. Цикл выполняется для всех страниц и
завершается либо аварийно, либо после вывода всего документа. В первом случае
следует вызвать функцию AbortDoc, а во
втором — EndDoc.


5. В заключение необходимо освободить используемый контекст устройства.


 Примечание


Пока мы описываем этот процесс в упрощенном виде, не
вдаваясь в некоторые детали, например, как определять окончание страницы или как
настроить параметры принтера


Подобный процесс, с теми или иными вариациями, выполняется при лю- бом
выводе документа на печать.


После того как сложилось общее представление об организации процесса печати в
Windows, вернемся к библиотеке MFC и рассмотрим, как реализован в ней вывод
данных документа на принтер и другие связанные с этим вопросы. Начнем с выбора и
настройки параметров принтера.



Выбор и настройка параметров принтера


Поскольку вся информация об установленных принтерах .хранится в самой
операционной системе, то вполне логичным выглядит то, что за рассматриваемый
процесс отвечает приложение. В классе CWinApp реализован специальный обработчик
стандартной команды ID_FILE_PRINT_SETUP:



afx_msg void
CWinApp::OnFilePrintSetup ()


Выводит на экран стандартный блок диалога
Print Setup (Настройка печати) для настройки текущей конфигурации
принтера.


Для подключения обработчика к команде в карту сообщений объекта "приложение"
необходимо добавить оператор




size=2>ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)


В большинстве случаев этого стандартного обработчика вполне достаточно, но
при необходимости можно создать и что-нибудь свое. Изобретать велосипед мы не
будем, а для иллюстрации рассмотрим лишь, каким образом можно реализовать
индивидуальную настройку стандартного блока диалога
size=2>Print Setup
. Прежде всего добавляем в карту сообщений класса
приложения оператор




size=2>ON_COMMAND«ID_PRINT_SETUP>, <OnPrintSetup>)


Примечание 


Как вы понимаете, имена в
угловых скобках могут быть произвольными.


Затем в описании класса регистрируем этот обработчик:



class CNDApp : public CWinApp
,


{


...


//{{AFX_MSG(CNDApp)


...


 afx_msg void
OnPrintSetup(); 


...


//}}AFX_MSG


 };


И, наконец, реализуем его. В данном примере при инициализации блока диалога
устанавливаем размер бумаги Letter 8'/2 х 11 in и альбомную ориентацию (рис.
19.1).



void
CNDApp::OnPrintSetup() 


{


// Создаем объект
"стандартный блок диалога


// Print Setup (Настройка
печати)


CPrintDialog
dlgPD(TRUE);


// Для получения параметров
принтера, установленного


// по умолчанию, создаем
объект


PRINTDLG pd;


// Устанавливаем указатель на
глобальный блок памяти в NULL


// с тем, чтобы запросить у
системы параметры принтера


pd.hDevMode = NULL;


// Позволяем получить текущую
конфигурацию принтера



size=2>GetPrinterDeviceDefaults(Spd);


// Поскольку параметр
pd.hDevMode указывает на глобальный


// перемещаемый блок памяти,
перед началом работы с ним


// необходимо его
зафиксировать, иначе ничего не получится


DEVMODE *dm = (DEVMODE
*)LocalLock(pd.hDevMode);


// Теперь можно заполнять
поля структуры DEVMODE


// значениями, необходимыми
для инициализации блока диалога


// Print Setup


dm->dmFields =
DMJDRIENTATION | DM_PAPERSIZE;


dm->dmOrientation =
DMORIENT_LANDSCAPE;


dm->dmPaperSize =
DMPAPER_LETTER;


// Передаем указатель на
заполненную структуру DEVMODE


// объекту класса
CPrintDialog


dlgPD.m_pd.hDevMode = pd.
hDevMode;


// Теперь зафиксированный
блок памяти можно освободить



size=2>LocalUnlock(pd.hDevMode);


// Выводим блок диалога на
экран


if(dlgPD.DoModalf) ==
IDOK)


{


// Здесь можно зафиксировать
выбор пользователя


...



}



...........................................................................1

Рис. 19.1. Стандартный блок диалога
size=2>Print Setup


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


Для того чтобы узнать текущую конфигурацию принтера, мы воспользовались
функцией:



BOOL
CWinApp::GetPrinterDeviceDefaults (PRINTDLG *pd)


Возвращает информацию либо о принтере,
заданном по умолчанию, либо о последней конфигурации принтера, установленной с
помощью блока диалога Print Setup. Параметр pd указывает на структуру
PRINTDLG, которая служит для настройки параметров блоков диалога Print
(Печать) и Print Setup:


...


PRINTDLG pd;


...


// Запрашиваем параметры
текущего


// или установленного по
умолчанию принтера


...



size=2>GetPrinterDeviceDefaults(&pd);


Эта часть работы, конечно же, достаточно важна, и обеспечить ее поддержку
необходимо, но библиотека MFC делает это прекрасно, а нас интересует дальнейший
процесс. Итак, принтер выбран, его параметры установлены — можно печатать.
Первоочередной задачей является, естественно, создание контекста устройства для
выбранного принтера.



Создание контекста устройства


Библиотека MFC и здесь приходит на помощь — у класса приложения (
face="Courier New" size=2>CWinApp
) имеется специальная функция: .



BOOL
CWinApp::CreatePrinterDC (CDC &dc)


Создает контекст устройства (параметр dc)
для выбранного в текущий момент принтера.


Перед тем как выводить что-либо на печать, достаточно вызвать эту функцию:



...


CDC dc;


// Создаем контекст
устройства для принтера



size=2>AfxGetApp()->CreatePrinterDC(&dc); 


 // Теперь можно
выводить информацию в контекст устройства dc


...


Вот и все. Не нужно каждый раз выполнять "утомительные" настройки специальных
структур, как это приходится делать при использовании Win32 API.


Для того чтобы полностью разобраться в рассматриваемом вопросе, нам осталось
только рассмотреть организацию собственно процесса печати.



Печать документов и библиотека MFC


Дальнейшее изложение мы будем вести применительно к архитектуре
"документ/представление". Это не случайно. Дело в том, что в классах
представлений реализована достаточно мощная поддержка процесса печати, которая
избавит вас от многих дополнительных трудностей, освободив время для решения
конкретных задач.


В процессе печати основная роль принадлежит объектам двух классов библиотеки
MFC — контексту устройства и представлению, которые тесно взаимодействуют между
собой. На рис. 19.2 представлена схема цикла печати, реализованная в библиотеке
MFC.


Как видите, представленный цикл очень напоминает используемый в Windows для
любого вывода на печать: на экран выводится блок диалога
face="Courier New" size=2>Print
, создается контекст устройства для
принтера и вызывается функция StartDoc



..............................................................................2

Рис. 19.2. Цикл печати библиотеки MFC


объекта CDC. Далее для каждой страницы документа библиотека вызывает функцию
StartPage контекста принтера,
осуществляет вывод данных в метафайл (функция
size=2>OnPrint
объекта "представление") и завершает печать страницы
вызовом функции EndPage объекта
face="Courier New" size=2>CDC
. Когда весь документ выведен на принтер,
библиотека вызывает функцию EndDoc,
завершая цикл печати. Разница заключается только в том, что в библиотеке MFC как
сам цикл, так и все участвующие в нем функции уже реализованы. Наряду с этим,
благодаря механизму виртуальных функций, программисту предоставляются широкие
возможности для "более тонкой" настройки всего процесса печати. Виртуальные
функции OnPreparePrinting, OnBeginPrinting,
OnPrepareDC, OnPrint
и
size=2>OnEndPrinting
определены в классе
size=2>Cview
, и при их переопределении необходимо соблюдать логику,
заложенную в библиотеке MFC. Но сначала рассмотрим сами функции:



virtual BOOL
CView::OnPreparePrinting (CPrintlnfo *plnfo)


Вызывается библиотекой MFC до начала
процесса печати или предварительного просмотра документа. Это самый удобный
момент для определения длины документа и настройки параметров печати. Для
реализации последней возможности необходимо просто вызвать функцию
DoPreparePrinting:


BOOL
CView::OnPreparePrinting(CPrintlnfo *plnfo)


{


return
DoPreparePrinting(pInfo); 



В качестве параметра функция принимает
указатель на структуру CPrintlnfo, которая хранит информацию о задачах печати
или предварительного просмотра
:


 Примечание 


О режиме предварительного просмотра документа см. далее в
этом разделе.


struct CPrintlnfo 


{


CPrintlnfo(); //
Конструктор


~CPrintlnfo();
//Деструктор


CPrintDialog *m_pPD; //
Указатель на объект "блок диалога Print"


BOOL m_bPreview; // TRUE, если
предварительный просмотр'


BOOL m_bDirect; // TRUE, если
без отображения на экране


// блока диалога Print .


BOOL m_bContinuePrinting; //
FALSE для прерывания печати


UINT m_nCurPage; // Номер
текущей печатаемой страницы 


UINT m_bNumPreviewPages; //
Число просматриваемых страниц 


CString m_strPageDesc; // Номер
изображаемой страницы 


LPVOID m_lpUserData; //
Указатель на данные пользователя


 CRect m_rectDraw; //
Прямоугольник, определяющий текущую


// используемую область
страницы 


void SetMinPage(UINT
nMiriPage); // Устанавливает номер первой


// печатаемой страницы
документа


void SetMaxPage(UINT nMaxPage);
// Устанавливает номер последней


// печатаемой страницы
документа


 UINT GetMinPageO const;
// Получает номер первой


// печатаемой страницы
документа 


UINT GetMaxPageO const; //
Получает номер последней


// печатаемой страницы
документа 


UINT GetFromPage() const; //
Получает номер первой


// печатаемой страницы
диапазона


 UINT GetToPageO const; //
Получает номер последней


 // печатаемой страницы
диапазона


 };


Объект этой структуры используется для обмена информацией между библиотекой и
классом представления во время процесса печати. Он создается при выборе команд
ID_FILE_PRINT_PREVIEW,
ID_FILE_PRINT_DIRECT
или
size=2>ID_FILE_PRINT
и разрушается после их выполнения.


Если возникнет необходимость инициализации блока диалога Print значениями,
отличными от заданных по умолчанию, то лучше всего это сделать до вызова функции
Do Prepare Printing.



BOOL
CView::DoPreparePrinting (CPrintlnfo *plnfo)


Возвращает TRUE, если можно начинать печать
или предварительный просмотр документа, и FALSE в противном случае. Поведение
этой функции зависит от того, вызывается ли она для печати документа или для
его предварительного просмотра, что определяется параметром m_bPreview
структуры, на которую указывает plnfo. В первом случае на экран выводится блок
диалога Print. После его закрытия функция создает контекст устройства на
основе установок, сделанных пользователем в этом блоке диалога для принтера,
который будет использоваться при печати документа. Если функция вызвана для
предварительного просмотра документа, то контекст устройства создается на
основе текущих установок принтера.


Для осуществления всех корректных установок в переопределенной версии этой
функции обязательно следует вызвать функцию базового класса 
face="Courier New" size=2>CView::DoPreparePrinting.


В приведенном ниже фрагменте кода демонстрируется принцип инициализации блока
диалога Print в зависимости от того,
имеется ли во введенном тексте выделенный фрагмент (рис. 19.3).



BOOL
CNoteView::OnPreparePrinting(CPrintlnfo* plnfo) 


{


if (plnfo != NULL &&
!pInfo->m_bPreview) 


{


// Документ не будет иметь
больше одной страницы



size=2>pInfo->SetMaxPage(l);


// Получаем указатель на
документ, с которым


// ассоциировано текущее
представление


CNoteDoc* pDoc =
(CNoteDoc*)GetDocument();


// Получаем позицию первого
представления, хранящегося


//в списке представлений,
ассоциированных с документом


POSITION pos =
pDoc->GetFirstViewPositipn();


// Получаем указатель на
первое представление в списке



size=2>pDoc->GetNextView(pos);


// Получаем указатель на
второе представление в списке


CTextView *pText = (CTextView
*)pDoc->GetNextView(pos);


CString str;


// Читаем данные
представления



size=2>pText->GetSelectedText(str);


iffIstr.IsEmptyO)


{


// Если есть выделенный
фрагмент, то переключатель


 // Print range
(Печатать) устанавливаем в положение


 // Selection
(Выделенный фрагмент)


CPrintDialog *pPD =
p!nfo->m_pPD;


pPD->mjpd.Flags &=
~PD_NOSELECTION;


pPD->m_pd.Flags |=
PD_SELECTION; 



}


// Если же выделенного
фрагмента нет, то используем 


// установки по
умолчанию


//Не забываем вызвать функцию
базового класса


 // для проведения
корректной инициализации 


return
DoPreparePrinting(plnfo); 


}



..............................................................................3

Рис. 19.3. Стандартный блок диалога
size=2>Print


Если необходим предварительный доступ к контексту устройства, то это можно
сделать, переопределив функцию:



virtual void
CView::OnBeginPrinting (


CDC *pDC,


CPrintlnfo *plnfo)


Вызывается библиотекой MFC перед началом
процесса печати или предварительного просмотра после того, как была вызвана
функция OnPreparePrinting.


Эта функция используется для размещения необходимых для печати ресурсов
face="Courier New" size=2>GDI
, таких как карандаши или шрифты. Сами
объекты GDI выбираются в контекст
устройства в функции OnPrint отдельно для
каждой страницы, которая их использует. Если одно и то же представление
используется для вывода на экран и на принтер, рекомендуется использовать
различные переменные для ресурсов GDI,
необходимых для каждого изображения, что позволит обновлять их на экране
непосредственно во время процесса печати. Кроме того, эту функцию можно
использовать для инициализации, зависящей от свойств контекста устройства
принтера, например, от числа печатаемых страниц. В качестве параметров
используются указатели на контекст устройства (параметр
size=2>pDC
) и на структуру pPrintlnfo, которая описывает текущее задание
печати (параметр plnfo).


Для корректного завершения процесса печати или предварительного просмотра
документа необходимо использовать функцию:



virtual void
CView::OnEndPrinting


CDC *pDC,


CPrintlnfo *plnfo)


Вызывается библиотекой MFC после окончания
процесса печати или предварительного просмотра. Если в функции OnBeginPrinting
были задействованы какие-либо ресурсы GDI, то здесь их необходимо вернуть
системе
.


Если две последние рассмотренные функции используются для документа в целом,
то для настройки параметров отдельной страницы разработчики реализовали функцию:



virtual void
CView::OnPrepareDC (


 CDC *pDC, 


CPrintlnfo *plnfo =
NULL)


Вызывается библиотекой MFC до функций
OnDraw (предназначенной для изображения на экране) или OnPrint (используемой
для каждой печатаемой или просматриваемой страницы). Если функция вызвана для
печати документа, то она проверяет информацию о странице, имеющуюся в
структуре, на которую указывает параметр plnfo. Если длина документа не
определена, она принудительно прерывает печать после того, как будет выведена
одна страница. Можно принудительно остановить процесс печати, записав в поле
m_bContinuePrinting структуры plnfo значение FALSE. Параметры: pDC— указатель
на контекст устройства, используемый для представления образа документа, и
plnfo — указатель на структуру CPrintlnfo, описывающую текущее задание. В
режиме печати или предварительного просмотра поле т_пСигРаде структуры
CPrintlnfo определяет номер печатаемой страницы (если функция вызвана для
вывода на экран, этот параметр должен быть установлен в NULL). При
переопределении этой функции прежде всего следует вызвать обработчик
OnPrepareDC базового класса.


Переопределение этой функции необходимо в следующих случаях:



  •  настройка атрибутов контекста устройства, необходимых для
    определенной страницы;
  •  динамическое определение числа страниц документа непосредственно во
    время печати (например, при распечатке неизвестного заранее числа записей базы
    данных);
  •  посылка на принтер ESC-кодов с использованием функции Escape объекта
    класса CDC, на который указывает параметр pDC.

Теперь, когда общая структура процесса печати достаточно ясна, самое время
посмотреть, где же осуществляется собственно вывод (или, если угодно, рисование)
данных документа. В главе 18 рассматривалась функция
size=2>OnDraw
, которая в рамках архитектуры "документ/представление"
отвечает за вывод на любое устройство, будь то экран, принтер или что-либо
другое. Его конкретный тип задается объектом контекста устройства, передаваемого
функции в качестве параметра. Например, для вывода на экран она получает в
качестве параметра указатель на объект класса
size=2>CPaintDC
, а для печати документа — на объект класса
face="Courier New" size=2>CDC
, ассоциированного с текущим принтером.


В принципе, можно было бы просто переопределить функцию
face="Courier New" size=2>OnDraw
для организации вывода на принтер, но
ведь печать осуществляется на листы бумаги, имеющие вполне определенные размеры,
которые необходимо учитывать. И для того чтобы не перегружать функцию
face="Courier New" size=2>OnDraw
, разработчики библиотеки для работы с
принтером реализовали в базовом классе всех представлений
face="Courier New" size=2>CView
отдельную функцию:



virtual void
CView::OnPrint ( .


CDC *pDC, 


CPrintlnfd *plnfo =
NULL)


Вызывается библиотекой MFC после вызова
функции OnPrepareDC для печати или предварительного просмотра каждой страницы
документа. Реализация по умолчанию просто вызывает функцию OnDraw с контекстом
устройства принтера.


Переопределение этой функции необходимо, например, в следующих случаях: 



  •  при печати многостраничных документов;
  •  для установки различных образов при выводе на принтер и экран, что
    допустимо, если приложение не поддерживает режима
    size=2>WYSIWYG
    ;
  •  для реализации изображения колонтитулов и примечаний.

Здесь есть два тонких момента, требующих пояснения. Пока речь идет о печати
одной страницы, то все в порядке — можно использовать реализацию по умолчанию, в
которой просто вызывается функция OnDraw,
Но, как вы помните, для вывода на принтер нескольких страниц необходимо
организовать цикл печати. И такой цикл действительно организован в обработчике:



void
CView::OnFilePrint {)


Просмотров: 341 | Добавил: epidemic | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
  Личный кабинет

Привет: Зашедший

Сообщения:
$$$ для web-мастеров
Гость, мы рады вас видеть. Пожалуйста
зарегистрируйтесь или
авторизуйтесь!

  Календарь

  Наши Друзья

Effectt.com - система обмена ссылками. обмен ссылками: добавить ссылку, прямые ссылки

Получить WMR-бонус на свой кошелек!
Linq.RU - Обмен ссылками
  Статистика сайта

Всего connect: 1
Зашедших: 1
Зареганых: 0

статистика Top 100: Развлечения, игры, юмор Бесплатная раскрутка сайта
Copyright Epidemic © 2019Используются технологии uCoz