Документация на основе RTF-шаблона

Документация на основе RTF-шаблона

Александр Харьков, “Комиздат”

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

До
последнего времени самым простым и широко применяемым решением представлялось
применение механизма OLE. Например, для комбинации Word и VisualBasic возможна
такая схема:

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

Пишем
программу с использованием объектной модели Word:

‘ NumStr – кол-во строк в отчете


NewData (5,NumStr) – массив с данными для заполнения


таблицы, заранее приведенными к символьному виду


Itog – сумма, приведенная к символьному виду


Pth – путь к исходному файлу

‘ Str_ndoc = “BS190”

‘ Str_name = “Петров И.И.”

……………..

Dim
objWord As Word.Application

Dim objDoc As Word.Document

Dim objTable As Word.Table

‘ создаем объект Word

Set
objWord = New Word.Application


делаем его видимым – это не обязательно,


но очень интересно 🙂

objWord.Visible
= True


открываем файл шаблона

Set
objDoc = objWord.Documents.Open (Pth)


делаем его активным

objDoc.Activate


заполняем “шапку документа” – номер и получатель


– закладки ‘ndoc’ и ‘name’ соответственно

objDoc.Bookmarks
(“ndoc”).Range.Text = Str_ndoc

objDoc.Bookmarks
(“name”).Range.Text = Str_name

‘ связывам объект с таблицей

Set objTable =
objWord.ActiveDocument.Tables (1)


выделяем 2-ю строку таблицы в шаблоне

objTable.Cell
(2, 1).Range.Select


вставляем нужное кол-во строк-1


(т.к. одна уже есть в шаблоне)

If NumStr > 0 Then
objWord.Selection.InsertRows (NumStr – 1)

‘ для каждой строки в каждую ячейку вставляем нужные

‘ данные из
массива

For i = 1 To NumStr

For j = 1 To 5

objTable.Cell (i + 1, j).Range.Text
= NewData (j, i)

Next
j

Next
i


проставляем сумму “Всего”

objTable.Cell (NumStr + 2,
5).Range.Text = Itog

Запускаем
ее в составе всего приложения и получаем результат (см. рис. 2).

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

Но
эту идиллическую картину омрачает несколько неприятных моментов. Во-первых,
недостаточная гибкость приложения – если вы захотите перейти на другой
редактор, то придется писать код заново. Во-вторых, приложение работает только
в среде пакета MS Office, а он стоит немалых денег. Если приложение должно
работать на 30-ти компьютерах предприятия, то установка на них MS Office
обойдется примерно в 40 тыс. гривен – не каждый бюджет выдержит.

В
то же время существует целый ряд бесплатных и достаточно полнофункциональных
офисных пакетов: OpenOffice, StarOffice, EasyOffice и др. Для большинства
операций, выполняемых обычно с документами, их возможностей вполне достаточно.
Но возможна ли их простая и эффективная интеграция в прикладное программное
обеспечение?

Решением
этой проблемы может быть использование RTF-файлов. Этот формат, предложенный
Microsoft как стандарт для обмена данными между текстовыми редакторами,
поддерживается абсолютным большинством офисных пакетов. Сама Microsoft
использует его в качестве формата, в котором данные передаются через буфер
обмена между различными приложениями Windows.
Кратко об RTF

В
формате RTF используются только коды, представляемые символами из наборов
ASCII, MAC и PC. Помимо текста, RTF-файл содержит команды управления в читаемой
форме. Документ состоит преимущественно из команд управления настройкой
программы чтения. Эти команды можно разделить на управляющие слова и
управляющие символы.

Управляющее
слово представляет собой последовательность символов с разделителем в конце.
Например, фрагмент:

…bkmkstart
ndoc…

соответствует
началу закладки ndoc.

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

пробел,
причем этот символ относится к управляющему слову;

цифра
или дефис (). После этих символов должен следовать параметр с
разделителем. В качестве разделителя может быть использован пробел или другие
символы (кроме цифр и букв);

все
символы, кроме цифр и букв. Эти символы не относятся к управляющему слову.

Для
задания управляющей последовательности в RTF-формате используются буквы от А до
Z и от а до z, а также цифры от 0 до 9. Национальные символы к управляющей
информации не относятся.

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

…f1fs20…

устанавливает
шрифт № 1 размером в 20 единиц.

Фрагмент
RTF-файла приведен ниже. Структура его, как можно видеть, напоминает структуру
HTML-документа:

intblphmrgposy371dxfrtext180dfrmtxtx180dfrmtxty0nowrap

aspalphaaspnumfaautoadjustrightrin0lin0f1fs20lang1049

langfe1049cgridlangnp1049langfenp1049{lang1033langfe1049

langnp1033 11cell 12cell 13cell}
pard ql li0ri0widctlparintbl

aspalphaaspnumfaautoadjustrightrin0lin0

В
RTF-формате существует возможность объединять отдельные последовательности в
группы при помощи скобок:

{группа}

Такие
группы создаются, например, при описании сносок, колонтитулов, закладок и т.п.

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

раr
– конец абзаца;

сеll
– конец столбца;

row
– конец строки (или таблицы);

*bkmkstart
*bkmkend – закладка. Пример: {*bkmkstart ndoc}
BS190{*bkmkend ndoc};

pard
– устанавливает стандартную настройку для абзаца;

