Создание PDF в. NET

Создание PDF в. NET

PDF (Portable Document Format) — этот формат хорошо подходящий для документов, имеющих четкую заранее определенную структуру (квитанции, анкеты и т. д.). Для работы с PDF в. NET существуют свободная библиотека PDF Clown, опытом работы с которой я и поделюсь в этой статье.

Подготовка

Скачать проект Visual Studio с исходными кодами можно со страницы библиотеки на sourceforge. net. Рекомендую подключать к своему проекту именно исходники, так как код еще немного сырой (пока имеется только alpha-версия), и вероятно временами придется расставлять костыли. Вместе с кодом в скачиваемой папке также вы найдете аж 44 примера использования PDF Clown для различных целей. Лицензия здесь самая щадящая — LGPG, можно использовать для коммерческих целей.

Помимо dotnet версии PDF Clown, также существует версия для Java. Поэтому, распаковав архив, Вы найдете три папки: main, dotNet, java. Для функционирования dotNet версии, нам понадобятся первые две директории. Причем main не обязательно добавлять в проект, можно просто положить рядом с папкой dotNET.

Помимо самой библиотеки, Вам потребуется кое-что еще. PDF-документы такие printable не просто так, и за это приходиться платить вдвойне. В частности, для того чтобы написать абзац, необходимо разбить его на строки, и каждую строку вывести в отдельности по вручную рассчитанным координатам. Поэтому Вам понадобиться pdf-редактор, позволяющий определять координаты элементов. Я пересмотрел достаточно много различных вариантов, и единственным более-менее подходящим оказался Foxit PDF Editor. Программа не бог весть какая, к тому же ось координат Y в нем направлена снизу вверх, тогда как в PDF Clown в обратную сторону (чтобы получить нужные мне координаты, я вычитал полученные там значения из числа 833). Однако во всех других редакторах либо координаты не выражаются в пикселях, либо вообще не отображаются. Я даже пробовал определять координаты на скриншоте страницы в графическом редакторе. Почему-то из этого ничего не вышло.

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

Другой интересной особенностью pdf, является то, что все необходимое он держит в себе. В том числе, шрифты. Шрифт, который использует PFD Clown по умолчанию, не умеет корректно рисовать кириллицу, поэтому стоит сразу позаботиться о том, чтобы прицепить к проекту все необходимые шрифты. Причем, загрузить в документ у меня получилось только ttf (True Type Font). Другие видимо тоже можно, но я так и не нашел как.

И последнее приготовление — трижды подумайте, прежде чем использовать pdf для вывода документов. Это настолько сложный и запутанный формат, что в PDF Clown, объектная модель, которой в точности соответствует спецификации, я смог освоиться только с самыми самыми верхами абстрагирования. Куда проще будет сгенерировать HTML или еще что-то в этом роде. В общем, если что, я Вас предупреждал.

Создание документа

Примеры кода есть в комплекте исходников библиотеки, однако я добавлю к ним некоторые комментарии из опыта, нажитого своими шишками. Итак, создаем документ.

Using org. pdfclown. documents; using org. pdfclown. documents. contents; using org. pdfclown. documents. contents. composition; using org. pdfclown. files; using org. pdfclown. objects; using org. pdfclown. tools;

// Вариант 1 — у нас есть шаблон

// За совпадение имени с System. IO. File разработчиков

// следует расстрелять на месте org. pdfclown. files. File file = new org. pdfclown. files. File("Resources\Template. pdf");

Document document = file. Document;

Page page = document. Pages[0];

// Вариант 2 — чисто новый файл org. pdfclown. files. File file = new org. pdfclown. files. File();

Page page = new Page(file. Document); file. Document. Pages. Add(page); page. Size = new SizeF(595, 842);

Обратите внимание, новую страницу (Page) сначала нужно добавить в коллекцию Pages документа, и только после этого можно задавать ей какие либо свойства. Размер для страницы задавать обязательно. Значения (595, 842) я подсмотрел в дебагере у стандартной A4 страницы.

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

File. Save("C:\HelloWorld. pdf", SerializationModeEnum. Incremental);

Рисуем текст

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

// Еще одно совпадение имен (System. Drawing. Font),

// разработчики допивают вторую бутылку йада org. pdfclown. documents. contents. fonts. Font font = org. pdfclown. documents. contents. fonts. Font. Get(document, "Resources/PT_Sans. ttf"); document. Resources = new Resources(document); document. Resources. Fonts = new FontResources(document); document. Resources. Fonts. Add(new PdfName("PT_Sans"), font);

Приготовления сделаны, пора рисовать. Поможет нам в этом некий PrimitiveComposer. Как видно из его названия, примитивный он и есть. Умеет только выводить всякую всячину по заранее рассчитанным координатам. В проектах у разработчиков PDF Clown сделать и более удобные composer'ы (flow-layout и все такое), но пока довольствуемся этим.

PageStamper stamper = new PageStamper(page);

PrimitiveComposer composer = stamper. Foreground; composer. BeginLocalState(); composer. SetFont(font, 14); // 14 — размер шрифта

// ... draw something composer. End(); composer. Flush(); stamper. Flush();

Здесь хочу обратить внимание на несколько вещей. PrimitiveComposer можно создать и с помощью конструктора new PrimitiveComposer(page), но чтобы иметь возможность рисовать поверх уже имеющегося содержимого, нужно использовать PageStamper. Второй важный момент, в том, что пока не вызовутся методы Flush, на документе ничего не появиться.

Локальные состояния удобны тем, что для некоторого участка текста можно временно назначить, например, другой шрифт, и после вызова End, все вернется к состоянию, вывшему на момент вызова BeginLocalState. Установить атрибуты "жирный" или "курсив" нельзя, для этого нужно загружать специальный файл шрифта.

Для рисования в классе PrimitiveComposer имеется целая куча всяких примитивных методов, вроде нарисовать текст, линию, эллипс, прямоугольник и т. д. Текст рисует метод ShowText.

PointF location = new PointF(50, 100); composer. ShowText("Hello, world!", location);

Рисуем картинки

Среди всех методов PrimitiveComposer вы не найдете ничего вроде ShowImage. С картинками особый случай, их нужно добавлять как x-объекты. Для начала надо создать объект Image. Это можно сделать, указав путь к файлу, или поток (удобно, если имеется массив байт, который можно залить в MemoryStream).

// И еще одно совпадение и убийственно длинное пространство имен

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

// чтобы вечно программировать под DOS на Turbo Pascal org. pdfclown. documents. contents. entities. Image img = null; try

{ img = org. pdfclown. documents. contents. entities. Image. Get("Resources//HelloWorld. jpg");

} catch(Exception e)

{

Logger. Log("Can't load logo image. Reason: " + e. Message);

}

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

Float imgHeight = img. Height; float imgWidth = img. Width;

Size MaxSize = new Size(500, 200); if (imgWidth > MaxSize. Width)

{ imgHeight = imgHeight * (MaxSize. Width / imgWidth); imgWidth = MaxSize. Width;

} if (imgHeight > MaxSize. Height)

{ imgWidth = imgWidth * (MaxSize. Height / imgHeight); imgHeight = MaxSize. Height;

}

PointF centerPageLocation = new PointF((page. Width - imgWidth) / 2, 120);

Осталось только преобразовать картинку в XObject, и нарисовать на странице.

Composer. ShowXObject(img. ToXObject(document), centerPageLocation, new SizeF(imgWidth, imgHeight));

Заключение

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

В общем, ничего приятного в формировании pdf-документов с помощью PDF Clown я не нашел. Других свободных библиотек, я также не нашел. Такие дела. На этом все, удачи!


Карта сайта


Информационный сайт Webavtocat.ru