Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Обучение от Mail.Ru Group.
Онлайн-университет
для программистов с
гарантией трудоустройства.
Набор открыт!

2004 г.

Как написать драйвер принтера в BeOS

Автор публикации - thorn, 6.04.2004

Оригинал статьи: http://open-beos.sourceforge.net/nsl.php?mode=display&id=51#181

Перевод опубликован на сайте www.qube.ru

Драйвер принтера в BeOS R5 — это модуль (add-on), который экспортирует специфический API. Этот модуль используется сервером печати (print_server) для того, чтобы добавить новый принтер, настроить страницу, управлять заданием печати (Print Job) и наконец напечатать задание на принтере. Эта статья поможет понять, как сервер печати взаимодействует с драйвером принтера.

Сервер печати отвечает за управление настройками (например размер бумаги). Обмен настройками между сервером печати и драйвером происходит при помощи BMessage. Если драйвер принтера возвращает объект BMessage и хочет указать на удачное завершение работы, то значением поля what будет 'okok'. При ошибке этот метод должен вернуть NULL. В большинстве случаев сервер печати интепретирует значение what, отличающееся от 'okok', как неудачную операцию. В любом случае драйвер принтера не должен полагаться на это и при ошибке всегда возвращать NULL.

Задание печати — файл, созданный печатающим приложением. Для создания задания, приложение использует класс BPrintJob из Interface Kit (рис 1). Этот файл содержит архивированные объекты BPicture, одно на каждый вызов BPrintJob::DrawView(). Для примера растровый драйвер принтера может использовать BPicture для каждой страницы и рисовать его в BBitmap. После этого конвертировать BBitmap в формат, понятный принтеру. Драйвер также может изменять BPicture, включая преобразование в полутона и цветокоррекцию. Учтите, что в BeOS не существует общего подхода для реализации таких особенностей печати.

Для передачи задания на принтер драйвер использует транспортный модуль (transport add-on). Этот модуль «знает» как передать данные на устройство (через LPT, USB, TCP/IP итп). Разделяя транспорт и драйвер принтера, мы получаем возможность использовать один драйвер для принтера, подключенного разными способами, например, через LPT или USB. Это позволяет упростить код. Драйвер не должен знать, каким способом будут доставляться данные на принтер, он—просто выводит поток данных.



Местонахождение драйвера

Системные драйвера, которые установлены с ОС, находятся в B_BEOS_ADDONS_DIRECTORY в подкаталоге Print (т.е. /boot/beos/system/add-ons/Print). Драйвера, установленные пользователем, находятся в B_USER_ADDONS_DIRECTORY в подкаталоге Print (т.е. /boot/home/config/add-ons/Print).

Транспортные модули находятся в пользовательском или системном каталоге драйверов в подкаталоге transport.

Жизнь драйвера принтера

Установка драйвера

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

Добавление нового принтера

Если пользователь добавит новый принтер через диалог Printers, он выберет имя принтера, модель принтера и транспортный модуль. Приложение Printers создаст каталог спулера (spooler), названный именем принтера в B_USER_PRINTERS_DIRECTORY

(например: /boot/home/config/settings/printers). Имя транспортного модуля сохранится в атрибуте transport каталога спулера. Тип этого атрибута - B_STRING_TYPE.

Когда сервер печати получит сообщение, что был добавлен новый принтер, то он вызовет драйвер, чтобы его настроить. Прототип функции в драйвере принтера выглядит так:

char* add_printer(char* printer_name);

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

Настройка страницы

Когда приложение пользователя вызовет BPrintJob::ConfigPage(), сервер печати вызовет следующую функцию в драйвере:

BMessage* config_page (BNode* spool_folder, BMessage* settings);

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

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

Поле Тип Примечание
printable_rect B_RECT_TYPE размер печатаемой области в разрешении 72 пикселя на дюйм.
paper_rect B_RECT_TYPE размер бумаги в разрешении 72 пикселя на дюйм
orientation B_INT32_TYPE 0-портрет, 1-пейзаж
xres B_INT32_TYPE горизонтальное разрешение
yres B_INT32_TYPE вертикальное разрешение


Если конфигурирование было удачным, то драйвер принтера должен вернуть указатель на BMessage с новыми настройками, в противном случае NULL (например пользователь нажал на кнопку «Отменить» в диалоге настройки станицы).

Транспортный модуль

Для непосредственной передачи задания печати, драйвер принтера должен загрузить ассоциируемый с ним транспортный модуль. Имя транспортного модуля, как было оговорено выше, драйвер может получить из атрибута transport каталога спулера. Сначала драйвер должен поискать модуль в пользовательском каталоге (B_USER_ADDONS_DIRECTORY каталог Print/transport) и только в случае неудачи — в системном (B_SYSTEM_ADDONS_DIRECTORY каталог Print/transport).

Транспортный модуль экспортирует две функции:

BDataIO* init_transport(BMessage *settings);
void exit_transport(void);

Единственный параметр init_transport() — это BMessage с записью printer_file (B_STRING_TYPE) внутри. Эта запись должна указывать на каталог спулера. При нормальном завершении, функция вернет указатель на BDataIO объект, а в случае неудачи NULL. Полученый объект будет использоваться для записи данных в принтер.

