[PF] Векторная печать PDF на C# теория

c4a93cc8bcb34cbb929d5b5f7787376e.jpg

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

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

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

e6a0f8d887ab43999e46c7d81a4f6f17.jpg

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

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

69cf0dab5cee4025acdd3cc73ad0214f.jpg

В Википедии написано:

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


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

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

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

97a6f6c109c4473ab13b8e3d4a779432.jpg

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

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

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

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

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

f481fe254f434ad9a1e233b6982680ac.jpg

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

Иерархия операторов PCL имеет следующий вид:

<сессия>
  <старница>элементы страницы
  <старница>элементы страницы
  <старница>элементы страницы
 

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

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

ad5b60484810450f804c05b29c172b3b.jpg

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

d749f8db1af84379aa544e4e513b0e66.jpg

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

13f4e32858c14e0399a29296478bc4e9.jpg

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

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

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

32d34e29c2774eb28bbc2786cfb8256f.jpg

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

ca1f20b5443f4e898a0dab6013b49c3f.jpg

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

bbf46c552f304cd595e8cc862cceaf78.jpg

BeginPage

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

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

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

3e430f0e430f4aee929845e33cb74f9e.jpg

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

fb3c998f35844be7ba46b422806bc77e.jpg

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

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

01f75a0b4d914848b96fdafb083ef48a.jpg

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

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

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

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

8bda17e065ad4c8ba6e5e8368f9ec49d.jpg

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

51016461f3de490e96bbe0dd26a46140.jpg

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

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

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

© Habrahabr.ru