Печатаем документы с веб-сервера::Журнал СА 12.2005
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
О журнале
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
20.12.2019г.
Просмотров: 1235
Комментарии: 0
Dr.Web: всё под контролем

 Читать далее...

04.12.2019г.
Просмотров: 1257
Комментарии: 0
Особенности сертификаций по этичному хакингу

 Читать далее...

28.05.2019г.
Просмотров: 3786
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 4008
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 3203
Комментарии: 0
Django 2 в примерах

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Печатаем документы с веб-сервера

Архив номеров / 2005 / Выпуск №12 (37) / Печатаем документы с веб-сервера

Рубрика: Веб /  Веб   | Дополнительные материалы

ДМИТРИЙ ОСТРЕЦОВ

Печатаем документы с веб-сервера

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

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

Для чего нужны корпоративные информационные системы, понятно всем. Большие и сложные обычно покупаются у сторонних производителей, но не о них речь. Нередко подобные системы создаются силами ИТотделов. Для этого обычно применяется некий инструментарий, объединяющий в себе средства создания экранных форм, работы с БД и создания печатных отчетов. Однако создание информационной системы на базе веб-технологий позволяет обойтись без этих средств – работа с формами и БД средствами CGI-скриптов на PHP или Perl реализуется без проблем. При этом мы получаем программу, не требующую для своей работы у клиента ничего, кроме браузера, и позволяющую организовать удаленный доступ.

И только одна проблема, элементарная для MS Access, не имеет адекватного решения для веб-программиста. Это печать документов.

В чем заключаются проблемы печати в системе, основанной на веб?

Мы хотим распечатывать именно документы, поэтому просто печать страницы из браузера не годится – сверху и снизу печатается служебная информация (номера страниц, URL и т. д.), отключение этих полей представляет собой задачу, не имеющую универсального решения. Кроме того, документы бывают книжной и альбомной ориентации, а способа указать, как должен быть ориентирован HTML-документ, при печати, нет. (Вернее, в стандарте есть способ это указать [2], но вот когда это будет реализовано в браузерах, не известно.)

Этих проблем лишен формат PDF. Но тут встает во весь рост вопрос формирования документа. Библиотеки для работы с PDF существуют, но сформировать красивый документ с их помощью будет непросто. А как быть с необходимостью изменять документы время от времени (взять хотя бы счет-фактуру)? Программист на Access просто подправит документ (report), возможно, одним движением мыши…

Я предлагаю организовать печать с веб-сайта при помощи пакета OpenOffice. Цепочка действий будет такой: мы делаем в OpenOffice макет документа и помещаем его на сервер. Веб-скрипт заполнит поля в макете и добавит необходимые строки, в результате чего получится готовый документ, который останется только отправить на принтер.

Что такое OpenOffice?

OpenOffice – это бесплатный пакет офисных программ [3]. Данный проект возник по инициативе и существует при поддержке фирмы Sun. Недавно вышла версия 2.0, но описываемая здесь технология реализована мной для версий 1.1.*, хотя есть основания полагать, что все будет работать и с последней версией, несмотря на переименование суффиксов у файлов документов. OpenOffice способен открывать и редактировать основные форматы MS Office и работает практически под любой ОС. Проект прилично документирован. В рамках данной статьи нас будет интересовать собственный формат документов OpenOffice.

Разные приложения OpenOffice (Writer, Calc…) используют собственный суффикс для файлов, но файлы эти имеют почти схожую структуру – это ZIP, который содержит главный файл content.xml и вспомогательные, содержащие стили, пользовательские настройки, ресурсы и т. д. Content.xml – это обычный XML-файл, который можно обрабатывать при помощи всех известных XML-средств.

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

Как сгенерировать файл

С точки зрения оформления документа, возможностей редактора OpenOffice (OOWriter) вполне достаточно, поэтому я ограничусь примерами использования файлов формата SXW, хотя предлагаемая технология будет работать практически с любым файлом, созданным приложениями из пакета OpenOffice. В версии 2.0 основной формат документов OOWriter имеет суффикс ODT, но структура файлов очень похожа.

Для начала мы создаем макет в OOWriter. Макет – это почти готовый документ, в котором программно заполняемые поля обозначаются специальными сигнальными фразами. Выделяется специальными маркерами и табличная часть, если таковая имеется. Щедро используем шрифты, рамки, графические элементы, колонтитулы, устанавливаем необходимую ориентацию документа. Макет используется многократно, и при необходимости внести в форму документа изменения его нужно просто загрузить в редактор. Пример макета представлен на рис. 1.

Рисунок 1. Макет

Рисунок 1. Макет

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

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

fl.<part>.<field>

fl – это метка начала имени поля, <part> – указатель части документа (hd – начальная часть, ft – конечная, или имя вида строки табличной части), <field> – имя поля данной части документа. Например:

fl.ft.Total

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

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

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

Это место требует пояснения. Под элементом я имею в виду XML-элемент файла OpenOffice. Именно с этими элементами имеет дело скрипт при обработке файла. Однако при редактировании макета в OO.Writer никаких тегов не видно (этот редактор такой же WYSWYG, как и WinWord). Как же в нем указывать XML-элемент для строки табличной части? Тем не менее никакой особой сложности нет. Опыт показал, что наиболее удобно в качестве строки табличной части использовать или параграф, или строку размеченной таблицы. Просто аккуратно оформляем каждую строку табличной части в виде параграфа или размечаем таблицу средствами редактора. В примере на рис. 1 строкой табличной части является параграф, а для фиксирования ширины полей служат соответственно расставленные табуляции. При настройке скрипта останется только указать, что строка табличной части будет элементом типа <text:p>. Если мы будем использовать размеченную таблицу, то укажем тег <table:table-row>. После примера это станет более-менее понятно, а пока последнее трудное место.

Конец табличной части должен быть явно обозначен. Это принципиально для работы скрипта. Для обозначения используется элемент того же типа, что и строка табличной части, но содержащий только текст «fl.EOT».В примере на рис. 1 хорошо виден такой параграф. А вот как выглядят эти параграфы табличной части непосредственно в XML (см. рис. 2).

Рисунок 2. Фрагмент XML-кода табличной части макета

Рисунок 2. Фрагмент XML-кода табличной части макета

Пример скрипта

Что, собственно, нужно сделать? Распаковать ZIP, «пройтись» по XML-файлу и заменить одни текстовые значения на другие. Отыскать элемент строки табличной части, размножить его по числу строк, опять же, заменяя поля значениями. Наконец нужно поместить новый XML в копию старго ZIP и сохранить ZIP под новым именем.

Возможность работы с ZIP и XML сейчас имеется, наверное, во всех языках. Здесь представлен пример реализации на PHP. Собственно операции по обработке SXW-файла убраны в библиотечный файл OODocFiller.php. Библиотека очень незатейлива, но все же для публикации ее текста в журнале великовата. Скачать ее можно по ссылке [1], никаких условий по ее применению автор не накладывает. Платформа может быть любой, для работы самого скрипта OpenOffice не нужен (он понадобится для печати). PHP должен быть скомпилирован со следующими модулями: ZIP, XPATH (для разбора XML), ENCODE (для перекодирования текстовых значений в UTF 8). К сожалению, в 4 версии PHP нет штатного модуля для создания ZIP-файлов, поэтому я использую библиотеку zip.lib.php из состава дистрибутива phpMyAdmin. Скачать ее можно с [1] или [4].

Представленный ниже скрипт будет создавать документ OpenOffice, используя макет из рис. 1. Макет лежит в файле template.sxw в той же директории, что и скрипт.

01: include('OODocFiller.php');

02:

03: // Подключаем макет

04: $doc = new OODocFiller('template.sxw');

05: // Задаем параметры

06: $doc->SetParameter("RowElement", 'text:p');

07: $doc->SetParameter("Encoding", "WINDOWS-1251");

08:

09: // Определяем поля в начальной части

10: $hdr = &$doc->GetHeaderPart("Number", "Date", "Byer");

11: // Определяем поля в 2 типах строк табличной части

12: // (С белым и серым фоном)

13: $row1 = &$doc->GetNewRow("tb1", "Description", "Numb", "Price", "Amount");

14: $row2 = &$doc->GetNewRow("tb2", "Description", "Numb", "Price", "Amount"); 

15: // Определяем поле в конечной части

16: $ftr = &$doc->GetFooterPart("Total");

17:

18: // Формирование документа

19: // Заполняем начальную часть

20: $hdr->Fill("12", "1.10.2005", "И.И. Иванов");

21: // Заполняем табличную часть

22: // Виды строк чередуются для «полосатости»