Драйвер не должен удалять BDataIO объект. Удаление объекта происходит при вызове exit_transport() и выгрузке транспортного модуля.

Пример кода можно посмотреть в методе Open() класса PrinterTransport в каталоге

current/src/add-ons/print/shared/ CVS-репозитория OBOS. Заголовочный файл
PrintTransport.h находится в каталоге current/headers/private/print.

Печатаем задание печати

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

BMessage* take_job(BFile* print_job, BNode* spool_folder, BMessage* settings);

Задание печати передается в print_job. Формат задания будет описан далее. Атрибуты спулера печати могут быть доступны через spool_folder, а в settings передаются параметры страницы, например ее размер.

Для передачи задания на принтер, драйвер должен загрузить к этому времени транспортный модуль и получить указатель на объект BDataIO.

Для чтения задания печати может быть использован класс PrintJobReader. Его можно найти в репозитории OBOS. Небольшой фрагмент кода демонстрирует как этот класс может быть использован:

PrintJobReader reader(print_job);
if (reader.InitCheck() == B_OK) {
  // the settings stored in the print job
  BMessage* settings = reader.JobSettings();

  // получим номер первой страницы
  int32 firstPage = reader.FirstPage();

  // получим номер последней станицы
  int32 lastPage = reader.LastPage();

  // размер бумаги и размер печатаемой области
  BRect paperRect = reader.PaperRect();
  BRect printableRect = reader.PrintableRect();

  // разрешение
  int32 xdpi, ydpi;
  reader.GetResolution(pi, &ydpi);

  int32 pages = reader.NumberOfPages();
  // для каждой страницы
  for (int page = 0; page < pages; page ++) {

    PrintJobPage pjp;

    if (reader.GetPage(page, pjp) == B_OK) { 
      BPicture picture; 
      BPoint point;
      BRect rect;

      // для каждого изображения на странице
      while (pjp.NextPicture(picture, point, rect) == B_OK) {
        // проделываем "черную работу" с каждым изображением
      }
    }
  }
}


Описание предпечатной обработки данных драйвером не попадает в рамки этой статьи, но вы можете посмотреть примеры драйверов в репозитории OBOS. Примеры растровых драйверов Canon LIPS, PCL5 или PCL6 драйвера. Примером «векторного» драйвера будет OBOS PDF драйвер.

Заголовок задания печати

struct print_file_header { int32 version; int32 page_count; off_t first_page; int32 _reserved_3_; int32 _reserved_4_; int32 _reserved_5_; };
Эта структура декларирована в PrintJob.h. В файле задания печати после заголовка следует
плоский BMessage, содержащий в себе настройки переданные take_job().Задание печати содержит
в себе page_count страниц. Первая станица находится по смещению first_page.


Заголовок Страницы

struct page_header { int32 picture_count; off_t next_page; int32 _reserved[10]; };
Этот заголовок находится перед picture_count изображений. Следующая станица начинается со
смещения next_page.


Заголовок Изображения

struct _picture_header_ { BPoint point; BRect rect; };
Содержит плоский BPicture объект.


Удаление принтера

Принтер может быть удален через аплет Printers. Удаление каталога спулера происходит только когда в спулере не находится ни одного задания печати.

Деинсталяция драйвера

Драйвер может быть деинсталирован через аплет Printers только когда все принтеры, зависящие от этого драйвера, были удалены. Для удаления драйвера надо просто удалить модуль из соответсвующего каталога. Следует заметить, что удаление драйвера не принесет большой пользы т.к. от этого не уменьшится ни время загрузки системы, как и не увеличитсяпроизводительность.

Обновление драйвера

Обычно при установки новой версии драйвера не требуется удалять и добавлять заново уже установленные принтеры. В большинстве случаев достаточно заменить соответствующий модуль.

Возможные проблемы при написании драйвера

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

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

Многопоточность в драйвере тоже может стать проблемой. Вы должны завершать все потоки, запущенные внутри драйвера, пока он еще не вышел из функции. Т.е. когда драйвер открывает окно для конфигурирования страницы в config_page(), паралельно запускается поток окна. А config_page() ожидает, пока окно будет закрыто и все объекты, принадлежащие окну, будут уничтожены. Если не делать этого, то может произойти ситуация когда драйвер будет выгружен, но поток окна будет исполняться. Это вызовет ошибку доступа (access violation) т.к. поток окна попытается работать с памятью драйвера, который уже выгружен.

Ссылки на источники кода

Файл Местонахождение
PrintJobReader.cpp current/src/add-ons/print/shared
PrintJobReader.h current/headers/private/print
Printer Drivers current/src/add-ons/print
Transport Protocols current/src/add-ons/print/transports

Новости мира IT:

Архив новостей

Последние комментарии:

Релиз ядра Linux 4.14  (6)
Пятница 17.11, 16:12
Apple запустила Pay Cash (2)
Четверг 09.11, 21:15
Loading

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 985 1945361
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2015 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...