company_banner

[PF] Печать PDF под .NET, векторный подход, теория

  • Tutorial


Продолжаю тему печати PDF документов из под .NET.

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

Первое, что пришло в голову — рендерить страницы документа в растровые картинки и при помощи стандартных средств .NET выводить их на печать. Хорошо, что класс PrintDocument позволяет налету менять лоток принтера. О таком подходе я писал в предыдущей статье



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

Мне помог язык управления принтером PCL от Hewlett-Packard. В интернете информации по нему оказалось крайне мало, не говоря уже о рунете. Единственное внятное описание — официальная спецификация. Сказано — сделано. Запустил HEX редактор + утилитку от HP JetAsm и начал изучать PCL. Не так страшен черт, как его малюют. Язык хоть и бинарный, но довольно простой и логичный. В результате, через пару часов собрал тестовое приложение. И во благо всем, кому это может быть полезно и интересно, расскажу немного про PCL.



В Википедии написано:
“PCL (от англ. Printer Command Language) — язык управления принтером, разработанный компанией Hewlett-Packard. В первой версии это был просто набор команд для печати ASCII-символов, теперь же, в версиях PCL6 и PCL-X стало возможным печатать в цвете, а также печатать изображения, но вне Microsoft Windows и HP-UX этот язык редко используется”

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

Официальное описание PCL есть в интернете. Также понять и валидировать содержимое PCL файлов поможет бесплатная утилита от HP JetAsm. Ее и другие полезные файлы можно взять с официального сайта без регистрации. Для это нужно перейти по ссылке, нажать SDK->Public и найти интересующие файлы.

Старый добрый GhostScript





Для начала нам нужно где-то достать pcl файл. Известная по прошлой статье утилита GhostScript из коробки умеет конвертировать PDF в PCL. Сделать это можно командой:

gswin64c.exe   -o example.pcl   -sDEVICE=pxlmono   example.pdf


Таким образом, без лишних движений мы получили PCL файл, который можно отправлять на принтер в качестве RawData.

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

Итак, откроем PCL файл в каком-либо HEX редакторе. Я использовал WinHex.



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

Иерархия операторов PCL имеет следующий вид:
<сессия>
  <страница>элементы страницы</страница>
  <страница>элементы страницы</страница>
  <страница>элементы страницы</страница>
</сессия> 


При этом сессий может быть несколько, а элементы страницы могут быть вложенными в другие элементы.

Начнем по порядку





Я уже говорил, что вначале потока идет стандартная строка. Именно по ней устройство определяет, что дальше будут передаваться данные в формате PСLXL. Длина строки инициализации 99 байт.



Дальше начинается поток данных PCL. Чтобы начать описывать элементы документа, должна быть открыта сессия, для этого существует команда BeginSession с кодом 0x41. Этот и другие коды команд можно найти в мануале. В одном файле может быть одна или несколько сессий, но чаще одна.



Там же смотрим аргументы, которые необходимо передать в BeginSession.

Аргументы имеют следующую структуру: [тип данных][данные][тип атрибута][атрибут]

UnitsPerMeasure — разрешение рабочей области x и y, имеет тип данных uint16_xy — структура из двух двухбайтовых слов.



Measure — перечисление единиц измерения, в которых будем работать {eInch | eMillimeter | eTenthsOfAMillimeter}



ErrorReport — перечисление, определяющее, как устройство будет обрабатывать ошибки.



BeginPage



Аналогичным образом инициализируется страница. Именно оператор BeginPage позволяет задать режим дуплекса и указать лоток из которого будет производиться забор бумаги.

Разберем аргументы оператора BeginPage.

Orientation — ориентация страницы, может принимать значения {ePortraitOrientation | eLandscapeOrientation | eReversePortrait | eReverseLandscape}



MediaSize — размер страницы, перечисление, в нашем случае eA4Paper. Вместо типа MediaSize может быть CustomMediaSize, CustomMediaSizeUnits.



MediaSource — источник бумаги.
0 — eDefaultSource
1 — eAutoSelect
2 — eManualFeed
3 — eMultiPurposeTray
4 — eUpperCassette
5 — eLowerCassette
6 — eEnvelopeTray
7 — eThirdCassette
1-248 — External Trays

Вместо MediaSource может быть MediaType с именем источника.



SimplexPageMode — одностраничный режим печати, обязательно должно быть значение eSimplexFrontSide = 0

Вместо SimplexPageMode может быть DuplexPageMode или DuplexPageSide.

DuplexPageSide — печатает одну страницу на одном листе, но можно задать на какой стороне листа {eFrontMediaSide | eBackMediaSide}

DuplexPageMode — две последовательные страницы печатаются на двух сторонах одного листа, можно задавать значения eDuplexHorizontalBinding = 0 и eDuplexVerticalBinding = 1



И завершается все это командой BeginPage с кодом 0x43



Надеюсь, принцип понятен. Этого достаточно для разработки приложения и модификации PCL файла таким образом, чтобы можно было менять режим Duplex — Simplex и указывать, из какого лотка забирать бумагу. Для этого в файле нужно найти объявление очередной страницы и изменить ее нужным образом.

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

Если есть неточности или нужно больше деталей, пожалуйста, напишите об этом в комментариях.

Цикл статей:
Растровый подход
Векторный подход теория
Векторный подход практика
  • +15
  • 7.2k
  • 3
Tinkoff.ru
141.07
IT’s Tinkoff.ru — просто о сложном
Share post

Comments 3

    +1
    Исправьте <старница>элементы страницы</старница> на <страница>элементы страницы</страница>
      0
      Очевидно в статье нет .NET
        0
        Работаю над продолжением, там будет пример на C#. Хотя действительно, идею из данной статьи можно реализовать на чем угодно.

      Only users with full accounts can post comments. Log in, please.