
    ObjectProfessional

    7. ПЕЧАТЬ

    А. Обзор

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

Для реализации поддержки печатных форм и режимов принтера в
ObjectProfessional используется четыре модуля.  Следующая
таблица поясняет их назначение:

    ┌───────────────────────────────────────┐
    │ Печатные формы               OPFORM   │
    ├───────────────────────────────────────┤
    │ Индивидуальная настройка     OPDEVICE │
    ├───────────────────────────────────────┤
    │ Логическое управление        OPPRINT  │
    ├───────────────────────────────────────┤
    │ Физическое управление        OPPRNLOW │
    └───────────────────────────────────────┘

Модуль OPPRNLOW обеспечивает поддержку на физическом уровне.
Он определяет, каким образом записывается передаваемый
символ и как обрабатывается и интерпретируется информация о
состоянии принтера. Конечно, вы могли бы предположить, что
подобные функции низкого уровня представляют собой
скомпанованый блок языковых микрокоманд или даже обычных
процедур; но OPPRNLOW является комплектом объектных модулей
и имеет ощутимые преимущества по отношению к OOP.

Объектные модули OPPRNLOW начинаются с безотносительного
модуля BasePrinter (базовый принтер), который определяет
основополагающие операции для печати: инициализацию,
подключение для вывода, трансляцию символа, запись символа,
распечатка и отключение устройства. Производные от
BasePrinter объектные модули осуществляют те же операции
иными способами: DosPrinter (принтер дисковой операционной
системы) использует файлы DOS для обслуживания процедур
вывода, BiosPrinter (принтер базовой системы ввода/вывода)
использует для работы возможности BIOS. Достоинства и
недостатки этих двух объектных модулей будут обсуждаться
далее.

Одно из самых выгодных отличий от алгоритма OOP заключается
в обработке ошибок печати, алгоритм которой представляет
собой, вероятно, наихитрейшее изобретение. Как вы
определяете, в какой момент принтер готов позволить
осуществить вывод? К сожалению, не существует тестов, дающих
100% уверенность. OPPRNLOW предлагает наилучшее из решений,
которые вы можете реализовать при помощи трансляции
виртуальным методом и обработки указателей с целью
достижения достаточной гибкости.

Другая важная черта OPPRNLOW заключается в способности
подключать объектный модуль BasePrinter (и его производные)
к стандартному приборному драйверу текстовых фалов (TFDD).
При этом для вывода на печать с высокой скоростью и
тщательной обработкой ошибок могут быть задействованы
полноформатные возможности операторов Write и WriteLn.

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

OPPRINT применяет объектно-ориентированный подход для
обеспечения требуемой степени гибкости. Первым объектным
модулем OPPRINT является PrinterType (тип принтера). Он
включает в себя указатель на объектный модуль BasePrinter и
таким образом использует возможности физического управления
коммуникациями из OPPRNLOW. Более важно то, что Printer
управляет скомпанованым списком объектов PrintMode (режим
печати). Каждый из них определяет, как активизировать и как
отключить индивидуальные характеристики принтера, а также
может определить сам для себя имя, номер или параметры, как
например: ширина и высота шрифта. Объектный модуль Printer
содержит также встроенные отладочные средства для тех
процедур принтера, которые требуют нечто кроме управляющих
кодов; как например прорисовка линий, полутоновая закраска.

OPDEVICE обеспечивает наличие специфической информации об
устройстве, которая необходима OPPRINT для индивидуальной
поддержки принтеров. OPDEVICE включает в себя управляющие
коды, таблицы трансляции символов, специальные подпрограммы
встроенной диагностики для принтеров типа Epson, Diablo и
LaserJet. Специальные возможности включают поддержку шрифтов
типа: bold, italic, compressed, underline; управление
интервалом между строк и размером страницы; и отрисовку
линий, если принтер поддерживает этот режим. OPDEVICE не
поддерживает такое количество принтеров, которое доступно
для WordPerfect, однако со временем предполагается эти
возможности расширить.

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

Подобно OPENTRY, OPFORM обеспечивает широкий ассортимент
объектных модулей, предназначенных для преобразования данных
Паскаля (типа строки, слова, действительной и булевой
величины и т.п.) в зоны печати. Каждая зона печати
характеризуется позицией, режимом печати, маской изображения
и возможностью быть напечатанной отдельно. Объектный модуль
PrintBlock (блок печати) управляет списком зон печати.
PrintPage (страница печати) комбинирует список из блоков
печати в соответствии с алгоритмом формирования страниц,
полей сверху и снизу.  Объектный модуль PrintedForm
(печатная форма) управляет списком страниц печати. Он
также содержит указатель для объектного модуля Printer
(откуда он получает индивидуальные параметры устройства),
информацию о расположении зон и расширений, и несколько
специальных отладочных процедур для дополнительного
управления. Наконец, объектный модуль Report (отчет)
содержит список печатных форм.

Вы можете заметить, что эта глава чрезвычайно насыщена.
Возможно, не все из того, что в ней содержится, вам будет
необходимо. Для многих прикладных программ будет вполне
достаточно гибких функциональных возможностей OPPRNLOW по
выводу данных и обнаружению ошибок. Кто-то посчитает, что
для выполнения работы будет достаточно управления режимом
печати и размещения заголовка при помощи OPPRINT. Если же
вас заинтересуют всесторонние возможности генерации печатных
сообщений, дочитайте до раздела OPFORM. Глава заканчивается
описанием FORMTEST, демонстрационной программы, в которой
сочетаются возможности всех четырех модулей.



В. OPPRNLOW: Поддержка принтера на физическом уровне

TurboPascal имеет стандартный модуль PRINTER для передачи
выходной информации на принтер, но этот модуль обеспечивает
доступ только к системному устройству PRN и имеет очень
ограниченные возможности по обработке ошибок.  OPPRNLOW
разработан для предоставления значительно большей гибкости,
включая поддержку всех устройств LPT (LPT1, LPT2, LPT3,
также как и PRN, и дисковых файлов), гибкую обработку
ошибок, автоматическую трансляцию символов и прочее.

OPPRNLOW составляют несколько объектных модулей,
предназначенных для поддержки процесса печати на
элементарном уровне. Он также обеспечивает способ сочетания
этих объектных модулей с приборным драйвером текстовых
файлов (TFDD), входящим в TurboPascal. Имея столь
благоприятные возможности, прикладная программа может
повысить эффективность поддержки принтера, не отказываясь
при этом от удобства применения операторов Write и WriteLn.

Следующий список поясняет иерархию объектных модулей,
входящих в OPPRNLOW:

    BasePrinter
       DosPrinter
       FlexiblePrinter*
            BiosPrinter

Хотя OPPRNLOW является модулем низкого уровня по отношению к
вышестоящим программным модулям обслуживающим печать,
объектные модули DosPrinter и BiosPrinter, и особенно
поддержка сочетания с TFDD применяются самостоятельно.


    Объектный модуль BasePrinter

Объектный модуль BasePrinter является модулем абстрактного
типа, который используется для реализации других объектных
модулей обслуживания печати. Он определяет общие методы и
описывает зоны данных, которые являются общими для всех
других объектных модулей. OPPRINT и OPFORM описывают все
принтеры, помещая указатель в BasePrinter, и допуская, что
на основе использования BasePrinter может быть создан
объектный модуль любого типа.

Объектный модуль BasePrinter имеет зоны данных, которые
содержат информацию о модели принтера, руководство по
управлению принтером (которое может представлять собой
драйвер файла DOS, номер LPT или иной размер слова, или
меньший объем), информацию о коде последней ошибки и
операции, количестве символов записанных и выведенных по
последнему обращению, таблицу трансляции символов.

Многие из методов, определенных в объектном модуле
BasePrinter, являются виртуальными, и поэтому могут быть
неприемлемыми для производных модулей. Виртуальные методы
включают:

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

Заметим, что BasePrinter сам по себе не может осуществлять
вывод на печать. Он только создает условия для работы
последующих объектных модулей. Мы опишем только некоторые из
виртуальных методов BasePrinter: те, которые можно
непосредственно вызвать из прикладной программы, и те, от
которых вы сможете при желании отказаться.

Поскольку многие алгоритмы DosPrinter и BiosPrinter приводят
к одному результату (хотя достигают их различными
способами), мы опишем их только один раз в разделе о
BasePrinter. А в соответствующих разделах о DosPrinter и
BiosPrinter вам будут представлены только специфические
алгоритмы.


    Объектный модуль DosPrinter

Объектный модуль DosPrinter использует стандартное обращение
к функции DOS для передачи символов в файл DOS или на
указанное устройство. Допустимые системные устройства
включают такие как PRN, LPT1, CON и AUX.

В использовании возможностей DOS по обслуживанию вывода на
печать имеются и положительные и отрицательные стороны.
Преимуществами при этом можно считать то, что:

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

Среди недостатков отметим такие как:

- контроль состояния принтера ограничен жесткой кодировкой
  обращений DOS IOCTRL;
- скорость печати значительно ниже, чем при посредстве BIOS.

Мы надеемся, что в ObjectProfessional достоинства возьмут
верх над недостатками, поскольку OPPRNLOW делает возможной
любую прямую связь между файлом для вывода и принтером для
вывода, и поскольку персональные компьютеры полностью
стандартизованы на уровне совместимости по BIOS. Здесь
показано, как применять объектный модуль DosPrinter:

    program DosPrtExample; {EXPRNLW1.PAS}
    uses
      OpRoot, OpPrnLow;
    const
      Hello : String[12] = 'Hello World'^L;
    var
      MyPrinter : DosPrinter;
    begin
      if not MyPrinter.Init('PRN', Prn) then begin
        WriteLn('Failed to init printer. Status = ', InitStatus);
        Halt;
      end;
      MyPrinter.PrnPutBlock(Hello[1], Length(Hello));
      MyPrinter.Done;
    end.

В этом примере DosPrinter используется для обращения к
стандартному принтеру DOS и передачи в него блока символов.
Блоком передаваемых символов является текст 'Hello World',
за которым следует символ <CTRL-L>(смена формата).


    Объектный модуль FlexiblePrinter

Является разновидностью принтера, который вероятно не будет
использоваться непосредственно. Он использует указатели
процедур и функций для обеспечения гибкости и настройки
параметров выводимых символов. Указатель используется по той
причине, что настройка режима этого объектного модуля может
быть произведена только после его запуска. Преимущество
этого режима находит свое воплощение в BiosPrinter, который
является производным от FlexiblePrinter.  Подробных описаний
FlexiblePrinter не будет; если вы решите, что вам необходимы
знания его возможностей, ознакомтесь с OPPRNLOW.PAS.


    Объектный модуль BiosPrinter.

Для связи с принтером BiosPrinter использует прерывание $17
базовой системы ввода/вывода. BIOS предоставляет по
прерыванию $17 три функции: функцию 0 для передачи символа
на принтер, функцию 1 для инициализации принтера и функцию 2
для выборки информации о состоянии принтера. BIOS также
поддерживает три устройства LPT: LPT1, LPT2 и LPT3. При
инициализации объектного модуля BiosPrinter вы можете
указать любое из них; и даже позже, обратившись к методу
SetLptPort, вы можете сделать переназначение среди них.

Одним из преимуществ работы с BIOS при выводе на печать
является то, что становится возможным непосредственный
контроль за обработкой ошибок. Основной стратегией при этом
является вызов прерывания $17 и функции 2 для приема байта
состояния перед распечаткой каждого символа. Если байт
состояния показывает, что принтер находится в состоянии
готовности, вызывается функция 0 для передачи символа, в
противном случае передается сообщение об ошибке. В этом
случае ошибка обрабатывается сразу; корректируемые ошибки
фиксируются и печать при этом завершается без потери
выводимой информации.

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

В результате, интерпретация байта состояния из функции 2
превращается в исскуство. Чтобы сориентироваться в
многовариантности, BiosPrinter предлагает вам на выбор
несколько альтернативных тестов. Фактически, отказавшись от
алгоритма PrnXlatErrorCode модуля BasePrinter, вы можете
переиначить вашу собственную подпрограмму, интерпретирующую
ошибки принтера, но это бывает необходимо исключительно
редко.

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

    бит    маска   значение при установке   имя для маски
 ───────────────────────────────────────────────────────────
     0     $01     временная задержка        bpTimeOut
     1     $02     --
     2     $04     --
     3     $08     ошибка ввода/вывода       bpIOError
     4     $10     принтер выбран            bpSelected
     5     $20     конец бумаги              bpOutOfPaper
     6     $40     подтверждение             bpAcknowledge
     7     $80     свободен                  bpNotBusy
 ───────────────────────────────────────────────────────────

В идеальном случае нам хотелось бы гарантировать, что биты
0, 3 и 5 сброшены, а биты 4 и 7 установлены. (6 бит,
подтверждение, устанавливается ненадолго и не обрабатывается
при контроле состояния.) Этот тест, однако, слишком строг;
практически ни одни принтер не удовлетворит его требованиям,
и поэтому ни в одной программе он не используется.

BiosPrinter обеспечивает пять различных способов проверки
этих бит. Выбранный способ определяется передачей номера
теста в конструктор InitCustom объектного модуля
BiosPrinter.

    Описание тестов:

0       Не является тестом. Предполагается, что принтер
        всегда готов.
1       Считается лучшим тестом, судя по последним известным
        публикациям. Он подтверждает корректность данных
        через заданный интервал времени, обрабатывает биты
        ошибки ввода/вывода, конца бумаги и бит 'свободно'
        следующим образом:
          ErrorCode := ErrorCode xor $80;
          PrinterReady := ((ErrorCode and $A9) = 0);
        Этот тест используется при обработке прерываний во
        время распечатки экрана в большинстве последних
        версий BIOS.
2       Немного слабее теста 1 тем, что не обрабатывает бит
        'свободно':
          PrinterReady := ((ErrorCode and $29) = 0);
3       Упрощенная версия теста 1, не обрабатывающая бит
        ошибки ввода/вывода и временную задержку:
          ErrorCode := ErrorCode xor $80;
          PrinterReady := ((ErrorCode and $A0) = 0);
4       Пользовательский тест, формируемый путем наложения
        на байт состояния маски функцией "И". Если результат
        идентичен маске, считается, что принтер готов к
        работе:
          PrinterReady := ((ErrorCode and Mask) = Mask);
        Многие программы используют этот тест со значением
        маски равным $10 (для проверки только бита выбора
        устройства) или $90 (для проверки бита выбора
        устройства и бита 'свободно').

Здесь показано, как использовать объектный модуль
BiosPrinter:

    program BiosPrtExample; {EXPRNLW2.PAS}
    uses
      OpRoot, OpPrnLow;
    const
      Hello : String[12] = 'Hello World'^L;
    var
      MyPrinter : BiosPrinter;
    begin
      if not MyPrinter.Init(Lpt1) then begin
        WriteLn('Failed to init printer. Status = ', InitStatus);
        Halt;
      end;
      MyPrinter.PrnPutBlock(Hello[1], Length(Hello));
      MyPrinter.Done;
    end.

В этом примере инициализируется модуль BiosPrinter для
вывода на LPT1, используя по умолчанию контроль готовности
принтера (тест1). Альтернативный конструктор (InitCustom)
позволит вам выбрать тест по вашему усмотрению.

Вы можете обнаружить, что ни один из тестов не обеспечивает
работу BiosPrinter во всех ситуациях. Если ваша прикладная
программа очень обширна и должна работать с неограниченным
количеством принтеров, вы можете применить метод
"калибровки" для регистрации ошибок. По этому методу вы
должны создать программу для диалоговой настройки, которая
запрашивает пользователя на предмет установки конкретного
принтера в различные переменные состояния (автоном, конец
бумаги, пассивно, выключено и готово), запомнить эти
состояния и модифицировать впоследствии байт состояния для
каждой ситуации. Мы прилагаем пример программы калибровки на
дискете BONUS. Для получения дополнительной информации
необходимо разархивировать файл CALIBRAT.LZH.

Однако не возлагайте слишком большие надежды на
использование лишь калибровки. Наиболее сложная проблема, с
которой мы имеем дело, заключается в том, что команда MODE,
поставляемая с операционной системой DOS версий более
ранних, чем 3.2, выдает в точности одно и то же значение
состояния независимо от того, находится ли последовательный
принтер в режиме online, offline, либо вообще отсоединен.
Если вы окажитесь в подобной ситуации, то лучший способ
выйти из нее: рекомендовать вашим пользователям
раскошелиться на новую версию DOS.

В действительности возможен подход к управлению печатью на
еще более низком уровне: прямой доступ к порту. Если
усовершенствование регистрации ошибок производить на этом
уровне, серьезных недостатков избежать невозможно. Прямой
доступ к порту значительно отличается от работы с
параллельным и последовательным портом, и он не совместим с
программой подкачки принтера, с сетевой и многозадачной
средой, подобной DesqView. Ни одна из инструментальных
программ Object Professional не работает на этом уровне.


    Специализированные объектные модули печати.

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

    type
      NetworkPrn =
        object(BiosPrinter)
          Spooling : Boolean;
          constructor Init(LPTNo : LPTType; SpoolingOn : Boolean);
            {-инициализация принтера}
          destructor Done; virtual;
            {-выполняется с принтером}
          procedure SpoolOn;
            {-активизация подкачки}
          procedure SpoolOff;
            {-отключение и очистка подкачки}
        end;
    constructor NetworkPrn.Init(LPTNo : LPTType; SpoolingOn : Boolean);
    begin
      if not BiosPrinter.Init(LPTNo) then
        Fail;
      Spooling := SpoolingOn;
      if SpoolingOn then
        SpoolingOn;
    end;
    destructor NetworkPrn.Done;
    begin
      if SpoolingOn then
        SpoolingOff;
      BiosPrinter.Done;
    end;

    procedure NetworkPrn.SpoolOn;
    var
      Regs : Registers;
    begin
      {установка локального принтера по умолчанию}
      with Regs do begin
        AX := $B805;
        DH := Byte(GetLptPort);
      end;
      MsDos(Regs); {NetWare устанавливает локальную функцию LPT}
      {включение подкачки принтера}
      with Regs do begin
        AX := $DF00;
        DX := $00;
      end;
      MsDos(Regs); {NetWare запускает функцию захвата LPT}
    end;

    procedure NetworkPrn.SpoolOff;
    var
      Regs : Registers;
    begin
      {отключение подкачки принтера}
      with Regs do begin
        AX := $DF00;
        DX := $01;
      end;
      MsDos(Regs);  {NetWare завершает функцию захвата LPT}
    end;

NetworkPrn добавляет новые функциональные возможности к
печати BiosPrinter. Сетевая подкачка может быть
активизирована при инициализации объектного модуля или
включена в любой другой момент. Пример с NetworkPrn является
примером интеграции одного из существующих объектных
модулей. Возможно у вас возникнет желание отказаться от
одного из виртуальных методов с целью реализации нового
алгоритма. Наиболее подходящим способом для этого является
PrnXlatErrorCode, который транслирует первоначальный код
ошибки в логику состояния принтера. Приведем пример:

    type
      SpecialBiosPrinter =
        object(BiosPrinter)
          function PrnXlatErrorCode(Call : PrnCallType;
                           ErrorCode : Word) : Word; virtual;
            {транслируется первоначальный код ошибки  }
            {в соответствующий код ошибки пользователя}
        end;

      function SpecialBiosPrinter.PrnXlatErrorCode(Call : PrnCallType;
                           ErrorCode : Word) : Word;
      begin
        PrnXlatErrorCode := 0;
        case Call of
          PutCall :
            if ErrorCode and bpSelected = 0 then
              PrnXlatErrorCode := PrinterNotReady;
          StatusCall :
            if ErrorCode and (bpSelected + bpNotBusy) = 0 then
              PrnXlatErrorCode := PrinterNotReady;
        end;
      end;

Таким образом осуществляется контроль различных состояний,
на основе результатов которого происходит обращение к BIOS.
PutCall (функция 0) проверяет только выбранный бит, а
StatusCall (функция 2) проверяет как выбранный бит, так и
невыбранные биты. Этот тест не рекомендуется для
специального применения, поскольку многие принтеры различно
реагируют на различные функции.


    Приборные драйверы текстового файла.

Болэнд ввел мощное расширение в TurboPascal, начиная с
версии 4.0, называемое Приборными Драйверами текстового
файла (TFDDs). TFDD обеспечивает взаимосвязь между чтением
или записью в текстовый файл и операцией физического ввода
или вывода. Используя TFDD, вы можете эффективно
специализировать возможности операторов Read, ReadLn, Write
и WriteLn.

OPPRNLOW имеет программы, которые соединяют любой объектный
модуль, производный от BiosPrinter, с файлом текстового
типа. Согласно этому, запись в такой файл в действительности
происходит под управлением объектного модуля печати; все,
что говорилось о гибкости этих объектных модулей,
переносится на текстовые файлы.

Следующий пример демонстрирует способ, которым вы можете
связать переменную Lst текстового файла с принтером BIOS,
ориентированным на LPT2:

    program TFDDExample;     {EXPRNLW3.PAS}
    uses OpRoot, OpPrnLow;
    var  Lst : Text;
         PrnLpt2 : BiosPrinter;
    begin
      {инициализация BiosPrinter для LPT2}
      if not PrnLpt2.Init(Lpt2) then begin
        WriteLn('Failed to init printer. Status = ', InitStatus); Halt;
      end;
            {назначение объектного модуля печати для
                    тестового файла Lst и вход в файл}
      AssignPrnObject(Lst, PrnLpt2);
      Rewrite(Lst);
      if IoResult <> 0  then begin
        WriteLn('Error opening TFDD'); Halt;
      end;
      WriteLn(Lst, 'This output goes to LPT2 via BIOS services.');
      Close(Lst);
    end;

Принтер BIOS с именем PrnLpt2 заключается в границы
текстового файла Lst. После этого переменная Lst текстового
файла будет использоваться как переменная с тем же именем из
стандартного модуля печати TurboPascal.

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


    Константы

    bpTimeOut      = $01;
    bpIOError      = $08;
    bpSelected     = $10;
    bpOutOfPaper   = $20;
    bpAcknowledge  = $40;
    bpNotBusy      = $80;

Битовые маски для байта состояния по прерыванию $17 BIOS.


    PrinterNotReady : Word = 152;

Величина вычисляется функцией PrnXlatErrorCode, а
следовательно при помощи IoResult, в тот момент, когда
фиксируется ошибка печати.


    LptNames : Array[LptType] of String[4] =
                                ('LPT1', 'LPT2', 'LPT3');

Имена трех устройств 'LPT'.


    PrnNames : Array[PrnType] of String[12] =
                  ('LPT1', 'LPT2', 'LPT3', 'PRN', 'Disk File',
                                          'User Defined');

Имя каждого типа принтера определяется при помощи PrnType.


    Типы

    PrnType = (LPT1, LPT2, LPT3, Prn, DiskFile, UserDefined);

Определяет различные типы устройств вывода, поддерживаемые
объектными модулями, производными от BasePrinter.


    LptType = LPT1...LPT3;

Подмножество типов для BasePrinter.


    PrnCallType = (OpenCall, PutCall, CloseCall, FlushCall,
                                    StatusCall, ResetCall);

Определяет операции, общие для всех объектных модулей,
производных от BasePrinter. Величина, описывающая тип,
передается в PrnXlatErrorCode для определения операции,
генерирующей первоначальный код ошибки.


    XlatCharTable   = Array[Char] of Char;

Определяет таблицу трансляции символов.


    XlatFunc        = function (Call : PrnCallType;
                              ErrorCode : Word) : Word;

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


    Алгоритмы OPPRNLOW/BasePrinter

    CharXlat
 ───────────────────────────────────────────────────────────
Объявление
  function CharXlat(C : Char) : Char; virtual;
Назначение
  Транслирует символ С в соответствии со встроенной
  таблицей трансляции.
Описание
  Подпрограммы вывода на печать обращаются к этой функции
  перед распечаткой каждого символа; прикладная программа
  обычно не обращается непосредственно к этой функции.
  По-умолчанию таблица трансляции вычисляет символ, не
  изменяя его, однако таблица может быть модифицирована
  описанным далее способом.
  Производные модули могут отказаться от этой функции с
  целью применить более сложный алгоритм трансляции символов
  вместо простого табличного. Другой возможной причиной для
  отказа от этого алгоритма может стать необходимость
  разрешать ситуацию, когда регистрируются нестандартные
  символы.  Например, буфер печати может очищаться  каждый
  раз по приходу на принтер символа 'конец формата' (form
  feed).

Пример

    C := ReadKey
    WriteLn('The character :', C, 'will be translated to:'
        PrnLpt2.CharXlat(C));

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

Справки
  NewXlatCharTable            XlatCharItem


    ClearXlatCharTable
 ───────────────────────────────────────────────────────────
Объявление
  procedure ClearXlatCharTable;
Назначение
  Устанавливает таблицу трансляции символов в состояние
  по умолчанию.
Описание
  В состоянии по умолчанию таблица трансляции символов,
  обрабатывая символ, оставляет его без изменений.

Пример

    PrnLpt2.ClearXlarCharTable;

  Устанавливает таблицу трансляции символов PrnLpt2 в
  состояние по умолчанию.

Справки
    NewXlatCharTable            XlatCharItem


    Done
 ───────────────────────────────────────────────────────────
Объявление
  destructor Done; virtual;
Назначение
  Очищает, закрывает и расформировывает память,
  использованную объектным модулем печати.
Описание
  Если принтер находился на системным обслуживании,
  очищается и закрывается файл DOS или устройство.
  Независимо от типа принтера, все массивы памяти,
  использованные объектным модулем печати, освобождаются.

Пример

    MyPrinter.Done;

  Закрывается файл или устройство, обозначенное MyPrinter.

Справки
  PrnFlush


    Load
 ───────────────────────────────────────────────────────────
Объявление
  constractor Load(var S : IdStream);
Назначение
  Загружает принтер из потока.
Описание
  S - это специально инициализированный объектный модуль
  потока (например, файл потока,открытый для чтения). Отметим,
  что S может являться любой производной от IdStream, которая
  включает в себя DosIdStream, BufIdStream и MemIdStream.
  Функция осуществляет чтение очередной последовательности
  байтов из потока S. Эти байты могли быть записаны при
  предыдущем обращении к DosPrinter.Store. Считываемая
  информация включает имя порта или устройства, таблицу
  трансляции символов и контрольную информацию о состоянии.
  Процедурой регистрации потока для DosPrinter является
  DosPrinterStream; для BiosPrinter - BiosPrinterStream. Любая
  написанная пользователем функция при работе с объектным
  модулем должна регистрироваться по вызову поточного
  алгоритма RegisterPointer перед обращением к Load. Наиболее
  подходящим кандидатом для такой обработки является
  подпрограмма активизируемая при помощи SetXlatErrorFunc
  (только для BiosPrinter).
  Если загрузка не состоится, InitStatus будет содержать
  код ошибки, описывающий причину сбоя.

Пример

    user OpRoot, OpPrnLow;
    var MyPrinter : DosPrinter;
        PS : BufIdStream;
    ...
    {загрузка принтера MyPrinter из потока}
    PS.Init('MYPRN.STM', SOpen, 1024);
    PS.RegisterHier(DosPrinterStream);
    PS.Get(MyPrinter);
    if PS.GetStatus <> 0 then begin
      WriteLn('Failed to load printer'); Halt;
    end;
    PS.Don;

  В этом примере инициализируется поток, регистрируется
  иерархия DosPrinter и вызывается поточная процедура Get,
  которая непосредственно реализует алгоритм Load в
  DosPrinter. Если загрузка не состоится, ошибка будет
  передана поточной функцией GetStatus. Если это произойдет,
  поток будет закрыт, а программа продолжит работу.

Справки
  BiosPrinter                 Store
  DosPrinter


    NameOfPrn
 ───────────────────────────────────────────────────────────
Объявление
  function NameOfPrn : String;
Назначение
  Передает имя принтера.
Описание
  Для DosPrinter именем является то, что указывается при
  входе в файл или устройство. Для BiosPrinter именами
  являются 'LPT1', 'LPT2', 'LPT3'.

Пример

    Name := MyPrinter.NameOfPPrn;

  Присваивается имя файла или устройства, связанного с
  MyPrinter.

Справки
  TypeOfPrn


    NewXlatCharTable
 ───────────────────────────────────────────────────────────
Объявление
  procedure NewXlatCharTable(var Table : XlatCharTable);
Назначение
  Устанавливает полную таблицу XlatChar.
Описание
  Эта подпрограмма заменяет встроенную в объектный файл
  таблицу символов на другую, указанную в аргументе.

Пример

    program AllUpper;
    uses
      OpRoot, OpCrt, OpPrnLow;
    var
      MyPrinter : BiosPrinter
      UpTable   : XlatCharTable;
      Lst       : Text;
      C         : Char;
    begin
      {построение новой таблицы перевода символов}
      DefaultXlatCharTable(UpTable);
      for C := 'a' to 'z' do
        UpTable[C] := UpCase(C);
      {инициализация объектного модуля печати, назначение его
        текстовым файлом}
      MyPrinter.Init(LPT1);
      MyPrinter.NewXlatCharTable(UpTable);
      AssignPrnObject(Lst, MyPrinter);
      Rewrite(Lst);
      {демонстрация результатов}
      WriteLn(Lst, 'abcdefghijklmnopqrstuvwxyz0123456789');
      MyPrinter.Done;
    end.

Справки
  CharXlat                    XlatCharItem
  ClearXlatCharTable


    PrnError
 ───────────────────────────────────────────────────────────
Объявление
  function PrnError : Word;
Назначение
  Передает последнюю логическую ошибку и сбрасывает
  внутреннюю переменную состояния.
Описание
  Передаваемый код является кодом "логической" ошибки,
  совместимым с системой кодирования ошибок IoResult в Turbo и
  в ObjectProfessional. Для получения первоначального кода
  ошибки (как он описан для байта состояния BIOS) используйте
  вместо PrnError функцию PrnRawError.

Пример

    ErrorCode := MyPrinter.PrnError;
    if ErrorCode <> 0 then begin
      WriteLn('MyPrinter error = ', ErrorCode);
      Halt;
    end;

  Получаем код последней ошибки от MyPrinter, запоминаем
  его в ErrorCode, выводим на экран сообщение и прерываем
  работу, если ErrorCode не обнулился.

Справки
  PPrnErrorOp         PrnRawError


    PrnErrorOp
 ───────────────────────────────────────────────────────────
Объявление
  function PrnErrorOp : PrnCallType;
Назначение
  Передает содержание последней операции.
Описание
  При возникновении ошибки бывает полезно знать, что
  именно делал принтер в момент сбоя. Далее об этом читайте в
  PrnCallType.

