Документація на основі RTF-шаблону h2>
Олександр Харків, "Комиздат" p>
Розробка
прикладного ПЗ - це, як відомо, не лише написання коду програм, а й
проектування друкованих документів і звітів. Практично всі інтегровані
середовища мають у своєму складі генератори звітів, у тому або іншому ступені
допомагають вирішити це завдання. Однак, незважаючи на явні переваги,
використання генераторів звітів має ряд недоліків. Вони зводяться, головним чином,
до неможливості вносити правки в сформований документ, а також змінювати
шаблони звіту звичними засобами, наприклад звичайним текстовим редактором. p>
p>
До
останнього часу найпростішим і широко застосовуваним рішенням представлялося
застосування механізму OLE. Наприклад, для комбінації Word і VisualBasic можлива
така схема: p>
Створюємо
якийсь файл - шаблон документа. Там, де має бути "шапка" (дата,
номер документа тощо), використовуємо закладки, а для основної частини звіту
створюємо таблицю-заготовку відповідної структури. Приклад такого шаблону
наведено на рис. 1. p>
Пишемо
програму з використанням об'єктної моделі Word: p>
'NumStr - кількість рядків у звіті p>
'
NewData (5, NumStr) - масив з даними для заповнення p>
'
таблиці, заздалегідь приведеними до символьному увазі p>
'
Itog - сума, приведена до символьному увазі p>
'
Pth - шлях до вихідного файлу p>
'Str_ndoc = "BS190" p>
'Str_name = "Петров І.І." p>
................. p>
Dim
objWord As Word.Application p>
Dim objDoc As Word.Document p>
Dim objTable As Word.Table p>
'створюємо об'єкт Word p>
Set
objWord = New Word.Application p>
'
робимо його видимим - це не обов'язково, p>
'
але дуже цікаво:) p>
objWord.Visible
= True p>
'
відкриваємо файл шаблону p>
Set
objDoc = objWord.Documents.Open (Pth) p>
'
робимо його активним p>
objDoc.Activate
p>
'
заповнюємо "шапку документа" - номер і одержувач p>
'
- Закладки 'ndoc' і 'name' відповідно p>
objDoc.Bookmarks
( "ndoc"). Range.Text = Str_ndoc p>
objDoc.Bookmarks
( "name"). Range.Text = Str_name p>
'пов'язуючи об'єкт з таблицею p>
Set objTable =
objWord.ActiveDocument.Tables (1) p>
'
виділяємо 2-й рядок таблиці в шаблоні p>
objTable.Cell
(2, 1). Range.Select p>
'
вставляємо потрібну кількість рядків-1 p>
'
(тому що одна вже є в шаблоні) p>
If NumStr> 0 Then
objWord.Selection.InsertRows (NumStr - 1) p>
'для кожного рядка в кожну клітинку вставляємо потрібні p>
'дані з
масиву p>
For i = 1 To NumStr p>
For j = 1 To 5 p>
objTable.Cell (i + 1, j). Range.Text
= NewData (j, i) p>
Next
j p>
Next
i p>
'
проставляємо суму "Усього" p>
objTable.Cell (NumStr + 2,
5). Range.Text = Itog p>
Запускаємо
її в складі всього програми і отримуємо результат (див. рис. 2). p>
Користувач,
отримавши звіт у вигляді doc-файлу, може легко внести до документа будь-які зміни,
відправити його по електронній пошті, роздрукувати - одним словом, розпорядитися
на свій розсуд у звичній йому середовищі. Так само легко він може змінити і
шаблон документа - для цього достатньо вміти працювати в текстовому редакторі. p>
Але
цю ідилічну картину затьмарює кілька неприємних моментів. По-перше,
недостатня гнучкість програми - якщо ви захочете перейти на інший
редактор, то доведеться писати код заново. По-друге, програма працює тільки
у середовищі пакета MS Office, а він коштує чималих грошей. Якщо програма має
працювати на 30-ти комп'ютерах підприємства, то його встановлення на них MS Office
обійдеться приблизно в 40 тис. гривень - не кожен бюджет витримає. p>
p>
В
той же час існує цілий ряд безкоштовних і досить повнофункціональних
офісних пакетів: OpenOffice, StarOffice, EasyOffice та ін Для більшості
операцій, що виконуються зазвичай з документами, їх можливостей цілком достатньо.
Але чи можлива їх проста і ефективна інтеграція в прикладне програмне
забезпечення? p>
Рішенням
цієї проблеми може бути використання RTF-файлів. Цей формат, запропонований
Microsoft як стандарт для обміну даними між текстовими редакторами,
підтримується абсолютною більшістю офісних пакетів. Сама Microsoft
використовує його як формат, в якому дані передаються через буфер
обміну між різними додатками Windows. p>
Коротко про RTF h2>
В
форматі RTF використовуються тільки коди, що представляються символами з наборів
ASCII, MAC і PC. Крім тексту, RTF-файл містить команди управління в яку читає
формі. Документ складається переважно з команд управління налаштуванням
програми читання. Ці команди можна розділити на керуючі слова і
керуючі символи. p>
Управляюче
слово являє собою послідовність символів з роздільником в кінці.
Наприклад, фрагмент: p>
... bkmkstart
ndoc ... p>
відповідає
початок закладку ndoc. p>
Перед
керуючим словом вводиться зворотна коса риска (). Як роздільників
можуть використовуватися такі символи: p>
пробіл,
причому цей символ належить до керуючого слову; p>
цифра
або дефіс (<->). Після цих символів має слідувати параметр з
роздільником. Як роздільник може бути використаний пробіл або інші
символи (окрім цифр і букв); p>
все
символи, окрім цифр і букв. Ці символи не належать до керуючого слова. p>
Для
завдання керуючої послідовності в RTF-форматі використовуються літери від А до
Z і від а до z, а також цифри від 0 до 9. Національні символи до керуючої
інформації не відносяться. p>
В
як керуючих символів використовуються окремі літери. Перед кожним
керуючим символом вводиться зворотна коса риска (). Наприклад, фрагмент: p>
... f1fs20 ... p>
встановлює
шрифт № 1 розміром в 20 одиниць. p>
Фрагмент
RTF-файлу наведено нижче. Структура його, як можна бачити, нагадує структуру
HTML-документа: p>
intblphmrgposy371dxfrtext180dfrmtxtx180dfrmtxty0nowrap p>
aspalphaaspnumfaautoadjustrightrin0lin0f1fs20lang1049 p>
langfe1049cgridlangnp1049langfenp1049 (lang1033langfe1049 p>
langnp1033 11cell 12cell 13cell)
pard ql li0ri0widctlparintbl p>
aspalphaaspnumfaautoadjustrightrin0lin0
p>
В
RTF-форматі існує можливість поєднувати окремі послідовності в
групи за допомогою дужок: p>
(група) p>
Такі
групи створюються, наприклад, при описі виносок, колонтитулів, закладок і т.п. p>
Ось
деякі керуючі слова і символи, що мають безпосереднє відношення до
теми нашої статті: p>
раr
- Кінець абзацу; p>
сеll
- Кінець стовпця; p>
row
- Кінець рядка (або таблиці); p>
* bkmkstart
<назву закладки> * bkmkend - закладка. Приклад: (* bkmkstart ndoc)
BS190 (* bkmkend ndoc); p>
pard
- Встановлює стандартну настройку для абзацу; p>
intbl
... Intbl - виділяє область таблиці; p>
'
- Прямий введення в текст шістнадцятиричних чисел. При збереженні кириличного
тексту він зазвичай зберігається в шістнадцятковій формі, наприклад: p>
'd1'f2'f0'ee'ea'e0 (' рядок ') p>
Оскільки
нас цікавлять тільки певні завдання, знання наведених вище керуючих
слів і символів цілком достатньо. Умовимося для простоти називати керуючі
слова і символи тегами. p>
А
тепер розглянемо алгоритми рішення трьох основних завдань, що виникають при
створення документації. p>
Вставка рядка на місці закладки p>
Приклад
такої закладки: p>
... (* bkmkstart ndoc) <значення закладку> (* bkmkend
ndoc) ... p>
Для
вирішення даного завдання можна запропонувати наступний алгоритм. p>
Читаємо
послідовно рядка вхідного файлу (у більшості випадків рядок більше 255
символів). p>
Шукаємо
в поточному рядку тег 'bkmkstart'. p>
Якщо
знаходимо, то виділяємо назву закладки і порівнюємо його з шуканої. p>
Якщо
збігається, то записуємо строкову рядок даних після закриває дужки ()). p>
Алгоритм
реалізований у вигляді функції In_Zakl1 (pth As String, zakl As String, data As
String), де pth - ім'я RTF-файлу, zakl - ім'я закладки, data - рядок для
додати у файл. p>
Додавання рядків у таблицю p>
Припустимо,
нам потрібно знайти m-й рядок у n-тої таблиці і повторити її в цій таблиці p
разів. Для пошуку початку рядка таблиці ми будемо використовувати тег intbl, а для
пошуку кінця - тег row. Кінець самої таблиці визначається за послідовністю
тегів row ... pard ... par. p>
Алгоритм
вирішення цього завдання наступний. p>
Читаємо
послідовно рядка вхідного файлу. p>
Шукаємо
послідовність ... row ... pard ... par ... intbl ... (не обов'язково в одному рядку)
(n-1) разів. Після цього ми перебуваємо на початку потрібної таблиці. p>
Шукаємо
тег row (m-1) разів. Після цього знаходимося перед потрібної рядком таблиці. p>
Шукаємо
наступний тег row і копіюємо вміст файлу від (m-1)-го до m-го тега row
(між row і intbl містяться налаштування рядки, вони нам теж потрібні). p>
Вставляємо
після m-го тега row скопійовану нами підрядок p раз. p>
Слід
відзначити, що недоліком запропонованого алгоритму є те, що він може
копіювати будь-який рядок таблиці, крім першої. Але в більшості випадків першим
рядок є "шапкою" документа і копіювати її немає необхідності.
p>
Алгоритм
реалізований у вигляді функції In_TStr (pth As String, itbl As Integer, irow As
Integer, kol As Integer), де pth - ім'я RTF-файлу, itbl - номер таблиці, irow --
номер рядка, kol - кількість повторів рядка. p>
Заповнення комірки
таблиці h2>
Уявімо,
що потрібно знайти k-у клітинку в m-й рядку n-й таблиці і вставити в неї
текстовий рядок даних. Приклад таких осередків: p>
... (lang1033cgrid0 <вміст
1-й осередки> p>
cell <вміст
2-й осередки> cell) ... p>
Завдання
може бути вирішена за наступним алгоритмом. p>
Читаємо
послідовно рядка вхідного файлу. p>
Шукаємо
послідовність ... row ... pard ... par ... intbl ... (не обов'язково в одному рядку)
(n-1) разів. Після цього ми знаходимося перед потрібної нам таблицею. p>
Шукаємо
тег row (m-1) разів. Після цього ми перебуваємо на початку потрібної рядки таблиці. p>
Шукаємо
k-e входження тега cell. p>
Вставляємо
перед ним рядок даних. p>
Даний
алгоритм реалізований у вигляді функції In_Tcell1 (pth As String, itbl As Integer,
irow As Integer, icell As Integer, ndata As String), де pth - ім'я RTF-файлу,
itbl - номер таблиці, irow - номер рядка, icell - номер комірки, data - рядок
для занесення в комірку. p>
Програма
на VisualBasic, що демонструє застосування такої технології і функціонально
ідентична програмі, наведеної на початку цієї статті, виглядає так: p>
'NumStr - кількість рядків у звіті p>
'
NewData (5, NumStr) - масив з даними для заповнення p>
'
таблиці, заздалегідь приведеними до символьному увазі p>
'
Itog - сума, приведена до символьному увазі p>
'
pth - шлях до файлу p>
'
Str_ndoc = "BS190" p>
'
Str_name = "Петров І.І." p>
Dim
res As Boolean 'результат виконання функцій p>
'
заповнюємо "шапку документа" - номер і одержувач p>
'
- Закладки 'ndoc' і 'name' відповідно p>
res = In_Zakl1 (pth,
"ndoc", Str_ndoc) p>
res = In_Zakl1 (pth,
"name", Str_name) p>
'
вставляємо потрібну кількість рядків-1 p>
'
(тому що одна вже є в шаблоні) p>
res = In_TStr (pth, 1, 2, NumStr --
1) p>
'
для кожного рядка в кожну клітинку вставляємо p>
'
потрібні дані з масиву p>
For
i = 1 To NumStr p>
For j = 1 To 5 p>
res = In_Tcell1 (pth, 1, i + 1, j,
NewData (j, i)) p>
Next j p>
Next i p>
res = In_Tcell1 (pth, 1, NumStr + 2,
5, Itog) p>
'
проставляємо суму "Усього" p>
Висновок h2>
Які
переваги та недоліки запропонованої технології? Почнемо з переваг.
По-перше, це більш гнучка технологія для формування звітів - навіть якщо
частина користувачів працює з OpenOffice, а частина з MS Office, програма
створення звітних документів універсальна. По-друге, незважаючи на багаторазову
перезапис файлу шаблону під час роботи, ця програма працює швидше, ніж
зв'язка OLE + Word. Тим більше, що наведені вище алгоритми можуть
удосконалюватися. Один із прикладів кардинального підвищення продуктивності
наведено в лістингах варіанту для PascalDelphi. По-третє, користуючись вільним
ПЗ, ви економите гроші. p>
Тепер
про проблеми. Основна з них - це недостатня стандартизація формату RTF.
Виробники ПЗ, в цілому дотримуючись єдиного стандарту, допускають кілька
вільне трактування приватних моментів. Результат - проблеми з використанням
"чужих" RTF-файлів, підготовлених в інших редакторах. Наприклад, MS
Word зберігає графічні зображення усередині RTF-файла у вигляді послідовності
шістнадцятиричних кодів, а OOWriter - як зовнішній файл. p>
Втім,
ці проблеми вирішуються - варто лише прикласти деякі зусилля. p>
Список літератури h2>
Для
підготовки даної роботи були використані матеріали з сайту http://www.citforum.ru/
p>