intbl
… intbl – выделяет область таблицы;


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

‘d1’f2’f0’ee’ea’e0 (‘строка’)

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

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

Вставка строки на месте закладки

Пример
такой закладки:

…{*bkmkstart ndoc}{*bkmkend
ndoc}…

Для
решения данной задачи можно предложить следующий алгоритм.

Читаем
последовательно строки входного файла (в большинстве случаев строка больше 255
символов).

Ищем
в текущей строке тег ‘bkmkstart’.

Если
находим, то выделяем название закладки и сравниваем его с искомой.

Если
совпадает, то записываем строковую строку данных после закрывающей скобки (}).

Алгоритм
реализован в виде функции In_Zakl1(pth As String, zakl As String, data As
String), где pth – имя RTF-файла, zakl – имя закладки, data – строка для
добавления в файл.

Добавление строк в таблицу

Предположим,
нам требуется найти m-ю строку в n-той таблице и повторить ее в этой таблице p
раз. Для поиска начала строки таблицы мы будем использовать тег intbl, а для
поиска конца – тег row. Конец самой таблицы определяется по последовательности
тегов row…pard…par.

Алгоритм
решения этой задачи следующий.

Читаем
последовательно строки входного файла.

Ищем
последовательность …row…pard…par…intbl… (не обязательно в одной строке)
(n-1) раз. После этого мы находимся в начале нужной таблицы.

Ищем
тег row (m-1) раз. После этого находимся перед нужной строкой таблицы.

Ищем
следующий тег row и копируем содержимое файла от (m-1)-го до m-го тега row
(между row и intbl содержатся настройки строки, они нам тоже нужны).

Вставляем
после m-го тега row скопированную нами подстроку p раз.

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

Алгоритм
реализован в виде функции In_TStr (pth As String, itbl As Integer, irow As
Integer, kol As Integer), где pth – имя RTF-файла, itbl – номер таблицы, irow –
номер строки, kol – количество повторов строки.
Заполнение ячейки
таблицы

Представим,
что требуется найти k-ю ячейку в m-й строке n-й таблицы и вставить в нее
текстовую строку данных. Пример таких ячеек:

…{lang1033cgrid0

cellcell}…

Задача
может быть решена по следующему алгоритму.

Читаем
последовательно строки входного файла.

Ищем
последовательность …row…pard…par…intbl… (не обязательно в одной строке)
(n-1) раз. После этого мы находимся перед нужной нам таблицей.

Ищем
тег row (m-1) раз. После этого мы находимся в начале нужной строки таблицы.

Ищем
k-e вхождение тега cell.

Вставляем
перед ним строку данных.

Данный
алгоритм реализован в виде функции In_Tcell1(pth As String, itbl As Integer,
irow As Integer, icell As Integer, ndata As String), где pth – имя RTF-файла,
itbl – номер таблицы, irow – номер строки, icell – номер ячейки, data – строка
для занесения в ячейку.

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

‘ NumStr – кол-во строк в отчете


NewData (5,NumStr) – массив с данными для заполнения


таблицы, заранее приведенными к символьному виду


Itog – сумма, приведенная к символьному виду


pth – путь к файлу


Str_ndoc = “BS190”


Str_name = “Петров И.И.”

Dim
res As Boolean ‘ результат выполнения функций


заполняем “шапку документа” – номер и получатель


– закладки ‘ndoc’ и ‘name’ соответственно

res = In_Zakl1(pth,
“ndoc”, Str_ndoc)

res = In_Zakl1(pth,
“name”, Str_name)


вставляем нужное кол-во строк-1


(т.к. одна уже есть в шаблоне)

res = In_TStr (pth, 1, 2, NumStr –
1)


для каждой строки в каждую ячейку вставляем


нужные данные из массива

For
i = 1 To NumStr

For j = 1 To 5

res = In_Tcell1(pth, 1, i + 1, j,
NewData (j, i))

Next j

Next i

res = In_Tcell1(pth, 1, NumStr + 2,
5, Itog)


проставляем сумму “Всего”
Заключение

Каковы
преимущества и недостатки предложенной технологии? Начнем с достоинств.
Во-первых, это более гибкая технология для формирования отчетов – даже если
часть пользователей работает с OpenOffice, а часть с MS Office, программа
создания отчетных документов универсальна. Во-вторых, несмотря на многоразовую
перезапись файла шаблона во время работы, эта программа работает быстрее, чем
связка OLE+Word. Тем более что приведенные выше алгоритмы могут
совершенствоваться. Один из примеров кардинального повышения производительности
приведен в листингах варианта для PascalDelphi. В-третьих, пользуясь свободным
ПО, вы экономите деньги.

Теперь
о проблемах. Основная из них – это недостаточная стандартизация формата RTF.
Производители ПО, в целом придерживаясь единого стандарта, допускают несколько
свободную трактовку частных моментов. Результат – проблемы с использованием
“чужих” RTF-файлов, подготовленных в других редакторах. Например, MS
Word сохраняет графические изображения внутри RTF-файла в виде последовательности
шестнадцатеричных кодов, а OOWriter – как внешний файл.

Впрочем,
эти проблемы решаются – стоит только приложить некоторые усилия.
Список литературы

Для
подготовки данной работы были использованы материалы с сайта http://www.citforum.ru/