Пример

    ErrorCode := MyPrinter.PrnError;
    if ErrorCode <> 0 then begin
      WriteLn('The operation that caused the error was an
      attempt to ');
      case MyPrinter.PrnErrorOp of
        OpenCall   : Write('open');
        PutCall    : Write('output characters to');
        CloseCall  : Write('close');
        FlushCall  : Write('flush');
        StatusCall : Write('return the status of');
        ResetCall  : Write('reset');
      end;
      WriteLn(' MyPrinter');
    end;

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

Справки
  PrnError


    PrnFlush
 ───────────────────────────────────────────────────────────
Объявление
  procedure PrnFlush; virtual;
Назначение
  Очищает устройство.
Описание
  В случае с DosPrinter вызов этого алгоритма очистит файл
  или устройство, отдублировав обработку файла и закрыв этот
  дубликат. В случае с BiosPrinter этот алгоритм не работает.

Пример

    MyPrinter.PrnFlush;

  Очищает файл или прибор, связанный с MyPrinter.


    PrnLastPut
 ───────────────────────────────────────────────────────────
Объявление
  function PrnLastPut : Word;
Назначение
  Передает количество символов, успешно переданных до
  момента сбоя с помощью PrnPutBlock.
Описание
  Эта функция может использоваться для создания системы
  устранения ошибок. В момент появления ошибки при работе с
  PrnPutBlock, при помощи PrnLastPut определяется количество
  символов, которые успели записаться до момента сбоя, затем
  ошибка обрабатывается, а символы, которые необходимо
  дописать, посылаются.

Пример

    MyPrinter.PrnPutBlock(S[1], Length(S));
    while MyPrinter.PrnError <> 0 do begin
      WriteLn('An error has occurred, please ensure printer is
            ready');
      WriteLn('Press any key when ready... ');
      C := ReadKey;
      Delete(S, 1, CharsWritten);
      MyPrinter.PrnPutBlock(S[1], Length(S));
    end;

  Проверяется наличие ошибки, и осуществляется повторная
  передача до тех пор, пока не будут записаны все символы.

Справки
  PrnError                    PrnErrorOp


    PrnPutBlock
 ───────────────────────────────────────────────────────────
Объявление
  procedure PrnPutBlock(var Block; BlockSize : Word);virtual;
Назначение
  Передает блок символов на устройство печати.
Описание
  Эта процедура передает байты по-блочно на устройство
  печати, начиная с адреса блока. Если будет зарегистрирована
  ошибка, процедура будет немедленно завершена. Для всех
  предлагаемых объектных модулей печати эта процедура
  обращается к процедуре PrnPutChar для передачи каждого
  символа. Компилятор не обеспечивает обработки ошибок при
  передаче в PrnPutBlock параметра, чей тип не описан в блоке
  переменных. Поэтому вы должны знать, что печатаете.
  Параметром, помещаемым в Block, обычно является первый
  символ строки. В случае ошибки, можно обратиться к функции
  PrnLastPut для определения количества успешно переданных
  символов до сбоя.

Пример

    S := 'Send this string'#13#10;
    MyPrinter.PrnPutBlock(S[1], Lenght(S));

  В этом примере граница строки переменной S задается с
  помощью <CR><LF>, затем строка передается на принтер.

    S := 'Send this string'#13#10;
    MyPrinter.PrnPutBlock(S, Lenght(S));

  Неправильно! В этом случае печать начинается с S[0], то
  есть с нулевого байта, который определяет длину строки.

Справки
  PrnPutChar


    PrnPutChar
 ───────────────────────────────────────────────────────────
Объявление
  procedure PrnPutChar(Character : Char); virtual
Назначение
  Передает на принтер один символ.
Описание
  Символ, который необходимо передать, сначала транслируется
  при помощи вызова CharXlat. После действительного
  обращения для записи, PrnPutChar вызывает PrnXlatErrorCode
  для трансляции первоначального кода ошибки, полученного с
  утройства вывода, в логический номер ошибки.

Пример

    MyPrinter : BiosPrinter;

  Передает символ 'конец формата' на устройство вывода.

Справки
  PrnPutBlock


    PrnRawError
 ───────────────────────────────────────────────────────────
Объявление
  function PrnRawError : Word;
Назначение
  Передает первоначальный код ошибки, полученный после
  последней операции.
Описание
  Эта функция позволяет вам увидеть действительный код,
  переданный в результате последней операции. В особенности
  это относится к объектному модулю BiosPrinter, в котором
  вы можете использовать эту функцию для классификации ошибок
  и, следовательно, сможете выдавать более осмысленные
  сообщения об ошибках.

Пример

   var
     MyPrinter : BiosPrinter;
   ...
   if MyPrinter.PrmRawError and bpOutOfPaper <> 0 then begin
       {на принтере отсутствует бумага}
   end;

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

Справки
  PrnError                    PrnErrorOp


    PrnReset
 ───────────────────────────────────────────────────────────
Объявление
  procedure PrnReset; virtual;
Назначение
  Производит установку устройства вывода в исходное
  состояние.
Описание
  Для BiosPrinter эта процедура означает вызов функции 1
  по прерыванию $17 BIOS для сброса параллельного порта. Для
  других объектных модулей эта процедура не работает.
  Заметьте, что PrnReset делает не то же самое, что и нажатие
  кнопки 'Reset' на панели управления принтера (для лазерного
  принтера это означает, например, установку типа шрифта
  по умолчанию).

Пример

    MyPrinter.PrnReset;

  Устанавливает в первоначальное состояние параллельный
  порт MyPrinter.


    PrnStatus
 ───────────────────────────────────────────────────────────
Объявление
  function PrnStatus : Word; virtual;
Назначение
  Передает состояние прибора.
Описание
  Если результат этой функции равен нулю, значит прибор
  готов к приему выводимой информации. В противном случае
  возникает проблема, связанная с прибором. PrnStatus сначала
  фиксирует состояние прибора в том виде, как оно поступило,
  затем вызывает PrnXlatErrorCode для трансляции состояния в
  логический номер ошибки. Таким образом, значение ненулевого
  кода ошибки определяет процедура PrnXlatErrorCode.

Пример

    while MyPrinter.PrnStatus <> 0 then begin
      WriteLn('Printer is not ready')
      WriteLn('Please prepare printer and press any key
                                (ESC to abort)');
      if ReadKey = #27 then
        Halt;
    end;
    MyPrinter.PrnPutBlock(S[1], Length(S));

  Происходит ожидание готовности принтера, затем передача
  на печать строки S.


    PrnXlatErrorCode
 ───────────────────────────────────────────────────────────
Объявление
  function PrnXlatErrorCode(Call : PrnCallType;
                ErrorCode : Word) : Word; virtual;
Назначение
  Транслирует первоначальный код ошибки в логический код
  ошибки.
Описание
  Процедуры BasePrinter, которые могут генерировать
  ошибку, обращаются к PrnXlatErrorCode для трансляции
  первоначального кода ошибки в логический код ошибки. Будучи
  процедурой DosPrinter, PrnXlatErrorCode выдает тот же код
  ошибки, что и сама операционная система. Версия BiosPrinter
  выдает константу PrinterNotReady в том случае, если один из
  тестов опишет более ранний сбой. Вы можете отказаться от
  этой функции, чтобы изменить алгоритм обработки ошибок.
  PrnXlatErrorCode при отсутствии ошибки всегда должна
  выдавать нулевое значение, значение PrnXlatErrorCode не
  равное нулю означает для прикланой программы наличие ошибки.
  Параметр вызова Call говорит о типе вызова, передавшем
  код на трансляцию, а параметр ErrorCode является самим этим
  кодом. Обратитесь к параграфу "Специализированные
  объектные модули печати", где обсуждается альтернатива
  применения этой функции.
Справки
  PrnError


    Store
 ───────────────────────────────────────────────────────────
Объявление
  procedure Store(var S : IdStream);
Назначение
  Запоминает образ принтера в потоке.
Описание
  S является специально инициализированным потоковым
  объектным модулем (файл потока обычно открыт по записи).
  Отметим, что S может быть любой модификацией идентификатора
  потока IdStream, включая DosIdStream, BufIdStream и
  MemIdStream (хотя вы, конечно, никогда не будете обращаться
  к MemIdStream).
  Процедура Store записывает образ DosPrinter в поток S.
  Записываемая информация включает основные данные о принтере
  и таблицу трансляции символов. Для поиска ошибок, которые
  могут произойти при архивизации, отбратитесь к потоковой
  функции GetStatus.
  Процедурой регистрации потока для DosPrinter является
  DosPrinterStream, для BiosPrinter - процедура
  BiosPrinterStream. Любые пользовательские функции,
  используемые с объектными модулями, должны быть
  зарегистрированы путем обращения к регистратору потока
  RegisterPointer до начала архивизации. Наиболее подходящим
  для этого алгоритмом является подпрограмма, активизируемая с
  помощью SetXlatErrorFunc (только для BiosPrinter).

Пример

    user
      OpRoot, OpPrnLow;
    var
      MyPrinter : DosPrinter;
      PS : BufIdStream;
    begin
      {вход в DosPrinter}
      MyPrinter.Init('PRN', Prn);
      {вход в поток и архивизация принтера в нем}
      PS.Init('MYPRN.STM', SCreate, 1024);
      PS.RegisterHier(DosPrinterStream);
      PS.Put(MyPrinter);
      PS.Done;
    end.

  Архивируется принтер типа BiosPrinter в потоке PS.

Справки
  BiosPrinter.Init                    Load
  Dos.Printer.Init


    TypeOfPrn
 ───────────────────────────────────────────────────────────
Объявление
  function TypeOfPrn : PrnType;
Назначение
  Передает данные о типе принтера.
Описание
  Эта функция передает данные о типе следующих устройств:
  LPT1, LPT2, LPT3, Prn, DiskFile или UserDefined.

Пример

    WriteLn('MyPrinter is ', PrnNames[MyPrinter.PrnType]);

  Данные о типе принтера записываются в предварительно
  описанную константу PrnNames.

Справки
  NameOfPrn


    XlatCharItem
 ───────────────────────────────────────────────────────────
Объявление
  procedure XlatCharItem(CharIndex : Char; Character : Char);
Назначение
  Заменяет индекс символа CharIndex на символ в таблице
  трансляции.
Описание
  После обращения к XlatCharItem, когда индекс символа
  передан в PrnPutChar или в PrnPutBlock, он автоматически
  заменяется самим символом.

Пример

    MyPrinter.XlatCharItem('a','A');
    S := 'an apple a day';
    MyPrinter.PrnPutBlock(S[1],Length(S));

  XlatCharItem cообщает указанному принтеру о
  необходимости заменить все символы 'a' на символы 'A'. В
  результате PrnPutBlock должен напечатать: 'An Apple A dAy'.

Справки
  ClearXlatClearTable                 XlatChar
  NewXlatCharTable




    Алгоритмы OPPRNLOW/DosPrinter

    Init
 ───────────────────────────────────────────────────────────
Объявление
  constructor Init(PrnName : PathStr; TypeOfPrinter :PrnType);
Назначение
  Инициализирует печать DosPrinter.
Описание
  Объектные модули DosPrinter используют стандартные
  обращения к функции DOS для вывода символов. Имя принтера
  PrnName является именем входного файла или устройства. Типом
  печатающего устройства TypeOfPrinter может быть Prn или
  DiskFile. Если именем является 'PRN', вывод будет направлен
  на стандартное устройство DOS PRN (управление номер 4) без
  использования дополнительной файловой поддержки.

Пример

     MyPrinter.Init('C:\SPOOL\PRINTEST.DAT', DiskFile);

  Инициализирует указанный принтер для записи данных его
  вывода на дисковый файл.

     MyPrinter.Init('PRN', Prn);

  Инициализирует указанный принтер для записи данных его
  вывода на стандартное устройство печати DOS PRN.

     MyPrinter.Init('LPT2', Prn);

  Инициализирует указанный принтер для записи данных его
  вывода на устройство печати LPT2.

Справки
  BiosPrinter.Init


    Алгоритмы OPPRNLOW/BiosPrinter

    GetLptPort
 ───────────────────────────────────────────────────────────

Объявление
  function GetLptPort : LPTType;
Назначение
  Сообщает номер порта LPT, по которому работает данный
  принтер.
Описание
  Эта функция передает номер любого из портов LPT1, LPT2
  или LPT3.

Пример

    WriteLn(MyPrinter is using ', LptNames[MyPrinter.GetLptPort]);

  Предварительно описанная константа LptNames используется
  для записи в нее названия порта печати вашего принтера.

Справки
  SetLptPort


    GetTestAndMask
 ───────────────────────────────────────────────────────────
Объявление
  procedure GetTestAndMask(var PrinterTestNumber, Mask : Byte);
Назначение
  Передает номер теста печати и входную маску.
Описание
  Обратитесь к параграфу "Объектный модуль BiosPrinter",
  расположенному в начале этого раздела, в нем содержится
  описание тестов печати и принцип работы масок.

Пример

    MyPrinter.GetTestAndMask(TestNo, SuccessMask);
    WriteLn('MyPrinter is currently using printer test
                                            number ', TestNo);

  Выводит на экран номер теста, используемого вашим
  принтером в данный момент.

Справки
  SetTestAndMask


    GetXlatErrorFunc
 ───────────────────────────────────────────────────────────
Объявление
  function GetXlatErrorFunc : Pointer;
Назначение
  Передает подпрограмму XlatError, которую использует в
  данный момент процедура PrnXlatErrorCode.
Описание
  Эта функция используется в том случае, если вам
  необходимо сохранить текущую функцию XlatError, временно
  включить другую, а затем восстановить первоначальную.
  Поскольку эта функция передает нетипизированный указатель,
  ее результат должен быть типизирован для того, чтобы потом
  функцию XlatError использовать так, как подобает для функции
  данного типа.

Пример

    var
      MyPrinter : BiosPrinter;
      SaveXlat : Pointer;
    {$F+}
    function NewXlatFunc(Call : PrnCallType;
                        ErrorCode : Word) : Word;
    begin
      ...
    end;
    {$F+}
      ...
    SaveXlat := MyPrinter.GetXlatErrorFunc;
    MyPrinter.SetXlatErrorFunc(NewXlatFunc);
    ...
    MyPrinter.SetXlatErrorFunc(XlatFunc(SaveXlat));

  GetXlatErrorFunc передает текущую функцию XlatError и
  запоминает ее в переменной SaveXlat для нетипизированного
  указателя. Указатель впоследствии типизируется на
  соответствие XlatFunc для восстановления оригинальной
  функции трансляции ошибок.

Справки
  SetXlatErrorFunc


    Init
 ───────────────────────────────────────────────────────────
Объявление
  constractor Init(LPTNumber : LPTType);
Назначение
  Инициализирует печать BiosPrinter, используя лучший тест
  проверки готовности.
Описание
  Конструктор Init обращается к альтернативному
  конструктору InitCustom, выбирая тест 1 для проверки
  готовности принтера. Обратитесь к InitCustom и к параграфу
  "Объектный модуль BiosPrinter" за дополнительной
  информацией.

Пример

    MyPrinter.Init(LPT3);

  Инициализирует указанный принтер для работы с LPT3.

Справки
  DosPrinter.Init                     Load
  InitCustom


    InitCustom
 ───────────────────────────────────────────────────────────
Объявление
  constructor InitCustom(LPTNumer : LPTType;
                     PrinterTestNumber, SuccessMask : Byte);
Назначение
  Инициализирует печать BiosPrinter c заданными
  расширениями.
Описание
  Номер LPTNumber указывает, какой именно порт LPT
  подлежит использованию (LPT1, LPT2 или LPT3).
  PrinterTestNumber указывает, какой из предлагаемых тестов
  должен быть использован для трансляции байта первоначальной
  ошибки. Корректным номером теста является целое число в
  интервале 0...4. Маска SuccessMask используется только для
  теста 4. За дополнительной информацией обращайтесь к
  параграфу "Объектный модуль BiosPrinter".

Пример

    MyPrinter.InitCustom(LPT2, 2, 0);

  Инициализирует указанный прибор для вывода на LPT2 с
  использованием теста 2.

    MyPrinter.InitCustom(LPT1, 4, bpSelected+bpNotBusy);

  Инициализирует данный принтер для вывода на LPT1 с
  использованием теста 4 и маски, определяемой как
  bpSelected+bpNotBusy.

Справки
  DosPrinter.Init                     Load
  Init


    SetLptPort
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetLptPort(LPTNo : LPTType);
Назначение
  Устанавливает номер порта LPT для указанного принтера.
Описание
  Процедура SetLptPort может переназначить порт LPT для
  вывода после запуска объектного модуля.

Пример

    var
      MyPrinter : BiosPrinter;
      Lst : Text;
    begin
      if not MyPrinter.Init(LPT1) then begin
        WriteLn('Failed to Init, Status = ', InitStatus);
        Halt;
      end;
      AssignPrnObject(Lst, MyPrinter);
      Rewrite(Lst);
      WriteLn(Lst,'This output goes to LPT1');
      MyPrinter.SetLptPort(LPT2);
      MyPrinter.Done;
    end.

  В этой программе указанный принтер инициализируется для
  вывода на LPT1 и связывается с текстовым файлом Lst. Первая
  строка пишется на LPT1. После обращения к SetLptPort, вторая
  строка пишется на LPT2.

Справки
  GetLptPort


    SetTestAndMask
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetTestAndMask(PrinterTestNumber, Mask : Byte);
Назначение
  Устанавливает номер теста печати и входную маску.
Описание
  Процедура SetTestAndMask может переназначить тест
  готовности принтера после запуска объектного модуля. Это в
  частности используется при создании программ с
  альтернативной обработкой ошибок. Об особенностях обработки
  ошибок для печати BIOS вы можете прочитать в параграфе
  "Объектный модуль BiosPrinter".

Пример

    MyPrinter.SetTestAndMask(2, 0);

  Устанавливает тест 2 для печати. Входная маска в этом
  тесте игнорируется.

Справки
  GetTestAndMask


    SetXlatErrorFunc
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetXlatErrorFunc(Xlat : XlatFunc);
Назначение
  Устанавливает функцию XlatError, к которой обращается
  PrnXlatErrorCode.
Описание
  Обычно объектный модуль BiosPrinter использует тест
  печати, указанный в параметре PrinterTestNumber, переданном
  в InitCustom. Процедура SetXlatErrorFunc дает возможность
  включить пользовательскую функцию обработки ошибок. Xlat
  должна быть функцией, откомпилированной в модели FAR
  (используя {$F+}), должна быть глобальной, и иметь
  объявление в соответствии со следующим образцом:

    {$F+}
    function MyXlatFunc(Call : PrnCallType;
                                ErrorCode : Word) : Word;

   Функция должна давать нулевой результат, если ErrorCode
  говорит об успешно завершенной операции, и ненулевой
  результат в противном случае. Если вы передадите NoXlatFunc
  в SetXlatErrorFunc, BiosPrinter вернется к использованию
  стандартного теста печати. За дополнительной информацией
  обращайтесь к параграфу "Специализированные объектные модули
  печати".

Пример

    {$F+}
    function MyXlatFunc(Call : PrnCallType; ErrorCode : Word) : Word;
    begin
    ...
    end;
    {$F-}
    ...
    MyPrinter.SetXlatErrorFunc(MyXlatFunc);
    ...
    MyPrinter.SetXlatErrorFunc(NoXlatFunc);

  Сначала осуществляется передача трансляции ошибок на
  MyXlatFunc, а затем восстанавливается режим по умолчанию.

Справки
  BasePrinter.PrnXlatErrorCode        GetXlatErrorFunc


    Процедуры OPPRNLOW

    AssignPrnObject
 ───────────────────────────────────────────────────────────
Объявление
  procedure AssignPrnObject(var F : Text; var PrinterObj : BasePrinter);
Назначение
  Cвязывает текстовую переменную F с объектным модулем
  PrinterObj.
Описание
  Эта процедура работает как стандартная процедура
  назначения Assign, с той разницей, что она связывает
  текстовый файл с обслуживанием печати, используя указанный
  объектный модуль печати. AssignPrnObject работает при помощи
  приборного драйвера текстовых файлов (TFDD). F является
  назначаемым текстовым файлом. Он не должен быть открыт в
  момент вызова AssignPrnObject. PrinterObj является любым
  инициализированным объектным модулем печати, производным от
  BasePrinter. Он может быть модулем BiosPrinter или
  DosPrinter. AssignPrnObject предоставляет для текстового
  файла буфер размером 128 (значение буфера для переменной
  Text по умолчанию). Этот размер может быть увеличен с
  помощью обращения к стандартной процедуре SetTextBuf.
  Однако, дополнительная буферизация используется редко,
  поскольку приборный драйвер текстового файла передает каждый
  символ индивидуально, даже при очищенном буфере.
  После обращения к AssignPrnObject, вы используете
  Rewrite(F) для открывания файла на вывод, а затем
  контролируете IoResult.

Пример

    {$I-}
    uses
      OpRoot, OpPrnLow;
    var
      MyPrinter : BiosPrinter;
      Lst : Text;
    begin
      if not MyPrinter.Init(LPT1) then begin
        WriteLn('Failed to Init, Status = ', InitStatus);
        Halt;
      end;
      AssignPrnObject(Lst, MyPrinter);
      Rewrite(Lst);
      if IoResult <> then begin
        WriteLn('Error initializing printer');
        Halt;
      end;
      Close(Lst);
      MyPrinter.Done;
    end.

  Принтер объектного модуля BiosPrinter связывается с
  текстовым файлом Lst посредством обращения к
  AssignPrnObject.  Если вызов Rewrite завершится успешно,
  образец может быть записан в файл Lst как обычно. Сделанные
  в конце этого примера операции Close и Done не являются
  столь уж важными, однако их использвание является полезной
  привычкой.

Справки
  BiosPrinter.Init            DosPrinter.Init


    CallPrnStatus
 ───────────────────────────────────────────────────────────
Объявление
  function CallPrnStatus(var F ; Text) : Word;
Назначение
  Передает результат функции PrnStatus объектного модуля
  печати, связанного с переменной F.
Описание
  Если текстовый файл F не имеет объектного модуля печати,
  связанного с ним, CallPrnStatus передает $FFFF. Функция
  CallPrnStatus обнаруживает различия между нормальными
  текстовыми файлами и такими, которые имеют отношение к
  объектному модулю печати.
  Эту функцию можно использовать для определения
  готовности принтера перед началом вывода.

Пример

    var
      Lst : Text;
      LstPrn : BiosPrinter;
    ...
    if not LstPrn.Init(LPT1) then
      Halt;
    AssignPrnObject(Lst, LstPrn);
    Rewrite(Lst);
    if IoResult <> 0 then
      Halt;
    if CallPrnStatus(Lst) = 0 then
      WriteLn(Lst, 'This string goes to the printer');
    else
      WriteLn('Printer is not ready');

  Программа открывает файл Text, используя возможности
  BiosPrinter, и перед записью в него контролирует момент,
  когда принтер будет готов.

Справки
  AssignPrnObject             BasePrinter.PrnStatus


    DefaultXlatCharTable
 ───────────────────────────────────────────────────────────
Объявление
  procedure DefaultXlatCharTable(var Table : XlatCharTable);
Назначение
  Инициализирует Table, устанавливая ее в первоначальное
  состояние.
Описание
  После завершения этой процедуры, Table содержит
  тождественную кодировку. Это означает, что передаваемые
  символы при трансляции не подвергаются измению. Эта
  процедура используется в BiosPrinter, вчастности в
  NewXlatCharTable и в ClearXlatTable.
Справки
  BasePrinter.ClearXlatTable     BasePrinter.XlatCharItem
  BasePrinter.XlatChar


    GetPrnObject
 ───────────────────────────────────────────────────────────
Объявление
  function GetPrnObject(var F : Text) : BasePrinterPtr;
Назначение
  Передает указатель объектного модуля печати, связанного
  с текстовым файлом.
Описание
  Эта функция выдает нулевое значение в том случае, если
  текстовый файл не имеет связанного с ним производного
  объектного модуля BasePrinter. Эта подпрограмма
  предназначена в первую очередь для внутреннего использвания.

Пример

    var
      P : BasePrinterPtr;
      ...
    P := GetPrnObject(PrnTextFile);
    if P = nil then
      WriteLn('No printer object assiciated with PrnTextFile')
    else
      WriteLn('PrnTextFile = ', P^.NameOfPrn);

  Переносит указатель на объектный модуль, связанный с
  PrnTextFile. Если переданный указатель не нулевой, на
  экран выводится имя печатающего устройтсва.

Справки
  CallPrnStatus



    C. OPPRINT: Поддержка принтера на логическом уровне.


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

Объектный модуль Printer ничего не знает о каких либо особых
устройствах печати. Для получения такой информации
необходимо произвести настройку объектного модуля Printer.
Иметь представление об уникальных устройствах входит в
компетенцию модуля OPDEVICE; описание которого расположено в
следующем разделе.

(Обратите внимание, что мы пишем Printer с заглавной буквы в
том случае, когда имеем в виду объектный модуль, и пишем
'принтер' с маленькой буквы, ссылаясь на саму печатающую
установку, чью вилку вы вставляете в розетку.)

Величайшим достижением является то, что OPDEVICE может знать
все, что необходимо знать о 1000 различных принтерах. Вы
только должны обратиться к OPDEVICE для того, чтобы
зарегистрировать объектный модуль Printer как печатающее
устройство, к примеру, "Epson 4000ZX", затребовать гарнитуру
шрифта red-on-green helvetica bold для 24-х пиновой
печатающей головки и "жарить" во всю! В этом случае вам не
нужно многое знать об объектном модуле Printer, за
исключением умения инициализировать его, затребовать
конкретный режим печати и напечатать строку. Безусловно, мы
еще далеки от состояния нирваны. Если принтер, который вы
собираетесь поддерживать, имеется в списке широко
используемых - матричный принтер Epson, лазерный принтер H-P
LaserJet, роторная лепестковая печать Diablo и большое
количество других - вы можете проигнорировать многие
подробности работы с объектным модулем Printer. Если же вам
придется поддерживать другие устройства или применять редко
используемые возможности принтера, то вам необходимо будет
глубже изучить Printer. В любом случае вам, к сожалению,
потребуется дочитать это введение до конца.

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

    Backspace (забой, отмена)

Способность принтера вернуться на одну или более символьных
позиций в той же строке. Обычно задается символом отмены (8
символ таблицы ASCII, <Ctrl-H>).

    BasePrinter (базовый принтер)

Любой объектный модуль, производный от BasePrinter и
определенный в OPPRNLOW; используется в качестве
сокращенного обозначения для "BiosPrinter, DosPrinter и,
возможно, FlexiblePrinter".

    Control sequence (управляющая последовательность)

Последовательность символов, посылаемых в принтер,
интерпретируемых и исполняемых им как команды. Например,
переключение режима печати, смена шрифта или перемещение
печатающей головки. Сами символы этой последовательности на
странице не печатаются.

    Dowble-strike (двойная печать)

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

    Characters per inch (CPI) (число символов на дюйм)

Параметр плотности печати, задающий число символов,
размещаемых горизонтально на длине в 1 дюйм.

    Character pitch (ширина символа)

Параметр плотности печати, обратный числу символов на дюйм,
задающий ширину символа в дюймах.

    Character point

Высота символа, заданная в точках (точка = 1/72 дюйма).

    CR/LF (на русских клавиатурах = ВК/ПС)

Аббревиатура, обозначающая последовательность команд
"возврат каретки" (3 символ ASCII, <Ctrl-M>) и "перевод
строки" (10 символ ASCII, <Ctrl-J>). Интерпретируется
большинством принтеров как команда "перевод печатающей
головки в начало следующей строки".

    Decipoint

Единица измерения расстояния, равная 1/720 дюйма.

    Escape sequence

ESC-последовательность. Управляющая последовательность,
построенная с применением символа ESC (27 символ ASCII,
<Ctrl-[>).

    Font

Особый стиль печати символа, сочетающийся с конкретным
размером. Например, bold 12 point Roman (жирный шрифт Роман
высотой 12 точек). В отличие от настоящих экспертов печати,
мы не придаем значения отличию терминов font (программно
заданная гарнитура шрифта) и typeface (аппаратно
установленная гарнитура).

    Form feed (перевод формата)

Команда, интерпретируемая принтером как "перемещение
печатающей головки в начало следующей страницы. Является
символом (12 ASCII, <Ctrl-L>).

    Landscape mode

Ориентация страницы, когда горизонтальное направление печати
выбирается параллельным более протяженному краю бумаги -
"продольная печать". В отличие от обычного Portrait mode.

    Leading

Дистанция между двумя строками символов. Обратите внимание,
что высота символа (character points) и дистанция между
строк вовсе не одно и то же. Дистанция является параметром,
обратным числу строк на дюйм. Произносится "лиддинг".

    Lines per inch (LPI)

Число строк символов, размещающихся на интервале 1 дюйм.

    Lines per page (LPP)

Число строк символов, размещающихся на странице.

    Point (точка)

Единица измерения равная 1/72 дюйма (сравните decipoint =
0,1 point), обычно используемая для задания высоты символа.
Символы высотой 12 точек печатаются обычно с интервалом
6 строк на дюйм.

    Portrait mode

Ориентация страницы, при которой горизонтальное направление
печати выбирается параллельным узкой стороне бумаги -
"нормальная печать". В отличие от landscape mode.

    Positionable printer (позиционируемый принтер)

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

    Printer intensity (интенсивность печати)

Интенсивность черного цвета в результате печати. Может быть
повышена с помощью встроенного режима принтера или методом
двойной печати.

    Printhead position

Размещение печатающей головки напротив того места на
странице, где предполагается в следующий момент производить
печать. В лазерном принтере печатающей головки физически не
существует, однако  концепция позиционирования головки для
него вполне применима.

    Print modes (режимы печати)

Зависят в основном от возможностей конкретной печати.
Например, шрифты, направление печати и дистанция между
строк.

    Reverse linefeed

Способность принтера возвращаться на одну или более строк на
той же странице.

    Rule

Горизонтальная и вертикальная прорисовка линий принтером.



    Объектный модуль PrintMode

PrintMode является объектным модулем, который выделяет две
строки из последовательности, управляющей принтером:  одну
для активизации конкретного режима, а другую для его
отключения. Вам обычно не требуется оговаривать привязку
записей PrintMode, поскольку их размещение и управление ими
осуществляет объектный модуль Printer. Более того, PrintMode
является основой для реализации гибких возможностей Printer,
поэтому важно их изучить.

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

Поскольку доступ к объектному модулю PrintMode реализуется
строго через алгоритмы Printer, мы не станем описывать
методы PrintMode. Однако подробную информацию о расширениях
PrintMode можно найти в разделе "Константы", следующем
далее.


    Объектный модуль Printer

Объектный модуль Printer велик, он включает свыше 50
задокументированных алгоритмов и 70 других, предназначенных
для внутреннего использования. Если принтер, к которому вы
желаете осуществить доступ, уже зарегистрирован при помощи
OPDEVICE, кажущаяся сложность Printer моментально исчезает,
поскольку многие алгоритмы предполагают инициализацию
отдельных характеристик принтера, а OPDEVICE об этом уже
позаботился. Оставшиеся алгоритмы используются в первую
очередь для включения и выключения режимов принтера,
перемещения печатающей головки и записи строк.

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

    program PrinterExample; {EXPRINT.PAS}
    uses OpRoot, OpPrnLow, OpPrint;
    var MyPrinter : Printer;
    begin
      if not MyPrinter.Init('PRN', Prn, 0) then begin
        WriteLn('Failed to init printer. Status = ', InitStatus);
        Halt;
      end;
      MyPrinter.AddPrintMode('Italics', #27'4', #27'5', 0, 0, Italics, 0);
      MyPrinter.TurboOnByID(Italics);
      MyPrinter.PrintStr('Hello World');
      MyPrinter.Position(50, 1, rtAbsolute);
      MyPrinter.TurboOffByID(Italics);
      MyPrinter.PrintStr('Goodbye World');
      MyPrinter.FormFeed;
      MyPrinter.Done;
    end.

В этом примере приводится объектный модуль Printer, который
содержит DosPrinter (из OPPRNLOW) для записи на устройство
PRN. PrintMode добавлен для задания управляющей
последовательности, устанавливающей на принтере Epson шрифт
Italic. После установки шрифта, строка распечатывается на
странице в текущей позиции. Затем печатающая головка
перемещается в сторону конца страницы, шрифт Italic
отключается, и печатается другая строка. После этого
запускается FormFeed для подачи бланка из принтера и
вызывается процедура Done.

Этот пример примечателен тем, что специфическим в нем для
печатающего устройства является только обращение к
AddPrintMode. Одно из главных преимуществ объектного модуля
Printer заключается в возможности отделить алгоритмы
распечатки строки от алгоритмов инициализации специфической
аппаратуры устройства.

Объектный модуль Printer обращается к ряду зон данных и
параметров, используя тип данных Dimension. Dimension
является значением координаты позиции на печатной странице.
Единица измерения умышленно неопределена, потому что
объектный модуль Printer не накладывает ограничений на
способ измерения этого расстояния. Dimension является
синонимом шести байтов данных типа Real, или типа Double,
если установлено расширение компилятора {$N+}. Однако
переменные типа Dimension могут быть описаны с произвольной
точностью.

По-умолчанию модули OPPRINT, OPDEVICE и OPFORM измеряют
большинство переменных типа Dimension в строках и столбцах.
Например, алгоритм Printer для вывода на печать строки в
указанной позиции объявляется так:

    procedure PrintStrPos(S : String; Row, Col : Dimension;
                            Relative : RelativeType); virtual;
    {- Печать строки в указанной позиции}

Если осуществлять алгоритм PrintStrPos на непозиционирующем
принтере, OPPRINT сгенерирует последовательность CR/LF и
пробелы/забои, необходимые для установки каретки в указанную
позицию указанной строки.  Дробные значения строк и столбцов
округляются до целого в сторону уменьшения. Для принтеров,
поддерживающих режим позиционирования, подобных LaserJet,
все величины строк и столбцов преобразуются в
соответствующие последовательности, которые позиционируют
гипотетическую каретку в указанную позицию в пределах
разрешающей способности принтера.

OPPRINT обеспечивает иной уровень гибкости при определении
координат печатающей головки. Перед запуском какой-либо
команды позиционирования, OPPRINT умножает значения
координат указанных пользователем на собственные масштабные
коэффициенты по осям X и Y. Этот коэффициент по умолчанию
равен 1.0, но может быть установлен в любое значение.
Используя эти коэффициенты, вы можете оперативно
переопределить единицы измерения из "строк" и "столбцов" в
дюймы или сантиметры.
Среди зон данных модуля Printer есть такие:

  - слово расширений битовой карты принтера;
  - переменная состояния ошибки;
  - код, определяющий тип зарегистрированного принтера;
  - последняя зафиксированная позиция печатающей головки;
  - масштабные коэффициенты для осей X и Y;
  - текущие значения LPI, CPI, LPP и высоты символов;
  - код идентификатора ID и адрес текущего режима печати;
  - адрес ассоциативного объектного модуля BasePrinter;
  - скомпанованный список используемых объектных модулей
    PrintMode;
  - стековая память ByteStack, куда заносят коды
    идентификаторов активных шрифтов;
  - битовый код BitSet идентификаторов всех режимов печати;
  - число ударов для режима Double-strike (двойная печать);
  - указатель функции и процедура поддержки расширенных
    возможностей, как например произвольного позиционирования
    каретки и пользовательских встроенных средств диагностики.

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

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

    Конструкторы

Init, InitCustom

    Зоны данных для модификации и восстановления

SetLPP, SetYRatio, SetXRatio, SetRegisteredType, pOptionOn, pOptionOff,
GetLPP, GetYRatio, GetXRatio, GetRegisteredType, pOptionAreOn, pGetLastError

    Доступ и объединение с объектным модулем BasePrinter

LinkTopPrinter, GetBasePrinter, PrinterReady

    Добавление новых режимов печати

AddPrintMode, LinkToModes, DisposeAllModes

    Активизация пользовательских диагностических программ

SetPrintErrorFunc, SetPositionPrim

    Позиционирование печатающей головки

Position, FormFeed, AdvanceLine, AdvanceSingleLine, ResetPage

    Выбор и доступ к режимам печати

SetDefaultFont, EnableDefaultFont, TurnOnByID, TurnOffByID,
TurnOnByName, TurnOffByName, TurnOffAllActiveModes,
GetCurrentFont, GetDefaultFont, GetPrintModeByID,
GetPrintModeByName, InLandscape

    Управление режимом двойной печати

EnableDoubleStrike, DisableDoubleStrike, SetStrikeCount,
InDoubleStrike, StrikeCount, CurrentStrike

    Печать строки

PrintStr, PrintStrPos

    Разборщик

Done

    Поддержка в режиме потока

Load, Store

Некоторые зоны данных и алгоритмы Printer несут чисто
информационную нагрузку, то есть зоны могут быть переписаны
и прочитаны, но Printer не использует эти значения
непосредственно для модификации процесса печати на странице.
Зоны данных, где хранятся параметры LPI, CPI, высота символа
и верхнее поле, относятся к этой категории. (OPDEVICE
обращается к LPI и CPI при поддержке LaserJet с
десятикратной точностью [decipoint]). Когда вы
переключаетесь на другой режим печати, имеющий иные
установки флагов, зоны этих данных переписываются с целью
соответствовать содержанию нового режима. Ваша программа
может впоследствии сама по себе обратиться к этим данным для
осуществления расчетов координат. Следующие алгоритмы также
попадают в эту категорию и не описаны подробно. Дальнейшая
информация о них может быть получена в AddPrintMode.

procedure SetPrinterInfo(LPI, CPI, Point, LPP : Dimension);
{-установка строк на дюйм, символов на дюйм, высоты символа,
                                        и строк на страницу}

procedure SetLPI(LPI : Dimension);
{-установка строк на дюйм}

procedure SetCPI(CPI : Dimension);
{-установка символов на дюйм}

procedure SetPoint(Point : Dimension);
{-установка высоты символа}

procedure SetPhysicalTopMargin(Top : Dimension);
{-установка физической верхней границы}

procedure GetPrinterInfo(LPI, CPI, Point, LPP : Dimension);
{-выборка строк на дюйм, символов на дюйм, высоты символа,
                                        и строк на страницу}

function GetLPI(LPI : Dimension);
{-выборка строк на дюйм}

function GetCPI(CPI : Dimension);
{-выборка символов на дюйм}

function GetPoint(Point : Dimension);
{-выборка высоты символа}

procedure GetPhysicalTopMargin(var Top : Dimension);
{-выборка физической верхней границы}



    Шрифты и стек шрифта

По отношению к объектному модулю Printer, шрифт представляет
собой не более чем разновидность режима печати.  Конечно,
для вас шрифт является одной из важнейших характеристик
принтерного вывода, вам необходим bold, italic, compressed и
прочие стили печати.

Объектному модулю Printer часто приходится предпринимать
дополнительные шаги для включения и выключения шрифтов,
совместно с которыми он осуществляет некоторые другие режимы
печати. Например, если вы печатаете шрифтом italic, затем
включаете шрифт bold и печатаете строку, после чего
отключаете шрифт bold, вы вероятно ожидаете, что следующий
вывод будет осуществляться шрифтом italic. Некоторые
принтеры имеют подобные возможности поведения во встроенном
варианте. Например многие шрифты матричного принтера Epson
являются реальными характеристиками, которые могут быть
включены при помощи одной управляющей последовательности и
выключены при помощи другой. Однако другие принтеры могут не
реализовывать управляющих последовательностей отключающих
конкретный шрифт; следовательно объектному модулю Printer
необходимо немного придти в себя для восстановления
предыдущего шрифта в тот момент, когда вы требуете отключить
текущий шрифт.

Для того, чтобы иметь дело со шрифтами, которые не могут
работать подобно переключателям Epson, Printer управляет
стеком активных шрифтов. Когда бы вы не востребовали
специальный режим печати, Printer должен выполнить операции
в следующей последовательности:

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

Когда вы отключаете режим печати, Printer должен
воспользоваться стеком для восстановления предыдущего
шрифта:

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

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

Не все шрифты необходимо заряжать в обойму стек шрифта.
Заряжаются только объектные модули PrintMode, обладающие
установкой флага pmFont. Когда вы добавляете новый режим
печати, вы можете в большинстве случаев сделать выбор: то ли
устанавливать флаг pmFont в соответствии с запросом, то ли
шрифт имеет жесткую управляющую последовательность,
способную отключить шрифт. Если такой управляющей
последовательности нет, либо если такая управляющая
последовательность может повредить предыдущему активному
шрифту, то следует обратиться к флагу pmFont.

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

Необходимо отметить значение BitSet в структуре алгоритма.
OPPRINT обслуживает этот битовый набор (идентификаторы
активных режимов печати) таким образом, что
TurnOffAllActiveModes может отключить все активные режимы.
BitSet отслеживает до 255 (MaxPrintModes) самостоятельных
режимов печати для каждого объектного модуля Printer.


    Поддержка лазерного принтера

OPPRINT включает в себя несколько программных секций,
предназначенных для поддержки уникальных функциональных
возможностей таких устройств печати, как Hewlett-Packard
LaserJet. Проще всего реализуется продольная ориентация
печати. Соответствующий PrintMode может включить режим
принтера и установить внутренний флаг, по которому Printer
сможет определить установку режима продольной печати.
Дополнительную информацию можно найти в описании алгоритма
AddPrintMode.

Лазерный принтер поддерживает дискретность позиционирования
1/720 дюйма (decipoint). Если в Printer установлено
расширение pUseDecipoints, то координаты строки и столбца,
переданные в алгоритмы Printer, интерпретируются как
координаты, выраженные в единицах равных 1/720 дюйма, а не в
значениях ширины и высоты символа.

Лазерный принтер предоставляет специальные команды для
прорисовки вертикальных и горизонтальных линий произвольной
ширины и расположения. Больше того, лазерный принтер
поддерживает режимы сплошной и полутоновой заливки. Если
некий принтер эмулирует эти возможности LaserJet, то в
Printer должен быть установлен флаг pSupportHPRules.
Несмотря на то, что OPPRINT не интерпретирует подобное
расширение, это делает OPFORM, и вы можете использовать
объектный модуль LineField для создания выразительной
печатной продукции, аналогичной лазерному принтеру.


    Режим двойной печати

Если в предыдущем параграфе шла речь, можно сказать, о
принтере будущего; то здесь мы беремся анатомировать
динозавров среди них. Роторная лепестковая печать и
некоторые очень старые матричные принтеры синтезируют
печать bold (жирный шрифт) методом возврата и повторной
пропечатки каждого символа. Пришлось немного "прогнуться",
чтобы объектный модуль Printer смог поддерживать такой режим
работы. Не смотря на то, что этот режим явился
благотворительной акцией по отношению к примитивным
принтерам, он может оказаться полезным при интенсификации
шрифтов на супер-принтерах, либо может использоваться при
составлении мультипликационных форм.  Однако необходимо
учесть, что возврат и повторная пропечатка на лазерном
принтере не дают различимого эффекта, поскольку на
протяжении всего буфера страницы в принтере установлены
каждый раз одни и те же биты.

Хотя название "двойная печать" означает возврат и повторную
пропечатку только один раз, OPPRINT позволяет указать до 127
надпечаток. Алгоритм SetStrikeCount ведет отсчет этого
параметра, начиная от значения по умолчанию равного 2.
StrikeCount передает число надпечаток, заданное последний
раз.

EnableDoubleStrike устанавливает флаг, указывающий OPPRINT
напечатать каждый символ то количество раз, которое записано
в StrikeCount. DisableDoubleStrike сбрасывает этот флаг.
Если режим двойной печати отключился, InDoubleStrike даст
подтверждение 'истинно'.

В течение того интервала времени, когда включен режим
двойной печати, нельзя печатать строку, которая содержит
'возврат каретки', 'смену строки' или другие символы, эффект
которых не отменяется передачей 'забоя'. Используйте вместо
них:  AdvanceSingleLine, FormFeed, TurnOnByID и другие
алгоритмы, на них режим двойной печати не оказывает никакого
воздействия.

Когда вы вызываете TurnOnByID, указывая идентификатор
DoubleStrike, OPPRINT генерирует вызов EnableDoubleStrike
вместо того, чтобы послать на принтер управляющую
последовательность. И наоборот, вызов TurnOffByID с тем же
идентификатором приводит к генерации DisableDoubleStrike.

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

    procedure EnableDoubleStrike;
    {включает режим двойной печати}

    procedure DisableDoubleStrike;
    {отключает режим двойной печати}

    function InDoubleStrike : Boolean;
    {передает 'истинно', если режим двойной печати включен}

    function StrikeCount : ShortInt;
    {передает количество надпечаток для режима двойной печати}

    procedure SetStrikeCount(Strike : ShortInt);
    {устанавливает число надпечаток для режима двойной печати}


    Отладчик ошибок принтера

Ошибки, возникающие по вине принтера, обычно имеют случайный
характер и могут быть исправлены. Для того, чтобы добиться
этого, вы устаналиваете отладчик ошибок, к которому
обращается OPPRINT каждый раз, когда бы не произошла ошибка.
OPPRINT информирует отладчик о том, является ли ошибка
исправимой; отладчик передает оператору подсказку для
решения проблемы; если оператор подтвердил сообщение,
отладчик передает с помощью установки флага указание OPPRINT
повторить операцию.

При отсутствии подобного отладчика, во время появления
ошибки, объектный модуль Printer просто запоминает код
ошибки во внутренней переменной и обрабатывает ошибку как
неисправимую. Операция, послужившая причиной ошибки, либо
игнорируется, либо прерывается. Прикладная программа должна
обращаться к алгоритму pGetLastError после каждого вызова
алгоритма Printer.

При помощи алгоритма SetPrintErrorFunc вы можете установить
свой собственный отладчик ошибок:

    procedure SetPrintErrorFunc(PEF : PrintErrorFunc);
    {установка функции обработки ошибок принтера}

PEF является функцией, которая должна быть откомпилирована
по модели FAR (используя директиву компилятора {$F+}),
должна быть глобальной и иметь параметры, соответствующие
следующему типовому описанию (определенная для OPPRINT
запись PEF):

    {$F+}
    function MyPrintErrorFunc(P : PrinterPtr; ErrorCode : Word;
                            Recoverable : Boolean) : Boolean;

Когда OPPRINT обращается к этой подпрограмме, он передает в
нее адрес (P) секции Printer, где была вычислена ошибка, код
ошибки (ErrorCode), описание которого находится в приложении
D, логическую переменную (Recoverable), которая выражает
мнение OPPRINT о том, является ли ошибка исправимой. По
значениям ErrorCode и Recoverable отладчик ошибок выводит на
дисплей сообщение об ошибке и подсказку для пользователя,
как ему исправить ситуацию. После того, как пользователь
сообщит о том, что ошибка исправлена, отладчик ошибок должен
выдать 'истинно' для повторения операции, или 'ложно' для ее
прерывания.

Это простой пример отладчика ошибок:

    {$F+}
    function MyPrintErrorFunc(P : PrinterPtr; ErrorCode : Word;
                            Recoverable : Boolean) : Boolean;
    begin
      MyPrintErrorFunc := False;
      WriteLn('A printer error has occured, the error code is ',
                                            ErrorCode);
      if Recoverable then begin
        WriteLn('Please correct the problem');
        WriteLn('Press any key when ready, or <Esc> to abort');
        MyPrintErrorFunc := (ReadKey <> #27);
      end else
        WriteLn('Operation aborted');
    end;

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

Параметр PrinterPtr применяется в тех случаях, когда от
объектного модуля Printer требуется получить более
специфические данные. Предположим, например, что вы хотели
зарегистрировать первоначальный код ошибки при поддержке
объектного модуля BasePrinter; следующий пример показывает,
как можно это сделать:

    with P^ do
      if TypeOf(GetBasePrinter^) = TypeOf(BiosPrinter) then
        if GetBasePrinter^.PrnRawError and bpOutOfPaper <> 0 then
          {на принтере нет бумаги};

Если вместо BiosPrinter использовать BasePrinter, то бит
отсутствия бумаги на принтере контролируется по
первоначальному коду ошибки, переданному его алгоритмом
PrnRawError.


    Объектные модули Printer и потоки

Подобно большинству объектных модулей в ObjectProfesional,
объектные модули в OPPRINT могут архивизироваться в потоках.
С объектным модулем Printer потоки особенно эффективны,
поскольку образ Printer в потоке содержит все управляющие
последовательности для режимов печати и другие
микроскопические подробности об устройстве печати.  Описание
модуля OPDEVICE особенно подробно освещает этот вопрос.


    Константы

    pmFont              = $0001;
    pmChangesLPI        = $0004;
    pmChangesLPP        = $0008;
    pmChangesCPI        = $0010;
    pmChangesPoint      = $0020;
    pmChangesTopMargin  = $0040;
    pmChangesLandscape  = $0080;
    pmChangesStrike     = $0100;

Это расширения, применяемые к каждому режиму печати,
ассоциативно связанному с Printer. Одна или более из этих
величин передается в алгоритм AddPrintMode, который обладает
более развернутой информацией обо всех этих расширениях.
Константа pmFont служит сигналом о том, что в указанном
режиме используется непереключаемый шрифт (например, bold
Roman на лазерном принтере) и, следовательно он должен быть
обслужен в соответствии с инструкцией в разделе "Шрифты и
стек шрифтов", расположенном выше. Константы pmChangesLPI,
pmChangesCPI, pmChangesLPP, pmChangesPoint и
pmChangesTopMargin говорят о том, что новый режим печати
изменил указанные параметры страницы и размеры символа.
Когда включается такой режим, текущие значения LPI, LPP,
CPI, Point или TopMargin в объектном модуле Рrinter
сменяются записью соответствующих размерных величин из
объектного модуля PrintMode. Константа pmChangesLandscape
показывает, что новый режим изменил ориентацию печати, и она
же восстанавливает внутренний флаг объектного модуля Printer
при отключении этого режима. Константа pmChangesStrike
говорит о том, что новый режим изменил режим двойной печати
на принтере.

    pPlain = 0;

Аппаратный регистрационный номер, означающий, что в заданном
модуле Printer не зарегистрировано устройств печати особого
типа. Дополнительные регистрационные номера и подпрограммы
для особых принтеров реализованы в алгоритмах модуля
OPDEVICE.

    pMaxPrintModes = 255;

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

    pPosotionable      =  $0001;
    pBackSpaceAllowed  =  $0002;
    pNoFormFeed        =  $0004;
    pSoftFormFeed      =  $0008;
    pSupportHPRules    =  $0010;
    pUseDeciPoints     =  $0020;

Эти флаги расширений применяются к модулю Printer вцелом.
pPositionable показывает, что физический внутренний курсор
принтера поддается произвольному позиционированию на
странице; главным образом это используется для лазерного
принтера. pBackSpaceAllowed показывает, что принтер
воспринимает символ ^H как команду переместить печатающую
головку влево на одну позицию. Если включен pNoFormFeed,
Printer игнорирует вызов алгоритма FormFeed; это может
использоваться при генерации черновой версии распечатки
(draft). Если включен pSoftFormFeed, Printer прогоняет
страницу с помощью записи достаточного для заполнения
страницы количества CR/LF;  в противном случае производится
просто запись символа ^L (смена формата). pSupportHPRules
показывает, что прибор поддерживает команды лазерного
принтера Хьюлетт-Паккард по прорисовке линий и
прямоугольников. Это расширение получает большие
функциональные возможности в модуле OPFORM. Если установлена
pUseDeciPoints и зарегистрирован лазерный принтер (см.
OPDEVICE), то координаты переданные в алгоритмы Position и
PrintStrPos интерпретируются в единицах равных 1/720 дюйма,
а не в строках и столбцах.

    DoubleStrike        = 255;
    PrinterReset        = 254;
    Italic              = 253;
    Bold                = 252;
    Dim                 = 251;
    Compressed          = 250;
    Underline           = 249;
    Unidirectional      = 248;
    DoubleWidth         = 247;
    LetterQuality       = 246;
    SelectPrimaryFont   = 245;
    SelectSecondaryFont = 244;
    DefaultFont         = 243;

Стандартные коды идентификаторов, соответствующие объектным
модулям PrintMode. Рекомендуется передавать эти константы в
алгоритмы подобные TurnOnByID или TurnOffByID вместо того,
чтобы ссылаться на конкретные величины. Двойная печать
интерпретируется особым способом: когда этот режим
установлен, OPPRINT возвращается и надпечатывает символы
указанное число раз; смотрите предшествующее пояснение в
параграфе "Режим двойной печати". Сброс принтера также при
активизации интерпретируется особо: кроме передачи
управляющей последовательности, он также очищает стек
шрифтов, как ранее было описано в параграфе "Шрифты и стек
шрифтов". Не все принтеры способны поддерживать все эти
режимы, и OPDEVICE не устанавливает любой режим для всех
принтеров подряд. Вы можете назначить дополнительные коды в
диапазоне 1...242.

  DefaultStrikes : Byte = 2;

Установка по умолчанию числа ударов для двойной печати.

  DefaultLPI : Dimension = 6.0;

Установка по умолчанию числа строк на дюйм, используемая в
Printer.Init.

  DefaultCPI : Dimension = 10.0;

Установка по умолчанию числа символов на дюйм, используемая
в Printer.Init.

  DefaultPoint : Dimension = 12.0;

Установка по умолчанию высоты символа, используемая в
Printer.Init.

  DefaultLPP : Dimension = 66.0;

Установка по умолчанию числа строк на страницу, используемая
в Printer.Init.

  BackSpaceChar : Char = #8;

Символ, используемый для возврата печатающей головки на одну
символьную позицию.

  LineAdvanceStr : String[11] = ^M^J;

Строка, используемая для перехода в начало следующей строки.

  FormFeedStr : String[11] = ^L;

Строка, используемая для исполнения смены формата.


    Типы

    {$IFOPT N+}
    Dimension = Double;
    {$ELSE}
    Dimension = Real;
    {$ENDIF}

Типы данных, используемые для архивирования всех координат
печатающей головки, данных высоты и ширины при печати внутри
конкретного режима печати и объектного модуля Printer. Эти
типы данных также используются модулем OPDEVICE и OPFORM.
Если работает директива компилятора N+, то Dimension
однозначно соответствует типу Double, в противном случае
Dimension однозначно соответствует Real.

    RelativeType = (rtAbsolute, rtCurrent);

Два возможных способа позиционирования: rtAbsolute означает,
что указанные координаты Row и Col (строки и столбцы)
отсчитываются по отношению к левому верхнему углу страницы;
rtCurrent означает, что эти координаты задаются относительно
последней известной позиции печатающей головки. Обратите
внимание на то, что несмотря на установку флага
pPosotionable в модуле Printer, печатающая головка не может
переместиться вертикально вверх.

    PrintErrorFunc = function(P : PrinterPtr; ErrorCode : Word;
                         Recoverable : Boolean) : Boolean;

Тип функции, предназначенной для отладки принтерных сбоев.
Подробную информацию вы найдете в параграфе "Отладчик ошибок
принтера".

    PrinterPosFunc = function(Row, Col : Dimension;
        Relative : RelativeType; P : PrinterPtr) : Word;

Определение типа функции для элементарной подпрограммы
управления позиционированием печатающей головки. Подробную
информацию вы найдете в описании SetPositionPrim.

    PrintModePtr = ^PrintMode;
    PrintMode =
      object(SingleListNode)
        ...
      end;

Тип объектного модуля PrintMode. Этот объектный модуль не
описан в последующем изложении. Подробную информацию о нем
можно найти в OPPRINT.PAS.

    PrinterPtr = ^Printer;
    Printer =
      object(Root);
        ...
      end;

Тип объектного модуля Printer. Все необходимые зоны данных
являются доступными при помощи алгоритмов, описанных в
следующем разделе.

    Алгоритмы OPPRINT\Printer


    AddPrintMode
 ───────────────────────────────────────────────────────────
Объявление
  procedure AddPrintMode(Name, OnEscSeq, OffEscSeq : String;
                Width, Height : Dimension; ID : Byte;
                OptionFlags : Word);
Назначение
  Добавляет режим печати к скомпанованному списку
  режимов Printer.
Описание
  Name указывает название режима в кодах ASCII. Алгоритм
  GetPrintModeByName использует эту строку для поиска
  соответствующего режима печати. Name может быть пустой
  строкой в том случае, если вы не собираетесь вести поиск
  этим методом. Алгоритм GetPrintModeByID реализуется только в
  том случае, если Name опущено.
  OnEscSeq является управляющей последовательностью для
  установки указанного режима печати, то есть той строкой,
  которая передается на принтер по обращению к TurnOnByID.
  OffEscSeq является управляющей последовательностью,
  используемой для отключения указанного расширения, то есть
  строкой, которая передается на принтер при помощи
  TurnOffByID. AddPrintMode выделяет исключительно необходимый
  массив пространства для сохранения каждой строки. Либо
  строка может быть пустой.
  Параметры высоты и ширины имеют отношение к процедуре
  лишь в том случае, если в OptionFlags указано одно из
  расширений pmChangesXxx. Интерпретация этих расширений
  описана в следующей таблице.

    OptionFlag         ширина          высота
     ───────────────────────────────────────────────────
    pmChangesLPI       --              строк/дюйм
    pmChangesLPP       --              строк/страницу
    pmChangesCPI       символов/дюйм   --
    pmChangesPoint     --              высота символа
    pmChangesTopMargin --              верхнее поле
    pmChangesLandscape --              0 для нормальной печати
                                       1 для продольной печати
    pmChangesStrike    --              число ударов (1 и более)

  При активизации какого-либо режима PrintMode, во
  внутренние зоны данных объектного модуля Printer
  записываются соответствующие величины Width или Height. В
  большинстве случаев для избежания конфликтов при
  интерпретации этих величин, за одно обращение к процедуре
  AddPrintMode указывается только один бит OptionFlag.

  ID представляет собой величину, однозначно определяющую
  данный режим печати (за предварительным определением
  величины ID обратитесь к параграфу "Константы", который
  расположен выше).

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

  Если для сохранения нового режима печати не хватает
  места, процедура AddPrintMode генерирует ошибку (epNonFatal
  + ecOutMemory) и завершается. Если уже установлено 255
  режимов (pMaxPrintModes), AddPrintMode генерирует другую
  обратимую ошибку. Ошибки могут быть обработаны при помощи
  процедуры pGetLastError.

Пример

    var MyPrinter : Printer;
    ...
    MyPrinter.AddPrintMode('Italic', #27'4', #27,'5', 0, 0, Italic, 0);
    ...
    MyPrinter.TurnOnByID(Italic);
    MyPrinter.PrintStr('Hello World');

  В ваш принтер добавляется новый режим 'Italic'. Для
  данного принтера (совместимого с EpsonMX) шрифт Italic
  устанавливается при помощи <Esc>4 и отключается при помощи
  <Esc>5. Для этого расширения не требуются параметры ширины и
  высоты. Константа Italic впоследствии используется для
  обращения к этому PrintMode. Обратите внимание, что этот
  режим печати добавляется автоамтически процедурой
  EpsonMXRegister, описанной в OPDEVICE.

Справки
  TurnOffByID            TurnOnByID
  TurnOffByName          TurnOnByName


    AdvanceLine
 ───────────────────────────────────────────────────────────
Объявление
  procedure AdvanceLine(NumLines : Byte);
Назначение
  Перемещает печатающую головку на число строк, заданное в
  NumLines.
Описание
  Эта подпрограмма при помощи NumLines обращается к
  виртуальному алгоритму AdvanceSingleLine.

Пример

    MyPrinter.AdvanceLine(5);

  Перемещает печатающую головку вперед на 5 строк.

Справки
  FormFeed


    AdvanceSingleLine
 ───────────────────────────────────────────────────────────
Объявление
  procedure AdvanceSingleLine; virtual;
Назначение
  Перемещает печатающую головку в начало следующей строки.
Описание
  Эта процедура передает на принтер команду LineAdvanceStr
  (что по умолчанию соответствует CR/LF) и затем
  переписывает внутренние переменные, содержащие текущие
  координаты строки и столбца печатающей головки.
  Этот алгоритм является виртуальным, что означает
  возможность управления переводом печатающей головки на
  следующую строку со стороны процедур, производных от
  AdvanceSingleLine.

Пример

    MyPrinter.AdvanceSingleLine;

  Переводит печатающую головку на следующую строку.

Справки
  FormFeed


    DisposeAllModes
 ───────────────────────────────────────────────────────────
Объявление
  procedure DisposeAllModes;
Назначение
  Отстраняет все установленные режимы печати.
Описание
  Эта процедура обеспечивает быстрый способ отключения
  всех режимов печати, ассоциативно связанных с объектным
  модулем Printer, не отключая при этом сам объектный
  модуль.  После выполнения этой процедуры, любой новый
  режим печати может быть добавлен при помощи вызова
  AddPrintMode, либо по обращению к процедурам аппаратной
  регистрации более высокого уровня (описано в OPDEVICE).

Пример

    MyPrinter.DisposeAllModes;

  Отстраняются все режимы печати на указанном принтере.

Справки
  AddPrintMode


    Done
 ───────────────────────────────────────────────────────────
Объявление
  destructor Done; virtual;
Назначение
  Разрушает собранную конструкцию объектного модуля
  Printer.
Описание
  К этой процедуре необходимо обращаться после того, как вы
  закончили работу, связанную с использованием объектного
  модуля Printer. При этом отключается принтер и
  освобождается вся занятая память.
  Если при помощи объектного модуля Printer, был размещен
  ассоциативно связанный с ним модуль BasePrinter (возникший
  при обращении к конструктору Init), Done вызывает алгоритм
  разрушения BasePrinter, удаляя этот модуль из памяти. Если
  же BasePrinter не был размещен при помощи Init (тот самый
  случай, когда был вызван InitCustom), Done не закрывает и
  не устраняет модуль BasePrinter.

Пример

    var
      MyPrinter : Printer;
    ...
      MyPrinter.Done;

  Отключает статический объектный модуль Printer для
  указанного принтера.

    var
      MyPrinterPtr : PrinterPtr;
    ...
      Dispose(MyPrinterPtr, Done);

  Отключает и устраняет оперативно востребованный Printer.

Справки
  Init                        LinkToPrinter
  InitCustom                  Load


    EnableDefaultFont
 ───────────────────────────────────────────────────────────
Объявление
  procedure EnableDefaultFont;
Назначение
  Активизирует шрифт, заданный по умолчанию.
Описание
  Идентификатор шрифта, определенного по умолчанию,
  назначается алгоритмом SetDefaultFont и передается при
  помощи  GetDefaultFont. Процедура EnableDefaultFont
  выполняет операцию, эквивалентную TurnOnByID
  (GetDefaultFont). Если тип шрифта по умолчанию не
  установлен, процедура GetDefaultFont выдает нулевой
  результат и EnableDefaultFont не выполняется.
  Иногда OPPRINT вызывает установку шрифта по умолчанию
  автоматически в том случае, когда при обращении к стеку
  шрифтов выясняется, что он пуст (смотрите параграф "Шрифты и
  стек шрифтов", расположденный выше).
  Этот алгоритм чаще всего используется подпрограммой
  аппаратной регистрации в OPDEVICE.

Пример

    MyPrinter.EnableDefaultFont;

  Активизируется шрифт по умолчанию для указанного
  принтера.

Справки
  GetDefaultFont              TurnOnByID
  SetDefaultFont


    FormFeed
 ───────────────────────────────────────────────────────────
Объявление
  procedure FormFeed;
Назначение
  Реализует переход на следующую страницу.
Описание
  Если установлено расширение pNoFormFeed, то процедура
  FormFeed не выполняет никакого действия. Если установлено
  pSoftFormFeed, то процедура FormFeed передает команды CR/LF,
  заполняя ими полностью текущую страницу (исходя при этом из
  заданного числа строк на страницу и текущей позиции
  печатающей головки). Если ни то, ни другое расширение не
  установлено, то процедура FormFeed передает FormFeedStr, что
  по умолчанию равнозначно <Ctrl-L>.
  Во всех этих случаях FormFeed обращается к ResetPage для
  установки в первоначальное состояние внутренней записи
  объектного модуля Printer о позиции печатающей головки.

Пример

    MyPrinter.FormFeed;

  Печатающая головка на указанном принтере переводится в
  начало следующей страницы.

Справки
  AdvanceLine                 ResetPage


    GetBasePrinter
 ───────────────────────────────────────────────────────────
Объявление
  function GetBasePrinter : BasePrinterPtr;
Назначение
  Передает указатель BasePrinterPtr для соответствующего
  модуля Printer.
Описание
  Каждый объектный модуль Printer содержит указатель на
  ассоциативный объектный модуль BasePrinter. Функция
  GetBasePrinter передает указатель на этот модуль и
  позволяет, таким образом, осуществить доступ к его
  алгоритмам или данным. Дальнейшая информация представлена в
  разделе OPPRNLOW.

Пример

    MyPrinter.GetBasePrinter^.PrnReset;

  Происходит вызов алгоритма PrnReset для указанного
  принтера из ассоциативного модуля BasePrinter.

Справки
  LinkToPrinter


    GetCurrentFont
 ───────────────────────────────────────────────────────────
Объявление
  function GetCurrentFont : Byte;
Назначение
  Передает идентификатор режима печати текущего шрифта,
  или ноль, если в текущий момент никакой шрифт не
  используется.
Описание
  Когда бы не был установлен непереключаемый шрифт (по
  обращению к TurnOnByID или TurnInByName), его идентификатор
  заряжается в стек шрифтов. Функция GetCurrentFont перемещает
  этот идентификатор на верхнее положение в стеке шрифтов.

Пример

    FontInUse := MyPrinter.GetCurrentFont;

  Происходит запоминание текущего шрифта.

Справки
  GetDefaultFont


    GetDefaultFont
 ───────────────────────────────────────────────────────────
Объявление
  function GetDefaultFont : Byte;
Назначение
  Передает идентификатор режима печати для шрифта
  по умолчанию.
Описание
  Шрифт, идентификатор которого передан при помощи
  GetDefaultFont, будет активизирован при обращении к
  EnableDefaultFont. Шрифт по умолчанию будет установлен при
  обращении к SetDefaultFont.

Пример

    if MyPrinter.GetDefaultFont <> MyPrinter.GetCurrentFont then
      MyPrinter.EnableDefaultFont;

  Шрифт по умолчанию активизируется, если он отличается от
  шрифта, используемого в данный момент.

Справки
  GetCurrentFont                     SetDefaultFont


    GetLPP
 ───────────────────────────────────────────────────────────
Объявление
  function GetLPP : Dimension;
Назначение
  Передает текущее значение параметра LPP (число строк на
  страницу).
Описание
  Этот алгоритм передает текущее число строк на страницу
  из внутренней зоны данных объектного модуля Printer. Он не
  требует непосредственной информации от принтера. OPPRINT
  использует значение, полученное от GetLPP, для вычисление
  количества CR/LF, необходимых для перехода на следующую
  страницу при активизации расширения pSoftFormFeed.
  Число строк на странице может быть установлено при
  помощи обращения к SetLPP. Включение режима печати, в
  котором активизировано расширение pmChangesLPP, также
  установит новое значение для внутренней переменной числа
  строк на странице. Дальнейшую информацию смотрите в
  AddPrintMode.

Пример

    WriteLn('The current number of lines per page is ',
                            MyPrinter.GetLPP:3:0);

  Выводит на экран количество строк на страницу, как
  параметр, используемый указанным принтером в данный момент.
  Если значение параметра в GetLPP задано типом Dimension (или
  Real, или Double, в зависимости от директивы $N), то это
  значение должно быть переформатировано так, чтобы не было
  числовых символов справа от десятичной точки.

Справки
  AddPrintMode                        SetLPP


    GetRegisterType
 ───────────────────────────────────────────────────────────
Объявление
  function GetRegisteredType : Word;
Назначение
  Передает тип аппаратных средств, зарегистрированных для
  данного модуля Printer.
Описание
  Объектный модуль Printer обслуживает зону данных,
  которая используется подпрограммой из модуля OPDEVICE для
  запоминания кода разновидности аппаратных средств. Сам же
  объектный модуль Printer не придает этой зоне особого
  значения.

Пример

    HardwareType := MyPrinter.GetRegisteredType;
    Write('Printer type is: ');
    case HardwareType of
      pLaserJet     : WriteLn('LaserJet');
      pDeskJet      : WriteLn('DeskJet');
      pEpsonMX      : WriteLn('Epson MX');
        ...
    end;

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

Справки
  SetRegisteredType


    GetXRatio
 ───────────────────────────────────────────────────────────
Объявление
  procedure GetXRatio : Dimension;
Назначение
  Передает масштабный коэффициент шкалы X.
Описание
  О масштабировании принтера можно прочитать во
  вступительном разделе этой главы. Значение масштаба оси X
  по умолчанию равно 1.0.

Пример

    WriteLn('MyPrinter''s X ratio is ', MyPrinter.GetXRatio);

  Выводит на экран текущее значение масштаба по оси X для
  указанного принтера.


    GetYRatio
 ───────────────────────────────────────────────────────────
Объявление
  procedure GetYRatio : Dimension;
Назначение
  Передает масштабный коэффициент шкалы Y.
Описание
  О масштабировании принтера можно прочитать во
  вступительном разделе этой главы. Значение масштаба оси Y
  по умолчанию равно 1.0.

Пример

    WriteLn('MyPrinter''s Y ratio is ', MyPrinter.GetYRatio);

  Выводит на экран текущее значение масштаба по оси Y для
  указанного принтера.


    Init
 ───────────────────────────────────────────────────────────
Объявление
  constructor Init(Name : String: DeviceType : PrnType:
                OptionFlag : Word);
Назначение
  Инициализирует объектный модуль Printer.
Описание
  Это простейшая программа сборки, работающая при
  поддержке объектного модуля Printer. Она размещает и
  присоединяет объектный модуль BasePrinter на основе данных
  об имени (Name) и типе устройства (DeviceType).
  Если тип устройства задан как LPT1, LPT2 или LPT3,
  алгоритм Init игнорирует параметр Name и размещает объектный
  модуль BasePrinter по указанному порту. Если тип устройства
  задан как Prn или DiskFile, алгоритм Init использует
  параметр Name для размещения DosPrinter по указанному
  названию. В последнем случае в качестве Name могут выступать
  'PRN', 'LPT1',  названия других устройств и 'DiskFile'.
  Параметр OptionFlags указывает на расширения Printer. За
  информацией по этому вопросу обратитесь к параграфу
  "Константы".
  Init инициализирует тип аппаратных средств в значении
  "простейший" (pPlain). Он инициализирует масштабные
  коэффициенты для осей X и Y в значении 1.0. Он обращается
  к следующим константам глобального типа (описано ранее)
  для инициализации остальных зон данных: DefaultStrikes
  (число ударов по умолчанию для двойной печати),
  DefaultLPI, DefaultCPI, DefaultPoint и DefaultLPP. Он
  размещает стек шрифтов заданного размера (FontStackSize) и
  BitSet, размер которого определяется в pMaxPrintModes.
  Если этот алгоритм потерпит неудачу, подробный код
  состояния будет находиться в InitStatus.

Пример

    if not MyPrinter.Init('', LPT1, pBackSpaceAllowed) then
      begin WriteLn('Failed to init printer. Status =
      ',InitStatus); Halt; end;

  Инициализирует указанный принтер как LPT1 с помощью
  BiosPrinter. Принтер поддерживает команду Backspace.

Справки
  InitCustom                  Load


    InitCustom
 ───────────────────────────────────────────────────────────
Объявление
  constructor InitCustom(Device : BasePrinterPtr;
        LPI, CPI, Point, LPP : Dimension; OptionFlags : Word);
Назначение
  Инициализирует принтер с полным управлением.
Описание
  Device это адрес объектного модуля BiosPrinter или
  DosPrinter, который инициализируется перед обращением к
  Printer.InitCustom. Этим способом вы можете получить любые
  желаемые характеристики для нижестоящего физического уровня.
  LPI, CPI, Point и LPP указывают значения по умолчанию для
  строк на дюйм, символов на дюйм, высоты символа и строк на
  страницу. Из них лишь LPP непосредственно используется
  объектным модулем Printer при выполнении FormFeed и при
  активизации расширения pSoftFormFeed. Все эти величины
  сохраняются объектным модулем, и к ним могут обращаться
  алгоритмы печати более высокого уровня. OptionFlags
  указывает на расширения модуля Printer. За дальнейшей
  информацией вы можете обратиться к параграфу "Константы".
  Алгоритм InitCustom при инициализации устанавливает тип
  аппаратных средств в значение pPlain. Он устанавливает
  значения масштабных коэффициентов по осям X и Y в значение
  1.0. Он обращается к константе глобального типа (смотрите
  выше) DefaultStrikes. Он размещает стек шрифтов заданного
  размера (FontStackSize) и BitSet, размер которого
  определяется в pMaxPrintModes.
  Если процедура не состоится, подробный код состояния
  будет находиться в InitStatus.

Пример

    var MyPrinter : Printer;
        BasePrinter : BiosPrinter;
    ...
    BasePrinter.InitCustom(@BaseDevice,
                    DefaultLPI, DefaultCPI, DefaultPoint,
                    DefaultLPP, 0) then begin
      WriteLn('Failed to init printer. Status = ',InitStatus); Halt;
    end;

  В качестве примера приведен MyPrinter. BaseDevice
  является стационарной реализацией BiosPrinter,
  инициализированной для печати на устройство LPT1. При
  помощи оператора '@' его адрес передан в
  MyPrinter.InitCustom.  Никаких расширений для принтера не
  указано.

Справки
  Init                        Load


    InLandscape
 ───────────────────────────────────────────────────────────
Объявление
  function InLandscape : Boolean;
Назначение
  Передает сигнал 'истинно', если установлен режим
  продольной печати.
Описание
  Внутренний флаг продольной печати устанавливается в тот
  момент, когда включается режим печати с битом
  pmChangesLandscape и высотой отличной от нуля.

Пример

    if MyPrinter.InLandscape then WriteLn('Currently in
                                landscape mode')
    else WriteLn('Currently in portrait mode');

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

Справки
  AddPrintMode


    LinkToModes
 ───────────────────────────────────────────────────────────
Объявление
  procedure LinkToModes(var Modes : SingleList);
Назначение
  Добавляет объектный модуль в указанный список.
Описание
  Эта процедура изменяет скомпанованный список режимов
  печати, сочетая его с указанным образцом. Это используется
  в тех ситуациях, когда общий список переменных режимов
  печати необходимо модифицировать. Например, лазерные
  принтеры позволяют загружать новые наборы шрифтов с
  кассеты шрифтов или при помощи программной загрузки с
  компьютера.
  Анулировать существующий список режимов (при
  необходимости) перед обращением к LinkToModes можно лишь при
  посредстве соответствующей прикладной программы.

Пример

    var
      MyPrinter : Printer;
      LaserModes : SingleList;
    ...
      MyPrinter.LinkToModes(LaserModes);

  Сочетает список режимов печати LaserModes с MyPrinter. В
  этом примере предполагается, что MyPrinter и LaserModes уже
  подготовлены и инициализированы до обращения к LinkToModes.

Справки
  DisposeAllModes


    LinkToPrinter
 ───────────────────────────────────────────────────────────
Объявление
  procedure LinkToPrinter(Device : BasePrinterPtr);
Назначение
  Компанует Printer с новым объектным модулем BasePrinter.
Описание
  Device является адресом модуля BiosPrinter или
  DosPrinter, который инициализирован до обращения к
  LinkToPrinter. Если в объектном модуле Printer уже
  содержится текущий модуль BasePrinter, то он будет
  анулирован и затем будет назначен новый. Printer никогда не
  анулирует тот BasePrinter, который был ему "сосватан" при
  помощи LinkToPrinter или InitCustom.

Пример

    var
      MyPrinter : Printer;
      MyBasePrinter : BiosPrinter;
    ...
      MyPrinter.LinkToPrinter(@MyBasePrinter);

  Сочетаются MyBasePrinter и MyPrinter. В этом примере
  предполагается, что MyPrinter и MyBasePrinter подготовлены и
  инициализированы до обращения к алгоритму LinkToPrinter.


    Load
 ───────────────────────────────────────────────────────────
Объявление
  constructor Load(var S : IdStream);
Назначение
  Загружает объектный модуль Printer из потока.
Описание
  S является соответствующим образом инициализированным
  объектным модулем потока (например, файл потока доступный по
  чтению). Обратите внимание, что S может быть произвольной
  структурой типа IdStream, включающей DosStream, BufStream и
  MemStream.
  Алгоритм Load осуществляет чтение последовательности
  байтов, следующей из потока S. Эти байты должны быть
  записаны предварительно при обращении к алгоритму
  Printer.Store. Алгоритм Load считывает фиксированные данные
  объектного модуля Printer, размещает и загружает
  динамическую версию BasePrinter, размещает и загружает все
  режимы печати, которые были активны в момент архивизации,
  размещает стек шрифтов и BitSet активного режима, считывает
  коды процедур для отладчика ошибок принтера и другие
  встроенные программные средства, активизирует шрифт
  по умолчанию, если в момент архивизации не было установлено
  другого шрифта.
  Процедурой регистрации потока для Printer является
  PrinterStream. Любые пользовательские программы, связанные с
  Printer (отладчик ошибок, примитивы позиционирования и
  т.п.), должны быть зарегистрированы до вызова Load.

Пример

    uses OpRoot, OpPrnLow;
    var MyPrinter : Printer;
        PS : BufIdStream;
    begin
      PS.Init('MYPRN.STM', SOpen, 1024);
      PS.RegisterHier(PrinterStream);
      PS.Get(MyPrinter);
      if PS.GetStatus <> 0 then begin
        WriteLn('Failed to load printer'); Halt;
      end;
      PS.Done;
    end.

  Указанный принтер запускается с помощью считывания его
  образа из потока PS.

Справки
  Store


    pGetLastError
 ───────────────────────────────────────────────────────────
Объявление
  function pGetLastError : Word;
Назначение
  Выполняет передачу кода последней ошибки и сброс в ноль.
Описание
  Эта подпрограмма должна вызываться по исполнении каждой
  принтерной операции в том случае, если не установлено
  никакого отладчика ошибок принтера. В приложении D этого
  руководства описано то, как интерпретируются коды ошибок.
  Принцип установки отладчика ошибок принтера описан выше в
  параграфе "Отладчик ошибок принтера".

Пример

    MyPrinter.PrintStr('Send this string to the printer');
    ErrorCode := MyPrinter.pGetLastError;
    if ErrorCode <> 0 then begin
      WriteLn(;Printer error number ', ErrorCode); Halt;
    end;


    pOptionsAreOn
 ───────────────────────────────────────────────────────────
Объявление
  function pOptionsAreOn(OptionFlags : Word) : Boolean;
Назначение
  Дает в результате логический уровень 'истинно', если
  все указанные флаги установлены.
Описание
  Этот алгоритм обеспечивает удобный способ проверки,
  активизированы или нет указанные расширения принтера.

Пример

    if MyPrinter.pOptionsAreOn(pSupportHPRules) then begin
      WriteLn('Using HP LaserJet compatible line drawing,
                                    boxes and shading');
    ...
    end;

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


    pOptionsOff
 ───────────────────────────────────────────────────────────
Объявление
  procedure pOptionsOff(OptionFlags : Word);
Назначение
  Отключает расширения принтера.
Описание
  При помощи обращения к этой процедуре может быть
  отключен один или более флагов расширений. Не будет никакого
  вреда, если вы отключите флаг расширения, который уже был
  отключен ранее.

Пример

    MyPrinter.pOptionsOff(pPositionable+pSupportsHPRules+pUseDeciPoints);

  В этом примере отключается возможность позиционирования,
  совместимость по прорисовке линий и прямоугольников HP
  LaserJet, и флаг расширения для перехода на измерения в
  единицах равных 1/720 дюйма для указанного принтера.


    pOptionsOn
 ───────────────────────────────────────────────────────────
Объявление
  procedure pOptionsOn(OptionFlags : Word);
Назначение
  Включает расширения Printer.
Описание
  При обращении к этой процедуре могут быть активизированы
  один или более флагов расширений. Активизация флага
  расширения, который ранее уже был активизирован, не
  причинит вреда.

Пример

    MyPrinter.pOptionsOn(pPositionable+pSupportsHPRules+pUseDeciPoints);

  В этом примере активизируются: возможность
  позиционирования, совместимость по прорисовке линий и
  прямоугольников HP LaserJet, и флаг расширения для
  перехода на измерения в единицах равных 1/720 дюйма для
  указанного принтера.


    Position
 ───────────────────────────────────────────────────────────
Объявление
  procedure Position(Row, Col : Dimension; Relative : RelativeType); virtual;
Назначение
  Перемещает печатающую головку в желаемую позицию на
  странице.
Описание
  Если в модуле Printer установлен флаг расширения
  pPositionable, то для выполнения операций позиционирования
  вызывается элементарная функция позиционирования.
  Элементарная функция обычно вычисляет управляющую
  последовательность и передает ее на принтер. В противном
  случае процедура Position записывает необходимую комбинацию
  CR/LF, Backspace и пробелов для перемещения печатающей
  головки на указанную позицию.
  Relative показывает, требуется ли относительное
  (rtCurrent) или абсолютное (rtAbsolute) позиционирование.
  rtCurrent показывает значения Row и Col, числа строк и
  позиций в строке, на которые необходимо сместить печатающую
  головку относительно ее текущего положения. Значения Row и
  Col умножаются на текущие значения масштабных коэффициентов
  по осям X и Y соответственно перед тем, как выполнить
  позиционирование.

Пример

    uses OpPrnLow, OpPrint;
    var MyPrinter : Printer;
    begin
      if not MyPrinter.Init('', LPT1, pBackSpaceAllowed) then begin
        WriteLn('Failed to init printer. Status = ', InitStatus); Halt;
      end;
      MyPrinter.Position(10, 1, rtAbsolute);
      MyPrinter.PrntStr('Hello World');
      MyPrinter.FormFeed;
      MyPrinter.Done;
    end.

  Запускается объектный модуль Printer, печатающая головка
  устанавливается в первую позицию десятой строки, печатается
  строка 'Hello World', осуществляется переход в начало
  следующей страницы и вызывается процедура разборки для
  MyPrinter.

Справки
  PrintStrPos                 SetPositionPrim


    PrinterReady
 ───────────────────────────────────────────────────────────
Объявление
  function PrinterReady : Boolean;
Назначение
  Передает сигнал 'истинно', если принтер находится в
  состоянии готовности.
Описание
  Эта функция просто обращается к алгоритму PrnStatus из
  нижестоящего объектного модуля BasePrinter и передает сигнал
  'истинно' в том случае, если алгоритм PrnStatus принимает
  нулевое значение. Ее способность определять момент
  готовности принтера полностью определяется качеством
  технологии BasePrinter.

Пример

    while not MyPrinter.PrinterReady do
      WriteLn('The Printer is not ready. Please correct the error condition.');
      WriteLn(Press any key (<Esc> to abirt).');
      if ReadKey = #27 then Halt;
    end;

  Программа находится в замкнутом цикле до тех пор, пока
  либо не придет сообщение о готовности от BasePrinter
  ассоциативно связанного MyPrinter, либо пока пользователь не
  нажмет <Esc>, прервав программу.

Справки
  BasePrinter.PrnStatus


    PrinStr
 ───────────────────────────────────────────────────────────
Объявление
  procedure PrinStr(S : String); virtual;
Назначение
  Печатает строку в текущей позиции печатающей головки.
Описание
  Если активизирован режим двойной печати, PrinStr
  печатает каждый символ строки S, затем возвращается и
  печатает символ снова, повторяя этот цикл столько раз,
  сколько требует StrikeCount. Во всех других случаях
  процедура PrinStr печатает строку только один раз.
  Ошибки, обнаруженные в процессе записи символов,
  считаются корректируемыми. По событию ошибки происходит
  обращение к алгоритму PrintErrorFunc. Если PrintErrorFunc
  примет значение 'истинно', это будет означать, что ошибка
  исправлена и PrinStr автоматически дошлет те символы,
  которые еще не напечатаны. Если же PrintErrorFunc примет
  значение 'ложно', то оставшаяся часть выходных данных
  передана не будет.

Пример

    MyPrinter.Position(10, 1, rtAbsolute);
    MyPrinter.PrintStr('Hello World');

  Печатает строку 'Hello world' в первой позиции десятой
  строки.

Справки
  Position


    PrintStrPos
 ───────────────────────────────────────────────────────────
Объявление
  procedure PrintStrPos(S : String; Row, Col : Dimension;
                        Relative : RelativeType); virtual;
Назначение
  Печатает строку в указанной позиции.
Описание
  Эта процедура генерирует соответствующие обращения к
  Position и PrintStr.

Пример

    MyPrinter.PrintStr('Hello World', 10, 1, rtAbsolute);

  Печатает строку 'Hello world' в первой позиции десятой
  строки.

Справки
  Position


    ResetPage
 ───────────────────────────────────────────────────────────
Объявление
  procedure ResetPage;
Назначение
  Сбрасывает информацию в объектном модуле Printer о позиции
  на странице.
Описание
  Алгоритм FormFeed вызывает процедуру ResetPage
  автоматически. Вызов этой процедуры вам может понадобиться
  в том случае, когда вы допечатали очередную страницу и
  достигли таким образом начала следующей страницы, не
  прибегая к обращению к FormFeed.

Пример

    var
      MyPrinter : Printer;
      I         ; Word;
    ...
    for I := 1 to Trunc(MyPrinter.GetLPP) do
        MyPrinter.PrintStrPos('Hello World', I, I, rtAbsolute);
      MyPrinter.ResetPage;

  Вызывается GetLPP для передачи числа строк на странице.
  Строка 'Hello World' печатается на каждой строке. ResetPage
  информирует указанный принтер о том, что печатающая головка
  теперь находится в левой верхнем углу новой страницы.

Справки
  FormFeed


    SetDefaultFont
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetDefaultFont(Attr : Byte);
Назначение
  Устанавливает для данного принтера тип шрифта
  по умолчанию.
Описание
  Эта процедура реализует алгоритм установки
  идентификатора режима печати, который будет использоваться
  как шрифт по умолчанию. Шрифт по умолчанию активизируется
  при вызове алгоритма EnableDefaultFont. Обычно
  EnableDefaultFont вызывается внутри процедуры аппаратной
  регистрации (подобной той, которая реализуется в OPDEVICE)
  для занесения первоначального кода шрифта в стек шрифтов.
  Это означает, что шрифт по умолчанию будет восстановлен при
  том условии, что все установленные позднее шрифты будут
  отключены.

Пример

    MyPrinter.SetDefaultFont(Italic);

  Устанавливает на указанный принтер по умолчанию
  идентификатор режима печати Italic.

Справки
  EnableDefaultFont           GetDefaultFont


    SetLPP
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetLPP(LPP :Dimension);
Назначение
  Устанавливает число строк на странице.
Описание
  Внутрення установка числа строк на странице имеет
  значение только в том случае, если она используется с
  объектным модулем Printer, у которого установлен флаг
  расширения pSoftFormFeed. В этом случае параметр LPP
  используется для определения числа переходов на новую
  строку, необходимых для перехода в начало новой страницы.
  К этой процедуре обращаются по внутреннему вызову в любой
  момент, когда включается режим печати с установленным
  флагом pmChangesLPP.

Пример

    MyPrinter.SetLPP(60);

  Устанавливает для указанного принтера число строк на
  страницу.

Справки
  GetLPP


    SetPositionPrim
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetPositionPrim(PosFunc : PrinterPosFunc);
Назначение
  Устанавливает функцию позиционирования печатающей
  головки.
Описание
  В некоторых случаях, подобных лазерному принтеру,
  техника, использумая при позиционировании печатающей
  головки, сложнее (и вместе с тем гибче), чем простое
  перемещение на определенное количество строк и символьных
  позиций. Для лазерного принтера управляющая
  последовательность должна быть сконструирована с учетом
  способа позиционирования (абсолютное или относительное) и
  системы вычисления координат (строка/столбец или 1/720
  дюйма). Функция, обозначенная PosFunc, решает эту задачу.
  Алгоритм печатающего устройства, с помощью которого
  устанавливается печатающая головка, обращается к PosFunc
  каждый раз, когда активизируется расширение pPositionable.
  Эта подпрограмма в первую очередь используется
  процедурой аппаратной регистрации в OPDEVICE.

Пример

    MyPrinter.SetPositionPrim(LaserPosition);

  Устанавливает элементарную процедуру позиционирования на
  указанном принтере в режим LaserPosition.


    SetPrintErrorFunc
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetPrintErrorFunc(REF : PrintErrorFunc);
Назначение
  Устанавливает функцию диагностики принтера.
Описание
  Функция диагностики принтера, регистрируемая этой
  процедурой, вызывается каждый раз при обнаружении ошибки.
  Дальнейшую информацию вы можете найти в параграфе "Отладчик
  ошибок принтера".


    SetXRatio
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetXRatio(Ratio : Dimension);
Назначение
  Устанавливает масштабный коэффициент для оси X.
Описание
  Printer умножает значение координаты Col, передаваемой в
  Position и PrintStrPos, на величину масштабного коэффициента
  оси X перед тем, как приступить к выполнению этих операций.
  Значение коэффициента по умолчанию равно 1.0. Дополнительную
  информацию вы можете найти в параграфе "Объектный модуль
  Printer".

Пример

    MyPrinter.SetXRatio(2.0);

  Устанавливает масштабный коэффициент оси X для
  указанного принтера в значение 2.0.

Справки
  SetYRatio


    SetYRatio
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetYRatio(Ratio : Dimension);
Назначение
  Устанавливает масштабный коэффициент для оси Y.
Описание
  Printer умножает значение координаты Row, передаваемой в
  Position и PrintStrPos, на величину масштабного коэффициента
  оси Y перед тем, как приступить к выполнению этих операций.
  Значение коэффициента по умолчанию равно 1.0. Дополнительную
  информацию вы можете найти в параграфе "Объектный модуль
  Printer".

Пример

    MyPrinter.SetYRatio(2.0);

  Устанавливает масштабный коэффициент оси Y для
  указанного принтера в значение 2.0.

Справки
  SetXRatio


    Store
 ───────────────────────────────────────────────────────────
Объявление
  procedure Store(var S : IdStream);
Назначение
  Запоминает образ Printer в потоке.
Описание
  S является объектом потока, соответствующим образом
  инициализированным (файл потока обычно открыт по записи).
  Необходимо заметить, что S может быть любой производной типа
  IdStream, что включает DosIdStream, BufIdStream и
  MemIdStream (хотя вы, разумеется, никогда не станете
  обращаться к MemIdStream).
  Процедура Store записывает образ Printer в поток S. Она
  записывает фиксированные данные объектного модуля Printer,
  обращается к BasePrinter.Store для записи его образа,
  запоминает все режимы печати, которые были аккумулированы, и
  записывает код процедуры отладчика ошибок принтера и
  других встроенных подпрограмм.
  Процедурой регистрации потока для модуля Printer
  является PrinterStream. Любые пользовательские функции,
  имеющие ассоциативную связь с Printer (отладчик ошибок,
  примитив для позиционирования и т.п.) должны быть
  зарегистрированы до вызова Load.

Пример

    uses OpRoot, OpPrnLow;
    var MyPrinter : Printer;
        PS : BufIdStream;
    begin
      if not MyPrinter.Init('PRN', Prn,0) then begin
        WriteLn('Failed to init Printer. Status = ', InitStatus); Halt;
      end;
      MyPrinter.AddPrintMode('Italic', #27'4', #27'5', 0, 0, Italic, 0);
      MyPrinter.SetDefaultFont(Italic);
      PS.Init('MYPRN.STM', SCreate, 1024);
      PS.RegisterHier(PrinterStream);
      PS.Put(MyPrinter);
      if PS.GetStatus <> 0 then begin
        WriteLn('Failed to store Printer'); Halt;
      end;
      PS.Done;
      MyPrinter.Done;
    end.

  Записывает образ реализации указанного объектного модуля
  Printer в поток.

Справки
  Load


    TurnOffAllActiveModes
 ───────────────────────────────────────────────────────────
Объявление
  procedure TurnOffAllActiveModes;
Назначение
  Выключает все активные в данный момент режимы печати.
Описание
  Объектный модуль Printer занимается обслуживанием
  параметра BitSet активизированного в данный момент режима
  печати. Процедура TurnOffAllActiveModes имеет дело с
  конкретным BitSet для выключения всех установленных в нем
  бит.
  Подобная возможность выключать все активные режимы
  печати будет полезной в ситуации, когда завершенный список
  возможных режимов печати потребуется изменить. Очевидным
  является тот факт, что запускать процедуру
  TurnOffAllActiveModes нужно до обращения к LinkToModes,
  если какой-либо из режимов печати является активным (был
  активизирован и до сих пор не отключен).

Пример

    var
      MyPrinter : Printer;
      LaserModes : SingleList;
    ...
    MyPrinter.TurnOfAllActiveModes;
    MyPrinter.LinkToModes(LaserModes);

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

Справки
  LinkToModes


    TurnOffByID
 ───────────────────────────────────────────────────────────
Объявление
  procedure TurnOffByID(ID : Byte);
Назначение
  Выключает режим печати по его идентификатору.
Описание
  Эта процедура просматривает скомпанованный список
  режимов печати для того, чтобы найти тот, который обозначен
  указанным кодом идентификатора. Если поиск заканчивается
  без результата, TurnOffByID ничего не предпринимает. В
  противоположном случае, процедура обращается к самому
  PrintMode, чтобы передать на принтер соответствующую
  управляющую последовательность для отключения, после чего
  извлекает указанный идентификатор из списка активных режимов
  BitSet. Конкретно, для идентификатора DoubleStrike
  TurnOffByID завершается обращением к DisableDoubleStrike.
  Если в наборе расширений указанного режима печати
  установлен бит pmFont, TurnOffByID извлекает также этот
  шрифт из стека шрифтов и восстанавливает шрифт в верхней
  части стека. Параграф "Шрифты и стек шрифтов" расположен
  выше и содержит необходимую информацию.

Пример

    MyPrinter.AddPrintMode('Italic', #27'4', #27'5', 0, 0, Italic, 0);
    MyPrinter.TurnOnByID(Italic);
    ...
    MyPrinter.TurnOffByID(Italic);

  Добавляет режим печати с идентификатором Italic,
  активизирует его и затем отключает.

Справки
  TurnOnByID


    TurnOffByName
 ───────────────────────────────────────────────────────────
Объявление
  procedure TurnOffByName(Name : String);
Назначение
  Отключает режим печати по его имени.
Описание
  Работает так же, как TurnOffByID, с той лишь разницей,
  что просматривает скомпанованный список соответствующих
  записей имен, в отличие от просмотра кодов идентификаторов.
  При поиске игнорируются различия между прописными и
  строчными знаками. Поиск по указанному имени выполняется
  медленнее, чем поиск по указанному идентификатору.

Пример

    const
      ItalicStr = 'Italic';
    ...
    MyPrinter.AddPrintMode(ItalicStr, #27'4', #27'5', 0, 0, Italic, 0);
    MyPrinter.TurnOnByName(ItalicStr);
    ...
    MyPrinter.TurnOffByName(ItalicStr);

  Добавляет режим печати с именем ItalicStr, активизирует
  его, а потом отключает.



    TurnOnByID
 ───────────────────────────────────────────────────────────
Объявление
  procedure TurnOnByID(ID : Byte);
Назначение
  Включает режим печати по его идентификатору.
Описание
  Эта процедура просматривает скомпанованный список
  режимов печати для того, чтобы найти тот, который имеет
  указанный код идентификатора. Если поиск заканчивается без
  результата, TurnOnByID ничего не предпринимает. В
  противоположном случае, процедура обращается к самому
  PrintMode, чтобы передать на принтер соответствующую
  управляющую последовательность для включения, после чего
  добавляет указанный идентификатор в список активных режимов
  BitSet. Конкретно, для идентификатора DoubleStrike
  процедура TurnOnByID завершается обращением к
  EnableDoubleStrike.
  Если в наборе расширений указанного режима печати
  установлен бит pmFont, TurnOnByID добавляет также этот
  шрифт в стек шрифтов. Параграф "Шрифты и стек шрифтов"
  расположен выше и содержит необходимую информацию.
  При установке следующих флагов расширений режима печати
  предпринимаются специальные действия:

    pmChangesLPI, pmChangesLPP, pmChangesCPI, pmChangesPoint,
    pmChangesTopMargin, pmChangesLandscape, или pmChangesStrike.

  За дальнейшей информацией возвратитесь к AddPrintMode.

Пример

    MyPrinter.TurnOnByID(Italic);
        ...
    MyPrinter.TurnOffByID(Italic);

  Включает шрифт Italic по коду режима печати и впоследствии
  отключает его тем же способом.


    TurnOnByName
 ───────────────────────────────────────────────────────────
Объявление
  procedure TurnOnByName(Name : String);
Назначение
  Включает режим печати по его имени.
Описание
  Работает так же, как TurnOnByID, с той лишь разницей,
  что просматривает скомпанованный список соответствующих
  записей имен, в отличие от просмотра кодов идентификаторов.
  При поиске игнорируются различия между прописными и
  строчными знаками. Поиск по указанному имени выполняется
  медленнее, чем поиск по указанному идентификатору.

Пример

  Смотрите пример в описании TurnOffByName.



    D. OPDEVICE   Поддержка принтера по настройке
                  на специфику аппаратных средств.


OPDEVICE вносит вклад в основную структуру OPPRINT в плане
настройки на специфический принтер. OPDEVICE не объявляет
какие-либо объектные модули. Главным выходным продуктом
OPDEVICE является категория подпрограмм, к которым
обращаются процедуры аппаратной регистрации. Процедура
аппаратной регистрации организует реализацию объектного
модуля Printer для конкретного принтера. OPDEVICE содержит
также различные элементарные подпрограммы для
позиционирования печатающей головки и подобные им, однако у
вас не возникнет необходимости обращаться непосредственно к
ним.

Воспользуемся примером из раздела C:, модифицировав его для
использования процедуры аппаратной регистрации в
конфигурации MyPrinter.

    program PrinterExample;
    uses
      OpRoot, OpPrnLow, OpPrint, OpDevice;
    var
      MyPrinter : Printer;
    begin
      if not MyPrinter.Init('PRN', Prn, 0) then begin
        WriteLn('Failed to init printer. Status = ', InitStatus);
        Halt;
      end;
      GenericEpsonRegister(@MyPrinter);

      MyPrinter.TurnOnByID(Italics);
      MyPrinter.PrintStr('Hello World');
      MyPrinter.Position(50, 1, rtAbsolute);
      MyPrinter.TurnOffByID(Italics);
      MyPrinter.PrintStr('Goodbye World');
      MyPrinter.FormFeed;
      MyPrinter.Done;
    end.

В этой программе есть лишь два отличия: мы добавили OPDEVICE
в список используемых модулей; а также мы обращаемся к
GenericEpsonRegister после подготовки MyPrinter.
GenericEpsonRegister осуществляет последовательность
обращений к AddPrintMode для запоминания управляющих
последовательностей для Italic, Bold и других шрифтов. Она
также устанавливает необходимые флаги в Printer с целью
показать, что для обобщенного Epson предполагается
отсутствие возможности свободного позиционирования (смотрите
подробное описание недалеко от начала раздела C:), но
гарантирована поддержка режима возврата на позицию.
GenericEpsonRegister заказывает для MyPrinter таблицу
трансляции символов, благодаря которой символы прорисовки
линии в формате IBM будут транслироваться в эквиваленты для
Epson. И наконец, GenericEpsonRegister запоминает код
аппаратных средств pGenericEpson внутри MyPrinter для того,
чтобы позднее мог быть определен тип принтера.

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

OPDEVICE является модулем, который мы не в состоянии
когда-либо завершить. В настоящее время он поддерживает 14
принтеров широкого применения. Мы предполагаем дальнейшее
расширение их числа и развитие возможностей тех, для которых
поддержка уже реализована. Мы надеемся, что если вы напишете
процедуру регистрации и подпрограмму поддержки для нового
принтера, вы пришлете их нам, чтобы мы могли дать
возможность другим пользоваться ими. Ознакомтесь с файлом
READ.ME, где вы найдете последнюю информацию о составе
принтеров, описанных в модуле.

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

 Константа     Номер     Процедура            Модели
                         регистрации          принтеров
 ────────────────────────────────────────────────────────────────────

 pLaserJet       1     LJRegister            совместимые с LaserJet

    LaserJet является произвольно позиционируемым принтером,
    а также поддерживает режимы прорисовки линий,
    прямоугольников и полутоновой закраски. Процедура
    регистрации ставит необходимые флаги и устанавливает
    различные элементарные программы обработки для доступа к
    этим возможностям.  Параметры принтера, включающие LPI,
    CPI, LPP, высоту символа и физическое верхнее поле,
    устанавливаются для LaserJet по умолчанию при включении
    питания. PrintMode для шрифта по умолчанию
    устанавливается и активизируется. Кроме этого могут быть
    добавлены следующие режимы: Italic, Bold, Dim,
    Compressed, Underline и PrintReset.  Отдельная процедура
    LJFontGRegister устанавливает таблицу трансляции для
    символов прорисовки линий из комплекта HP на кассете G.
    Она может быть вызвана только после того, как LJRegister
    будет использована для регистрации варианта Printer в
    качестве LaserJet.

 pDeskJet        2     HPDeskJetRegister     HP DeskJet

    Могут быть добавлены следующие режимы: Bold, Compressed,
    Underline и PrintReset.

 pEpsonMX        4     EpsonMXRegister       Epson MX и совместимые

    Могут быть добавлены следующие режимы: Italic, Bold,
    Compressed, Underline, Unidirectional, DoubleWidth и
    PrintReset. Символы прорисовки линий транслируются в
    стандартный код Epson.

 pEpsonLQ        6     EpsonLQRegister       Epson LQ и совместимые

    Могут быть добавлены следующие режимы: Italic, Bold,
    Compressed, Underline, Unidirectional, DoubleWidth,
    PrintReset и LetterQuality. Символы прорисовки линий
    транслируются в стандартный код Epson.

 pGenericEpson   7     GenericEpsonRegister  совместимые с GenericEpson

    Эта подпрограмма используется для тех принтеров, для
    которых предполагается совместимость с обощенным Epson.
    Это самый низкий знаменатель из тех, которые подводятся
    под совместимость с Epson. Могут быть добавлены
    следующие режимы: Italic, Bold, Compressed, Underline и
    PrintReset.  Символы прорисовки линий транслируются в
    стандартный код Epson.

 pDiablo         10    DiabloRegister        Diablo и совместимые

    Могут быть добавлены следующие режимы:  Underline и
    Doublestrike. Число ударов по умолчанию опускается.
    Символы прорисовки линий транслируются в коды ASCII:
    ' | - + '.

 pProprinterII   11    ProprinterIIRegister  IBM ProprinterII

    Могут быть добавлены следующие режимы: Italic, Bold,
    Compressed, Underline и PrintReset.

 pKXP1093        12    PXP1093Register       Panasonic KX-P1093

    Могут быть добавлены следующие режимы: Bold, Underline и
    PrintReset. Символы прорисовки линий транслируются в
    стандартный код Epson.

 pToshiba321     13    Toshiba321Register    Toshiba P321SL или P341SL

    Могут быть добавлены следующие режимы: Italic, Bold,
    Compressed, Underline и PrintReset.

 pStarMicronics  14    StarMicronicsRegister Star Micronics

    Могут быть добавлены следующие режимы: Italic, Bold,
    Compressed и PrintReset. Символы прорисовки линий
    транслируются в стандартный код Epson.

 pNec3510        15    Nec3510Register       Nec 3510/3530

    Могут быть добавлены следующие режимы: Bold, Underline,
    и PrintReset. Символы прорисовки линий транслируются в
    коды ASCII:  '| - + '.

 pNec8800        16    Nec8800Register       Nec 7710/7730/8810/8830

    Могут быть добавлены следующие режимы: Bold, Underline,
    и PrintReset. Отличие от pNec3510 заключается лишь в
    управляющей последовательности для PrintReset. Символы
    прорисовки линий транслируются в коды ASCII:  '| - + '.

 pProwriter      17    ProwriterRegister     C. Itoh 8510

    Могут быть добавлены следующие режимы: Bold, Compressed,
    Underline и PrintReset. Символы прорисовки линий
    транслируются в коды ASCII:  ' | - + '.

 pFacit          18    FacitRegister         Facit 4512

    Могут быть добавлены следующие режимы: Bold, Compressed,
    Underline и PrintReset. Символы прорисовки линий
    транслируются в коды ASCII:  ' | - + '.

 pDataProducts   19    DataProductsRegister  DataProducts DP

    Могут быть добавлены следующие режимы: Bold и
    PrintReset. Символы прорисовки линий транслируются в
    коды ASCII:  ' | - + '.

 ────────────────────────────────────────────────────────────────────

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


    Библиотеки печатающих устройств

Если вы пишите прикладную программу, которая при своей
реализации существенно использует принтер, вы, вероятно,
пожелаете предусмотреть поддержку для максимально возможного
числа устройств. Вы обнаружите, что работать с библиотекой
объектных модулей печати гораздо эффективнее, чем
скомпановывать вместе все процедуры регистрации. Образцы
объектных модулей Printer, как было сказано ранее, могут
архивироваться в потоке. ObjectProfesional может
комбинировать составные потоки в библиотеке в режиме
произвольного доступа. Синтезировав эти две идеи, вы сможете
загружать предварительно составленные объектные модули,
извлекая их из библиотеки.

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

Следующая программа демонстрирует, как записать ранее
составленный объектный модуль Printer в библиотеку.

    program MakeLibrary; {EXPRNLIB.PAS}
    uses
      OpRoot, OpPrnLow, OpPrint, OpDevice;
    const
      LibSig = 'PRNLIB'
      LibName = 'PRINTER.OPL';
      MaxEntries = 16;
    var
      Lib : Library;
      Dev : Printer;

    procedure ConstructorError(ObjectType : String);
    begin
      WriteLn('Error creating ', ObjectType, ' Status = ', InitStatus);
      Halt;
    end;
    procedure CheckLibError;
    var
      Status : Word;
    begin
      Status := Lib.GetStatus;
      if Status <> 0 then begin
        WriteLn('Library error. Status = ', Status);
        Halt;
      end;
    end;

    begin
      {создание новой библиотеки}
      if not Lib.Create(LibName, 4096, LibSig, MaxEntries) then
        ConstructorError('Library');
      {регистрация в библиотеки типа объектного модуля и процедуры}
      Lib.RegisterHier(PrinterStream);
      Lib.RegisterHier(LJDeviceStream);

      {запись в библиотеку устройства GenericEpson}
      if not Dev.Init('', LPT!, 0) then
        ConstructorError('Printer');
      GenericEpsonRegister(@Dev);
      Lib.PutEntry('GENERICEPSON', Dev);
      CheckLibError;

      {запись в библиотеку устройства LaserJet}
      Deregister(@Dev);
      LJRegister(@Dev);
      Lib.PutEntry('LASERJET', Dev);
      CheckLibError;

      {...запись других устройств...}

      {библиотека закрывается}
      Lib.Done;
    end.

Вы можете легко использовать эту программу в качестве основы
при создании своих собственных библиотек для печатающих
устройств. Версия MakeLibrary в EXAMPLES.LZH записывает в
библиотеку практически все возможные типы устройств печати.

MakeLibrary начинает с создания файла новой библиотеки, в
данном случае названного 'PIRNTER.OPL'. Библиотека имеет
каталог, в котором можно разместить до 16 принтеров. Мы даем
библиотеке специальный отличительный код 'PRNLIB', благодаря
чему другие программы, подобные MAKEMENU и MAKESCRN, не
имеют возможности открыть ее.

Далее, MakeLibrary регистрирует типы объектов необходимые
для объектных модулей Printer. Поскольку мы собираемся
создать входную библиотеку для LaserJet, мы используем,
кроме прочего, специальную потоковую процедуру регистрации
LJDeviceStream, которая регистрирует ссылки на требуемые
процедуры. Делать это не обязательно в том случае, если мы
имеем дело с устройством отличным от LaserJet. В настоящее
время никакие другие принтеры, чья поддержка осуществляется
посредством OPDEVICE, не требуют отдельной подпрограммы
регистрации, подобной LJDeviceStream.

Первый тип принтера, который мы записываем в библиотеку, это
обобщенный Epson. Для этого мы создаем вариант объектного
модуля Printer и вызываем после этого процедуру
GenericEpsonRegister для установки соответствующих
расширений, управляющих последовательностей и таблиц
трансляции символов. PutEntry записывает заказанный
объектный модуль Printer в библиотеку. Когда позже мы будем
осуществлять к нему доступ, мы будем обращаться к нему как к
'GENERICEPSON'. Заметьте, что мы инициализировали этот
объектный модуль Printer для вывода на печать через порт
LPT1, используя средства обслуживания уровня BIOS. Выбор
портов вывода не является критическим на этом этапе,
поскольку мы можем изменить его после повторного чтения
объектного модуля. После того, как произошла запись принтера
класса Epson, мы отменяем регистрацию объектного модуля
Printer для анулирования его динамических параметров и для
подготовки его к перерегистрации в качестве принтера
LaserJet. Демонстрационную последовательность можно
продолжить для всех принтеров в OPDEVICE (вплоть до их
максимального количества, равного MaxEntries). В завершение
всего, библиотека закрывается, чтобы гарантировать
заполнение всех внутренних буферов и новой записи системного
каталога DOS.

Теперь покажем, как инициализировать объектный модуль
Printer при помощи той же самой библиотеки.

    program ReadLibrary;
    uses
          OpRoot, OpPrnLow, OpPrint, OpDevice;
    const
      LibSig = 'PRNLIB';
      LibName = 'PRINTER.OPL';
    var
      Lib : Library;
      Dev : Printer;
      LptPort : BiosPrinter;

    procedure ConstructorError(ObjectType : String);
    ...
    procedure CheckLibError;
    ...
    begin
      {открываем библиотеку печати}
      if not Lib.Init(LibName, SOpen, 4096, LibSig) then
        ConstructorError('Library');
      {регистрируем в библиотеке типы объекта и процедуры}
      Lib.RegisterHier(PrinterStream);
      Lib.RegisterHier(LJDeviceStream);

      {считываем устройство LaserJet и закрываем библиотеку}
      Lib.GetEntry('LASERJET', Dev);
      CheckLibError;
      Lib.Done;

      {устанавливаем Dev  на другой порт печати}
      if not LptPort.Init(LPT2) then
        ConstructorError('BiosPrinter');
      Dev.LinkToPrinter(@LptPort);

      {...используем Dev...}

      Dev.Done;
    end.

Lib.Init открывает существующий библиотечный файл, а затем
обращается к RegisterHier, чтобы информировать управление
потоком о соответствующих типах данных. Опять таки,
обращение к LJDeviceStream необходимо лишь в том случае,
если используется устройство LaserJet. Lib.GetEntry читает
ту запись, которая обозначена в каталоге именем 'LASERJET'.
Далее в примере показано, как включать различные объектные
модули BasePrinter. В данном случае используется
BiosPrinter, который будет связан с LPT2. Алгоритм
LinkToPrinter отменяет предыдущий BiosPrinter (для LPT1) и
связывает Dev с новым объектом. С этой точки зрения, Dev
является полностью инициализированным устройством и мы можем
использовать все возможности объектного модуля Printer.


    Константы

    pLaserJet          = 1;
    pDeskJet           = 2;
    pEpsonMX           = 4;
    pEpsonLQ           = 6;
    pGenericEpson      = 7;
    pDiablo            = 10;
    pProprinterII      = 11;
    pKXP1093           = 12;
    pToshiba321        = 13;
    pStarMicronics     = 14;
    pNec3510           = 15;
    pNec8800           = 16;
    pProwriter         = 17;
    pFacit             = 18;
    pDataProducts      = 19;

Здесь указаны регистрационные номера для всех устройств
печати, чья программа поддержки реализована в OPDEVICE.
Регистрационный номер хранится в соответствующей зоне данных
объектного модуля Printer и может быть восстановлен
процедурой GetRegisteredType.


    Описание алгоритмов OPDEVICE:

    Deregister
 ───────────────────────────────────────────────────────────
Объявление
  procedure Deregister(P : PrinterPtr);
Назначение
  Удаляет динамические параметры и сбрасывает P в значение
  простейшего принтера.
Описание
  Эту процедуру вызывают перед регистрацией P в качестве
  принтера нового типа. Она обращается к
  Printer.DisposeAllModes, сбрасывает таблицу трансляции
  символов, очищает стек шрифтов, отменяет набор активных
  режимов и устанавливает тип аппаратных средств в значение
  Plain.

Пример

    EpsonMXRegister(MyPrinterPtr);
    ...
    Deregister(MyPrinterPtr);
    LJRegister(MyPrinterPtr);

  Объект MyPrinterPtr регистрируется как совместимый с
  Epson MX. Вдальнейшем регистрация MyPrinterPtr отменяется и
  происходит перерегистрация объекта в качестве LaserJet. В
  данном примере предполагается, что либо подчиненный
  BasePrinter был настроен на печать через другой
  коммуникационный порт, либо для включения вывода
  MyPrinterPtr на лазерный принтер был использован какой-то
  другой механизм вместо печатающего устройства Epson.


    LJCol2Deci
 ───────────────────────────────────────────────────────────
Объявление
  function LJCol2Deci(Col : Dimension; P : PrinterPtr) : Dimension;
Назначение
  Осуществляет преобразование номера колонки в координату,
  выраженную в единицах, равных 1/720 дюйма.
Описание
  Эта функция требует, чтобы принтер P гарантировал
  корректную величину CPI для активного шрифта.
  Демонстрационная программа FORMTEST.PAS использует эту
  функцию в том случае, если она определит, что реализация
  Printer зарегистрирована как LaserJet.

Пример

    var
      Deci : Dimension;
    ...
      Deci := LJCol2Deci(10, MyPrinterPtr);

  Вычисляется координата в единицах равных 1/720 дюйма,
  соответствующая десятой колонке, и результат присваивается
  величине Deci. Вычисление координаты производится на
  основе информации о CPI, переданной при посредстве
  MyPrinterPtr.

Справки
  LJDeci2Col                  Printer.SetCPI
  LJRow2Deci                  Printer.AddMode


    LJDeci2Col
 ───────────────────────────────────────────────────────────
Объявление
  function LJDeci2Col(Decipoint : Dimension; P : PrinterPtr) : Dimension;
Назначение
  Преобразует координату, выраженную в единицах, равных
  1/720 дюйма, в номер колонки.
Описание
  Эта функция является обратной по отношению к LJCol2Deci.
  Подобно LJCol2Deci, она требует, чтобы значение параметра
  CPI, сохраненное с помощью Printer, было корректным.

Пример

    Column := LJDeci2Col(Decipoint, MyPrinterPtr);

  Преобразует Decipoint в номер колонки и присваивает
  результат величине Column.

Справки
  LJCol2Deci                  Printer.SetCPI
  Printer.AddMode


    LJDeci2Row
 ───────────────────────────────────────────────────────────
Объявление
  function LJDeci2Row(Decipoint : Dimension; P : PrinterPtr) : Dimension;
Назначение
  Преобразует координату, выраженую в единицах, равных
  1/720 дюйма, в номер строки.
Описание
  Эта функция требует, чтобы устройство печати P
  гарантировало корректность величины LPI для активного
  шрифта.


    LJRow2Deci
 ───────────────────────────────────────────────────────────
Объявление
  function LJRow2Deci(Row : Dimension; P : PrinterPtr) : Dimension;
Назначение
  Осуществляет преобразование номера строки в координату,
  выраженную в единицах, равных 1/720 дюйма.
Описание
  Эта функция требует, чтобы устройство печати P
  гарантировало корректность величины LPI для активного
  шрифта.
Справки
  LJCol2Deci


    XxxRegister
 ───────────────────────────────────────────────────────────
Объявление
  procedure XxxRegister(P : PrinterPtr);
Назначение
  Регистрирует P в качестве принтера с указанным типом
  аппаратных средств.
Описание
  Здесь описаны все процедуры аппаратной регистрации,
  реализованные в OPDEVICE. P является адресом объектного
  модуля Printer, который инициализирован как простейший
  принтер. XxxRegister заказывает реализацию модуля, в том
  числе:  флаги специальных расширений, режимы печати,
  таблицы трансляции символов, шрифт по умолчанию и так
  далее.  Процедура XxxRegister не затрагивает подчиненный
  BasePrinter, масштабные коэффициенты по X и Y, а также
  пользовательский отладчик ошибок принтера.
  В следующей таблице показаны: наименование кода
  идентификатора аппаратных средств, номер исполнительного
  кода, имя процедуры регистрации и модели принтеров, которые
  охватываются соответствующей регистрацией. Более подробная
  версия этой таблицы приведена в начале этого раздела.

 Константа     Номер     Процедура            Модели
                         регистрации          принтеров
 ────────────────────────────────────────────────────────────────────
 pLaserJet       1     LJRegister            совместимые с LaserJet
 pDeskJet        2     HPDeskJetRegister     HP DeskJet
 pEpsonMX        4     EpsonMXRegister       Epson MX и совместимые
 pEpsonLQ        6     EpsonLQRegister       Epson LQ и совместимые
 pGenericEpson   7     GenericEpsonRegister  совместимые с GenericEpson
 pDiablo         10    DiabloRegister        Diablo и совместимые
 pProprinterII   11    ProprinterIIRegister  IBM ProprinterII
 pKXP1093        12    PXP1093Register       Panasonic KX-P1093
 pToshiba321     13    Toshiba321Register    Toshiba P321SL или P341SL
 pStarMicronics  14    StarMicronicsRegister Star Micronics
 pNec3510        15    Nec3510Register       Nec 3510/3530
 pNec8800        16    Nec8800Register       Nec 7710/7730/8810/8830
 pProwriter      17    ProwriterRegister     C. Itoh 8510
 pFacit          18    FacitRegister         Facit 4512
 pDataProducts   19    DataProductsRegister  DataProducts DP

Пример
  Смотрите вводную демонстрационную программу.
Справки
  Deregister

    E: OPFORM     Печатные формы


OPFORM является модулем поддержки принтера самого высокого
уровня. Будучи построенным на базе OPPRNLOW, OPPRINT и
OPDEVICE, он обеспечивает возможность создания печатных форм
подобно тому, как OPENTRY поддерживает размещение данных на
экране. Печатные формы строятся при помощи набора объектных
модулей, высшим уровнем иерархии которых является
PrintedForm. Здесь показана соответствующая иерархия.

    [Root]
      [DoubleListNode]
        [AbstractField]
          PrintField
            StringPrintField
            CharPrintField
            WordPrintField
            BooleanPrintField
            YesNoPrintField
            LongPrintField
            IntPrintField
            BytePrintField
            ShortPrintField
            RealPrintField
            BCDPrintField
            ExtPrintField
            DblPrintField
            SglPrintField
            CompPrintField
            DatePrintField
            TimePrintField
            LineField
              ShadedField
    [SingleListNode]
      FieldListNode
      PrintBlock
      PrintPage
      PrintedForm
    Report

Эта иерархия гораздо проще для понимания, чем это кажется.
К тому же, объектные модули, имена которых заключены в
квадратные скобки, описаны в других разделах этого
руководства и приведены здесь лишь для ориентировки. Кроме
того, большинство объектных модулей, производных от
PrintField, один другого проще. Подобно тому, как
производные от EntryField в модуле OPFIELD, производные от
PrintField используются для преобразования специфического
для Паскаля типа данных в формат, который может быть
использован в объектных модулях более высокого уровня.

Приняв это к сведению, сделаем краткий обзор остальных
объектных модулей в этой иерархии. PrintField запоминает
расположение, размеры и режимы печати (смотрите OPPRINT) для
отдельной позиции, выводимой на печать. Не смотря на то, что
PrintField связан с конкретной переменной, он не знает типа
этой переменной. Это абстрактный объектный модуль и вы не
должны конкретизировать его. Он только определяет зоны
данных и алгоритмы, которые могут быть задействованы
совместно с более специфическими типами зон:
StringPrintField, WordPrintField, RealPrintField и так
далее. Однако, благодаря таинствам виртуальных алгоритмов и
использованию ссылок на процедуры, множество объектов
различных типов могут быть обработаны вместе как однотипные,
как PrintField.  Нет ничего страшного в том, что
AbstractField, являясь непосредственным прототипом
PrintField, имеет дело с экранно-ориентированными зонами,
описанными в OPFIELD и OPENTRY. В результате, системы
построения экранного изображения и печатной формы используют
идентичный код (в OPABSFLD) для преобразования переменных
Паскаля в строки и сочетания этих строк с маской
изображения.

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

Важнейшим компонентом блока печати PrintBlock является
список зон печати. Он составляет промежуточный уровень в
организации печатной формы; уровень, подобного которому не
существует в организации экрана.  Дополнительные данные в
блоке печати включают базовые позиции строки и колонки, и
определяемую пользователем блочную функцию, которая
вызывается только перед распечаткой блока.  Лучший способ
разобраться в использовании PrintBlock, это обсудить его
работу на примере. Предположим, что вы хотели печатать
многочисленные почтовые этикетки на отдельной странице. Для
того, чтобы это сделать, вы должны будете создать
PrintBlock, чей список зон PrintField будет размещен в левом
верхнем углу страницы и будет включать в себя указатели для
ссылок на имя, адрес и другие индивидуальные атрибуты
адресата. Затем вы должны будете скопировать этот
PrintBlock (используя предоставленные процедуры PrintedForm)
во всех необходимых позициях на странице. OPFORM вычислит
для вас абсолютные координаты расположения всех зон печати,
но он утратит все переменные параметры ссылок на
первоначально указанные вами установки. Вот именно здесь и
вступает в действие блочная функция, выбираемая
пользователем. Перед тем, как печатать каждый блок, OPFORM
будет опрашивать вашу подпрограмму, которая, основываясь на
информации хранимой внутри блока, сможет восстановить
фактические данные о почтовых атрибутах любым из возможных
способов (хотя бы из базы данных) и сохранит их в отдельной
рабочей переменной.

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

Как вы можете убедиться, печатная форма PrintedForm содержит
список модулей PrintPage. Однако, в отличие от некоторых
других систем, формы ObjectProfesional могут достигать
произвольного числа страниц и страничных форматов.  Именно
объектному модулю PrintedForm будет уделено самое большое
внимание на всем протяжении этого раздела.  PrintedForm
предусматривает алгоритмы для размещения, организации
доступа и управления всеми объектными модулями более низкого
уровня; благодаря чему, за редким исключением, у вас не
возникнет необходимости использовать какой-либо из этих
объектных модулей непосредственно. В соответствие со своей
центральной ролью в системе OPFORM, PrintedForm включает в
себя ограниченное число дополнительных зон данных, которые
указывают, где и как будет напечатана форма.  Здесь
представлен полный список наиболее важных из них.

  - флаги расширений битовой карты для всей печатной формы;
  - верхнее, нижнее, правое и левое поля;
  - внутренняя переменная состояния и заданный
    пользователем отладчик ошибок принтера;
  - расширения по умолчанию для зон, добавленных к форме;
  - указатель на объектный модуль Printer в OPPRINT;
  - пользовательская функция предварительной подготовки формы;
  - четыре байта - резерв данных для пользователя;

Наиболее важным из них является указатель на объектный
модуль Printer в OPPRINT. Через этот указатель PrintedForm
заимствует все функциональные возможности модулей печати
более низкого уровня, которые уже были описаны - возможность
печатать через любой порт или в любой дисковый файл, гибкая
обработка ошибок, автоматическая трансляция символов,
передача управляющих последовательностей для установки
различных режимов печати на различные принтеры и так далее.

Есть еще один объектный модуль, Report (отчет), который
управляет списком печатных форм PrintedForm. Report в
действительности очень простой объектный модуль, поскольку
все основные элементы обрабатываются и формируются до него.
Разделение труда среди объектных модулей дает один
интересный результат: Report может распечатывать выходные
данные на многие принтеры. Такая возможность может быть
очень полезна для многих деловых применений.

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

    {$V-}                          <- {требуется для OPFORM}
    program FormExample;  {EXFORM.PAS}
    uses
      Dos, OpInline, OpString, OpRoot, OpDos, OpDate,
      OpPrnLow, OpPrint, OpDevice, OpForm;
    var
      PrnPtr : PrinterPtr;       {указатель на объект Printer}
      FrmPtr : PrinterFormPtr;   {указатель на объект PrinterForm}
      Index : Word;              {счетчик записей}
      DataRec :                  {записи данных}
        record
          Name : String;
          Addr : String;
          DaysLate : Word;
          Balance : Real;
        end;
    {$F+}
    function GetNextRecord(P : PrintBlockPtr) : Boolean;
    begin
      if Index > 10 then
        GetNextRecord := False
      else
        with DataRec do begin
          {заполнение данными глобальной переменной var}
          Name := 'Name' + Long2Str(Index);
          Addr := 'Address' + Long2Str(Index);
          DaysLate := 120;
          Balance := 12.34;
          GetNextRecord := True;
          Inc(Index);
        end;
    end;
    {$F-}

    procedure BuildForm;
    var
      I : Word;
    begin
      {подготовка устройства для печати и формы}
      PrnPtr := New(PrinterPtr, Init('DUMP.PRN', DiskFile, 0));
      if PrnPtr = nil then begin
        WriteLn('Failed to init Printer');
        Halt;
      end;
      FrmPtr := New(PrintedFormPtr, Init(1, PrnPtr));
      if FrmPtr = nil then begin
        WriteLn('Failed to init Form');
        Halt;
      end;
      {Определение зон для формы}
      FrmPtr^.NewPage;
      FrmPtr^.SetHeader(Center(FormExample Header', 80), 1, 1, Dim);
      FrmPtr^.NewBlock(2, 1, GetNexyRecord);
      FrmPtr^.AddStringField(1, 'Customer Name:', 2, 3, Bold, 16,
                             2, NullPicture, 2, 20, Dim, 20, DataRec.Name);
      FrmPtr^.AddStringField(3, 'Customer Address:', 3, 3, Bold, 16,
                             4, NullPicture, 3, 20, Dim, 20, DataRec.Addr);
      FrmPtr^.AddWordField(5, 'Days Late:', 4, 3, Bold,  16,
                           6, NullPicture,, 4, 20, Dim, 5, DataRec.DaysLate);
      FrmPtr^.AddRealField(7, 'Overdue Balance:', 4, 30, Bold, 16,
                           8, '$###,###,##', 4, 48, Dim, 2, 11, DataRec.Balance);
      FrmPtr^.AddBox(10, 1, 1, 65, 5, 0, 0, SingleLineBox, Bold);
      for I := 1 to 9 do
        FrmPtr^.DupCyrrentBlock(2+(I*5), 1);
    end;

    begin
      BuildForm;                    {инициализация формы}
      Index := 1;                   {запуск с первой записи}
      FrmPtr^.Process;              {распечатка отчета}
      Dispose(FrmPtr, Done);        {очистка}
      Dispose(PrnPtr, Done);
    end.

Первое, что необходимо отметить, это директива компилятора
{$V-}, расположенная в начале программы. Эта директива
необходима почти для каждой программы или модуля,
написанного с применением OPFORM. Если вы опустите эту
директиву, то должны будете обнаружить ошибку компилятора
при попытке добавить к форме зону типа String.

Полный список USES составлен таким образом, что вы можете
видеть все модули, вовлеченные в применение OPFORM.
Единственный модуль, который может при этом остаться
невостребованным, это OPDATE; вы можете удалить его, если не
позаботились о переменных для даты и времени. Чтобы
предостеречь собственно OPFORM от использования этого
модуля, вы должны дать комментарий об извлечении UseDate,
условно определенного в OPDEFINE.INC.

Приведенная в качестве примера программа типична для многих
прикладных программ OPFORM, в которых каждой из зон в
PrintedForm соответствует зона в переменной записи - в
данном случае в DataRec. Однако это не является твердым и
нерушимым требованием. Зоне в печатной форме может
соответствовать любая переменная. В наших примерах
используется переменная записи только потому, что так чаще
всего поступают в прикладных программах.

Давайте забежим немного вперед и рассмотрим процедуру
BuildForm, которая первой инициализирует тот объектный
модуль Printer, в который передается выходная информация.
Вместо того, чтобы повторять часть материала из раздела
OPPRINT, мы предпочтем для записи выходных данных дисковый
файл DUMP.PRN, обойдясь таким образом без построения
специальной управляющей последовательности для оформления
печати.  Достаточно будет сказать, что переменная PrnPtr
может быть инициализирована так же, как и любой другой
объектный модуль Printer - при помощи обращения к процедуре
аппаратной регистрации, добавления режимов печати и так
далее.

Далее, динамическая реализация переменной PrintedForm
размещается и инициализируется. Мы используем простую
процедуру сборки Init для установки кода идентификатора
равным 1 и назначения уже инициализированного объектного
модуля Printer для этой формы. Использование кода
идентификатора формы оказывается полезным при построении
отчетов из большого количества печатных форм.

Теперь начинается действительно интересный материал. Каждая
форма должна начинаться с вызова NewPage (новая страница),
которая размещает PrintPage (страница печати) и компанует ее
в список страниц. SetHeader описывает строку заголовка, ее
атрибут печати и позицию для этой новой страницы. Обращение
к SetHeader является образцом того метода, который часто
использовался при создании модуля OPFORM. Есть также в
объектном модуле PrintPage алгоритм, который называется
SetHeader, и это именно тот алгоритм, который выполняет
основную часть работы по формированию пространства для
строки заголовка и сохранению необходимых для этого данных.
SetHeader, как версия объектного модуля PrintedForm, служит
лишь гарантией того, что есть текущая страница, и
осуществляет обращение к соответствующему объектному модулю.
В данном случае мы можем предпринять чуть больше процедур по
контролю ошибок и нет необходимости изменять позиции
указателей.

Каждая страница должна начинаться с обращения к NewBlock
(новый блок). Во многих случаях подобный блок является лишь
формальностью; однако в данном примере мы собираемся
эффективно использовать блок для размножения зоны печати с
формой для бланка оплаты по факту. Первые два параметра
NewBlock определяют координаты базовой строки (2) и колонки
(1) в блоке. Все зоны в этом блоке будут заданы относительно
этих координат. Третий параметр NewBlock определяет имя
пользовательской блочной функции, которую OPFORM вызывает
перед тем, как напечатать блок. Мы еще вернемся к
рассмотрению этой функции.

Следующие четыре обращения к AddXxxField генерируют зоны,
которые будут размещены на странице. Параметры этих вызовов
были описаны ранее. А теперь мы обратим внимание на то, что
каждое обращение включает: "строку подсказки" для зоны
(неизменяемый текст; обычно заголовок, следующий за
номером); позицию этой строки, атрибут печати и ширину;
маску изображения для группы данных зоны; позицию группы
данных, атрибут печати, ширину и переменную зоны. В данном
случае, переменные являются зонами в нашей записи DataRec.

Мы ввели также, с помощью обращения к AddBox, специальный
тип зоны, "зону прямоугольника". Этот специальный
прямоугольник охватывает все остальные зоны символами
прорисовки одиночной линии. То, как это выглядит на бумаге,
зависит, конечно, от принтера; но поскольку мы записываем
это в дисковый файл, то внешне это прекрасно выглядит в
соответствие с символами расширенной таблицы ASCII IBM.

Если вы действительно разобрались в том, что было написано
до сих пор, у вас теперь может возникнуть вопрос:  поскольку
верхняя грань прямоугольника находится над первой зоной
печати, к которой мы делали последующие добавления, то не
значит ли это, что принтеру необходимо будет вернуться вверх
в тот момент, когда он начнет печатать прямоугольник?
Ответ, к счастью, отрицательный. Почему отрицательный?
Во-первых, для принтеров, которые не умеют рисовать
вертикальные линии, OPFORM перекраивает зону прямоугольника
в серию отдельных зон печати, по одной для каждой из строк,
где присутствует элемент прямоугольника. Во-вторых, OPFORM
печатает зоны в порядке, определяемом их расположением на
странице, независимо от того, в какой последовательности они
были введены в форму. Этот алгоритм отличается от OPENTRY,
который требует, чтобы зоны вводились в последовательности,
соответствующей их физическому размещению.

Циклическая процедура, названная DupCurrentBlock,
демонстрирует реальное достоинство блочной концепции. Здесь
мы создаем девять копий только что определенного блока,
каждый из которых размещается на 5 строк ниже предыдущего.
Помните, что, хотя каждый блок состоит из уникальных
объектных модулей PrintField, все соответствующие зоны
обращаются к одним и тем же переменным, в данном случае к
зонам записи DataRec.

Заметьте, что нет необходимости в вызове специальной
процедуры для завершения процесса создания печатной формы.

Хорошо, теперь мы готовы продолжить наше изучение основного
блока кодов. Сначала он обращается к BuildForm для того,
чтобы открыть принтер и создать форму, ссылка на которую
хранится в глобальной переменной FrmPtr. Далее он
устанавливает значение глобальной переменной Index. Эта
переменная определяет, какой из бланков оплаты по факту мы
передаем в следующем сеансе печати. Обращение к алгоритму
Process связанному с FrmPtr запускает печать формы; два
последующих обращения к Dispose освобождают всю память,
занятую объектными модулями PrintedForm и Printer, и
закрывают файл вывода.

Теперь давайте вернемся и рассмотрим функцию GetNextRecord.
Алгоритм Process печатной формы вызывает эту подпрограмму
каждый раз перед распечаткой блока. Process пропускает адрес
объектного модуля PrintedForm, который в данном случае не
используется, и контролирует прием сигнала 'истинно' в том
случае, если функция завершится успешно; и 'ложно', если
нет. Блочно-ориентированная функция ничего не печатает. Ее
главной задачей является гарантия того, что зоны данных,
связанных с этим блоком, содержат корректные величины для
всех последующих блоков. Глобальная переменная Index
определяет, какую именно запись мы осуществляем, и мы таким
образом корректно переписываем DataRec. GetNextRecord
определен только на передачу десяти записей; он передаст
сигнал 'ложно', если мы будем требовать больше этого
количества, и это приведет к тому, что OPFORM не будет
печатать блок.

Это длинное объяснение, вероятно, даст вам хорошее
представление о тех вещах, которые вы можете реализовать с
помощью OPFORM. С некоторыми подглядками в алгоритм,
описанный далее в этой главе, вы, вероятно, можете уже
сейчас создать кое-какие интересные на вид формы, имеющие
изящный стиль. Тем не менее, существует еще несколько
функциональных возможностей, и остальная часть этого раздела
должна продемонстрировать вам, как они взаимодействуют.
Это краткий список предстоящего материала:

  - добавление зон к печатной форме
  - расширения зоны
  - подпрограммы преобразования зоны
  - заглавия и подножия
  - дополнительные встроенные отладчики
  - обработка ошибок
  - архивирование печатной формы в потоках
  - как работает PrintedForm


    Добавление зон к печатной форме

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

    параметр    определение         типы зон
 ───────────────────────────────────────────────────────────

  pFieldID      Идентификатор     Все, кроме директивных зон
                указателя
  Prompt        Сообщение
                указателя          "
  pRow, pCol    Координаты
                указателя          "
  pAttr         Идентификатор
                режима печати
                указателя          "
  pWidth        Ширина указателя   "
  Picture       Маска изображения Все, кроме текста и зон
                                  прорисовки линии
  fFieldID      Идентификатор зоны "
  fRow, fCol    Координаты зоны    "
  fAttr         Идентификатор
                режима печати зоны "
  fWidth        Ширина зоны       Все, кроме текста,
                                  вещественных переменных
                                  и зон прорисовки линии
  fDPlaces      Количество        Все вещественные,
                десятичных        кроме Comp
                позиций

Параметры, которые начинаются с буквы 'p', относятся к той
части зоны, где размещен указатель (Prompt); а те, которые
начинаются с 'f', относятся к той части зоны, где размещены
данные.

pFieldID и fFieldID являются номерами идентификаторов
(величина типа Word), относящихся соответственно к участку
указателя и участку данных зоны. В отличие от OPENTRY,
определение этих номеров осуществляет программист, а не
система. Номера не обязаны быть уникальными в пределах
печатной формы. Наоборот, они предоставляют удобную
возможность определять особые зоны печати путем своей
динамической модификации. Наиболее распространенным подходом
в данном случае является последовательная нумерация кодов
идентификаторов; альтернативным методом может быть
назначение общего кода для отдельной группы. Будьте
внимательны! - номер идентификатора $FFFF зарезервирован для
внутреннего применения в OPFORM. Для дополнительной
информации обратитесь к следующим алгоритмам PrintedForm:
FindField, FindNextField и ChangeConversion.

Указатель является фиксированной строкой, которая должна
напечататься в каждом месте, где напечатана переменная
зоны. Указатель может быть пустой строкой, в этом случае
значения pRow и pCol не играют роли. Обратите внимание на
то, что указатель и данные не должны оказаться на одной и
той же строке в печатной форме. pRow и pCol указывают
позицию указателя используя абсолютные координаты страницы в
единицах Dimension. (Обратитесь к вводной части раздела
OPPRINT, где описан этот тип данных).

pAttr является кодом идентификатора режима печати, который
активизируется перед распечаткой указателя и после
отключается. Код идентификатора режима печати является
аналогом видео-атрибута вводимой зоны; имейте это ввиду
потом, когда мы будем использовать аббревиатуру Attr.

pWidth определяет желаемую длину указания в символьных
позициях; указание будет растянуто или обрублено под эту
длину перед выводом на печать.

Picture является маской изображения для участка данных зоны,
которая описывает формат зоны и то, как она преобразуется из
типа данных Паскаля в строку. Для всех простых зон маска
изображения является необязательной и в этой позиции может
быть указана пустая строка. Когда маска изображения не
указана, способ форматирования строки определяется при
помощи fWidth и fDplaces (для вещественных). Для дальнейшего
анализа маски изображения смотрите главу OPABSFLD. Несмотря
на то, что маски изображения очевидно не играют заметной
роли при коррекции входных данных для модуля OPFORM, они
используются именно так, как это делается для форматирования
вывода в OPENTRY.

fFieldID, fRow, fCol, fAttr и fWidth играют такую же роль
для участка данных зоны, какую играют описанные перед этим
параметры для участка указателя.

fDPlaces используется для зон данных с плавающей точкой
(типа Real, BCD, Single, Double и Extended). Если маска
изображения зоны не зафиксировала десятичной точки, fDPlaces
определяет число десятичных позиций для формата вывода. Если
маска изображения содержит десятичную точку, fDPlaces
игнорируется.

Последний параметр алгоритма AddField (PrintXxx) почти
всегда является именем переменной, связанной с данной зоной.
Единственными исключениями являются: AddTextField, AddBox,
AddDirective, AddLine и AddShaded, ни один из которых не
ассоциируется с какой бы то ни было переменной.

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


    Текстовая зона

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

procedure AddTextField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
    {-добавляет зону текстовой строки}


    Простые зоны

Следующие алгоритмы относятся к зонам, использующим основные
типы переменных Turbo Pascal.

procedure AddBooleanField(pFieldID : Word; Prompt : String;
                          pRow, pCol : Dimension; pAttr, pWidth : Byte);
                          fFieldID : Word; Picture : String;
                          fRow, fCol : Dimension; fAttr, fWidth : Byte);
                          var PrintBool : Boolean);
    {-добавляет зону типа Boolean}

procedure AddByteField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintByte : Byte);
    {-добавляет зону типа Byte}

procedure AddCharField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintChar : Char);
    {-добавляет зону типа Char}

procedure AddIntField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintInt : Int);
    {-добавляет зону типа Integer}

procedure AddLongField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintLong : LongInt);
    {-добавляет зону типа LongInt}

procedure AddRealField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fDPlaces, fWidth : Byte);
                       var PrintReal : Real);
    {-добавляет зону типа Real}

procedure AddShortField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintShort : ShortInt);
    {-добавляет зону типа ShortInt}

procedure AddStringField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintSt : String);
    {-добавляет зону типа String}

procedure AddWordField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintWord : Word);
    {-добавляет зону типа Word}

procedure AddYesNoField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintYesNo : Boolean);
    {-добавляет зону YesNo}

{$IFDEF UseBCD}
procedure AddBCDField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fDPlaces, fWidth : Byte);
                       var PrintBCD : BCD);
    {-добавляет зону типа BCD}
{$ENDIF}
{$IFOPT N+}
procedure AddExtField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fDPlaces, fWidth : Byte);
                       var PrintExt : Extended);
    {-добавляет зону типа Extended}

procedure AddDblField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fDPlaces, fWidth : Byte);
                       var PrintDbl : Double);
    {-добавляет зону типа Double}

procedure AddSglField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fDPlaces, fWidth : Byte);
                       var PrintSgl : Single);
    {-добавляет зону типа Single}

procedure AddCompField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintComp : Comp);
    {-добавляет зону типа Comp}
{$ENDIF}


    Зоны даты и времени

Следующие алгоритмы относятся к зонам времени и даты. Если
маски озображения пусты, OPFORM устанавливает по умолчанию
форматы 'mm/dd/yy' и 'hh:mm:ss'.

{$IFDEF UseDates}
procedure AddDateField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintDate : Date);
    {-добавляет зону Date}

procedure AddTimeField(pFieldID : Word; Prompt : String;
                       pRow, pCol : Dimension; pAttr, pWidth : Byte);
                       fFieldID : Word; Picture : String;
                       fRow, fCol : Dimension; fAttr, fWidth : Byte);
                       var PrintTime : Time);
    {-добавляет зону Time}
{$ENDIF}


    Зоны прорисовки линии

Следующие алгоритмы добавляют зоны, которые записывают
специальные выходные последовательности для прорисовки
линии. Дополнительную информацию вы найдете в описании,
которое следует за объявлениями этих процедур.

procedure AddBox(FieldID : Word; Row, Col, Width, Height : Dimension;
                 HLineThickness, VLineThickness : Dimension;
                 BoxChars : BoxCharSet; Attr : Byte);
{-добавляет зону прямоугольника}

procedure AddLine(FieldID : Word; Row, Col, Len, LineThickness : Dimension;
                  Horizontal : Boolean; LineChar : Char; Attr : Byte);
{-добавляет зону линии}

procedure AddShaded(FieldID : Word; Row, Col, Width, Height : Dimension;
                    Intensity : Byte);
{-добавляет зону теневого поля}

procedure AddDirective(FieldID : Word; Row, Col : Dimension;
                       On : Boolean; Attr : Byte);
{-добавляет директиву печати}


AddBox обусловливает порисовку четырех сторон прямоугольника
на странице вывода. То, как будет построен прямоугольник,
зависит от того, будет ли установлен флаг pSupportHPRules в
подчиненном модуле Printer данной печатной формы.  Если флаг
не установлен, PrintedForm преобразует прямоугольник в две
горизонтальные линии (включая и угловые позиции) и
последовательность вертикальный линейных сегментов, по
одному на строку.  Для определения, какой именно символ
использовать в каджом конкретном линейном сегменте,
применяется массив символов BoxChar. Когда форма напечатана,
алгоритм Process заполняет промежутки между линейными
сегментами другими зонами печати таким образом, что
печатающей головке не нужно возвращаться на строку вверх.
Если флаг pSupportHPRules установлен, AddBox игнорирует
параметр BoxChar и вместо него использует HLineThickness и
VLineThickness для определения толшины горизонтальных и
вертикальных (соответственно) линейных сегментов (в единицах
измерения, равных 1/720 дюйма). Row, Col, Width и Height
определяют левый верхний угол и габариты прямоугольника в
единицах измерения, равных ширине и высоте символа (если в
модуле Printer установлен флаг UseDecipoints, то в этом
случае габариты будут измеряться тоже в 1/720 дюйма).

