1 #====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #====================================================================
 
   9 package SimpleTemplate;
 
  12 #   1. The template's file name
 
  13 #   2. A reference to the Form object
 
  14 #   3. A reference to the myconfig hash
 
  17 #   A new template object
 
  31   $self->{source}    = shift;
 
  32   $self->{form}      = shift;
 
  33   $self->{myconfig}  = shift;
 
  34   $self->{userspath} = shift;
 
  36   $self->{error}     = undef;
 
  37   $self->{quot_re}   = '"';
 
  39   $self->set_tag_style('<%', '%>');
 
  44   my $tag_start               = shift;
 
  47   $self->{tag_start}          = $tag_start;
 
  48   $self->{tag_end}            = $tag_end;
 
  49   $self->{tag_start_qm}       = quotemeta $tag_start;
 
  50   $self->{tag_end_qm}         = quotemeta $tag_end;
 
  52   $self->{substitute_vars_re} = "$self->{tag_start_qm}(.+?)$self->{tag_end_qm}";
 
  60 #   1. A typeglob for the file handle. The output will be written
 
  61 #      to this file handle.
 
  64 #   1 on success and undef or 0 if there was an error. In the latter case
 
  65 #   the calling function can retrieve the error message via $obj->get_error()
 
  70   print(OUT "Hallo!\n");
 
  76   return $self->{"error"};
 
  83 sub _get_loop_variable {
 
  86   my $get_array = shift;
 
  89   my $form      = $self->{form};
 
  92   if (($get_array || @indices) && (ref $form->{TEMPLATE_ARRAYS} eq 'HASH') && (ref $form->{TEMPLATE_ARRAYS}->{$var} eq 'ARRAY')) {
 
  93     $value = $form->{TEMPLATE_ARRAYS}->{$var};
 
  95     $value = $form->{$var};
 
  98   for (my $i = 0; $i < scalar(@indices); $i++) {
 
  99     last unless (ref($value) eq "ARRAY");
 
 100     $value = $value->[$indices[$i]];
 
 106 sub substitute_vars {
 
 107   my ($self, $text, @indices) = @_;
 
 109   my $form = $self->{"form"};
 
 111   while ($text =~ /$self->{substitute_vars_re}/) {
 
 112     my ($tag_pos, $tag_len) = ($-[0], $+[0] - $-[0]);
 
 113     my ($var, @options)     = split(/\s+/, $1);
 
 115     my $value               = $self->_get_loop_variable($var, 0, @indices);
 
 116     $value                  = $self->format_string($value) unless (grep(/^NOESCAPE$/, @options));
 
 118     substr($text, $tag_pos, $tag_len, $value);
 
 124 sub _parse_block_if {
 
 125   $main::lxdebug->enter_sub();
 
 128   my $contents     = shift;
 
 129   my $new_contents = shift;
 
 133   $$new_contents .= $self->substitute_vars(substr($$contents, 0, $pos_if), @indices);
 
 134   substr($$contents, 0, $pos_if) = "";
 
 136   if ($$contents !~ m/^$self->{tag_start_qm}if
 
 138                      (not\b|\!)?           # $1 -- Eventuelle Negierung
 
 140                      (\b.+?\b)             # $2 -- Name der zu überprüfenden Variablen
 
 141                      (                     # $3 -- Beginn des optionalen Vergleiches
 
 143                        ([!=])              # $4 -- Negierung des Vergleiches speichern
 
 144                        ([=~])              # $5 -- Art des Vergleiches speichern
 
 146                        (                   # $6 -- Gequoteter String oder Bareword
 
 148                          (.*?)(?<!\\)      # $7 -- Gequoteter String -- direkter Vergleich mit eq bzw. ne oder Patternmatching; Escapete Anführungs als Teil des Strings belassen
 
 151                          (\b.+?\b)         # $8 -- Bareword -- als Index für $form benutzen
 
 157     $self->{"error"} = "Malformed $self->{tag_start}if$self->{tag_end}.";
 
 158     $main::lxdebug->leave_sub();
 
 164   my $operator_neg  = $4; # '=' oder '!' oder undef, wenn kein Vergleich erkannt
 
 165   my $operator_type = $5; # '=' oder '~' für Stringvergleich oder Regex
 
 166   my $quoted_word   = $7; # nur gültig, wenn quoted string angegeben (siehe unten); dann "value" aus <%if var == "value" %>
 
 167   my $bareword      = $8; # undef, falls quoted string angegeben wurde; andernfalls "othervar" aus <%if var == othervar %>
 
 169   $not = !$not if ($operator_neg && $operator_neg eq '!');
 
 171   substr($$contents, 0, length($&)) = "";
 
 174   ($block, $$contents) = $self->find_end($$contents, 0, $var, $not);
 
 176     $self->{"error"} = "Unclosed $self->{tag_start}if$self->{tag_end}." unless ($self->{"error"});
 
 177     $main::lxdebug->leave_sub();
 
 181   my $value = $self->_get_loop_variable($var, 0, @indices);
 
 184   if ($operator_type) {
 
 185     my $compare_to = $bareword ? $self->_get_loop_variable($bareword, 0, @indices) : $quoted_word;
 
 186     if ($operator_type eq '=') {
 
 187       $hit         = ($not && !($value eq $compare_to))     || (!$not && ($value eq $compare_to));
 
 189       $hit         = ($not && !($value =~ m/$compare_to/i)) || (!$not && ($value =~ m/$compare_to/i));
 
 193     $hit           = ($not && ! $value)                     || (!$not &&  $value);
 
 197     my $new_text = $self->parse_block($block, @indices);
 
 198     if (!defined($new_text)) {
 
 199       $main::lxdebug->leave_sub();
 
 202     $$new_contents .= $new_text;
 
 205   $main::lxdebug->leave_sub();
 
 216 package LaTeXTemplate;
 
 220 @ISA = qw(SimpleTemplate);
 
 225   my $self = $type->SUPER::new(@_);
 
 231   my ($self, $variable) = @_;
 
 232   my $form = $self->{"form"};
 
 234   $variable = $main::locale->quote_special_chars('Template/LaTeX', $variable);
 
 236   # Allow some HTML markup to be converted into the output format's
 
 237   # corresponding markup code, e.g. bold or italic.
 
 238   my %markup_replace = ('b' => 'textbf',
 
 242   foreach my $key (keys(%markup_replace)) {
 
 243     my $new = $markup_replace{$key};
 
 244     $variable =~ s/\$\<\$${key}\$\>\$(.*?)\$<\$\/${key}\$>\$/\\${new}\{$1\}/gi;
 
 247   $variable =~ s/[\x00-\x1f]//g;
 
 253   my ($self, $var, $text, $start_tag, $end_tag, @indices) = @_;
 
 255   my ($form, $new_contents) = ($self->{"form"}, "");
 
 257   my $ary = $self->_get_loop_variable($var, 1, @indices);
 
 260   my $current_page                 = 1;
 
 261   my ($current_line, $corrent_row) = (0, 1);
 
 262   my $description_array            = $self->_get_loop_variable("description",     1);
 
 263   my $longdescription_array        = $self->_get_loop_variable("longdescription", 1);
 
 264   my $linetotal_array              = $self->_get_loop_variable("linetotal",       1);
 
 266   $form->{TEMPLATE_ARRAYS}->{cumulatelinetotal} = [];
 
 268   for (my $i = 0; $i < scalar(@{$ary}); $i++) {
 
 269     $form->{"__first__"}   = $i == 1;
 
 270     $form->{"__last__"}    = ($i + 1) == scalar(@{$ary});
 
 271     $form->{"__odd__"}     = (($i + 1) % 2) == 1;
 
 272     $form->{"__counter__"} = $i + 1;
 
 274     if (scalar @{$description_array} == scalar @{$ary} && $self->{"chars_per_line"} != 0) {
 
 275       my $lines = int(length($description_array->[$i]) / $self->{"chars_per_line"});
 
 278       $description_array->[$i] =~ s/(\\newline\s?)*$//;
 
 279       my $_description = $description_array->[$i];
 
 280       while ($_description =~ /\\newline/) {
 
 282         $_description =~ s/\\newline//;
 
 286       if ($current_page == 1) {
 
 287         $lpp = $self->{"lines_on_first_page"};
 
 289         $lpp = $self->{"lines_on_second_page"};
 
 292       # Yes we need a manual page break -- or the user has forced one
 
 293       if ((($current_line + $lines) > $lpp) || ($description_array->[$i] =~ /<pagebreak>/) || ($longdescription_array->[$i] =~ /<pagebreak>/)) {
 
 294         my $pb = $self->{"pagebreak_block"};
 
 296         # replace the special variables <%sumcarriedforward%>
 
 299         my $psum = $form->format_amount($self->{"myconfig"}, $sum, 2);
 
 300         $pb =~ s/$self->{tag_start_qm}sumcarriedforward$self->{tag_end_qm}/$psum/g;
 
 301         $pb =~ s/$self->{tag_start_qm}lastpage$self->{tag_end_qm}/$current_page/g;
 
 303         my $new_text = $self->parse_block($pb, (@indices, $i));
 
 304         return undef unless (defined($new_text));
 
 305         $new_contents .= $new_text;
 
 310       $current_line += $lines;
 
 313     if ($i < scalar(@{$linetotal_array})) {
 
 314       $sum += $form->parse_amount($self->{"myconfig"}, $linetotal_array->[$i]);
 
 317     $form->{TEMPLATE_ARRAYS}->{cumulatelinetotal}->[$i] = $form->format_amount($self->{"myconfig"}, $sum, 2);
 
 319     my $new_text = $self->parse_block($text, (@indices, $i));
 
 320     return undef unless (defined($new_text));
 
 321     $new_contents .= $start_tag . $new_text . $end_tag;
 
 323   map({ delete($form->{"__${_}__"}); } qw(first last odd counter));
 
 325   return $new_contents;
 
 329   my ($self, $text, $pos, $var, $not) = @_;
 
 331   my $tag_start_len = length $self->{tag_start};
 
 334   $pos = 0 unless ($pos);
 
 336   while ($pos < length($text)) {
 
 339     next if (substr($text, $pos - 1, length($self->{tag_start})) ne $self->{tag_start});
 
 341     my $keyword_pos = $pos - 1 + $tag_start_len;
 
 343     if ((substr($text, $keyword_pos, 2) eq 'if') || (substr($text, $keyword_pos, 3) eq 'for')) {
 
 346     } elsif ((substr($text, $keyword_pos, 4) eq 'else') && (1 == $depth)) {
 
 349             "$self->{tag_start}else$self->{tag_end} outside of "
 
 350           . "$self->{tag_start}if$self->{tag_end} / "
 
 351           . "$self->{tag_start}ifnot$self->{tag_end}.";
 
 355       my $block = substr($text, 0, $pos - 1);
 
 356       substr($text, 0, $pos - 1) = "";
 
 357       $text =~ s!^$self->{tag_start_qm}.+?$self->{tag_end_qm}!!;
 
 358       $text =  $self->{tag_start} . 'if' . ($not ?  " " : "not ") . $var . $self->{tag_end} . $text;
 
 360       return ($block, $text);
 
 362     } elsif (substr($text, $keyword_pos, 3) eq 'end') {
 
 365         my $block = substr($text, 0, $pos - 1);
 
 366         substr($text, 0, $pos - 1) = "";
 
 367         $text =~ s!^$self->{tag_start_qm}.+?$self->{tag_end_qm}!!;
 
 369         return ($block, $text);
 
 378   $main::lxdebug->enter_sub();
 
 380   my ($self, $contents, @indices) = @_;
 
 382   my $new_contents = "";
 
 384   while ($contents ne "") {
 
 385     my $pos_if      = index($contents, $self->{tag_start} . 'if');
 
 386     my $pos_foreach = index($contents, $self->{tag_start} . 'foreach');
 
 388     if ((-1 == $pos_if) && (-1 == $pos_foreach)) {
 
 389       $new_contents .= $self->substitute_vars($contents, @indices);
 
 393     if ((-1 == $pos_if) || ((-1 != $pos_foreach) && ($pos_if > $pos_foreach))) {
 
 394       $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_foreach), @indices);
 
 395       substr($contents, 0, $pos_foreach) = "";
 
 397       if ($contents !~ m|^$self->{tag_start_qm}foreach (.+?)$self->{tag_end_qm}|) {
 
 398         $self->{"error"} = "Malformed $self->{tag_start}foreach$self->{tag_end}.";
 
 399         $main::lxdebug->leave_sub();
 
 405       substr($contents, 0, length($&)) = "";
 
 408       ($block, $contents) = $self->find_end($contents);
 
 410         $self->{"error"} = "Unclosed $self->{tag_start}foreach$self->{tag_end}." unless ($self->{"error"});
 
 411         $main::lxdebug->leave_sub();
 
 415       my $new_text = $self->parse_foreach($var, $block, "", "", @indices);
 
 416       if (!defined($new_text)) {
 
 417         $main::lxdebug->leave_sub();
 
 420       $new_contents .= $new_text;
 
 423       if (!$self->_parse_block_if(\$contents, \$new_contents, $pos_if, @indices)) {
 
 424         $main::lxdebug->leave_sub();
 
 430   $main::lxdebug->leave_sub();
 
 432   return $new_contents;
 
 435 sub parse_first_line {
 
 437   my $line = shift || "";
 
 439   if ($line =~ m/([^\s]+)set-tag-style([^\s]+)/) {
 
 441       $self->{error} = "The tag start and end markers must not be equal.";
 
 445     $self->set_tag_style($1, $2);
 
 451 sub _parse_config_option {
 
 458   my ($key, $value) = split m/\s*=\s*/, $line, 2;
 
 460   if ($key eq 'tag-style') {
 
 461     $self->set_tag_style(split(m/\s+/, $value, 2));
 
 465 sub _parse_config_lines {
 
 469   my ($comment_start, $comment_end) = ("", "");
 
 471   if (ref $self eq 'LaTeXTemplate') {
 
 472     $comment_start = '\s*%';
 
 473   } elsif (ref $self eq 'HTMLTemplate') {
 
 474     $comment_start = '\s*<!--';
 
 475     $comment_end   = '>\s*';
 
 477     $comment_start = '\s*\#';
 
 480   my $num_lines = scalar @{ $lines };
 
 483   while ($i < $num_lines) {
 
 484     my $line = $lines->[$i];
 
 486     if ($line !~ m/^${comment_start}\s*config\s*:(.*)${comment_end}$/i) {
 
 491     $self->_parse_config_option($1);
 
 492     splice @{ $lines }, $i, 1;
 
 497 sub _force_mandatory_packages {
 
 501   my (%used_packages, $document_start_line);
 
 503   foreach my $i (0 .. scalar @{ $lines } - 1) {
 
 504     if ($lines->[$i] =~ m/\\usepackage[^\{]*{(.*?)}/) {
 
 505       $used_packages{$1} = 1;
 
 507     } elsif ($lines->[$i] =~ m/\\begin{document}/) {
 
 508       $document_start_line = $i;
 
 514   $document_start_line = scalar @{ $lines } - 1 if (!defined $document_start_line);
 
 516   if (!$used_packages{textcomp}) {
 
 517     splice @{ $lines }, $document_start_line, 0, "\\usepackage{textcomp}\n";
 
 518     $document_start_line++;
 
 525   my $form = $self->{"form"};
 
 527   if (!open(IN, "$form->{templates}/$form->{IN}")) {
 
 528     $self->{"error"} = "$!";
 
 534   $self->_parse_config_lines(\@lines);
 
 535   $self->_force_mandatory_packages(\@lines) if (ref $self eq 'LaTeXTemplate');
 
 537   my $contents = join("", @lines);
 
 539   # detect pagebreak block and its parameters
 
 540   if ($contents =~ /$self->{tag_start_qm}pagebreak\s+(\d+)\s+(\d+)\s+(\d+)\s*$self->{tag_end_qm}(.*?)$self->{tag_start_qm}end(\s*pagebreak)?$self->{tag_end_qm}/s) {
 
 541     $self->{"chars_per_line"} = $1;
 
 542     $self->{"lines_on_first_page"} = $2;
 
 543     $self->{"lines_on_second_page"} = $3;
 
 544     $self->{"pagebreak_block"} = $4;
 
 546     substr($contents, length($`), length($&)) = "";
 
 549   $self->{"forced_pagebreaks"} = [];
 
 551   my $new_contents = $self->parse_block($contents);
 
 552   if (!defined($new_contents)) {
 
 553     $main::lxdebug->leave_sub();
 
 557   print(OUT $new_contents);
 
 559   if ($form->{"format"} =~ /postscript/i) {
 
 560     return $self->convert_to_postscript();
 
 561   } elsif ($form->{"format"} =~ /pdf/i) {
 
 562     return $self->convert_to_pdf();
 
 568 sub convert_to_postscript {
 
 570   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
 
 572   # Convert the tex file to postscript
 
 574   if (!chdir("$userspath")) {
 
 575     $self->{"error"} = "chdir : $!";
 
 580   $form->{tmpfile} =~ s/\Q$userspath\E\///g;
 
 582   my $latex = $self->_get_latex_path();
 
 584   for (my $run = 1; $run <= 2; $run++) {
 
 585     system("${latex} --interaction=nonstopmode $form->{tmpfile} " .
 
 586            "> $form->{tmpfile}.err");
 
 588       $self->{"error"} = $form->cleanup();
 
 594   $form->{tmpfile} =~ s/tex$/dvi/;
 
 596   system("dvips $form->{tmpfile} -o -q > /dev/null");
 
 598     $self->{"error"} = "dvips : $!";
 
 602   $form->{tmpfile} =~ s/dvi$/ps/;
 
 611   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
 
 613   # Convert the tex file to PDF
 
 615   if (!chdir("$userspath")) {
 
 616     $self->{"error"} = "chdir : $!";
 
 621   $form->{tmpfile} =~ s/\Q$userspath\E\///g;
 
 623   my $latex = $self->_get_latex_path();
 
 625   for (my $run = 1; $run <= 2; $run++) {
 
 626     system("${latex} --interaction=nonstopmode $form->{tmpfile} " .
 
 627            "> $form->{tmpfile}.err");
 
 629       $self->{"error"} = $form->cleanup();
 
 635   $form->{tmpfile} =~ s/tex$/pdf/;
 
 640 sub _get_latex_path {
 
 641   return $main::latex_bin || 'pdflatex';
 
 644 sub get_mime_type() {
 
 647   if ($self->{"form"}->{"format"} =~ /postscript/i) {
 
 648     return "application/postscript";
 
 650     return "application/pdf";
 
 663 package HTMLTemplate;
 
 667 @ISA = qw(LaTeXTemplate);
 
 672   return $type->SUPER::new(@_);
 
 676   my ($self, $variable) = @_;
 
 677   my $form = $self->{"form"};
 
 679   $variable = $main::locale->quote_special_chars('Template/HTML', $variable);
 
 681   # Allow some HTML markup to be converted into the output format's
 
 682   # corresponding markup code, e.g. bold or italic.
 
 683   my @markup_replace = ('b', 'i', 's', 'u', 'sub', 'sup');
 
 685   foreach my $key (@markup_replace) {
 
 686     $variable =~ s/\<(\/?)${key}\>/<$1${key}>/g;
 
 692 sub get_mime_type() {
 
 695   if ($self->{"form"}->{"format"} =~ /postscript/i) {
 
 696     return "application/postscript";
 
 697   } elsif ($self->{"form"}->{"format"} =~ /pdf/i) {
 
 698     return "application/pdf";
 
 707   if ($self->{"form"}->{"format"} =~ /postscript/i) {
 
 709   } elsif ($self->{"form"}->{"format"} =~ /pdf/i) {
 
 716 sub convert_to_postscript {
 
 718   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
 
 720   # Convert the HTML file to postscript
 
 722   if (!chdir("$userspath")) {
 
 723     $self->{"error"} = "chdir : $!";
 
 728   $form->{"tmpfile"} =~ s/\Q$userspath\E\///g;
 
 729   my $psfile = $form->{"tmpfile"};
 
 730   $psfile =~ s/.html/.ps/;
 
 731   if ($psfile eq $form->{"tmpfile"}) {
 
 735   system("html2ps -f html2ps-config < $form->{tmpfile} > $psfile");
 
 737     $self->{"error"} = $form->cleanup();
 
 742   $form->{"tmpfile"} = $psfile;
 
 751   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
 
 753   # Convert the HTML file to PDF
 
 755   if (!chdir("$userspath")) {
 
 756     $self->{"error"} = "chdir : $!";
 
 761   $form->{"tmpfile"} =~ s/\Q$userspath\E\///g;
 
 762   my $pdffile = $form->{"tmpfile"};
 
 763   $pdffile =~ s/.html/.pdf/;
 
 764   if ($pdffile eq $form->{"tmpfile"}) {
 
 768   system("html2ps -f html2ps-config < $form->{tmpfile} | ps2pdf - $pdffile");
 
 770     $self->{"error"} = $form->cleanup();
 
 775   $form->{"tmpfile"} = $pdffile;
 
 784 #### PlainTextTemplate
 
 787 package PlainTextTemplate;
 
 791 @ISA = qw(LaTeXTemplate);
 
 796   return $type->SUPER::new(@_);
 
 800   my ($self, $variable) = @_;
 
 815 #### OpenDocumentTemplate
 
 818 package OpenDocumentTemplate;
 
 826 # use File::Temp qw(:mktemp);
 
 829 @ISA = qw(SimpleTemplate);
 
 834   $self = $type->SUPER::new(@_);
 
 836   foreach my $module (qw(Archive::Zip Text::Iconv)) {
 
 837     eval("use ${module};");
 
 839       $self->{"form"}->error("The Perl module '${module}' could not be " .
 
 840                              "loaded. Support for OpenDocument templates " .
 
 841                              "does not work without it. Please install your " .
 
 842                              "distribution's package or get the module from " .
 
 843                              "CPAN ( http://www.cpan.org ).");
 
 847   $self->{"rnd"}   = int(rand(1000000));
 
 848   $self->{"iconv"} = Text::Iconv->new($main::dbcharset, "UTF-8");
 
 850   $self->set_tag_style('<%', '%>');
 
 851   $self->{quot_re} = '"';
 
 857   my ($self, $var, $text, $start_tag, $end_tag, @indices) = @_;
 
 859   my ($form, $new_contents) = ($self->{"form"}, "");
 
 861   my $ary = $self->_get_loop_variable($var, 1, @indices);
 
 863   for (my $i = 0; $i < scalar(@{$ary}); $i++) {
 
 864     $form->{"__first__"} = $i == 0;
 
 865     $form->{"__last__"} = ($i + 1) == scalar(@{$ary});
 
 866     $form->{"__odd__"} = (($i + 1) % 2) == 1;
 
 867     $form->{"__counter__"} = $i + 1;
 
 868     my $new_text = $self->parse_block($text, (@indices, $i));
 
 869     return undef unless (defined($new_text));
 
 870     $new_contents .= $start_tag . $new_text . $end_tag;
 
 872   map({ delete($form->{"__${_}__"}); } qw(first last odd counter));
 
 874   return $new_contents;
 
 878   my ($self, $text, $pos, $var, $not) = @_;
 
 881   $pos = 0 unless ($pos);
 
 883   while ($pos < length($text)) {
 
 886     next if (substr($text, $pos - 1, 5) ne '<%');
 
 888     if ((substr($text, $pos + 4, 2) eq 'if') || (substr($text, $pos + 4, 3) eq 'for')) {
 
 891     } elsif ((substr($text, $pos + 4, 4) eq 'else') && (1 == $depth)) {
 
 893         $self->{"error"} = '<%else%> outside of <%if%> / <%ifnot%>.';
 
 897       my $block = substr($text, 0, $pos - 1);
 
 898       substr($text, 0, $pos - 1) = "";
 
 899       $text =~ s!^\<\%[^\%]+\%\>!!;
 
 900       $text = '<%if' . ($not ?  " " : "not ") . $var . '%>' . $text;
 
 902       return ($block, $text);
 
 904     } elsif (substr($text, $pos + 4, 3) eq 'end') {
 
 907         my $block = substr($text, 0, $pos - 1);
 
 908         substr($text, 0, $pos - 1) = "";
 
 909         $text =~ s!^\<\%[^\%]+\%\>!!;
 
 911         return ($block, $text);
 
 920   $main::lxdebug->enter_sub();
 
 922   my ($self, $contents, @indices) = @_;
 
 924   my $new_contents = "";
 
 926   while ($contents ne "") {
 
 927     if (substr($contents, 0, 1) eq "<") {
 
 928       $contents =~ m|^<[^>]+>|;
 
 930       substr($contents, 0, length($&)) = "";
 
 932       if ($tag =~ m|<table:table-row|) {
 
 933         $contents =~ m|^(.*?)(</table:table-row[^>]*>)|;
 
 936         substr($contents, 0, length($1) + length($end_tag)) = "";
 
 938         if ($table_row =~ m|\<\%foreachrow\s+(.*?)\%\>|) {
 
 941           substr($table_row, length($`), length($&)) = "";
 
 943           my ($t1, $t2) = $self->find_end($table_row, length($`));
 
 945             $self->{"error"} = "Unclosed <\%foreachrow\%>." unless ($self->{"error"});
 
 946             $main::lxdebug->leave_sub();
 
 950           my $new_text = $self->parse_foreach($var, $t1 . $t2, $tag, $end_tag, @indices);
 
 951           if (!defined($new_text)) {
 
 952             $main::lxdebug->leave_sub();
 
 955           $new_contents .= $new_text;
 
 958           my $new_text = $self->parse_block($table_row, @indices);
 
 959           if (!defined($new_text)) {
 
 960             $main::lxdebug->leave_sub();
 
 963           $new_contents .= $tag . $new_text . $end_tag;
 
 967         $new_contents .= $tag;
 
 971       $contents =~ /^[^<]+/;
 
 974       my $pos_if = index($text, '<%if');
 
 975       my $pos_foreach = index($text, '<%foreach');
 
 977       if ((-1 == $pos_if) && (-1 == $pos_foreach)) {
 
 978         substr($contents, 0, length($text)) = "";
 
 979         $new_contents .= $self->substitute_vars($text, @indices);
 
 983       if ((-1 == $pos_if) || ((-1 != $pos_foreach) && ($pos_if > $pos_foreach))) {
 
 984         $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_foreach), @indices);
 
 985         substr($contents, 0, $pos_foreach) = "";
 
 987         if ($contents !~ m|^\<\%foreach (.*?)\%\>|) {
 
 988           $self->{"error"} = "Malformed <\%foreach\%>.";
 
 989           $main::lxdebug->leave_sub();
 
 995         substr($contents, 0, length($&)) = "";
 
 998         ($block, $contents) = $self->find_end($contents);
 
1000           $self->{"error"} = "Unclosed <\%foreach\%>." unless ($self->{"error"});
 
1001           $main::lxdebug->leave_sub();
 
1005         my $new_text = $self->parse_foreach($var, $block, "", "", @indices);
 
1006         if (!defined($new_text)) {
 
1007           $main::lxdebug->leave_sub();
 
1010         $new_contents .= $new_text;
 
1013         if (!$self->_parse_block_if(\$contents, \$new_contents, $pos_if, @indices)) {
 
1014           $main::lxdebug->leave_sub();
 
1021   $main::lxdebug->leave_sub();
 
1023   return $new_contents;
 
1027   $main::lxdebug->enter_sub();
 
1031   my $form = $self->{"form"};
 
1036   if ($form->{"IN"} =~ m|^/|) {
 
1037     $file_name = $form->{"IN"};
 
1039     $file_name = $form->{"templates"} . "/" . $form->{"IN"};
 
1042   my $zip = Archive::Zip->new();
 
1043   if (Archive::Zip::AZ_OK != $zip->read($file_name)) {
 
1044     $self->{"error"} = "File not found/is not a OpenDocument file.";
 
1045     $main::lxdebug->leave_sub();
 
1049   my $contents = $zip->contents("content.xml");
 
1051     $self->{"error"} = "File is not a OpenDocument file.";
 
1052     $main::lxdebug->leave_sub();
 
1056   my $rnd = $self->{"rnd"};
 
1057   my $new_styles = qq|<style:style style:name="TLXO${rnd}BOLD" style:family="text">
 
1058 <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
 
1060 <style:style style:name="TLXO${rnd}ITALIC" style:family="text">
 
1061 <style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"/>
 
1063 <style:style style:name="TLXO${rnd}UNDERLINE" style:family="text">
 
1064 <style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
 
1066 <style:style style:name="TLXO${rnd}STRIKETHROUGH" style:family="text">
 
1067 <style:text-properties style:text-line-through-style="solid"/>
 
1069 <style:style style:name="TLXO${rnd}SUPER" style:family="text">
 
1070 <style:text-properties style:text-position="super 58%"/>
 
1072 <style:style style:name="TLXO${rnd}SUB" style:family="text">
 
1073 <style:text-properties style:text-position="sub 58%"/>
 
1077   $contents =~ s|</office:automatic-styles>|${new_styles}</office:automatic-styles>|;
 
1078   $contents =~ s|[\n\r]||gm;
 
1080   my $new_contents = $self->parse_block($contents);
 
1081   if (!defined($new_contents)) {
 
1082     $main::lxdebug->leave_sub();
 
1086 #   $new_contents =~ s|>|>\n|g;
 
1088   $zip->contents("content.xml", $new_contents);
 
1090   my $styles = $zip->contents("styles.xml");
 
1092     my $new_styles = $self->parse_block($styles);
 
1093     if (!defined($new_contents)) {
 
1094       $main::lxdebug->leave_sub();
 
1097     $zip->contents("styles.xml", $new_styles);
 
1100   $zip->writeToFileNamed($form->{"tmpfile"}, 1);
 
1103   if ($form->{"format"} =~ /pdf/) {
 
1104     $res = $self->convert_to_pdf();
 
1107   $main::lxdebug->leave_sub();
 
1111 sub is_xvfb_running {
 
1112   $main::lxdebug->enter_sub();
 
1117   my $dfname = $self->{"userspath"} . "/xvfb_display";
 
1120   $main::lxdebug->message(LXDebug::DEBUG2, "    Looking for $dfname\n");
 
1121   if ((-f $dfname) && open(IN, $dfname)) {
 
1126     my $xauthority = <IN>;
 
1130     $main::lxdebug->message(LXDebug::DEBUG2, "      found with $pid and $display\n");
 
1132     if ((! -d "/proc/$pid") || !open(IN, "/proc/$pid/cmdline")) {
 
1133       $main::lxdebug->message(LXDebug::DEBUG2, "  no/wrong process #1\n");
 
1134       unlink($dfname, $xauthority);
 
1135       $main::lxdebug->leave_sub();
 
1140     if ($line !~ /xvfb/i) {
 
1141       $main::lxdebug->message(LXDebug::DEBUG2, "      no/wrong process #2\n");
 
1142       unlink($dfname, $xauthority);
 
1143       $main::lxdebug->leave_sub();
 
1147     $ENV{"XAUTHORITY"} = $xauthority;
 
1148     $ENV{"DISPLAY"} = $display;
 
1150     $main::lxdebug->message(LXDebug::DEBUG2, "      not found\n");
 
1153   $main::lxdebug->leave_sub();
 
1159   $main::lxdebug->enter_sub();
 
1163   $main::lxdebug->message(LXDebug::DEBUG2, "spawn_xvfb()\n");
 
1165   my $display = $self->is_xvfb_running();
 
1168     $main::lxdebug->leave_sub();
 
1173   while ( -f "/tmp/.X${display}-lock") {
 
1176   $display = ":${display}";
 
1177   $main::lxdebug->message(LXDebug::DEBUG2, "  display $display\n");
 
1179   my $mcookie = `mcookie`;
 
1180   die("Installation error: mcookie not found.") if ($? != 0);
 
1183   $main::lxdebug->message(LXDebug::DEBUG2, "  mcookie $mcookie\n");
 
1185   my $xauthority = "/tmp/.Xauthority-" . $$ . "-" . time() . "-" . int(rand(9999999));
 
1186   $ENV{"XAUTHORITY"} = $xauthority;
 
1188   $main::lxdebug->message(LXDebug::DEBUG2, "  xauthority $xauthority\n");
 
1190   system("xauth add \"${display}\" . \"${mcookie}\"");
 
1192     $self->{"error"} = "Conversion to PDF failed because OpenOffice could not be started (xauth: $!)";
 
1193     $main::lxdebug->leave_sub();
 
1197   $main::lxdebug->message(LXDebug::DEBUG2, "  about to fork()\n");
 
1201     $main::lxdebug->message(LXDebug::DEBUG2, "  Child execing\n");
 
1202     exec($main::xvfb_bin, $display, "-screen", "0", "640x480x8", "-nolisten", "tcp");
 
1205   $main::lxdebug->message(LXDebug::DEBUG2, "  parent dont sleeping\n");
 
1208   my $dfname = $self->{"userspath"} . "/xvfb_display";
 
1209   if (!open(OUT, ">$dfname")) {
 
1210     $self->{"error"} = "Conversion to PDF failed because OpenOffice could not be started ($dfname: $!)";
 
1211     unlink($xauthority);
 
1213     $main::lxdebug->leave_sub();
 
1216   print(OUT "$pid\n$display\n$xauthority\n");
 
1219   $main::lxdebug->message(LXDebug::DEBUG2, "  parent re-testing\n");
 
1221   if (!$self->is_xvfb_running()) {
 
1222     $self->{"error"} = "Conversion to PDF failed because OpenOffice could not be started.";
 
1223     unlink($xauthority, $dfname);
 
1225     $main::lxdebug->leave_sub();
 
1229   $main::lxdebug->message(LXDebug::DEBUG2, "  spawn OK\n");
 
1231   $main::lxdebug->leave_sub();
 
1236 sub is_openoffice_running {
 
1237   $main::lxdebug->enter_sub();
 
1239   system("./scripts/oo-uno-test-conn.py $main::openofficeorg_daemon_port " .
 
1240          "> /dev/null 2> /dev/null");
 
1242   $main::lxdebug->message(LXDebug::DEBUG2, "  is_openoffice_running(): $?\n");
 
1244   $main::lxdebug->leave_sub();
 
1249 sub spawn_openoffice {
 
1250   $main::lxdebug->enter_sub();
 
1254   $main::lxdebug->message(LXDebug::DEBUG2, "spawn_openoffice()\n");
 
1256   my ($try, $spawned_oo, $res);
 
1259   for ($try = 0; $try < 15; $try++) {
 
1260     if ($self->is_openoffice_running()) {
 
1268         $main::lxdebug->message(LXDebug::DEBUG2, "  Child daemonizing\n");
 
1270         open(STDIN, '/dev/null');
 
1271         open(STDOUT, '>/dev/null');
 
1272         my $new_pid = fork();
 
1274         my $ssres = setsid();
 
1275         $main::lxdebug->message(LXDebug::DEBUG2, "  Child execing\n");
 
1276         my @cmdline = ($main::openofficeorg_writer_bin,
 
1277                        "-minimized", "-norestore", "-nologo", "-nolockcheck",
 
1279                        "-accept=socket,host=localhost,port=" .
 
1280                        $main::openofficeorg_daemon_port . ";urp;");
 
1284       $main::lxdebug->message(LXDebug::DEBUG2, "  Parent after fork\n");
 
1289     sleep($try >= 5 ? 2 : 1);
 
1293     $self->{"error"} = "Conversion from OpenDocument to PDF failed because " .
 
1294       "OpenOffice could not be started.";
 
1297   $main::lxdebug->leave_sub();
 
1302 sub convert_to_pdf {
 
1303   $main::lxdebug->enter_sub();
 
1307   my $form = $self->{"form"};
 
1309   my $filename = $form->{"tmpfile"};
 
1310   $filename =~ s/.odt$//;
 
1311   if (substr($filename, 0, 1) ne "/") {
 
1312     $filename = getcwd() . "/${filename}";
 
1315   if (substr($self->{"userspath"}, 0, 1) eq "/") {
 
1316     $ENV{'HOME'} = $self->{"userspath"};
 
1318     $ENV{'HOME'} = getcwd() . "/" . $self->{"userspath"};
 
1321   if (!$self->spawn_xvfb()) {
 
1322     $main::lxdebug->leave_sub();
 
1327   if (!$main::openofficeorg_daemon) {
 
1328     @cmdline = ($main::openofficeorg_writer_bin,
 
1329                 "-minimized", "-norestore", "-nologo", "-nolockcheck",
 
1331                 "file:${filename}.odt",
 
1332                 "macro://" . (split('/', $filename))[-1] .
 
1333                 "/Standard.Conversion.ConvertSelfToPDF()");
 
1335     if (!$self->spawn_openoffice()) {
 
1336       $main::lxdebug->leave_sub();
 
1340     @cmdline = ("./scripts/oo-uno-convert-pdf.py",
 
1341                 $main::openofficeorg_daemon_port,
 
1349     $form->{"tmpfile"} =~ s/odt$/pdf/;
 
1351     unlink($filename . ".odt");
 
1353     $main::lxdebug->leave_sub();
 
1358   unlink($filename . ".odt", $filename . ".pdf");
 
1359   $self->{"error"} = "Conversion from OpenDocument to PDF failed. " .
 
1362   $main::lxdebug->leave_sub();
 
1367   my ($self, $variable) = @_;
 
1368   my $form = $self->{"form"};
 
1369   my $iconv = $self->{"iconv"};
 
1371   $variable = $main::locale->quote_special_chars('Template/OpenDocument', $variable);
 
1373   # Allow some HTML markup to be converted into the output format's
 
1374   # corresponding markup code, e.g. bold or italic.
 
1375   my $rnd = $self->{"rnd"};
 
1376   my %markup_replace = ("b" => "BOLD", "i" => "ITALIC", "s" => "STRIKETHROUGH",
 
1377                         "u" => "UNDERLINE", "sup" => "SUPER", "sub" => "SUB");
 
1379   foreach my $key (keys(%markup_replace)) {
 
1380     my $value = $markup_replace{$key};
 
1381     $variable =~ s|\<${key}\>|<text:span text:style-name=\"TLXO${rnd}${value}\">|gi; #"
 
1382     $variable =~ s|\</${key}\>|</text:span>|gi;
 
1385   return $iconv->convert($variable);
 
1388 sub get_mime_type() {
 
1389   if ($self->{"form"}->{"format"} =~ /pdf/) {
 
1390     return "application/pdf";
 
1392     return "application/vnd.oasis.opendocument.text";
 
1396 sub uses_temp_file {
 
1401 ##########################################################
 
1405 ##########################################################
 
1407 package XMLTemplate;
 
1411 @ISA = qw(HTMLTemplate);
 
1414   #evtl auskommentieren
 
1417   return $type->SUPER::new(@_);
 
1421   my ($self, $variable) = @_;
 
1422   my $form = $self->{"form"};
 
1424   $variable = $main::locale->quote_special_chars('Template/XML', $variable);
 
1426   # Allow no markup to be converted into the output format
 
1427   my @markup_replace = ('b', 'i', 's', 'u', 'sub', 'sup');
 
1429   foreach my $key (@markup_replace) {
 
1430     $variable =~ s/\<(\/?)${key}\>//g;
 
1436 sub get_mime_type() {
 
1439   if ($self->{"form"}->{"format"} =~ /elsterwinston/i) {
 
1440     return "application/xml ";
 
1441   } elsif ($self->{"form"}->{"format"} =~ /elstertaxbird/i) {
 
1442     return "application/x-taxbird";
 
1448 sub uses_temp_file {
 
1449   # tempfile needet for XML Output