23: $row1->Fill("Сапоги»,"2","12,50", "25,00");

24: $row2->Fill("Пироги»,"1","10,00", "10,00");

25: $row1->Fill("Плюшки»,"10","1,00", "10,00");

26: $row2->Fill("DVD+R","1","5,00", "5,00");

27: // Заполняем конечную часть

28: $ftr->Fill(«50,00»);

29: // Остается только сохранить сформированный документ в файл

30: if($doc->Error == ""){

31:     $fd = fopen ("report.sxw", "wb");

32:     $out = fwrite ($fd, $doc->GetBinaryResult());

33:     fclose($fd);

34: }

35: else{

36:     print "Error: ".$doc->Error;

37: }

После запуска скрипт должен создать файл report.sxw в этой же директории (см. рис. 3). Если, конечно, вы не забыли обеспечить соответствующие права.

Небольшие пояснения. Строки 6 и 7 задают параметры. Параметров у этой библиотеки хватает, ознакомиться с ними можно в файле OODocFiller.php. Все они имеют значения по умолчанию, поэтому если вас устроят стандартные настройки, то явно задавать ничего не нужно. Даже здесь строки 6 и 7 избыточны (даны для примера), они задают значения, которые используются по умолчанию. Внутреннее представление OpenOffice – UTF-8, поэтому важно правильно задать кодировку, в которой ваш скрипт будет давать значения полей при вызове библиотечных функций.

Рисунок 3. Сформированный документ

Рисунок 3. Сформированный документ

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

Строка 20 заполняет поля начальной части. Парметр Fill может быть единым массивом значений, что удобно при работе с БД. Вместо строк 23-26 обычно находится цикл выборки значений из таблицы БД.

Печать

Итак, мы получили файл. Как отправить его на принтер?

Первый способ – это печать с клиента. Для этого на клиенте должен быть установлен OpenOffice. При инсталляции он добавляет свои типы файлов в системный MIME, поэтому серверу достаточно передать сгенерированный файл с нужным HTTP-заголовком, и OpenOffice будет автоматически запущен браузером.

Вот пример скрипта, который отправляет файл подобным образом:

$content = file_get_contents("report.sxw");

header("Pragma: public");

header("Expires: 0");

header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

header("Cache-Control: private",false);

header("Content-type: application/vnd.sun.xml.writer");

header("Content-Disposition: attachment; filename=report.sxw;");

header("Content-Transfer-Encoding: binary");

header("Content-Length: ".filesize("report.sxw"));

print $content;

Второй способ – это отправка на печать непосредственно с сервера (мы ведь ведем речь о внутрикорпоративной информационной системе).

Для отправки документа на печать из-под Windows достаточно запустить следующее:

<путь к исп.файлам OO>\SOFFICE.EXE –pt"<принтер>" <документ>

или при печати на принтер по умолчанию:

<путь к исп.файлам OO>\SOFFICE.EXE –p <документ>

На Linux-сервере все будет немного сложнее, плюс для настройки потребуются привилегии root. Но преодоление трудностей всегда раздвигало горизонты.

Прежде всего, для OpenOffice, даже для работы в пакетном режиме, необходимо наличие запущенного X-терминала. На сайте oooforum.org [5] можно найти рецепты, как организовать для этих целей виртуальный терминал, например Xvfb [6], который вовсе не имеет доступа к экрану. Мы же на своем сервере организовали вторую X-консоль. Вот что мы добавили в наш /etc/inittab файл:

x1:5:respawn:/usr/X11R6/bin/xinit /usr/X11R6/bin/xclock -- /usr/X11R6/bin/Xorg :1

Смысл полей этой строки примерно следующий:

  • x1 – это просто уникальный идентификатор строки;
  • 5 – это уровень инициализации, при котором строка будет использоваться (5 – графический режим);
  • respawn – перезапускать при завершении;
  • xinit xclock – Xorg-запуск графической консоли с программой часы (наличие часов не дает xinit завершиться);
  • 1 – номер создаваемого графического экрана. Этот номер экрана мы будем сообщать OpenOffice при запуске в пакетном режиме.

Теперь нужно определиться, под каким пользователем веб-сервер будет запускать скрипты для печати. Этот пользователь должен обязательно иметь домашнюю директорию. Необходимо один раз зайти этим пользователем и запустить любую программу OpenOffice. При этом в домашней директории создадутся персональные файлы OpenOffice. Без этих файлов пакетный режим, к сожалению, не сработает, поэтому важно, чтобы при работе скрипта переменная окружения HOME была определена и указывала именно на домашнюю директорию этого пользователя.

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

<путь к исп.файлам OO>/soffice -display :1 –p <документ>

распечатает документ на принтер, установленный по умолчанию. Обратите внимание, что число у display – это номер графического экрана из inittab.

Если же на вашем Linux-сервере определено несколько принтеров и вы хотите при печати иметь возможность выбора принтера, то познавательный процесс настроек будет продолжен.

Дело в том, что в версиях 1.1.* не убрали какую-то отладочную заглушку. Поэтому в пакетном режиме OpenOffice всегда печатает на принтере, являющимся по умолчанию на момент старта программы.

Как же быть? Мы еще не тестировали на этот счет OpenOffice 2.0, а для версии 1.1.* вышли из положения при помощи печати через PDF. Файлы PDF печатаются системными средствами Linux куда угодно, и кроме того, возможность генерации PDF имеет самостоятельную ценность.

Как конвертировать SXW в PDF

OpenOffice хорошо сохраняет свои файлы в PDF. Можно это делать и в пакетном режиме. Вот как выглядит подготовка Linux-сервера для реализации такой возможности.

Прежде всего необходимо добавить макрос в среду OpenOffice. Заходим в систему пользователем, под которым будут работать скрипты, и запускаем OpenOffice. Проходим по меню «Tools  Macros  Macro» и нажимаем кнопку «Organizer», выбираем закладку (tab) «Libraries» и в выпадающем списке «Application/Document» выбираем «soffice». Нажимаем кнопку «New» и вводим имя своей библиотеки, например Example. Переходим на закладку «Modules» и выбираем свою библиотеку (Example) из списка. Нажимаем кнопку «New Module» и даем ему имя (Print). Теперь выделяем появившийся модуль и нажимаем «Edit». Добрались (см. рис. 4)!

Рисунок 4. Окно редактора basic скрипта OpenOffice

Рисунок 4. Окно редактора basic скрипта OpenOffice

Мы в более-менее привычном редакторе языка Basic. Текст необходимых процедур:

Sub ConvertWordToPDF( cFile )

   On Error GoTo ErrHandler

   ' Открываем файл

   cURL = ConvertToURL( cFile )

   oDoc = StarDesktop.loadComponentFromURL(cURL, "_blank", 0, Array(MakePropertyValue( "Hidden", True ),))

   ' Получаем имя для PDF-файла

   cFile = Left( cFile, Len( cFile ) - 4 ) + ".pdf"

   ' Сохраняем через PDF-фильтр

   cURL = ConvertToURL( cFile )

   oDoc.storeToURL( cURL, Array(MakePropertyValue( "FilterName", "writer_pdf_Export" ),)

   oDoc.close( True )

ExitSub:

   Exit Sub

ErrHandler:

   Shell("logger", 6, "-t ooffice ConvertWordToPDF: " + Error$)

End Sub

Function MakePropertyValue( Optional cName As String, Optional uValue ) As com.sun.star.beans.PropertyValue

   Dim oPropertyValue As New com.sun.star.beans.PropertyValue

   If Not IsMissing( cName ) Then

      oPropertyValue.Name = cName

   EndIf

   If Not IsMissing( uValue ) Then

      oPropertyValue.Value = uValue

   EndIf

   MakePropertyValue() = oPropertyValue

End Function

Это все есть в файле print.bas на [1] – открываем его каким-нибудь редактором и копируем содержимое в модуль. Сохраняемся и выходим из OpenOffice.

Для упрощения использования механизма печати через PDF создадим небольшой shell-скрипт, который разместим в директории, упоминаемой в PATH, например, пусть полное имя будет /usr/local/lib/ooprint:

#!/bin/bash

# Первый параметр – имя принтера, второй – имя (лучше полное) SXW-файла.

Printer=$1

# Получаем имя для PDF-файла

PDFname=${2/.sxw/.pdf}

# Внимание, замените :1 на номер графического дисплея, который вы указали в inittab

# Если вы назвали свою библиотеку не Example, то не забудьте подправить и это

/usr/lib/ooo-1.1/program/soffice -display :1 "macro:///Example.Print.ConvertWordToPDF($2)"

# В предыдущей строке мы создали PDF-файл с тем же именем (кроме суффикса) и в том же месте, что и SXW

# А теперь печатаем PDF

lp -s -d $Printer $PDFname

# Удаляем PDF

rm $PDFname

Теперь, чтобы напечатать SXW-документ, нам достаточно запустить:

ooprint myprinter report.sxw

Если же вместо печати нужна просто конвертация в PDF, то нужно создать модифицированную версию ooprint. Надеюсь, что это ни для кого не составит трудности.

Заключение

Смысл данной статьи заключается не в описании представленной библиотеки, а в демонстрации идеи. Для того чтобы добавить в веб-систему функцию печати, вовсе не обязательно покупать что-то вроде CrystalReport. Обработка XML не сложна и может быть осуществлена на любом языке. Кроме того, в качестве принт-процессора можно использовать, например, TeX. Главное, берем макет, отыскиваем в нем поля…

Но OpenOffice все-таки заслуживает внимания. В целом это зрелый и устойчивый продукт.

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

Во-первых, у OpenOffice есть резидентный модуль quickstart, который должен ускорять запуск. Можно попытаться запускать quickstart из inittab.

Во-вторых, у OpenOffice есть серверный режим, при котором он будет обслуживать удаленные запросы. Есть API. Но с этим разбираться и разбираться.

Успехов!

Ссылки:

  1. Файлы к статье – http://samag.ru/cgi-bin/go.pl?q=source.
  2. Cпецификация w3c для CSS 2 – http://www.w3.org/TR/REC-CSS2/page.html.
  3. OpenOffice – http://www.openoffice.org.
  4. PhpMyAdmin – http://www.phpmyadmin.net.
  5. Форум по OpenOffice – http://www.oooforum.org.
  6. Virtual framebuffer X server for X Version 11  – http://www.xfree86.org/current/Xvfb.1.html.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru