Pull to refresh

Простой способ подготовки отчетов на основе rtf-бланков

Reading time 4 min
Views 15K
При практической эксплуатации информационных систем уровня предприятия, особенно при недостаточно развитой системе подготовки отчетов, часто бывает необходимо заполнить разного рода бланки (например, заявления, справки, заключения и т.д.) или подготовить отчеты для распечатки на лазерном принтере, примерно в таком виде:

Пример rtf-бланка
Заголовок документа: PARAM1
Строка 1 Значение PARAM2
Строка 2 Значение PARAM3

Подпись под документом: PARAM4

Рис.1 Пример бланка в виде rtf-файла, переменные описаны в виде полей типа “QUOTE” – PARAM1,PARAM2,PARAM3,PARAM4



При заполнении бланка данными, переменные PARAM1...4 должны будут замениться на свои значения.
При этом форма бланка обычно остается постоянной и неизменной, а конкретные поля в бланке необходимо “подгружать” из таблиц баз данных, файлов с данными и т.д. В терминах языка Perl – значениями хеша, ключи которого соответствуют названиям переменных:
(PARAM1=>”ПАРАМЕТР1”, PARAM1=>”ПАРАМЕТР2”, PARAM1=>”ПАРАМЕТР3”,
PARAM1=>”ПАРАМЕТР4”)
В результате, готовый к печати на лазерном принтере бланк, будет иметь примерно такой вид:

Пример rtf-бланка
Заголовок документа: ПАРАМЕТР1
Строка 1 Значение ПАРАМЕТР2
Строка 2 Значение ПАРАМЕТР3

Подпись под документом: ПАРАМЕТР4

Рис.2 Результат заполнения rtf-бланка (рис.1) заменой полей типа “QUOTE” на значения соответствующих ключей хеша.


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

Можно, разумеется, сказать, что подобная технология слияния данных давно используется в MS Word, например, но она имеет серьезный недостаток: процедура слияния довольно сложна для неподготовленного пользователя (необходимо подготовить файл с данными, выбрать бланк, запустить процесс слияния, разобраться с результатами).

Другой вариант – использование бланков в форме XML-файлов и заполнение их в соответствии с правилами подстановки, описанными в XSLT-таблицах,- имеет тот недостаток, что неподготовленному пользователю, привыкшему работать с MS Word будет трудно создавать и редактировать бланки XML-файлов. Какой-либо общераспространенный инструмент для работы с XML, сравнимый по популярности с MS Word трудно найти.

Выходом, разработанным и апробированным уже в течение ряда лет при работе в крупной банковской информационной системе стал вариант автоматической подстановки данных в rtf-бланки отчетов с использованием простого скрипта (в данном случае использовался perl, но возможно использование любого языка, поддерживающего обработку регулярных выражений), осуществляющего поиск в текстовом rtf-документе описателей полей типа “QUOTE” и замену символических названий этих полей на значение переменной с аналогичным именем, извлеченной из таблицы БД, файла данных (в терминах perl – из хеша по ключу, соответствующему символическому названию поля).

Пример скрипта c комментариями, выполняющего подобную подстановку, приведен в конце постинга.

Скорее всего, даже краткого пояснения принципа работы скрипта не требуется (готовый rtf-бланк с выполненными подстановками значений переменных выводится во временный файл temp.rtf, подготовленный к отправке на принтер), единственная представляющая в нем интерес часть – регулярное выражение:

$rtf =~ s/\{\\field\b[^{}]*\{\\\*\\fldinst.*?{\\fldrslt\s*\{([^}]+\s)?\s*([^}]+\s) ?\s*([^}]+)\}\s*((?:\{[^{}]+\}\s*)*)\}\s*\}/dosubst($1,$2,$3,$p,$&)/gesx;


осуществляющее поиск в rtf-файле описателей полей типа “QUOTE” и замену их соответствующим значением из хеша %p, возвращаемым функцией dosubst. Работоспособность этого регулярного выражения проверена на rtf-файлах, подготавливаемых всеми пакетами MS Office – от Office 95 до Office 2010.

Конечно, данный способ имеет некоторые недостатки, например, довольно трудно осуществлять вывод в rtf-бланк табличных значений, особенно для длинных таблиц, (их, например, можно выводить сразу в готовом виде, строя в perl-скрипте), но имеет и серьезные плюсы:

— разработку бланка и расстановку в нем полей для размещения данных может выполнять обычный пользователь, знакомый с редактором Word (умеющий вставлять в текст поля типа “QUOTE”), сообщающий затем список названий полей данных и что в них желательно помещать при подстановке программисту или специалисту службы поддержки;
— разработку скрипта, выгружающего данные, осуществляет программист, а в дальнейшем, при необходимости косметических изменений бланка (шрифт, отступы, логотип и т.д.), программиста привлекать необходимости нет, с этой работой легко справляется или служба поддержки, или сам пользователь – автор бланка.

Таким образом, легко выполняется разделение формы представления данных, за которую отвечает пользователь или служба поддержки, от собственно данных, процедуру извлечения которых разрабатывает программист.

#!/usr/bin/perl
# filling rtf-blank
# Dim Kobzev, Andrew Sapognikov
# 2004

$==1000;
#-------------------------filling template file-------------------------------
sub dosubst {
    my ($pfx, $key, $xtrakey, $parm, $str) = @_;
    if (defined $xtrakey) {
        $xtrakey =~ s/[{}]//g;
        $xtrakey =~ s/\\w+//g;
        $xtrakey =~ s/\s//g;
        $key .= $xtrakey;
    }
    defined ($parm->{$key}) or return "\{$pfx\}";
    my $val = $parm->{$key};
    $val =~ s/([{}\\])/\\$1/g;
    $val =~ s/\n/\\line/g;
    $val =~ s/\r//g;
    return "\{$pfx$val\}";
}

sub template ($$) { #filling $filename with hash $p
 my ($filename,$p)=@_;
 my $rtf;
 local $/, *F;
 open (F,"< $filename") or die "Cannot open $filename";
 $rtf=<F>; close(F);
 #set win-1251 codepage
 $rtf =~ s/\\f(5|6)\\fmodern\\fcharset0/\\f$1\\fmodern\\fcharset144/;
 $rtf =~ s/\{\\field\b[^{}]*\{\\\*\\fldinst.*?{\\fldrslt\s*\{([^}]+\s)
 ?\s*([^}]+)\}\s*((?:\{[^{}]+\}\s*)*)\}\s*\}/dosubst($1,$2,$3,$p,$&)/gesx;
 return $rtf;
};

#-------------------------main--------------------------------------------------
%p=('PARAM1'=>"ПАРАМЕТР1",'PARAM2'=>"ПАРАМЕТР2",
'PARAM3'=>"ПАРАМЕТР3",'PARAM4'=>"ПАРАМЕТР4");
my $tfil='temp.rtf';
open *FILE, "> $tfil"
  or die "Cannot open temporary file $tfil";
print FILE template("blank.rtf",\%p);
close *FILE;
#---------------------------------end main-------------------------------------

Tags:
Hubs:
+12
Comments 16
Comments Comments 16

Articles