AddLine работает почти как AddBox, только, в отличие от этой
процедуры, прорисовывает одиночный сегмент. LineChar
определяет символ для прорисовки в том случае, если флаг
pSupportHPRules не установлен; в ином случае LineThickness
определяет толшину (в единицах, равных 1/720 дюйма).
Horizontal принимает значение 'истинно' для сегмента
горизонтальной линии, и значение 'ложно' для вертикальной.
Row и Col задают верхний или левый край отрезка, а Len
определяет длину сегмента.

AddShaded добавляет к выводу теневой прямоугольник. Эта
процедура игнорируется во всех случаях, за исключением
ситуации, когда в модуле Printer установлен флаг
pSupportHPRules, поскольку только он обусловливает генерацию
управляющей последовательности для режима затенения,
ориентированную на лазерный принтер LaserJet. Intensity
определяет тональность закраски, которая варьируется от 1
(едва заметная тонировка) до 99 (абсолютно черная закраска).

AddDirective добавляет специальную зону, называемую
директивой, которая не имеет ни строки указателя, ни
связанных с ним переменных данных. Единственное назначение
этой зоны заключается в передаче управляющей
последовательности для активизации (если On находится в
состоянии 'истинно') или отключения (если On в состоянии
'ложно') режима печати, определенного по его идентификатору
(Attr), в тот момент, когда печатающая головка в процессе
печати страницы достигнет позиции, указанной координатами
Row и Col. Если директива и зона печати стартуют с одной и
той же координаты, директива всегда исполняется первой.


    Функциональные расширения зоны

Каждая зона печати архивирует битовый образ LongInt флагов
функционального расширения. Эти флаги определяются в тех
случаях, когда зона добавляется (обратитесь к описанию
алгоритмов PromptOptionsOn, FieldOptionsOn, PromptOptionsOff
и FieldOptionsOff) или когда она может быть позже
модифицирована (обратитесь к FindField,
PrintField.pfOptionsOn и PrintField.pfOptionsOff). Обратите
внимание, что при работе с большинством алгоритмов
AddXxxField вы в действительности добавляете каждый раз две
зоны печати - первую для указателя и вторую для данных - а,
следовательно, вы можете модифицировать каждую из них
независимо. Мы опишем в этом параграфе наиболее важные из
функциональных расширений.

    pfSuppressZero

Если точное значение величины равно нулю зоны Integer (типа
Byte, ShortInt, Integer, Word, LongInt) не будут напечатаны.

    pfRightJustify

Зона будет выровнена справа по заданной ширине зоны.

    pfInhibitAttrOn

Во время печати зоны не может быть реализован режим печати,
связанный с ней.

    pfInhibitAttrOff

Во время печати зоны не может быть отменен режим печати,
связанный с ней.

    pfInhibitString

Во время печати зоны строка зоны не пишется.

    pfInhibitPos

Во время печати зоны происходит задержка перемещения
печатающей головки к зоне.

    pfHidden

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


    Функции преобразования зоны

Любая печатающаяся зона с записью данных пользователя имеет
связанную с ней функцию преобразования. Назначения функции
преобразования двояко: определить, должна ли вообще эта зона
быть напечатанной; и преобразовать эту зону из
первоначальной записи данных в строку печати. Функции
преобразования зон печати очень похожи на функции со схожими
названиями в OPFIELD, за исключением того, что только
функции зон печати вынуждены осуществлять преобразование в
одном направлении.

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

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

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

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

    {$F+}
    function MyPrintConversionFunc(PFP : PrintFieldPtr) : Boolean;

Параметр PFP является указателем зоны печати для
преобразования. Результатом вычисления функции в том случае,
если зона предназначена для печати, является логическое
значение 'истинно'; или значение 'ложно' в противоположном
случае.

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

    {$F+}
    function PrintWordConvert(PFP : PrintFieldPtr) : Boolean;
        {-функция преобразования для зон типа Word}
    var S : string;
    begin
      with PFP^ do begin
        S : pfLong2Str(Word(pfVarPtr^0, 4);
        MergePicture(S, pfString^);
        pfPadField;
        PrintWordConvert := True;
      end;
    end;
    {$F-}

Поскольку Word является величиной цифрового типа, а цель
заключается в том, чтобы создать отформатированную строку;
то первой задачей является преобразование переменной Word по
указателю pfVarPtr в строку. Для этого мы используем
pfLong2Str, статический вспомогательный алгоритм,
объявленный при помощи модуля PrintField и использующий два
параметра: длинную целую величину и ширину строки. Эта
функция обращается к одной из двух функций в OPSTRING, HexL
или Long2Str, которые за основу в своей работе берут маску
изображения. Если маска изображения определяет, что зона
представляет собой шестнадцатиричное значение, то ширина
строки сообщает pfLong2Str, сколько шестнадцатиричных
позиций должно быть в строке (2 для Byte или ShortInt, 4 для
Word или Integer, 8 для LongInt). После того, как данная
величина будет преобразована в строчную запись (S), для
сочетания этой записи с записью маски изображения и
сохранения результата в pfString^ будет использован
производный от AbstractField алгоритм MergePicture.

В конце концов, pfPadField (другой вспомогательный алгоритм
PrintField) распределит зону в соответствии с шириной,
определенной при добавлении зоны, осуществит выравнивание
справа или слева в зависимости от состояния расширения
pfRidhtJustify. Стандартная функция преобразования всегда
даст в результате величину 'истинно', поскольку алгоритм
Process в любом случае контролирует состояние флага pfHidden
для каждой зоны.

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

     Тип зоны     Функция преобразования
    ─────────────────────────────────────
    BCD            PrintBCDConvert
    Boolean        PrintBoolConvert
    Byte           PrintByteConvert
    Char           PrintCharConvert
    Comp           PrintCompConvert
    Date           PrintDateConvert
    Double         PrintDblConvert
    Extended       PrintExtConvert
    Integer        PrintIntConvert
    LongInt        PrintLongConvert
    Real           PrintRealConvert
    Single         PrintSglConvert
    ShortInt       PrintShortConvert
    String         PrintStringConvert
    Time           PrintTimeConvert
    Word           PrintWordConvert
    YesNo          PrintYesNoConvert

Не смотря на создание заказной функции преобразования, вы
должны также помнить несколько других зон данных объектного
модуля PrintField:

  - pfMaxLen: максимальная длина строки, которая может
    быть сохранена в pfString^
  - pfID: код идентификатора для зоны, предназначенной для
    преобразования
  - pfDPlaces: количество десятичных позиций, заданных при
    добавлении зоны

Смотрите также алгоритмы, описанные перед этим в параграфе
"Зона печати".

Для активизации заказной функции преобразования одной или
более зон вы должны использовать алгоритм ChangeConversion
из модуля PrintedForm.

    procedure ChangeConversion(FieldID : Word;
                             ConvertFunc : PrintConversionFunc);

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


    Заголовки и подножия

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

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

    procedure SetHeader(HeaderStr : String; Row, Col : Dimension;
                        HAttr : Byte;
{-Определение заголовка}

    procedure SetFooter(FooterStr : String; Row, Col : Dimension;
                        FAttr : Byte;
{-Определение подножия}

Заголовок или подножие добавляется почти так же, как зона
текста, но с некоторыми отличиями. Для заголовков и подножий
не существует номера идентификатора зоны (они используют
резервное значение $FFFF). Нет у них также и параметра
ширины Width, что следует из того факта, что по умолчанию
процедуры OPFORM не форматируют (не растягивают и не
обрубают) строки заголовка перед выводом на печать.

Даже не смотря на то, что заголовки не форматируются
по умолчанию, они обслуживаются в точности как и другие зоны
печати в том смысле, что перед их выводом на печать
вызывается стандартная (ничего не изменяющая) функция
преобразования. В результате, вы можете написать собственную
функцию преобразования для заголовка так, как это описано в
предыдущем материале. Чаще всего функция преобразования
заголовка преследует цель включить в него номер текущей
страницы. Поскольку заголовки и подножия не помечаются кодом
идентификатора зоны как другие зоны, объектный модуль
PrintedForm обеспечивает специальные алгоритмы для задания
для них пользовательских функций преобразования:

    procedure SetHeaderFunc(Picture : String;
                        HeadFunc : PrintConversionFunc);
{-установка указателя для функции HeaderFunc}

    procedure SetFooterFunc(Picture : String;
                        FootFunc : PrintConversionFunc);
{-установка указателя для функции FooterFunc}

Параметр Picture является в данном случае необязательным и
служит для удобства функции преобразования. Процедуры
SetHeaderFunc и SetFooterFunc создают лишь копии Picture в
массиве памяти и сохраняют их адреса в зоне pfPicture модуля
PrintField заголовка или подножия. Функция преобразования
может потом при необходимости обратиться к этой маске
изображения.

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

Как мы уже отмечали, OPFORM печатает заголовок вначале любых
других зон, а подножие - после. OPFORM не может применить к
этим особым зонам алгоритм сортировки зон (как описано далее
в параграфе "Как работает PrintForm"). Это приводит к
возникновению пассивного ограничения, выраженного в том, что
для принтеров, чьи печатающие головки не могут
позиционировать произвольно, позиция заголовка на странице
должна быть выше всех остальных зон, а позиция подножия -
ниже. Поскольку именно так обычно и распологаются заголовки
и подножия страниц, такое ограничение не является
существенным, но это важная информация, которую желательно
хорошо запомнить.


    Обработка ошибок

Технология, используемая для обработки ошибок в объектных
модулях PrintedForm и Report, идентична алгоритмам обработки
ошибок, используемым в модуле OPPRINT.  Подробное описание
вы можете отыскать в параграфе "Отладчик ошибок принтера" в
разделе, посвященном OPPRINT.

Любая некорректируемая ошибка, возникшая во время сеанса
работы PrintedForm, становится причиной прерывания операции.
Если установлен пользовательский отладчик ошибок, он будет
вызван для передачи сообщения об ошибке в прикладную
программу.  Если пользовательский отладчик ошибок не
установлен, прикладная программа будет вынуждена обратиться
к алгоритму fGetLastError после запроса алгоритма Process,
который должен определить, прошла операция или сорвалась.

Для установки пользовательского отладчика ошибок, обратитесь
к алгоритму SetErrorHandler из модуля PrintedForm. Этот
алгоритм устанавливает указанную функцию обработки ошибок и
для PrintedForm, и для подчиненного объектного модуля
Printer.

Здесь показано как добавить простой отладчик ошибок в
программу, приведенную в качестве примера в начале раздела.

    {$F+}
    function ErrHen(P : PrinterPtr; ErrorCode : Word;
                    Recoverable : Boolean) : Boolean;
    begin
      if Recoverable then begin
        WriteLn('A recoverable errorhas occurred. ErrorCode = ', ErrorCode);
        WriteLn('Please correct problem and press any key (<Esc> to abort)');
        ErrHan := (ReadKey <> #27);
      end else begin
        WriteLn('A serious printer eror has occurred. ErrorCode =', ErrorCode);
        WriteLn('Aborting prinout...');
        ErrHen := False;
      end;
    end;
    {$F-}

    begin
      BuildFOrm;                       {Инициализация формы}
      Index := 1;                      {Запуск с первой записи}
      FrmPtr^.SetErrorHandler(ErrHen); {Установка отладчика ошибок <------}
      FrmPtr^.Process;                 {Распечатка сообщения}
      Dispose(FrmPtr, Done);           {Очистка}
      Dispose(PrnPtr, Done);
    end.

Такой отладчик ошибок чрезвычайно примитивен, его заботы
дальше экрана не распространяются. Он просто пишет сообщение
в том месте, где в данный момент расположен курсор. Если
OPFORM сочтет данную ошибку корректируемой, отладчик ошибок
дождется нажатия клавиши (обращение к ReadKey требует,
чтобы OPCRT было добавлено в список USES), и выдаст
'истинно', если пользователь нажал любую клавишу, кроме
<Esc>. Если ErrHan выдает 'ложно', алгоритм Process не
допустит распечатки остальной части печатной формы.

Отладчик ошибок устанавливается при помощи обращения к
SetErrorHandler в любой момент после установки объектного
модуля PrintedForm. Если мы установили его раньше (во время
процедуры BuildForm), отладчик станет захватывать также и те
ошибки, которые будут возникать по ходу создания печатной
формы.


    Дополнительные средства диагностики пользователя

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

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

Форменная функция вызывается один раз в начале и один раз в
конце печатной формы. Это полезно использовать только в
тогда, когда вы работаете с объектом Report, состоящим из
множества форм, поскольку в этом случае вам проще работать с
форменной функцией, чем каждый раз обращаться
непосредственно к своей подпрограмме. Если вы работаете с
Report, состоящим из большого количества форм, форменная
функция может использоваться для указания оператору ввести
тип новой формы или переключить кабель для работы с другим
устройством.

Вы активизируете эту диагностическую подпрограмму,
обратившись к следующим алгоритмам из PrintedForm:

    procedure NewBlock(BaseRow, BaseCol : Dimension;
                           BlockFunc : UserBlockFunc);
{- запускается новый блок}

    procedure SetPrePageFunc(ppFunc : UserPageFunc);
{- определяется функция, которая будет вызываться
   перед печатью текущей страницы}

    procedure SetPrePostFormFunc(ppFunc : UserFormFunc);
{- определяется функция, которая будет вызываться
   перед печатью текущей формы}

UserBlockFunc принимает следующую форму:

    {$F+}
    function MyBlockFunc(PB : PrintBlockPtr) : Boolean;


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

Блочная функция осуществляет дальнейшее управление процессом
печати до тех пор, пока не будет получен результат этой
функции. Если блочная функция выдаст значение 'ложно', ни
одной зоны в этом блоке напечатано не будет. Такой принцип
может быть полезен, например, когда вы инициализировали
страницу как 60 одно-строчных много-зонных блоков, но
исчерпали на 30 блоках все данные, которые нужно было
распечатать. OPFORM будет продолжать обращаться к блочной
функции еще 30 раз, но поскольку каждый раз блочная функция
будет выдавать 'ложно', OPFORM ничего не будет печатать ни в
одной из зон блока. Однако, если на странице остались еще
зоны, не связанные с работой блочной функции, как например
подножие страницы, эти зоны будут все же напечатаны.

Функция UserBlockFunc принимает следующую форму:

    {$F+}
    function MyUserPageFunc(PP : PrintPagePtr) : Boolean;

PP является адресом объектного модуля PrintPage, который
предназначается для вывода на печать. Поскольку объектный
модуль PrintPage не описан в этом руководстве, вам
необходимо посмотреть файл OPFORM.PAS, где есть для этого
необходимая информация. Однако в большинстве случаев
использования страничной функции, как мы уже отмечали, у вас
нет необходимости даже обращаться к PP; вместо этого вы
можете использовать глобальную переменную для сохранения
последовательности счета страниц. Если страничная функция
выдаст 'ложно', данная страница не печатается.

Окончательно функция UserFormFunc объявляется следующим
образом:

    {$F+}
    function MyUserFormFunc(PreForm : Boolean;
                                F : PrintedFormPtr) : Boolean;

PreForm дает значение 'истинно', когда эта функция
вызывается перед печатью формы, и 'ложно' - если она
вызывается после того, как форма закончена. F является
адресом объекта PrintedForm, который выводится на печать.
Если форменная функция выдает значение 'ложно', тогда как
PreForm выдает значение 'истинно', форма не будет
напечатана.


    Печатные формы и потоки

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

Единственной неприятностью является то, что для архивизации
и повторной загрузки PrintedForm требуется больше усилий,
чем для большинства остальных объектных модулей. Для
эскалации требований есть две причины:  необходимость
регистрации всех печатных зон в списке зон и регистрации
переменных связанных с этими зонами. Первым делом мы
возьмемся за проблему списка зон.

Вы должны быть удивлены, что список зон может стать для вас
проблемой. Разве не может процедура регистрации постараться
для PrintedForm (PrintedFormStream) и просто
зарегистрировать все его зоны автоматически? Да, может, но
если она так сделает, то компилятор обязан будет считать,
что в вашей программе используются абсолютно все типы зон -
даже если реально используется лишь мизерная их часть - и в
результате, огромная толпа кодов-бездельников будет
прикомпанована к вашей программе безо всякой необходимости.
Кажущееся удобство такой регистрации всех зон для
PrintedForm, по сравнению с ручной регистрацией только тех
типов, которые действительно используются, может очень
дорого обойтись и вам, и вашим друзьям. Поэтому вы должны
осуществить регистрацию вручную.

Каждый реально используемый тип зоны должен быть
зарегистрирован до того, как вы сможете заархивировать вашу
PrintedForm в потоке. И именно эти типы зон должны быть
также зарегистрированы до того, как вы сможете повторно
загрузить их из потока. По этому поводу нами были сделаны
две вещи, упрощающие вашу задачу. Первое, мы объединили два
списка в таблицу (внизу), которая показывает взаимосвязь
между различными алгоритмами AddXxxField и связанными с ними
процедурами регистрации. Эта таблица значительно упрощает
выбор из списка процедуры регистрации для вызова. Второе,
что мы сделали, это предоставили несколько программ, которые
регистрируют по несколько типов зон одновременно:

    AllBasicFieldStream
    All8087FieldStream
    AllDateFieldStream
    AllLineFieldStream
    AllPrintFieldStream

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

Остальные четыре подпрограммы регистрируют лишь определенную
группу типов зон. Типы функций, регистрируемых каждой из
этих подпрограмм, показаны в таблице далее. Типы зон,
помеченные '*' в колонке Basic, регистрируются при помощи
AllBasicFieldStream. '*' в колонке 8087 показывает, что
данная зона регистрируется при помощи All8087FieldStream.
'*' в колонке Line говорит о том, что данная зона
регистрируется при помощи AllLineFieldStream.  А '*' в
колонке Date говорит о том, что зона регистрируется
AllDateFieldStream.

 Подпрограмма      Регистрация
 AddXxxField        в потоке               Basic  8087  Date  Line
 ──────────────────────────────────────────────────────────────────
 AddTextField      не требуется
 AddBooleanField   BooleanPrintFieldStream   *
 AddByteField      BytePrintFieldStream      *
 AddCharField      CharPrintFieldStream      *
 AddIntField       IntPrintFieldStream       *
 AddLongField      LongPrintFieldStream      *
 AddRealField      RealPrintFieldStream      *
 AddShortField     ShortPrintFieldStream     *
 AddStringField    StringPrintFieldStream    *
 AddWordField      WordPrintFieldStream      *
 AddYesNoField     YesNoPrintFieldStream     *
 AddBCDField       BCDPrintFieldStream       *
 AddExtField       ExtPrintFieldStream             *
 AddDblField       DblPrintFieldStream             *
 AddSglField       SglPrintFieldStream             *
 AddCompField      CompPrintFieldStream            *
 AddDateField      DatePrintFieldStream                  *
 AddTimeField      TimePrintFieldStream                  *
 AddBox            LineFieldStream                              *
 AddLine           LineFieldStream                              *
 AddShaded         ShadedFieldStream                            *
 AddDirective      не требуется

Например, если вы хотите загрузить или сохранить
PrintedForm, которая использует зоны строк, текста и
символов, так же как и некоторые типы 8087, для
инициализации поточного управления вы выполняете следующие
обращения:

    var
      S : BufIdStream;
    ...
    S.Init('MYSTREAM', ...);
    S.RegisterHier(FormStream);
    S.RegisterHier(CharPrintFieldStream);
    S.RegisterHier(StringPrintFieldStream);
    S.RegisterHier(All8087FieldStream);

При первом обращении к RegisterHier регистрируются те зоны,
чьи типы были использованы при определении самой
PrintedForm, в остальных обращениях регистрируются зоны, чьи
типы определяем мы сами.

OPFORM связан с еще одной процедурой потоковой регистрации -
ReportStream - которая может вам понадобиться только в том
случае, если вы архивируете в потоке объектный модуль
Report. ReportStream регистрирует все, что делает
FormStream, а также объектный модуль Report.

Вторая проблема включает в себя регистрацию всех переменных,
соответствующих зонам в PrintedForm. В худшем случае, если
другое решение невозможно, вы должны зарегистрировать адреса
всех и каждой из переменных, используя RegisterPointer. И вы
должны будете это сделать дважды: один раз, когда
PrintedForm будет архивироваться, и один раз, когда она
будет загружаться. Несмотря на то, что полностью
автоматизировать эту задачу нельзя, PrintedForm способен
упростить ее для вас, используя принцип "пользовательской
записи". По этому принципу необходимо предположить
следующее: в типичной печатной форме каждая зона печати
соответствует зоне отдельной записи переменной.  Если это
так, то процесс регистрации переменных может стать гораздо
проще.

PrintedForm предлагает алгоритм SetUserRecord, реализующий
принцип "пользовательской записи". SetUserRecord архивирует
базовые адреса указанных записей пользователя во внутренних
зонах данных, и таким образом алгоритмы Load и Store
объектного модуля PrintedForm могут позже использовать их
для косвенной регистрации. Когда данная зона записывается в
потоковый файл, алгоритм Store будет в состоянии определить,
что данная переменная является частью пользовательской
записи и просто запомнит ее смещение относительно начала
записи.

Есть еще одна "закавыка", которую необходимо принять в
расчет перед тем, как использовать SetUserRecord, и касается
она блоков. Мы уже формулировали концепцию блоков в печатной
форме. Отдублированные копии блоков относятся к одной и той
же пользовательской записи. В результате SetUserRecord
должна работать на уровне блоков. Поскольку это так, вы не
можете обращаться к SetUserRecord до тех пор, пока блок не
будет создан. Каждая форма имеет как минимум один блок, но
блок считается не существующим, пока вы не вызовете
NewBlock. Следовательно, вызов SetUserRecord должен быть
размещен после вызова NewBlock.

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

    procedure BuildForm;
    var
      I : Word;
      FS : BufIdStream;
    begin
      {подготовка устройства для печати и формы}
      PrnPtr := New(PrinterPtr, Init('DUMP.PRN', DiskFile, 0));
      if PrnPtr = nil then begin
        WriteLn('Failed to init Printer');
        Halt;
      end;
      FrmPtr := New(PrintedFormPtr, Init(1, PrnPtr));
      if FrmPtr = nil then begin
        WriteLn('Failed to init Form');
        Halt;
      end;

      {Определение зон для формы}
      FrmPtr^.NewPage;
      FrmPtr^.SetHeader(Center(FormExample Header', 80), 1, 1, Dim);
      FrmPtr^.NewBlock(2, 1, GetNexyRecord);
      FrmPtr^.SetUserRecord(DataRec, SizeOf(DataRec));          { <--------- }
      FrmPtr^.AddStringField(1, 'Customer Name:', 2, 3, Bold, 16,
                             2, NullPicture, 2, 20, Dim, 20, DataRec.Name);
      FrmPtr^.AddStringField(3, 'Customer Address:', 3, 3, Bold, 16,
                             4, NullPicture, 3, 20, Dim, 20, DataRec.Addr);
      FrmPtr^.AddWordField(5, 'Days Late:', 4, 3, Bold,  16,
                           6, NullPicture,, 4, 20, Dim, 5, DataRec.DaysLate);
      FrmPtr^.AddRealField(7, 'Overdue Balance:', 4, 30, Bold, 16,
                           8, '$###,###,##', 4, 48, Dim, 2, 11, DataRec.Balance);
      FrmPtr^.AddBox(10, 1, 1, 65, 5, 0, 0, SingleLineBox, Bold);
      for I := 1 to 9 do
        FrmPtr^.DupCyrrentBlock(2+(I*5), 1);

      {запись формы в потоковый файл}
      FS.Init('FORMEX.STM',SCreate, 2048);
      FS.RegisterHier(FormStream);
      FS.RegisterHier(AllPrintFieldStream);
      FS.RegisterPointer(1005, @DataRec);
      FS.RegisterPointer(1006, @GetNextRecord);
      FS.PutPtr(FrmPtr);
      FS.Done;
    end;

Обращение к SetUserRecord помечено стрелкой. Любые блоки,
отдублированные с этого блока, относятся к одной и той же
записи пользователя, и поэтому регистрируются автоматически
с помощью процедуры управления потоком.  В конце процедуры
BuildForm мы создаем новый потоковый файл, регистрируем типы
данных (упростив программу использованием
AllPrintFieldStream), регистрируем адреса записи данных
пользователя (DataRec) и блочной функции (GetNextRecord).
Затем мы обращаемся к потоковому алгоритму PutPtr, который
косвенно обращается к PrintedForm.Store для выписки формы,
затем закрываем потоковый файл.

Тот же метод может быть применен ко множеству независимых
блоков. Достаточно обратиться к SetUserRecord после каждого
вызова NewBlock и указать имена и размеры ассоциативных
записей данных.

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

1)  Если вы не зарегистрировали адрес пользовательской
    записи при архивации печатной формы, процедура сборки
    Load постарается разместить ее автоматически при
    повторной загрузке. Затем вы можете использовать
    GetUserRecord для того, чтобы убедиться в соответствии
    размера переменной записи вашему представлению о нем.

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

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

Далее мы приводим пример иного варианта процедуры
BuildForm, которая загружает форму из потока.

    procedure BuildForm;
    var
      FS : BufIdStream;
    begin
      FS.Init('FORMEX.STM',SOpen, 2048);
      FS.RegisterHier(FormStream);
      FS.RegisterHier(AllPrintFieldStream);
      FS.RegisterPointer(1005, @DataRec);
      FS.RegisterPointer(1006, @GetNextRecord);
      FrmPtr := FS.GetPtr;
      FS.Done;
    end.

В этом примере взят альтернативный метод 2), описанный выше,
по которому мы регистрируем адрес записи данных пользователя
перед загрузкой формы. В результате, функция GetNextRecord
может обратиться, как и раньше, к глобальной переменной
DataRec.


    Как работает PrintedForm

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

Логика управления в пределах алгоритма PrintedForm.Process
выглядит следующим образом:

┌───────────── 1 ──────────────┐
│взять следующую страницу      │<────────────┐
└──────────────────────────────┘             │
  ┌───────────── 2 ──────────────┐           │
  │взять следующий блок          │<───────┐  │
  └──────────────────────────────┘        │  │
  ┌───────────── 3 ──────────────┐        │  │
  │вызвать блочную функцию       │        │  │
  └──────────────────────────────┘        │  │
    ┌────────────── 4 ─────────────┐      │  │
    │взять следующую зону          │<─┐   │  │
    └──────────────────────────────┘  │   │  │
    ┌─────────────── 5 ────────────┐  │   │  │
    │вызвать функцию преобразования│  │   │  │
    └──────────────────────────────┘  │   │  │
    ┌─────────────── 6 ────────────┐  │   │  │
    │добавить к списку печати      ├──┘   │  │
    └────────────────────────────┬─┘      │  │
                                 └────────┘  │
  ┌───────────── 7 ──────────────┐           │
  │упорядочить список печати     │           │
  └──────────────────────────────┘           │
  ┌───────────── 8 ──────────────┐           │
  │напечатать заголовок          │           │
  └──────────────────────────────┘           │
  ┌───────────── 9 ──────────────┐           │
  │взять следующую зону из списка│<────┐     │
  └──────────────────────────────┘     │     │
  ┌───────────── 10 ─────────────┐     │     │
  │напечатать зону               ├─────┘     │
  └──────────────────────────────┘           │
  ┌───────────── 11 ─────────────┐           │
  │напечатать подножие           │           │
  └──────────────────────────────┘           │
  ┌───────────── 12 ─────────────┐           │
  │извлечь страницу              │           │
  └────────────────────────────┬─┘           │
                               └─────────────┘

Процесс печати начинается с передачи головного указателя
скомпанованного списка страниц (блок 1). Отыскивается первый
блок страницы (блок 2) и вызывается блочная функция
пользователя для этого блока (блока 3).

Если блочная функция выдает значение 'истинно', начинается
обработка зон. Отыскивается первая зона (блок 4) и
вызывается функция преобразования этой зоны (блок 5) для
созданиястроки печати из первоначального формата переменной
зоны. Если функция преобразования выдает 'истинно',
указатель зоны добавляется к временному "списку печати".
Этот список создается только на время вывода страницы на
печать и основная его цель заключается в упорядочивании зон
в последовательности их размещения по строкам/колонкам перед
тем, как передавать их на принтер.

Управление циклически возвращается на выбор следующей зоны в
блок (блок 4) и повторяются блоки 5 и 6. Когда все зоны
текущего блока будут обработаны, цикл возвратится обратно к
блоку 2, чтобы выбрать следующий блок печати.  После того,
как все блоки в странице будут обработаны, управление будет
передано блоку 7, где список всех зон будет упорядочен в
порядке физической последовательности на странице.
Дополнительную информацию о том, как осуществляется
упорядочивание зон печати, ищите в коментариях к описанию
алгоритма PrintPage.ppLowerField (OPFORM.IN1).

После того, как список печати будет упорядочен, начинается
непосредственно печать страницы. В первую очередь печатается
заголовок, если он определен (блок 8). После этого
передается первая зона из списка печати (блок 9) и
осуществляется печать этой зоны (блок 10). Для распечатки
зоны сначала посылается управляющая последовательность для
установки соответствующих режимов печати, затем сама строка,
после этого передается отключающая последовательность. Блоки
9 и 10 повторяются до тех пор, пока не будут напечатаны все
зоны из списка печати. После этого печатается подножие (блок
11), если оно определено. Здесь печать страницы завершается,
и она извлекается (блок 12) посредством операции смены
формата. Следующая страница отыскивается и передается из
печатной формы (блок 1) и весь этот процесс повторяется до
тех пор, пока в печатной форме не останется ненапечатанных
страниц.

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

Алгоритм Process объектного модуля Repotr повторяет процесс
печати для каждой печатной формы, являющейся частью
сообщения.


    Константы

    pfSuppressZero   = $00020000; {гашение нулевых значений}
    pfRightJustify   = $00010000; {выравнивание справа}
    pfInhibitAttrOn  = $00008000; {запрет на включение режима печати}
    pfInhibitAttrOff = $00004000; {запрет на отключение режима печати}
    pfInhibitString  = $00002000; {запрет на запись строки зоны}
    pfInhibitPos     = $00001000; {запрет на перемещение
                            печатающей головки в расположение зоны}
    pfHidden         = $00000200; {сокрытие зоны (она не будет напечатана)}

Это флаги расширений для объектных модулей, производных от
PrintField. Они управляют поведением зоны. Обратитесь к
тексту "Расширения зоны", который находится в предыдущем
материале этого раздела.

    DefPadCh : Char = ' ';

Символ, который используется по умолчанию для заполнения зон.

    NullPicture : String[1] = '';

Пустая маска изображения.

    SingleLineBox : BoxCharSet = ('┌└┐┘──││');
    DoubleLineBox : BoxCharSet = ('╔╚╗╝══║║');

Наборы символов для прорисовки прямоугольников одинарной и
двойной линией.


    Типы

    PrintConversionFunc = function(PFP : PrintFieldPtr) : Boolean;

Определение функции преобразования зоны печати. Функция
преобразования печати вызывается перед выводом на печать
каждой зоны. Дополнительную информацию можно найти в
параграфах "Функции преобразования зоны" и "Заголовки и
подножия".

    UserBlockFunc = function (PB : PrintBlockPtr) : Boolean;

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

    UserPageFunc = function (PP : PrintPagePtr) : Boolean;

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

    UserFormFunc = function (PreForm : Boolean; F : FormPtr) : Boolean;

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

    PrintFieldProc = procedure (pfPtr : PrintFieldPtr; var D; FPtr : FormPtr);

Процедура определения типа зоны печати, используемая
алгоритмом VIsitAllPrintFields.

    BoxCharType = frTL..frRR;
    BoxCharSet = array[BoxCharType] of Char;

Символьные элементы для подпрограммы прорисовки
прямоугольников. Обратите внимание на то, что константы
frTL..frRR согласованы с такими же константами,
определенными в OPCRT, но в OPFORM объявляются повторно для
того, чтобы избежать необходимости использования OPCRT в
этом модуле.

    PrintFieldPtr = ^PrintField;
    PrintField =
      object(AbstractField)
        ...
      end;

Зона абстрактного типа, из которой получают все
действительные зоны печати. Существует около 20 подобных
типов, в том числе StringPrintField, CharPrintField и так
далее. Смотрите параграф "Добавление зон к печатной форме",
где есть дополнительная информация. PrintField предназначен
исключительно для внутреннего использования; однако,
несколько его алгоритмов могут быть использованы при
создании заказных функций преобразования или других высоко
эффективных прикладных программ, применяющих OPFORM. Все эти
алгоритмы описаны в следующем разделе. В типовой ситуации
вы будете осуществлять доступ к PrintField при помощи
обращения к алгоритму FindFild, связанному с PrintField, для
получения его адреса.

    PrintBlockPtr = ^PrintBlock;
    PrintBlock =
      object(SingleListNode)
        ...
      end;

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

    PrintPagePtr = ^PrintPage;
    PrintPage =
      object(SingleListNode)
        ...
      end;

Другой объектный модуль промежуточного уровня,
предназначенный для организации зон. Печатная форма содержит
список страниц печати. Алгоритмы PrintPage используются
скрыто и здесь не описаны.

    PrintFormPtr = ^PrintForm;
    PrintForm =
      object(SingleListNode)
        ...
      end;

Центральный объектный модуль OPFORM. Его алгоритмы описаны в
последующем материале.

    ReportPtr = ^Report
    Report =
      object(Root)
        ...
      end;

Report содержит список печатных форм. Его алгоритмы также
описаны далее.

    Алгоритмы OPFORM\PrintField

    Convert
 ───────────────────────────────────────────────────────────
Объявление
  function Convert : Boolean; virtual;
Назначение
  Преобразует зону из первоначальной записи в строку
  печати.
Описание
  Функция преобразования является виртуальным алгоритмом,
  который вызывается во время вывода зоны на печать. Если он
  принимает значение 'истинно', то зона печатается; в
  противном случае зона игнорируется. Ваша программа не
  будет обращаться непосредственно к этому алгоритму.
  Convert сама по себе ничего не выполняет, но вызывает для
  поддержки соответствующую функцию преобразования,
  используя указатель, заархивированный вместе с зоной
  печати. Каждый объектный модуль, производный от
  PrintField, имеет стандартную функцию преобразования. Эти
  функции, так же как и представление о заказных функциях
  преобразования, описаны в параграфе "Функции
  преобразования зоны".


    GetFieldAttr
 ───────────────────────────────────────────────────────────
Объявление
  funсtion GetFieldAttr : Byte;
Назначение
  Передает атрибут печати для данной зоны.
Описание
  Этот атрибут печати является кодом идентификатора
  режима печати, который относится к объектному модулю
  Printer данной печатной формы. Таким образом, этот атрибут
  печати косвенно определяет, с какой управляющей
  последовательности начинается и какой последовательностью
  заканчивается вывод данной зоны на печать.


    GetFieldPos
 ───────────────────────────────────────────────────────────
Объявление
  procedure GetFieldPos(var Row, Col : Dimension);
Назначение
  Передает координаты строки и колонки для данной зоны.
Описание
  GetFieldPos передает абсолютные координаты зоны в пределах
  страницы, которые будут использованы при выводе на печать
  данной зоны. Обычно координаты задаются в единицах, равных
  соответственно высоте и ширине символа, но при
  необходимости они могут быть переданы в единицах, равных
  1/720 дюйма.


    pfOptionsAreOn
 ───────────────────────────────────────────────────────────
Объявление
  funсtion pfOptionsAreOn(OptionFlags : LongInt) : Boolean;
Назначение
  Определяет, все ли флаги расширений установлены.
Описание
  Этот алгоритм может быть использован для проверки
  состояния одного или более флагов расширений. Функция
  выдает значение 'истинно' в том случае, если все
  расширения, чьи параметры переданы в OptionFlags,
  установлены.

Пример

    if PfPtr^.pfOptionsAreOn(pfSuppressZero) then
      WriteLn('This PrintField will not print if its data value is 0');

  Если зона печати имеет расширение SuppressZero, на экран
  выводится сообщение "Эта зона печати не будет напечатана,
  если значение ее данных равно нулю".


    pfOptionsOff
 ───────────────────────────────────────────────────────────
Объявление
  procedure pfOptionsOff(OptionFlags : LongInt);
Назначение
  Отключает все расширения.
Описание
  Этот алгоритм отключит все расширения, переданные в
  OptionFlags, где может быть одно или более расширений.

Пример

    PfPtr^.pfOptionsOff(pfRightJustify);

  Гасит флаг расширения pfRightJustify, и таким образом зоны
  печати по всех последующих операциях будут выровнены
  слева.


    pfOptionsOn
 ───────────────────────────────────────────────────────────
Объявление
  procedure pfOptionsOn(OptionFlags : LongInt);
Назначение
  Активизирует все расширения.
Описание
  Этот алгоритм включит все расширения, переданные в
  OptionFlags, где может быть одно или более расширений.

Пример

    PfPtr^.pfOptionsOn(pfInhibitAttrOn+pfInhibitAttrOff);

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

    PfPtr^.pfOptionsOn(pfHidden);

  Зона будет скрыта (не будет выведена на печать вообще).


    pfSetPicture
 ───────────────────────────────────────────────────────────
Объявление
  procedure pfSetPicture(NewPicture : String);
Назначение
  Устанавливает маску изображения.
Описание
  Этот алгоритм позволяет менять маски изображения
  "на лету".

Пример

    PfPtr^.pfSetPicture('######');

  Меняет маску изображения для зоны печати, помеченную при
  помощи PfPtr, на '######'.


    Print
 ───────────────────────────────────────────────────────────
Объявление
  procedure Print(PrinterP : PrinterPtr); virtual;
Назначение
  Печатает зону.
Описание
  PrintedField и другие объектные модули обращаются к этому
  алгоритму для вывода на печать каждой зоны. Ваша
  прикладная программа не будет непосредственно обращаться к
  этому алгоритму. Он описывается здесь только для того,
  чтобы объяснить, что при этом происходит. Вывод зоны на
  печать разделяется на следующие этапы:

    1.Включение атрибута печати зоны.
    2.Перемещение печатающей головки в строку и колонку
      начальной позиции зоны.
    3.Печать зоны.
    4.Отключение атрибута печати зоны.

  Любой из этих этапов может быть опущен. Расширение зоны
  печати pfInhibitAttrOn отключает этап 1, pfInhibitPos
  отключает этап 2, pfInhibitString отключает этап 3 и
  pfInhibitAttrOff отключает этап 4.
  Процедура Print обслуживает некоторые режимы, например
  прорисовка линий на лазерном принтере, иначе.

Справки
  Printer.PrintStr


    SetConversion
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetConversion(CovertFunc : PrintConversionFunc);
Назначение
  Изменяет указатель функции преобразования, установленный
  при помощи PrintField.
Описание
  Эта процедура позволяет заменять функции преобразования
  "на лету". Дополнительную информацию можно найти в
  параграфе "Функции преобразования зоны".

Пример

    {$F+}
    function MySpecialConvert(PFP : PrintFieldPtr) : Boolean;
    begin
    ...
    end;
    {$F-}
    ...
    PfPtr := MyForm.FindField(22, PP, BP);
    if PfPtr <> nil then
      PfPtr^.SetConversion(MySpecialConvert);

  Отыскивает зону 22 в печатной форме MyForm и заменяет
  функцию ее преобразования на MySpecialConvert.

Справки
  PrintedForm.ChangeConversion           Convert


    SetFieldPos
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetFieldPos(Row, Col : Dimension);
Назначение
  Устанавливает координаты строки и колонки для данной зоны
  печати.
Описание
  Этот алгоритм позволяет изменять "на лету" координаты
  строки и колонки текущей зоны. Вы можете изменять
  координаты после того, как будет создана печатная форма,
  внутри блочной функции пользователя и внутри страничной
  функции пользователя. OPFORM не станет признавать эти
  координаты до тех пор, пока не упорядочит зоны в
  соответствии с их расположением (смотрите параграф "Как
  работает PrintedForm").

Пример

    PfPtr^.SetFieldPos(10, 1);

  Изменяет координаты строки и колонки для зоны печати
  помеченной при помощи PfPtr.


OPFORM\PrintedForm

    ChangeConversion
 ───────────────────────────────────────────────────────────
Объявление
  procedure ChangeConversion(FieldID : Word;
                             ConvertFunc : PrintConversionFunc);
Назначение
  Изменяет функцию преобразования зоны по номеру ее
  идентификатора FieldID.
Описание
  При добавлении каждой зоны печати, ей присваивается номер
  идентификатора. Этот номер не должен быть обязательно
  уникальным для каждой зоны, хотя обычно это так. Если
  параметр FieldID, переданный в ChangeConversion, является
  идентификатором, используемым более чем одной зоной, то
  для всех этих зон будет изменена функция преобразования.
  Если будет передан неправильный идентификатор, то ничего
  не произойдет.  За дополнительной информацией обратитесь к
  параграфу "Функции преобразования зоны".

Пример

    MyForm.ChangeConversion(NameID, NewConversionFunc);

  В этом примере для всех зон, чьи идентификаторы равны
  FieldID, функции преобразования изменяются на
  NewConversionFunc.

Справки
  PrintField.Convert             PrintField.SetConversion


    Done
 ───────────────────────────────────────────────────────────
Объявление
  destructor Done; virtual;
Назначение
  Удаляет объектный модуль PrintedForm.
Описание
  Данная процедура ликвидации освобождает весь массив
  памяти, который был использован для реализации PrintedForm
  (и всех подчиненных ему объектных модулей). Обратите
  внимание, однако, на то, что Done не закрывает и не
  ликвидирует объектный модуль Printer, связанный с данной
  печатной формой.

Пример

    MyForm.Done;
    MyPrinter.Done;

  Ликвидирует статический объектный модуль MyForm,
  освобождает весь массив занимаемой им памяти.
  MyPrinter.Done ликвидирует объектный модуль Printer,
  использованный для MyForm, установив тем самым, что
  никакая другая выходная информация не должна будет
  передаваться в него.

    var
      MyFormPtr : FormPtr:
    ...
    Dispose(MyFormPtr, Done);

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

Справки
  Init                           Load
  InitCustom

  DupCurrentBlock
 ───────────────────────────────────────────────────────────
Объявление
  procedure DupCurrentBlock(NewRow, NewCol : Dimension);
Назначение
  Дублирует текущий блок в блок с новыми координатами.
Описание
  Все зоны, добавленные после того, как будет определен
  самый последний блок, дублируются в новый блок. Координаты
  зоны переписываются, используя новые значения NewRow и
  NewCol как базовые. После дублирования блока вы можете
  добавить в него, если желаете, индивидуальную зону. Во
  вводной части этого раздела приведен один пример
  использования DupCurrentBlock; другой пример содержит
  демонстрационная программа FORMTEST.

Пример

    var
      Frm : FormPtr;
    ...
    with Frm^ do begin
      NewPage;
      NewBlock(2, 1, UserBlockFunc);
      AddStringField(11, AcctNumPrompt, 1, 1, Italic, 10,
                     12, NullPicture, 1, 18, Dim, 20, AcctNum);
      AddStringField(13, NamePrompt, 2, 1, Italic, 10,
                     14, NullPicture, 2, 18, Dim, 20, Name);
      DupCurrentBlock(6, 1);
      DupCurrentBlock(10, 1);
    end;

  Начинается новая страница, открывается новый блок и
  добавляется две строчных зоны. Блок копируется в новый
  блок, который затем копируется в третий. Так создаются три
  блока, каждый из которых начинается в колонке 1, строках
  2, 6 и 10.

Справки
  NewBlock

    DupCurrentPage
 ───────────────────────────────────────────────────────────
Объявление
  procedure DupCurrentPage;
Назначение
  Дублирует текущую страницу.
Описание
  Все блоки, зоны, заголовки и подножия, добавленные после
  последнего обращения к NewPage, дублируются и копируются
  на новую страницу. Это особенно удобно для распечатки
  отчетов, где таблицы распространяются более, чем на одну
  страницу.

Пример

    var
      Frm : FormPtr;
      I : Integer;
    ...
    with Frm^ do begin
      NewPage;
      NewBlock(1, 1, UserBlockFunc);
      AddStringField(11, AcctNumPrompt, 1, 1, Italic, 10,
                     12, NullPicture, 1, 18, Dim, 20, AcctNum);
      AddStringField(13, NamePrompt, 1, 40, Italic, 10,
                     14, NullPicture, 1, 58, Dim, 20, Name);
      for I := 2 to 50 do
        DupCurrentBlock(I, 1);
      for I := 2 to 10 do
        DupCurrentPage;
    end;

  Две строчные зоны размещаются на одной и той же строке для
  формирования заголовка. DupCurrentBlock добавляет блоки
  для построения 50 линий на всю страницу. DupCurrentPage
  затем добавляет страницы для создания 10 страниц на весь
  отчет.  При создании соответствующих блочной и страничной
  функций (смотрите параграф "Дополнительные средства
  диагностики пользователя"), эта форма может корректно
  распечатывать от 1 до 500 позиций строк.  Справки
  DupCurrentBlock                        NewPage


    fGetLastError
 ───────────────────────────────────────────────────────────
Объявление
  funсtion fGetLastError : Word;
Назначение
  Передает код последней ошибки и сбрасывается в ноль.
Описание
  Нулевое значение этой функции говорит об отсутствии
  ошибки. Отличное от нулевого значение функции может быть
  интерпретировано с помощью инструкций в приложении D.
  После вызова этого алгоритма, внутренний код ошибки
  сбрасывается в ноль.
  Если вы не установили отладчик ошибок для печатной формы,
  то после каждого обращения к PrintedForm вы должны
  обращаться к этой функции, которая сможет генерировать
  ошибку.

Пример

    Frm^.Process:
    ErrorCode := Frm^.fGetLastError;
    if ErrorCode <> ) then
      {обработка ошибки};

  В этом примере происходит обращение к алгоритму Process,
  затем величине ErrorCode присваивается значение кода
  ошибки.

Справки
  BasePrinter.PrnStatus


    FieldOptionsAreOn
 ───────────────────────────────────────────────────────────
Объявление
  funсtion FieldOptionsAreOn(OptionFlags : LongInt) : Boolean;
Назначение
  Выдает значение 'истинно', если указанное по умолчанию
  расширение зоны находится во включенном состоянии.
Описание
  Объектный модуль PrintedForm устанавливает две величины
  LongInt, которые используются как флаги расширений при
  добавлении зон. Одна из них архивирует флаги расширений
  для участка указателя зоны, а другая - для участка данных
  зоны. Этот алгоритм воздействует на флаги, установленные
  на участке данных зоны.

Пример

    if MyForm.FieldOptionsAreOn(pfRidhtJustify) then
      WriteLn('All subsequent fields added will be right justified');

  Если соответствующий флаг расширения установлен, то на
  экран выводится сообщение "Все последующие добавленные
  зоны будут выровнены справа".

Справки
  FieldOptionOn


    FieldOptionsOff
 ───────────────────────────────────────────────────────────
Объявление
  procedure FieldOptionsOff(OptionFlags : LongInt);
Назначение
  Отключает все флаги расширений по умолчанию.
Описание
  Этот алгоритм воздействует на расширения по умолчанию,
  находящиеся на участке данных в зонах, которые будут
  добавляться после этого.

Пример

    MyForm.FieldOptionsOff(pfRidhtJustify)

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

Справки
    FieldOptionsOn


    FieldOptionsOn
 ───────────────────────────────────────────────────────────
Объявление
  procedure FieldOptionsOn(OptionFlags : LongInt);
Назначение
  Включает все флаги расширений по умолчанию.
Описание
  Этот алгоритм воздействует на расширения по умолчанию,
  находящиеся на участке данных в зонах, которые будут
  добавляться после этого.

Пример

    MyForm.FieldOptionsOn(pfRidhtJustify)

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

Справки
    FieldOptionsAreOn            FieldOptionsOff


    FindField
 ───────────────────────────────────────────────────────────
Объявление
  function FindField(FieldID : Word; var Page : PrintPagePtr;
                     var Block : PrintBlockPtr) : PrintFieldPtr;
Назначение
  Находит первую зону по указанному идентификатору зоны.
Описание
  Этот алгоритм используется вместе с FindNextField для
  отыскания одной или более зон по указанному
  идентификатору. Указатель PrintField первой подходящей
  зоны выдается как результат функции. Этот указатель может
  быть использован для доступа к алгоритмам PrintField или
  зонам данных с целью модификации расширений зоны, ее
  расположения, маски изображения или функции
  преобразования.
  Если зон с указанным идентификатором нет, то результатом
  функции будет величина nil.
  Указатели на PrintPage и PrintBlock, связанные с данной
  зоной, передаются тоже.

Пример

    Field := MyForm.FindField(FieldID, Page, Block);
    while Field <> nil do begin
      {сокрытие зоны}
      Field^.pfOptionsOn(pfHidden);
      {переход к следующей зоне с тем же идентификатором,
       если таковая имеется}
      Field := MyForm.FindNextField(Field, Page, Block);
    end;

  Этот код находит все зоны с FieldID соответствующим номеру
  указанного идентификатора и делает их скрытыми.


  FindNextField
 ───────────────────────────────────────────────────────────
Объявление
  function FindNextField(Field : PrintFieldPtr; var Page : PrintPagePtr;
                     var Block : PrintBlockPtr) : PrintFieldPtr;
Назначение
  Отыскивает следующую зону с тем же идентификатором,
  что и зона Field.
Описание
  Эта функция создана для использования совместно с функцией
  FindField. Она берет указатель зоны печати и ищет
  следующую зону печати с тем же идентификатором.
  FindNextField выдает указатель на следующую зону печати
  или 'nil', если такой зоны нет.
  Указатели на PrintPage и PrintBlock, связанные с данной
  зоной, передаются тоже.
Пример
  Смотрите пример для функции FindField.


  fPrinterPtr
 ───────────────────────────────────────────────────────────
Объявление
  function fPrinterPtr : fPrinterPtr;
Назначение
  Передает адрес объектного модуля Printer, с которым
  связана данная печатная форма.
Описание
  Переданный указатель дает вам возможность доступа ко всем
  алгоритмам и данным объектного модуля Printer. Если вы
  создали форму с использованием процедуры Init, то вы,
  вероятно, будете где-то иметь глобальную переменную
  Printer, и в результате выполнения fPrinterPtr должен быть
  получен адрес именно этой переменной. Если вы загрузили
  форму из потока, то она будет иметь динамически
  размещаемый Printer, и функция fPrinterPtr является
  единственным способом доступа к ней.

Пример

    MyForm^.fPrinterPtr^.GetPrinterInfo(LPI, CPI, Point, LPP);

  Передает параметры: число строк на дюйм, символов на дюйм
  и строк на страницу для реализации Printer, связанного с
  MyPrinter.

    if MyForm^.fPrinterPtr^.PrinterReady then
      MyForm^.Process;

  Проверяет готовность принтера перед выводом формы на
  печать.

Справки
  Printer.GetBasePrinter


    GetFooter
 ───────────────────────────────────────────────────────────
Объявление
  function GetFooter : String;
Назначение
  Передает строку подножия для текущей страницы.
Описание
  Если не указана строка подножия при помощи обращения к
  SetFooter, этот алгоритм передает пустую строку. В ином
  случае GetFooter передает либо величину, указанную при
  добавлении подножия; либо самую последнюю из строк,
  заархивированных функцией преобразования подножия.

Пример

    var
      Foot : String;
    ...
    Foot := MyForm.GetFooter;
    if Foot = '' then
      WriteLn('No footer string is currently defined')
    else
      WriteLn('The current footer string is ', Foot);

  В этом примере текущая строка подножия печатной формы
  MyForm передается в переменную Foot. Если это пустая
  строка, то на экран выдается сообщение "Текущая строка
  подножия не определена". В противном случае на экран
  выводится строка подножия.

Справки
  SetFooter                      SetFooterFunc


    GetHeader
 ───────────────────────────────────────────────────────────
Объявление
  function GetHeader : String;
Назначение
  Передает строку заголовка для текущей страницы.
Описание
  Если не указана строка заголовка при помощи обращения к
  SetHeader, этот алгоритм передает пустую строку. В ином
  случае GetHeader передает либо величину, указанную при
  добавлении заголовка; либо самую последнюю из строк,
  заархивированных функцией преобзования заголовка.
Пример
  Смотрите пример в GetFooter.
Справки
  SetHeader                      SetHeaderFunc


    GetUserData
 ───────────────────────────────────────────────────────────
Объявление
  function GetUserData : Pointer;
Назначение
  Передает адрес зоны данных пользователя.
Описание
  Каждая реализация PrintedForm обслуживает четыре байта
  данных, которые могут быть использованы прикладной
  программой любым необходимым для нее способом. Обычный
  способ их использования заключается в передаче информации
  о номере страницы или записи между прикладной программой и
  блочной функцией, страничной функцией или функцией
  преобразования заголовка/подножия.
  Внутренняя область данных имеет тип указателя, поэтому она
  может обращаться к более обширным областям коллективного
  архива. Используйте преобразование типа области данных в
  LongInt, если требуется 4 байта скалярного массива памяти.

Пример

    {$F+}
    function GetNextRecord(P : PrintBlockPtr) : Boolean;
    var Index : LongInt;
    begin
      Index := LongInt(P^.GetUserData);
      if Index > 10 then
        GetNextRecord := False
      else with DataRec do begin
        {Заполнение данными глобальной записи var}
        Name := 'Name' + Long2Str(Index);
        Addr := 'Address' + Long2Str(Index);
        DaysLate := 120;
        Balance := 12.34;
        Inc(Index);
        P^.SetUserData(Pointer(Index));
        GetNextRecord := True;
      end;
    end;

  Избегайте использования глобальной переменной для блочной
  функции GetNextRecord вводного примера. Обратите внимание
  на то, как результат GetUserData изменяет свой тип на
  LongInt, и как SetUserData изменяет тип Index на Pointer.

Справки
  SetUserData


    GetUserRecord
 ───────────────────────────────────────────────────────────
Объявление
  function GetUserRecord : Pointer;
Назначение
  Передает адрес записи пользователя для текущего блока.
Описание
  Этот алгоритм передает указатель на "запись пользователя".
  Это необходимо только в тех случаях, когда печатная форма
  загружается из потока и конструктор Load должен
  локализовать эту запись. Обратитесь к параграфу "Печатные
  формы и потоки" в начале этого раздела.
Справки
  Load


    GetUserRecordSize
 ───────────────────────────────────────────────────────────
Объявление
  function GetUserRecordSize : Word;
Назначение
  Передает размер записи пользователя для текущей страницы.
Описание
  Этот алгоритм передает размер "записи пользователя",
  размещенной в тот момент, когда конструктор Load считывал
  форму способом 1), как описано выше в параграфе "Печатные
  формы и потоки".
Справки
  Load

    Init
 ───────────────────────────────────────────────────────────
Объявление
  constructor Init(FormID : Byte; PrinterP : PrinterPtr);
Назначение
  Инициализирует печатную форму.
Описание
  Эта процедура сборки реализует PrintedForm. Форме
  присваивается специфический идентификатор FormID, к ней
  присоединяется уже созданная реализация Printer,
  определенная как PrinterP. Параметры всех полей при этом
  обнуляются для того, чтобы они не могли оказать какого
  либо воздействия. Смотрите описание InitCustom, если вас
  интересуют подробности.

Пример

    if not Frm.Init(1, @MyPrinter) then begin
      WriteLn('Failed to init PrintedForm. Status = ', InitStatus);
      Halt;
    end;

  При помощи обращения к Frm с идентификатором равным 1
  реализуется печатная форма.  Она будет использовать
  статическую реализацию Printer (MyPrinter), который, как
  предполагается, уже установлен.

Справки
  InitCustom


    InitCustom
 ───────────────────────────────────────────────────────────
Объявление
  constructor InitCustom(FormID : Byte;
                         TopMargin, BottomMargin,
                         LeftMargin, RightMargin : Dimension;
                         PrinterP : PrinterPtr);
Назначение
  Инициализирует объектный модуль PrintedForm со
  специально оговоренными параметрами.
Описание
  Процедура специальной сборки InitCustom создает
  PrintedForm, используя указанный идентификатор,
  первоначальные значения полей и реализацию Printer.
  Расположение зоны устанавливается относительно указанных
  верхнего поля и левого поля. Например, если TopMargin =
  5.0, то все координаты строк, указанных в обращениях к
  AddXxxField, будут увеличены на величину 5.0. Значения
  нижнего поля и правого поля архивируются в реализованном
  модуле, но в данном случае не влияют на поведение
  PrintedForm.
  Если процедура InitCustom будет сорвана, необходимо
  проверить код ошибки в глобальной переменной InitStatus.

Пример

    if not Frm.InitCustom(1, TM, BM, LM, RM, MyPrinter) then
      WriteLn('Failed to Init PrintedForm, Status = ',InitStatus);
      Halt;
    end;

  Реализуется PrintedForm с именем Frm, идентификатором
  равным 1 и указанными полями. Frm будет использовать
  реализацию Printer, помеченную указателем MyPrinterPtr.

Справки
  Init

    Load
 ───────────────────────────────────────────────────────────
Объявление
  constructor Load(var S : IdStream);
Назначение
  Загружает печатную форму из потока.
Описание
  S представляет собой объект потока, инициализированный
  соответствующим образом (например, последовательный файл
  потока, всегда открытый по чтению). Обратите внимание, что
  S может быть любой величиной, производной от IdStream, в
  том числе DosIdStream, BufIdStream и MemIdStream.
  Load считывает очередную последовательность байтов из
  потока S. Эти байты должны быть записаны по предыдущему
  обращению к PrintedForm.Store. Содержимое байта включает в
  себя информацию о всех зонах, блоках и страницах формы.
  Всевозможные типы зон, используемые печатной формой,
  должны быть зарегистрированы до обращения к Load. Если это
  так, то должны быть зарегистрированы: адрес "записи
  пользователя", адреса всех пользовательских подпрограмм,
  связанных с PrintedForm (функций преобразования, функций
  преобразования заголовков/подножий, блочных функций,
  страничных функций и отладчиков ошибок).
  Потоковой процедурой регистрации для PruntedForm является
  FormStream.
  Подробности работы этого алгоритма описаны в параграфе
  "Печатные формы и потоки" в начале этого раздела.
Справки
  Store

    NewBlock
 ───────────────────────────────────────────────────────────
Объявление
  procedure NewBlock(BaseRow, BaseCol : Dimension;
                     BlockFunc : UserBlockFunc);
Назначение
  Открывает новый блок.
Описание
  Каждая страница печатной формы должна иметь как минимум
  один блок; следовательно, вызов NewBlock должен
  происходить после вызова NewPage, но до добавления какой
  либо зоны. Координаты зон, добавленных после обращения к
  этой процедуре, должны быть установлены относительно
  верхнего и левого полей формы, а также базовых координат
  строки (BaseRow) и колонки (BaseCol) массива, переданного
  в NewBlock. Значения этих координат могут быть нулевыми.
  Функция, определенная при помощи BlockFunc, будет
  вызываться непосредственно перед выводом блока на печать.
  Специальная блочная функция, называемая NoBlockFunc, имеет
  связь с OPFORM, и может быть определена для того, чтобы
  сигнализировать об отсутствии необходимости осуществлять
  какое либо действие.
  Минимально допустимое число блоков на странице - один.
  Максимальное количество блоков на странице ограничивается
  лишь возможностями памяти.

Пример

    with Frm^ do begin
      NewPage;
      NewBlock(0, 0, NoBlockFunc);
      AddStringField(11, AcctNumPrompt, 1, 1, Italic, 10,
                     12, NullPicture, 1, 18, Dim, 20, AcctNum);
      ...
    end;

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

    with Frm^ do begin
      NewPage;
      NewBlock(2, 2, GetNextRecord);
      ...
    end;

  Определяется новая страница со смещенным расположением
  блока в строке 2 и колонке 2. Перед тем, как реализовать
  блок, вызывается процедура GetNextRecord.

Справки
  NewPage


    NewPage
 ───────────────────────────────────────────────────────────
Объявление
  procedure NewPage;
Назначение
  Начинает новую страницу.
Описание
  Каждая печатная форма должна иметь как минимум одну
  страницу, NewPage должна вызываться после запуска
  PrintedForm и до вызова NewBlock, который тоже требуется.
  Все зоны, добавляемые после вызова NewPage и NewBlock,
  добавляются к объектному модулю более низкого уровня
  PrintPage.
  Для того, чтобы начать новую страницу печатной формы,
  вызывайте снова NewPage и NewBlock. Когда форма будет
  напечатана, OPFORM осуществит вызов Printer.FormFeed для
  извлечения предыдущей страницы и загрузки новой.
  Не используйте обращение к NewPage для завершения
  последовательности кода построения печатной формы.

Пример

    MyForm.NewPage;
    MyForm.NewBlock(0, 0, PrepFirstPage);
    MyForm.AddStringField(...);
    MyForm.NewPage;
    MyForm.NewBlock(0, 0, PrepSecondPage);
    MyForm.AddStringField(...);

  Создается печатная форма, состоящая из двух страниц,
  каждая из которых включает по одному блоку.

Справки
  NewBlock


    Process
 ───────────────────────────────────────────────────────────
Объявление
  procedure Process; virtual;
Назначение
  Печатает форму PrintedForm.
Описание
  Обратитесь к параграфу "Как работает PrintedForm", где
  подробно описана работа этой процедуры.
  Поскольку Process передает выходные данные на физическое
  устройство печати, необходимо до обращения к этой
  процедуре быть уверенным, что принтер находится в
  состоянии готовности, а после обращение к ней
  проконтролировать сообщения об ошибках. Вы можете, однако,
  установить отладчик ошибок при помощи SetErrorHandler и
  дать возможность OPFORM информировать о появлении ошибок.

Пример

    Aborting := False;
    while not (MyForm.fPrinterPtr^.PrinterIsReady or Aborting) do begin
      WriteLn('Printer not ready, press any key when ready (<Ecs> to Abort)');
      Aborting := (ReadKey = #27);
    end;
    if not Aborting then begin
      MyForm.Process;
      ErrorCode := MyForm.fGetLastError;
      if ErrorCode <> 0 then
        {обработка ошибок};
    end;

  Ожидается, пока принтер не будет готов; затем печатается
  форма, а после контролируются ошибки.

Справки
  Printer.PrinterIsReady


    PromptOptionsAreOn
 ───────────────────────────────────────────────────────────
Объявление
  function PromptOptionsAreOn(OptionFlags : LongInt) : Boolean;
Назначение
  Выдает значение 'истинно', если включены указанные
  расширения указателя по умолчанию.
Описание
  Объектный модуль PrintedForm устанавливает две целые
  величины LongInt, которые используются в качестве флагов
  расширений для добавляемых зон. Одна из них сохраняет
  флаги расширений, которые используются для участка
  указателя зоны, а другая - для участка данных зоны. Этот
  алгоритм оказывает воздействие на флаги указателя.

Пример

    if MyForm.PromptOptionsAreOn(pfRightJustify) then
      WriteLn('Prompts are being right-justified');

  При установке соответствующего флага, на экран выводится
  сообщение 'Указатели добавляемых зон будут выровнены
  справа'.

Справки
  PromptOptionsOn


    PromptOptionsOff
 ───────────────────────────────────────────────────────────
Объявление
  procedure PromptOptionsOff(OptionFlags : LongInt);
Назначение
  Отключает все расширения указателя по умолчанию.
Описание
  Этот алгоритм воздействует на расширения по умолчанию для
  участка указателя зон, добавляемых после выполнения этой
  процедуры.

Пример

    MyForm.PromptOptionsOff(pfHidden);
    MyForm.FieldOptionsOff(pfHidden);
    ...

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

Справки
  PromptOptionsOn


    PromptOptionsOn
 ───────────────────────────────────────────────────────────
Объявление
  procedure PromptOptionsOn(OptionFlags : LongInt);
Назначение
  Включает все расширения указателя по умолчанию.
Описание
  Этот алгоритм воздействует на расширения по умолчанию для
  участка указателя зон, добавляемых после выполнения этой
  процедуры.

Пример

    MyForm.PromptOptionsOn(pfHidden);
    MyForm.FieldOptionsOn(pfHidden);
    ...

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


    SetErrorHandler
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetErrorHandler(ErrorFunc : PrintErrorFunc);
Назначение
  Устанавливает пользовательский отладчик ошибок.
Описание
  Этот алгоритм устанавливает пользовательский отладчик
  ошибок как для реализации PrintedForm, так и для
  подчиненной реализации Printer.
  Подробное описание можно найти в параграфе "Обработка
  ошибок" в первой части этого раздела.

Пример

    {$F+}
    function OurErrorHandler(P : PrinterPtr; ErrorCode : Word;
                             Recoverable : Boolean) : Boolean;
    begin
      if Recoverable then begin
        WriteLn('A recoverable error has occured. ErrorCode = ', ErrorCode);
        WriteLn('Please correct problem and press any key (<Esc> to abort)');
        OurErrorHandler := (ReadKey <> #27);
      end else begin
        WriteLn('A serious printer error has occurred. ErrorCode = ', ErrorCode);
        WriteLn('Aborting printout...');
        OurErrorHandler := False
      end;
    end;
    {$F-}
    ...
    Frm^.SetErrorHandler(OurErrorHandler);

  Объявляется функция PrintErrorFunc, названная
  OurErrorHandler, которая затем устанавливается как
  поользовательский отладчик для реализации печатной формы
  Frm^.

Справки
  Printer.SetPrintErrorFunc

    SetFooter
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetFooter(FooterStr : String; Row, Col : Dimension; FAttr : Byte);
Назначение
  Определяет подножие для текущей страницы.
Описание
  FooterStr определяет строку для печати, Row и Col
  определяют ее положение на странице, а FAttr является
  атрибутом печати. Если строка подножия будет
  обрабатываться функцией преобразования подножия, будьте
  уверены в том, что первоначальная строка, переданная как
  FooterStr, имеет как минимум длину максимально
  определенной версии для заказанной строки.
  За дополнительной информацией обратитесь к параграфу
  "Заголовки и подножия" в начале этого раздела.

Пример

    MyForm.SetFooter('Some Footer', MyForm.fPrinterPtr^.GetLPP-1, 10, Italic);

  Определяется подножие, которое будет напечатано при помощи
  атрибута Italic в десятой колонке, на следующей строке
  после последней строки страницы.


    SetFooterFunc
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetFooterFunc(Picture : String; FootFunc : PrintConversionFunc);
Назначение
  Устанавливает указатель для функции подножия текущей
  страницы.
Описание
  Функция подножия вызывается после того, как все остальные
  зоны будут напечатаны, непосредственно перед выводом на
  печать самого подножия. Указанная функция может заказать
  строку подножия в пределах первоначально определенной
  длины подножия. SetFooterFunc сохраняет Picture в зоне
  маски изображения реализации PrintField подножия, где
  функция подножия может использовать эти данные по своему
  усмотрению.
  За дополнительной информацией обратитесь к параграфу
  "Заголовки и подножия" в начале этого раздела.

Пример

    {$F+}
    function FooterConversion(PFP : PrintFieldPtr) : Boolean;
    const
      Page : Word = 0;
    begin
      FooterConversion := True;
      Inc(Page);
      with PFP^ do
        pfString^ := Long2Str(Page);
    end;
    {$F-}
    ...
    MyForm.NewPage
    MyForm.SetFooter(CharStr('', 10), MyForm.fPrinterPtr^.LPP-1, 2, Bold);
    MyForm.SetFooterFunc('', FooterConversion);
    ...

  Здесь показана простая функция подножия, которая печатает
  номер страницы во второй колонке на следующей строке после
  последней строки страницы. Для того, чтобы этот пример
  реально работал, необходимо, чтобы все страницы,
  добавляемые к форме, устанавливали ту же функцию
  FooterConversion либо обратившись к SetFooter и
  SetFooterFunc явно, либо вызвав DupCurrentPage.


    SetHeader
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetHeader(HeaderStr : String; Row, Col : Dimension; FAttr : Byte);
Назначение
  Устанавливает заголовок для текущей страницы.
Описание
  Работает подобно SetFooter, с той лишь разницей, что
  заголовок печатается прежде всех остальных зон печати, а
  не после.
Справки
  SetFooter


    SetHeaderFunc
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetHeaderFunc(Picture : String; HeadFunc : PrintConversionFunc);
Назначение
  Устанавливает указатель на функцию заголовка для текущей
  страницы.
Описание
  Работает подобно SetFooterFunc, с той лишь разницей, что
  функция заголовка вызывается прежде, чем на странице будет
  напечатана какая либо зона.

Пример

    {$F+}
    function HeaderConversion(PFP : PrintFieldPtr) : Boolean;
    begin
      HeaderConversion := True;
      with PFP^ do
        pfString^ := LeftPad(CurrentTimeAndDateStr, 1, 80);
    end;
    {$F-}
    ...
    MyForm.NewPage
    MyForm.SetHeader(CharStr('', 80), 1, 1, Bold);
    MyForm.SetHeaderFunc(HeaderConversion);
    ...
  Показана простая функция заголовка, которая печатает в
  правом верхнем углу страницы строку, передаваемую
  гипотетической функцией CurrentTimeAndDate.


    SetPrePageFunc
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetPrePageFunc(ppFunc : UserPageFunc);
Назначение
  Устанавливает функцию, которая вызывается перед тем, как
  выводится на печать текущая страница.
Описание
  Информацию об этой функции можно найти в начале этого
  раздела в параграфе "Дополнительные средства диагностики
  пользователя".
Справки
  NewBlock                       SetPrePostFormFunc


    SetPrePostFormFunc
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetPrePostFormFunc(ppFunc : UserFormFunc);
Назначение
  Устанавливает функцию, которая вызывается перед и после
  распечатки формы.
Описание
  Эта процедура полезна только в том случае, когда
  печатаемый отчет состоит из множества форм.
  Информацию об этой функции можно найти в начале этого
  раздела в параграфе "Дополнительные средства диагностики
  пользователя".
Справки
  SetPrePageFunc

    SetUserData
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetUserData(P : Pointer);
Назначение
  Устанавливает зону пользовательского указателя.
Описание
  Каждая реализация PrintedForm обслуживает четыре байта
  данных, которые могут быть использованы прикладной
  программой любым необходимым для нее способом. Обычный
  способ их использования заключается в передаче информации
  о номере страницы или записи между прикладной программой и
  блочной функцией, страничной функцией или функцией
  преобразования заголовка/подножия.
  Внутренняя область данных имеет тип указателя, поэтому она
  может обращаться к более обширным областям коллективного
  архива. Используйте преобразование типа области данных в
  LongInt, если требуется не более 4 байт массива памяти.
Пример
  Смотрите пример в описании GetUserData.
Справки
  GetUserData


    SetUserRecord
 ───────────────────────────────────────────────────────────
Объявление
  procedure SetUserRecord(var UserRec; UserRecSize : Word);
Назначение
  Устанавливает размер и адрес пользовательской записи для
  текущего блока.
Описание
  Процедура SetUserRecord необходима только в том случае,
  когда созданная форма предназначена для архивации в
  потоке. Полное описание этой процедуры и примера ее
  применения можно найти в параграфе "Печатные формы и
  потоки".

Пример

    MyForm.NewBlock(1, 1, BlockFunction);
    MyForm.SetUserRecord(DataRec, SizeOf(DataRec));

  Устанавливается пользовательская запись DataRec для
  текущего блока.

Справки
  GetUserRecord                  GetUserRecordSize


    Store
 ───────────────────────────────────────────────────────────
Объявление
  procedure Store(var S : IdStream);
Назначение
  Архивирует печатную форму в потоке.
Описание
  S является специально инициализированным потоковым
  объектным модулем (файл потока обычно открыт по записи).
  Отметим, что S может быть любой модификацией идентификатора
  потока IdStream, включая DosIdStream, BufIdStream и
  MemIdStream (хотя вы, конечно, никогда не будете обращаться
  к MemIdStream).
  Процедура Store записывает образ PrintedForm в поток S.
  Образ включает информацию обо всех зонах, блоках и
  страницах формы, так же как и подчиненную реализацию
  модуля Printer.
  Различные типы зон, используемые в PrintedForm, должны
  быть зарегистрированы до обращения к Store. Если это так,
  то должен быть зарегистрирован адрес "записи
  пользователя" и таким образом адреса любой
  пользовательской подпрограммы будут связаны с PrintedForm
  (функции преобразования, функции преобразования
  заголовка/основания, блочные функции, страничные функции и
  отладчики ошибок).
  Для контролирования ошибок, которые могут возникнуть во
  время архивации, необходимо обратиться к потоковой функции
  GetStatus.
  Процедурой регистрации для PrintedForm является
  FormStream.
  Подробное описание процедуры архивации находится в
  параграфе "Печатные формы и потоки" в начале этого
  раздела.
Справки
  Load                   Printer.Store


    VisitAllPrintFields
 ───────────────────────────────────────────────────────────
Объявление
  procedure VisitAllPrintFields(pfProc : PrintFieldProc; var D);
Назначение
  Обращается к соответствующей процедуре pfProc для каждой
  зоны печати, определенной для данной формы.
Описание
  Процедура VisitAllPrintFields обеспечивает удобный способ
  заново переписать параметры каждой зоны в форме. pfProc
  определяет процедуру, вызываемую для каждой зоны; а
  переменный нетипизированный параметр D определяет данные
  пользователя, которые будут передаваться в pfProc.
  pfProc должна быть откомпилирована по модели FAR
  (используя директиву {$F+}), должна быть глобальной и
  обязана иметь параметры, соответствующие приведенному
  прототипу:

    {$F+}
    procedure MyPrintFieldProc(pfPtr : PrintFieldPtr; var D;
                               FPtr : PrintedFormPtr);

  По каждому обращению к MyPrintFieldProc, OPFORM будет
  передавать адреса зоны печати в pfPtr, адрес данных
  пользователя в D и адрес всей формы в Fptr.

Пример

    {$F+}
    procedure ClearForAllField(pfPtr : PrintFieldPtr; var D;
                               FPtr : PrintedFormPtr);
    begin
      pfPtr^.pfOptionsOff(LongInt(D));
    end;
    {$F-}
    var UnhideOpt : LongInt;
    ...
    UnhideOpt := pfHidden;
    MyForm.VisitAllPrintFields(clearForAllFields, UnhideOpt);

  В форме раскрываются все зоны. Процедура ClearForAllFields
  изменяет тип нетипизированного переменного параметра D на
  LongInt и вызывает алгоритм pfOptionsOff из реализации
  PrintField.

    Алгоритмы OPFORM\Report

    AddForm
 ───────────────────────────────────────────────────────────
Объявление
  procedure AddForm(fPtr : FormPtr);
Назначение
  Добавляет печатную форму PrintedForm к отчету Report.
Описание
  fPtr является адресом уже инициализированной формы.
  AddForm присоединяет эту форму к списку форм, составляющих
  отчет.

Пример

    var
      Form1, Form2 : PrintedForm;
      MonthEndReport : Report;
    ...
    {реализация Формы1 и Формы2}
    ...
    if not MonthEndReport.Init then begin
      WriteLn('Failed to init Report.Status = ', InitStatus);
      Halt;
    end;
    MonthEndReport.AddForm(@Form1);
    MonthEndReport.AddForm(@Form2);
    MonthEndReport.Process;

  Происходит формирование отчета из двух форм, затем вывод
  отчета на печать.


    Done
 ───────────────────────────────────────────────────────────
Объявление
  destructor Done; virtual;
Назначение
  Разрушает сформированный отчет.
Описание
  Эта процедура рекурсивно обращается к самой себе при
  каждом добавлении новой формы PrintedForm к Report.
  Обратите внимание, что подчиненный объектный модуль
  Printer, используемый соответствующей формой, не
  разрушается.

Пример

    MyReport.Done;

  Разрушается статическая реализация отчета MyReport.

    Dispose(MyReportPtr, Done);

  Разрушается динамическая реализация отчета по указателю
  MyReportPtr.

Справки
  Init                   Load


    FindForm
 ───────────────────────────────────────────────────────────
Объявление
  function FindForm(FormID : Byte) : FormPtr;
Назначение
  Передает указатель на PrintedForm по указанному
  идентификатору.
Описание
  Этот алгоритм просматривает скомпанованный список форм и
  передает указатель на реализацию PrintedForm по
  соответствию идентификатора формы FormID. Если не будет
  найдено PrintedForm с указанным идентификатором,
  результатом функции будет nil.

Пример

    TitleFormPtr := MyReport.FindForm(TitleID);

  Передает указатель на форму с идентификатором TitleID и
  архивирует результат в TitleFormPtr.

Справки
  AddForm


    Init
 ───────────────────────────────────────────────────────────
Объявление
  constructor Init;
Назначение
  Формирует отчет.
Описание
  Этот конструктор реализует составление Report. Этот
  конструктор потерпит неудачу только в том случае, если
  отчет является динамической реализацией и потерпит неудачу
  подпрограмма New.

Пример

    var
      DailyReport : reportPtr;
    begin
      DailyReport := New(ReportPtr, Init);
      if DailyReport = nil then begin
        WriteLn('Failed to Init Report. Status = ', InitStatus);
        Halt;
      end;
      ...

  Формируется реализация Report, названная DailyReport.
  Алгоритм останавливается с выдачей сообщения об ошибке в
  том случае, если в памяти нет достаточно места для
  размещения объектного модуля.

Справки
  Done                   Load


    Load
 ───────────────────────────────────────────────────────────
Объявление
  constructor Load(var S : IdStream);
Назначение
  Загружает Report из потока.
Описание
  S представляет собой объект потока, инициализированный
  соответствующим образом (например, последовательный файл
  потока, всегда открытый по чтению). Обратите внимание, что
  S может быть любой величиной, производной от IdStream, в
  том числе DosIdStream, BufIdStream и MemIdStream.
  Load считывает очередную последовательность байтов из
  потока S. Эти байты должны быть записаны по предыдущему
  обращению к Report.Store.
  Всевозможные типы зон, используемые подчиненными печатными
  формами, должны быть зарегистрированы до обращения к Load.
  Все требования по регистрации этих форм должны быть
  удовлетворены.
  Потоковой процедурой регистрации для Report является
  ReportStream. ReportStream автоматически вызывает
  FormStream.
  Подробности работы этого алгоритма описаны в параграфе
  "Печатные формы и потоки" в начале этого раздела.

Пример

    Stm.Get(SalesReport);
    if Stm.GetStatus <> 0 then begin
      WriteLn('Failed to load Report');
      Halt;
    end;

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

Справки
  PrintedForm.Load


    Process
 ───────────────────────────────────────────────────────────
Объявление
  procedure Process; virtual;
Назначение
  Осуществляет распечатку отчета.
Описание
  Эта процедура рекурсивно обращается к самой себе при
  добавлении к отчету каждой новой формы. Отметим, что
  каждая форма обращется к своей собственной перед/после
  форменной функции пользователя, которая может применяться
  для передачи указаний оператору сменить тип бумаги или
  подключить другой принтер.

Пример

    MyReport.Process;

  Печатается отчет MyReport.

Справки
  PrintedForm.Process


    rGetLastError
 ───────────────────────────────────────────────────────────
Объявление
  function rGetLastError : Word;
Назначение
  Извлекает код последней ошибки и сбрасывает внутреннюю
  переменную состояния.
Описание
  Если результатом функции является ноль (zero), значит
  никакой ошибки не зарегистрировано. В ином случае, код
  ошибки может быть интерпретирован в соответствии с
  приложением D.

Пример

    MyReport.Process;
    ErrorCode := MyReport.rGetLastError;
    if ErrorCode <> 0 then
      {обработка ошибки};

  Для отчета MyReport вызывается алгоритм Process, а затем
  проверяется наличие ошибки.

Справки
  PrintedForm.fGetLastError


    Store
 ───────────────────────────────────────────────────────────
Объявление
  procedure Store(var S : IdStream);
Назначение
  Архивирует отчет в потоке.
Описание
  S является специально инициализированным потоковым
  объектным модулем (файл потока обычно открыт по записи).
  Отметим, что S может быть любой модификацией идентификатора
  потока IdStream, включая DosIdStream, BufIdStream и
  MemIdStream (хотя вы, конечно, никогда не будете обращаться
  к MemIdStream).
  Процедура Store записывает образ Report в поток S.
  Всевозможные типы зон, используемые подчиненными печатными
  формами, должны быть зарегистрированы до обращения к
  Store.  Все требования по регистрации этих форм должны
  быть удовлетворены.
  Потоковой процедурой регистрации для Report является
  ReportStream. ReportStream автоматически вызывает
  FormStream.
  Подробное описание процедуры архивации находится в
  параграфе "Печатные формы и потоки" в начале этого
  раздела.

Пример

    MyReport.Store(Stm);

  Архивирует реализацию отчета MyReport в потоке Stm.

Справки
  Load                   PrintedForm.Store


    F: Образцы программ

В настоящее время в ObjectProfesional существует
единственный образец программы, которая иллюстрирует
применение OPFORM и других модулей, описанный в этой
главе, это программа FORMTEST.

    FORMTEST

В отличие от прочих показательных программ, разработанных
для ObjectProfesional, FORMTEST не является утилитом общего
назначения. Программа эта создана исключительно для того,
чтобы проиллюстрировать возможности модулей OPFORM,
OPDEVICE, OPPRINT и OPPRNLOW.

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

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

  Расширение    Назначение
 ───────────────────────────────────────────────────────────
  /?            Выводит на экран перечень возможных
                расширений.
  /Ln           Указывает номер порта LTP (где n = 1, 2 или 3).
  /Nимяфайла    Указывает имя файла или устройства печати.
  /Onxx         Указывает тест, используемый для проверки
                готовности принтера. n является номером
                теста в пределах от 0 до 4. Если номер теста
                4, то xx представляет собой в
                шестнадцатиричном коде маску байта состояния
                принтера.
  /R            Читает форму из потока.
  /S            Гасит команду прогона формата.
  /Tx           Указывает тип аппаратных средств принтера,
                где x определяется как:
                  L для совместимых с LaserJet;
                  M для Epson MX;
                  F для остальных Epson-совместимых;
                  I для IBM Proprinter
                  D для совместимых с Diablo;
  /W            Записывает форму в поток

Запустите FORMTEST сначала с расширением /? для того, чтобы
увидеть, не прибавилось ли новых расширений с того времени,
как было напечатано это руководство.

/L говорит о том, что при выводе на печать должны быть
использованы средства обслуживания BIOS. Номер LPT
показывает, какой именно порт используется при выводе.

/N говорит о том, что при выводе на печать должны быть
использованы средства обслуживания DOS. Следующее за этим
имя файла может быть PRN, LPT1, LPT2, LPT3, любой корректный
символ устройства DOS или действительное имя файла.

Если не указано ни /L, ни /N, FORMTEST ведет себя так, будто
было дано расширение '/Ndump.prn'. Одновременное указание /L
и /N является некорректным. Если вы, например, указали
/Nlpt2/L1, значит вы собираетесь печатать одновременно в
двух разных местах.

При использовании расширения /L, вы можете также
использовать расширение /O для определения, каким тестом
необходимо пользоваться для определения состояния готовности
принтера. В разделе OPPRNLOW описано назначение различных
тестов. По-умолчанию используется тест номер 1.  Если задан
тест номер 4, то два символа, следующих за '4',
интерпретируются как маска байта состояния принтера. Далее
приводятся несколько примеров использования расширения /O:

  Расширение     Применение
 ───────────────────────────────────────────────────────────
  /o1            Используется тест принтера 1
                 (по умолчанию);
  /o490          Используется тест принтера 4 с $90 в
                 качестве маски байта состояния принтера;
  /o410          Используется тест принтера 4 c $10 в
                 качестве маски байта состояния принтера;
  /o0            Тест принтера не используется, считается,
                 что принтер всегда находится в состоянии
                 готовности.

Расширение /T приводит к тому, что реализация Printer,
используемая FORMTEST, регистрируется с использованием
указанного типа аппаратных средств. Любые режимы печати,
определенные перед этим с помощью OPDEVICE для принтера
этого типа, применимы для данной формы. Наиболее
значительное воздействие оказывает использование /TL для
регистрации принтера, как совместимого с HP LaserJet. Это
позволяет использовать в печатной форме позиционирование с
повышенной точностью лазерного принтера, прорисовку линий,
прямоугольников и полутоновую закраску. Если расширение /T
отсутствует, FORMTEST работает с принтером по умолчанию
как с "простым", который не генерирует управляющие
последовательности, но использует для прямоугольников
символы прорисовки линий IBM.

Расширения /W и /R относятся к работе с потоками. Потоки
читают и пишут в файл, имеющий имя FORMTEST.STM. Если
используется расширение /W, то форма записывается в
потоковый файл после того, как она будет подготовлена и
инициализирована. Если используется расширение /R, то
форма будет воссоздана из той реализации, которая была
заархивирована в FORMTEST.STM. Прежде чем запускать
FORMTEST с расширением /R, вы должны запустить его с
расширением /W.

Далее приведены командные строки FORMTEST с кратким
описанием каждой:

    FORMTEST /L1 /O3 /TD

Указывает на принтер совместимый с Diablo, работающий
через порт LPT1 и использующий тест готовности 3.

    FORMTEST /NPRN /W /TI

Указывает на принтер совместимый с Proprinter с передачей
информации на устройство PRN. Записывает также потоковый
файл для формы.

    FORMTEST /L2 /O0 /TL

Указывает на принтер совместимый с лазерным и подключенный
через порт LPT2. Тест готовности принтера не используется
(предполагается, что принтер всегда готов).

    FORMTEST /R

Читает форму из потока и печатает ее в файл DUMP.PRN.

    FORMTEST /Nbilling.rep /TL

Записывает форму в файл с именем BILLING.REP. Этот
дисковый файл будет содержать управляющие
последовательности для работы с принтером совместимым с
лазерным.

Для того, чтобы пронаблюдать внутреннюю работу объектных
модулей PrintedForm, Printer и BasePrinter, осуществите
маленький эксперимент. Во-первых, введите FORMTEST в
командной строке без каких либо расширений. Посмотрите на
сгенерированный файл DUMP.PRN (вы можете при этом
использовать любой редактор текстовых файлов или программу
просмотра). Теперь запустите FORMTEST снова, указав в
командной строке расширение /TL для использования
управляющих последовательностей LaserJet. Если вы после
этого снова просмотрите DUMP.PRN, вы увидите какое
изменение произошло в нем благодаря лишь переопределению
модуля Printer.


