5763eee46e161ecd4387be4b7fd2a63661aac4dc
[kivitendo-erp.git] / SL / Template.pm
1 #====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #====================================================================
8
9 package SimpleTemplate;
10
11 # Parameters:
12 #   1. The template's file name
13 #   2. A reference to the Form object
14 #   3. A reference to the myconfig hash
15 #
16 # Returns:
17 #   A new template object
18 sub new {
19   my $type = shift;
20   my $self = {};
21
22   bless($self, $type);
23   $self->_init(@_);
24
25   return $self;
26 }
27
28 sub _init {
29   my $self = shift;
30
31   $self->{"source"} = shift;
32   $self->{"form"} = shift;
33   $self->{"myconfig"} = shift;
34   $self->{"userspath"} = shift;
35
36   $self->{"error"} = undef;
37 }
38
39 sub cleanup {
40   my ($self) = @_;
41 }
42
43 # Parameters:
44 #   1. A typeglob for the file handle. The output will be written
45 #      to this file handle.
46 #
47 # Returns:
48 #   1 on success and undef or 0 if there was an error. In the latter case
49 #   the calling function can retrieve the error message via $obj->get_error()
50 sub parse {
51   my $self = $_[0];
52   local *OUT = $_[1];
53
54   print(OUT "Hallo!\n");
55 }
56
57 sub get_error {
58   my $self = shift;
59
60   return $self->{"error"};
61 }
62
63 sub uses_temp_file {
64   return 0;
65 }
66
67 1;
68
69 ####
70 #### LaTeXTemplate
71 ####
72
73 package LaTeXTemplate;
74
75 use vars qw(@ISA);
76
77 @ISA = qw(SimpleTemplate);
78
79 sub new {
80   my $type = shift;
81
82   return $type->SUPER::new(@_);
83 }
84
85 sub format_string {
86   my ($self, $variable) = @_;
87   my $form = $self->{"form"};
88
89   my %replace =
90     ('order' => [
91                  '&', quotemeta("\n"),
92                  '"', '\$', '%', '_', '#', quotemeta('^'),
93                  '{', '}',  '<', '>', '£', "\r"
94                  ],
95      '"'             => "''",
96      '&'             => '\&',
97      '\$'            => '\$',
98      '%'             => '\%',
99      '_'             => '\_',
100      '#'             => '\#',
101      '{'             => '\{',
102      '}'             => '\}',
103      '<'             => '$<$',
104      '>'             => '$>$',
105      '£'             => '\pounds ',
106      "\r"            => "",
107      quotemeta('^')  => '\^\\',
108      quotemeta("\n") => '\newline '
109      );
110
111   map({ $variable =~ s/$_/$replace{$_}/g; } @{ $replace{"order"} });
112
113   # Allow some HTML markup to be converted into the output format's
114   # corresponding markup code, e.g. bold or italic.
115   my %markup_replace = ('b' => 'textbf',
116                         'i' => 'textit',
117                         'u' => 'underline');
118
119   foreach my $key (keys(%markup_replace)) {
120     my $new = $markup_replace{$key};
121     $variable =~ s/\$\<\$${key}\$\>\$(.*?)\$<\$\/${key}\$>\$/\\${new}\{$1\}/gi;
122   }
123
124   return $variable;
125 }
126
127 sub parse {
128   my $self = $_[0];
129   local *OUT = $_[1];
130   my ($form, $myconfig) = ($self->{"form"}, $self->{"myconfig"});
131
132   # Some variables used for page breaks
133   my ($chars_per_line, $lines_on_first_page, $lines_on_second_page) =
134     (0, 0, 0);
135   my ($current_page, $current_line, $current_row) = (1, 1, 0);
136   my ($pagebreak, $sum, $two_passes, $nodiscount_sum) = ("", 0, 0, 0);
137   my ($par, $var);
138
139   # Do we have to run LaTeX two times? This is needed if
140   # the template contains page references.
141   $two_passes = 0;
142
143   if (!open(IN, "$form->{templates}/$form->{IN}")) {
144     $self->{"error"} = "$!";
145     return 0;
146   }
147   @_ = <IN>;
148   close(IN);
149
150   # first we generate a tmpfile
151   # read file and replace <%variable%>
152   while ($_ = shift) {
153     $par = "";
154     $var = $_;
155
156     $two_passes = 1 if (/\\pageref/);
157
158     # detect pagebreak block and its parameters
159     if (/\s*<%pagebreak ([0-9]+) ([0-9]+) ([0-9]+)%>/) {
160       $chars_per_line       = $1;
161       $lines_on_first_page  = $2;
162       $lines_on_second_page = $3;
163
164       while ($_ = shift) {
165         last if (/\s*<%end pagebreak%>/);
166         $pagebreak .= $_;
167       }
168     }
169
170     if (/\s*<%foreach /) {
171
172       # this one we need for the count
173       chomp $var;
174       $var =~ s/\s*<%foreach (.+?)%>/$1/;
175       while ($_ = shift) {
176         last if (/\s*<%end /);
177
178         # store line in $par
179         $par .= $_;
180       }
181
182       # Count the number of "lines" for our variable. Also find the forced pagebreak entries.
183       my $num_entries = scalar(@{$form->{$var}});
184       my @forced_pagebreaks = ();
185       for (my $i = 0; $i < scalar(@{$form->{$var}}); $i++) {
186         if ($form->{$var}->[$i] =~ /<pagebreak>/) {
187           push(@forced_pagebreaks, $i);
188         }
189       }
190
191       $current_line = 1;
192       # display contents of $form->{number}[] array
193       for ($i = 0; $i < $num_entries; $i++) {
194         # Try to detect whether a manual page break is necessary
195         # but only if there was a <%pagebreak ...%> block before
196
197         if ($chars_per_line) {
198           my $lines =
199             int(length($form->{"description"}->[$i]) / $chars_per_line + 0.95);
200           my $lpp;
201
202           $form->{"description"}->[$i] =~ s/(\\newline\s?)*$//;
203           my $_description = $form->{"description"}->[$i];
204           while ($_description =~ /\\newline/) {
205             $lines++;
206             $_description =~ s/\\newline//;
207           }
208           $lines++;
209
210           if ($current_page == 1) {
211             $lpp = $lines_on_first_page;
212           } else {
213             $lpp = $lines_on_second_page;
214           }
215
216           # Yes we need a manual page break -- or the user has forced one
217           if ((($current_line + $lines) > $lpp) ||
218               grep(/^${current_row}$/, @forced_pagebreaks)) {
219             my $pb = $pagebreak;
220
221             # replace the special variables <%sumcarriedforward%>
222             # and <%lastpage%>
223
224             my $psum = $form->format_amount($myconfig, $sum, 2);
225             my $nodiscount_psum = $form->format_amount($myconfig, $nodiscount_sum, 2);
226             $pb =~ s/<%nodiscount_sumcarriedforward%>/$nodiscount_psum/g;
227             $pb =~ s/<%sumcarriedforward%>/$psum/g;
228             $pb =~ s/<%lastpage%>/$current_page/g;
229
230             # only "normal" variables are supported here
231             # (no <%if, no <%foreach, no <%include)
232
233             while ($pb =~ /<%(.*?)%>/) {
234               substr($pb, $-[0], $+[0] - $-[0]) =
235                 $self->format_string($form->{"$1"}->[$i]);
236             }
237
238             # page break block is ready to rock
239             print(OUT $pb);
240             $current_page++;
241             $current_line = 1;
242           }
243           $current_line += $lines;
244           $current_row++;
245         }
246         $sum += $form->parse_amount($myconfig, $form->{"linetotal"}->[$i]);
247         $nodiscount_sum += $form->parse_amount($myconfig, $form->{"nodiscount_linetotal"}->[$i]);
248
249         # don't parse par, we need it for each line
250         $_ = $par;
251         while (/<%(.*?)%>/) {
252           substr($_, $-[0], $+[0] - $-[0]) =
253             $self->format_string($form->{"$1"}->[$i]);
254         }
255         print OUT;
256       }
257       next;
258     }
259
260     # if not comes before if!
261     if (/\s*<%if not /) {
262
263       # check if it is not set and display
264       chop;
265       s/\s*<%if not (.+?)%>/$1/;
266
267       unless ($form->{$_}) {
268         while ($_ = shift) {
269           last if (/\s*<%end /);
270
271           # store line in $par
272           $par .= $_;
273         }
274
275         $_ = $par;
276
277       } else {
278         while ($_ = shift) {
279           last if (/\s*<%end /);
280         }
281         next;
282       }
283     }
284
285     if (/\s*<%if /) {
286
287       # check if it is set and display
288       chop;
289       s/\s*<%if (.+?)%>/$1/;
290
291       if ($form->{$_}) {
292         while ($_ = shift) {
293           last if (/\s*<%end /);
294
295           # store line in $par
296           $par .= $_;
297         }
298
299         $_ = $par;
300
301       } else {
302         while ($_ = shift) {
303           last if (/\s*<%end /);
304         }
305         next;
306       }
307     }
308
309     # check for <%include filename%>
310     if (/\s*<%include /) {
311
312       # get the filename
313       chomp $var;
314       $var =~ s/\s*<%include (.+?)%>/$1/;
315
316       # mangle filename
317       $var =~ s/(\/|\.\.)//g;
318
319       # prevent the infinite loop!
320       next if ($form->{"$var"});
321
322       open(INC, $form->{templates} . "/$var")
323         or $form->error($self->cleanup . $form->{templates} . "/$var : $!");
324       unshift(@_, <INC>);
325       close(INC);
326
327       $form->{"$var"} = 1;
328
329       next;
330     }
331
332     while (/<%(.*?)%>/) {
333       substr($_, $-[0], $+[0] - $-[0]) = $self->format_string($form->{$1});
334     }
335     print OUT;
336   }
337
338   if ($form->{"format"} =~ /postscript/i) {
339     return $self->convert_to_postscript($two_passes);
340   } elsif ($form->{"format"} =~ /pdf/i) {
341     return $self->convert_to_pdf($two_passes);
342   } else {
343     return 1;
344   }
345 }
346
347 sub convert_to_postscript {
348   my ($self, $two_passes) = @_;
349   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
350
351   # Convert the tex file to postscript
352
353   if (!chdir("$userspath")) {
354     $self->{"error"} = "chdir : $!";
355     $self->cleanup();
356     return 0;
357   }
358
359   $form->{tmpfile} =~ s/$userspath\///g;
360
361   system("latex --interaction=nonstopmode $form->{tmpfile} " .
362          "> $form->{tmpfile}.err");
363   if ($?) {
364     $self->{"error"} = $form->cleanup();
365     $self->cleanup();
366     return 0;
367   }
368   if ($two_passes) {
369     system("latex --interaction=nonstopmode $form->{tmpfile} " .
370            "> $form->{tmpfile}.err");
371     if ($?) {
372       $self->{"error"} = $form->cleanup();
373       $self->cleanup();
374       return 0;
375     }
376   }
377
378   $form->{tmpfile} =~ s/tex$/dvi/;
379
380   system("dvips $form->{tmpfile} -o -q > /dev/null");
381   if ($?) {
382     $self->{"error"} = "dvips : $!";
383     $self->cleanup();
384     return 0;
385   }
386   $form->{tmpfile} =~ s/dvi$/ps/;
387
388   $self->cleanup();
389
390   return 1;
391 }
392
393 sub convert_to_pdf {
394   my ($self, $two_passes) = @_;
395   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
396
397   # Convert the tex file to PDF
398
399   if (!chdir("$userspath")) {
400     $self->{"error"} = "chdir : $!";
401     $self->cleanup();
402     return 0;
403   }
404
405   $form->{tmpfile} =~ s/$userspath\///g;
406
407   system("pdflatex --interaction=nonstopmode $form->{tmpfile} " .
408          "> $form->{tmpfile}.err");
409   if ($?) {
410     $self->{"error"} = $form->cleanup();
411     $self->cleanup();
412     return 0;
413   }
414
415   if ($two_passes) {
416     system("pdflatex --interaction=nonstopmode $form->{tmpfile} " .
417            "> $form->{tmpfile}.err");
418     if ($?) {
419       $self->{"error"} = $form->cleanup();
420       $self->cleanup();
421       return 0;
422     }
423   }
424
425   $form->{tmpfile} =~ s/tex$/pdf/;
426
427   $self->cleanup();
428 }
429
430 sub get_mime_type() {
431   my ($self) = @_;
432
433   if ($self->{"form"}->{"format"} =~ /postscript/i) {
434     return "application/postscript";
435   } else {
436     return "application/pdf";
437   }
438 }
439
440 sub uses_temp_file {
441   return 1;
442 }
443
444
445 ####
446 #### HTMLTemplate
447 ####
448
449 package HTMLTemplate;
450
451 use vars qw(@ISA);
452
453 @ISA = qw(LaTeXTemplate);
454
455 sub new {
456   my $type = shift;
457
458   return $type->SUPER::new(@_);
459 }
460
461 sub format_string {
462   my ($self, $variable) = @_;
463   my $form = $self->{"form"};
464
465   my %replace =
466     ('order' => ['<', '>', quotemeta("\n")],
467      '<'             => '&lt;',
468      '>'             => '&gt;',
469      quotemeta("\n") => '<br>',
470      );
471
472   map({ $variable =~ s/$_/$replace{$_}/g; } @{ $replace{"order"} });
473
474   # Allow some HTML markup to be converted into the output format's
475   # corresponding markup code, e.g. bold or italic.
476   my @markup_replace = ('b', 'i', 's', 'u');
477
478   foreach my $key (@markup_replace) {
479     $variable =~ s/\&lt;(\/?)${key}\&gt;/<$1${key}>/g;
480   }
481
482   return $variable;
483 }
484
485 sub get_mime_type() {
486   return "text/html";
487 }
488
489 sub uses_temp_file {
490   return 0;
491 }
492
493
494
495 ####
496 #### HTMLTemplate
497 ####
498
499 package OpenDocumentTemplate;
500
501 use vars qw(@ISA);
502
503 use Cwd;
504 # use File::Copy;
505 # use File::Spec;
506 # use File::Temp qw(:mktemp);
507 use IO::File;
508
509 @ISA = qw(SimpleTemplate);
510
511 sub new {
512   my $type = shift;
513
514   $self = $type->SUPER::new(@_);
515
516   foreach my $module (qw(Archive::Zip Text::Iconv)) {
517     eval("use ${module};");
518     if ($@) {
519       $self->{"form"}->error("The Perl module '${module}' could not be " .
520                              "loaded. Support for OpenDocument templates " .
521                              "does not work without it. Please install your " .
522                              "distribution's package or get the module from " .
523                              "CPAN ( http://www.cpan.org ).");
524     }
525   }
526
527   $self->{"rnd"} = int(rand(1000000));
528   $self->{"iconv"} = Text::Iconv->new($main::dbcharset, "UTF-8");
529
530   return $self;
531 }
532
533 sub substitute_vars {
534   my ($self, $text, @indices) = @_;
535
536   my $form = $self->{"form"};
537
538   while ($text =~ /\&lt;\%(.*?)\%\&gt;/) {
539     my $value = $form->{$1};
540
541     for (my $i = 0; $i < scalar(@indices); $i++) {
542       last unless (ref($value) eq "ARRAY");
543       $value = $value->[$indices[$i]];
544     }
545     substr($text, $-[0], $+[0] - $-[0]) = $self->format_string($value);
546   }
547
548   return $text;
549 }
550
551 sub parse_foreach {
552   my ($self, $var, $text, $start_tag, $end_tag, @indices) = @_;
553
554   my ($form, $new_contents) = ($self->{"form"}, "");
555
556   my $ary = $form->{$var};
557   for (my $i = 0; $i < scalar(@indices); $i++) {
558     last unless (ref($ary) eq "ARRAY");
559     $ary = $ary->[$indices[$i]];
560   }
561
562   for (my $i = 0; $i < scalar(@{$ary}); $i++) {
563     my $new_text = $self->parse_block($text, (@indices, $i));
564     return undef unless (defined($new_text));
565     $new_contents .= $start_tag . $new_text . $end_tag;
566   }
567
568   return $new_contents;
569 }
570
571 sub parse_block {
572   $main::lxdebug->enter_sub();
573
574   my ($self, $contents, @indices) = @_;
575
576   my $new_contents = "";
577
578   while ($contents ne "") {
579     if (substr($contents, 0, 1) eq "<") {
580       $contents =~ m|^<[^>]+>|;
581       my $tag = $&;
582       substr($contents, 0, length($&)) = "";
583
584       if ($tag =~ m|<table:table-row|) {
585         $contents =~ m|^(.*?)(</table:table-row[^>]*>)|;
586         my $table_row = $1;
587         my $end_tag = $2;
588         substr($contents, 0, length($1) + length($end_tag)) = "";
589
590         if ($table_row =~ m|\&lt;\%foreachrow\s+(.*?)\%\&gt;|) {
591           my $var = $1;
592
593           $table_row =~ s|\&lt;\%foreachrow .*?\%\&gt;||g;
594           $table_row =~ s!\&lt;\%end(for|foreach)?row\s+${var}\%\&gt;!!g;
595
596           my $new_text = $self->parse_foreach($var, $table_row, $tag, $end_tag, @indices);
597           return undef unless (defined($new_text));
598           $new_contents .= $new_text;
599
600         } else {
601           my $new_text = $self->parse_block($table_row, @indices);
602           return undef unless (defined($new_text));
603           $new_contents .= $tag . $new_text . $end_tag;
604         }
605
606       } else {
607         $new_contents .= $tag;
608       }
609
610     } else {
611       $contents =~ /^[^<]+/;
612       my $text = $&;
613
614       my $pos_if = index($text, '&lt;%if');
615       my $pos_foreach = index($text, '&lt;%foreach');
616
617       if ((-1 == $pos_if) && (-1 == $pos_foreach)) {
618         substr($contents, 0, length($text)) = "";
619         $new_contents .= $self->substitute_vars($text, @indices);
620         next;
621       }
622
623       if ((-1 == $pos_if) || ((-1 != $pos_foreach) && ($pos_if > $pos_foreach))) {
624         $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_foreach), @indices);
625         substr($contents, 0, $pos_foreach) = "";
626
627         if ($contents !~ m|^\&lt;\%foreach (.*?)\%\&gt;|) {
628           $self->{"error"} = "Malformed <\%foreach\%>.";
629           $main::lxdebug->leave_sub();
630           return undef;
631         }
632
633         my $var = $1;
634
635         substr($contents, 0, length($&)) = "";
636
637         if ($contents !~ m!\&lt;\%end\s*?(for)?\s+${var}\%\&gt;!) {
638           $self->{"error"} = "Unclosed <\%foreach\%>.";
639           $main::lxdebug->leave_sub();
640           return undef;
641         }
642
643         substr($contents, 0, length($`) + length($&)) = "";
644         my $new_text = $self->parse_foreach($var, $`, "", "", @indices);
645         return undef unless (defined($new_text));
646         $new_contents .= $new_text;
647
648       } else {
649         $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_if), @indices);
650         substr($contents, 0, $pos_if) = "";
651
652         if ($contents !~ m|^\&lt;\%if(not)?\s+(.*?)\%\&gt;|) {
653           $self->{"error"} = "Malformed <\%if\%>.";
654           $main::lxdebug->leave_sub();
655           return undef;
656         }
657
658         my ($not, $var) = ($1, $2);
659
660         substr($contents, 0, length($&)) = "";
661
662         if ($contents !~ m!\&lt;\%endif${not}\s+${var}\%\&gt;!) {
663           $self->{"error"} = "Unclosed <\%if${not}\%>.";
664           $main::lxdebug->leave_sub();
665           return undef;
666         }
667
668         substr($contents, 0, length($`) + length($&)) = "";
669
670         my $value = $self->{"form"}->{$var};
671         for (my $i = 0; $i < scalar(@indices); $i++) {
672           last unless (ref($value) eq "ARRAY");
673           $value = $value->[$indices[$i]];
674         }
675
676         if (($not && !$value) || (!$not && $value)) {
677           my $new_text = $self->parse_block($`, @indices);
678           return undef unless (defined($new_text));
679           $new_contents .= $new_text;
680         }
681       }
682     }
683   }
684
685   return $new_contents;
686 }
687
688 sub parse {
689   $main::lxdebug->enter_sub();
690
691   my $self = $_[0];
692   local *OUT = $_[1];
693   my $form = $self->{"form"};
694
695   close(OUT);
696
697   my $zip = Archive::Zip->new();
698   if (Archive::Zip::AZ_OK != $zip->read("$form->{templates}/$form->{IN}")) {
699     $self->{"error"} = "File not found/is not a OpenDocument file.";
700     $main::lxdebug->leave_sub();
701     return 0;
702   }
703
704   my $contents = $zip->contents("content.xml");
705   if (!$contents) {
706     $self->{"error"} = "File is not a OpenDocument file.";
707     $main::lxdebug->leave_sub();
708     return 0;
709   }
710
711   my $rnd = $self->{"rnd"};
712   my $new_styles = qq|<style:style style:name="TLXO${rnd}BOLD" style:family="text">
713 <style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
714 </style:style>
715 <style:style style:name="TLXO${rnd}ITALIC" style:family="text">
716 <style:text-properties fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"/>
717 </style:style>
718 <style:style style:name="TLXO${rnd}UNDERLINE" style:family="text">
719 <style:text-properties style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
720 </style:style>
721 <style:style style:name="TLXO${rnd}STRIKETHROUGH" style:family="text">
722 <style:text-properties style:text-line-through-style="solid"/>
723 </style:style>|;
724
725   $contents =~ s|</office:automatic-styles>|${new_styles}</office:automatic-styles>|;
726   $contents =~ s|[\n\r]||gm;
727
728   my $new_contents = $self->parse_block($contents);
729   return 0 unless (defined($new_contents));
730
731 #   $new_contents =~ s|>|>\n|g;
732
733   $zip->contents("content.xml", $new_contents);
734   $zip->writeToFileNamed($form->{"tmpfile"}, 1);
735
736   my $res = 1;
737   if ($form->{"format"} =~ /pdf/) {
738     $res = $self->convert_to_pdf();
739   }
740
741   $main::lxdebug->leave_sub();
742   return $res;
743 }
744
745 sub convert_to_pdf {
746   my ($self) = @_;
747
748   my $form = $self->{"form"};
749
750   my $filename = $form->{"tmpfile"};
751   $filename =~ s/.odt$//;
752   if (substr($filename, 0, 1) ne "/") {
753     $filename = getcwd() . "/${filename}";
754   }
755
756   if (substr($self->{"userspath"}, 0, 1) eq "/") {
757     $ENV{'HOME'} = $self->{"userspath"};
758   } else {
759     $ENV{'HOME'} = getcwd() . "/" . $self->{"userspath"};
760   }
761
762   my @cmdline = ($main::xvfb_run_bin, $main::openofficeorg_writer_bin,
763                  "-minimized", "-norestore", "-nologo", "-nolockcheck",
764                  "-headless",
765                  "file:${filename}.odt",
766                  "macro://" . (split('/', $filename))[-1] .
767                  "/Standard.Conversion.ConvertSelfToPDF()");
768
769   system(@cmdline);
770
771   my $res = $?;
772   if (0 == $?) {
773     $form->{"tmpfile"} =~ s/odt$/pdf/;
774
775     unlink($filename . ".odt");
776
777     $main::lxdebug->leave_sub();
778     return 1;
779
780   }
781
782   unlink($filename . ".odt", $filename . ".pdf");
783   $self->{"error"} = "Conversion from OpenDocument to PDF failed. " .
784     "Exit code: $res";
785
786   $main::lxdebug->leave_sub();
787   return 0;
788 }
789
790 sub format_string {
791   my ($self, $variable) = @_;
792   my $form = $self->{"form"};
793   my $iconv = $self->{"iconv"};
794
795   my %replace =
796     ('order' => ['<', '>', '"', "'",
797                  '\x80',        # Euro
798                  quotemeta("\n"), quotemeta("\r"), '&'],
799      '<'             => '&lt;',
800      '>'             => '&gt;',
801      '"'             => '&quot;',
802      "'"             => '&apos;',
803      '&'             => '&quot;',
804      '\x80'          => chr(0xa4), # Euro
805      quotemeta("\n") => '<text:line-break/>',
806      quotemeta("\r") => '',
807      );
808
809   map({ $variable =~ s/$_/$replace{$_}/g; } @{ $replace{"order"} });
810
811   # Allow some HTML markup to be converted into the output format's
812   # corresponding markup code, e.g. bold or italic.
813   my $rnd = $self->{"rnd"};
814   my %markup_replace = ("b" => "BOLD", "i" => "ITALIC", "s" => "STRIKETHROUGH",
815                         "u" => "UNDERLINE");
816
817   foreach my $key (keys(%markup_replace)) {
818     my $value = $markup_replace{$key};
819     $variable =~ s|\&lt;${key}\&gt;|<text:span text:style-name=\"TLXO${rnd}${value}\">|g;
820     $variable =~ s|\&lt;/${key}\&gt;|</text:span>|g;
821   }
822
823   return $iconv->convert($variable);
824 }
825
826 sub get_mime_type() {
827   if ($self->{"form"}->{"format"} =~ /pdf/) {
828     return "application/pdf";
829   } else {
830     return "application/vnd.oasis.opendocument.text";
831   }
832 }
833
834 sub uses_temp_file {
835   return 1;
836 }
837
838 1;