]> wagnertech.de Git - mfinanz.git/blob - SL/Template/OpenDocument.pm
Merge branch 'master' of http://wagnertech.de/git/mfinanz
[mfinanz.git] / SL / Template / OpenDocument.pm
1 package SL::Template::OpenDocument;
2
3 use parent qw(SL::Template::Simple);
4
5 use Archive::Zip;
6 use Encode;
7 use HTML::Entities;
8 use POSIX 'setsid';
9 use XML::LibXML;
10 use File::Basename;
11
12 use SL::Iconv;
13 use SL::Template::OpenDocument::Styles;
14
15 use SL::Helper::QrBill;
16 use SL::Helper::QrBillFunctions qw(
17   get_ref_number_formatted get_iban_formatted get_amount_formatted
18   get_street_name_from_address_line get_building_number_from_address_line
19 );
20
21 use Cwd;
22 # use File::Copy;
23 # use File::Spec;
24 # use File::Temp qw(:mktemp);
25 use IO::File;
26
27 use strict;
28
29 my %text_markup_replace = (
30   b   => "BOLD",
31   i   => "ITALIC",
32   s   => "STRIKETHROUGH",
33   u   => "UNDERLINE",
34   sup => "SUPER",
35   sub => "SUB",
36 );
37
38 sub _format_text {
39   my ($self, $content, %params) = @_;
40
41   $content = $::locale->quote_special_chars('Template/OpenDocument', $content);
42
43   # Allow some HTML markup to be converted into the output format's
44   # corresponding markup code, e.g. bold or italic.
45   foreach my $key (keys(%text_markup_replace)) {
46     my $value = $text_markup_replace{$key};
47     $content =~ s|\&lt;${key}\&gt;|<text:span text:style-name=\"TKIVITENDO${value}\">|gi; #"
48     $content =~ s|\&lt;/${key}\&gt;|</text:span>|gi;
49   }
50
51   return $content;
52 }
53
54 my %html_replace = (
55   '</ul>'     => '</text:list>',
56   '</ol>'     => '</text:list>',
57   '</li>'     => '</text:p></text:list-item>',
58   '<b>'       => '<text:span text:style-name="TKIVITENDOBOLD">',
59   '</b>'      => '</text:span>',
60   '<strong>'  => '<text:span text:style-name="TKIVITENDOBOLD">',
61   '</strong>' => '</text:span>',
62   '<i>'       => '<text:span text:style-name="TKIVITENDOITALIC">',
63   '</i>'      => '</text:span>',
64   '<em>'      => '<text:span text:style-name="TKIVITENDOITALIC">',
65   '</em>'     => '</text:span>',
66   '<u>'       => '<text:span text:style-name="TKIVITENDOUNDERLINE">',
67   '</u>'      => '</text:span>',
68   '<s>'       => '<text:span text:style-name="TKIVITENDOSTRIKETHROUGH">',
69   '</s>'      => '</text:span>',
70   '<sub>'     => '<text:span text:style-name="TKIVITENDOSUB">',
71   '</sub>'    => '</text:span>',
72   '<sup>'     => '<text:span text:style-name="TKIVITENDOSUPER">',
73   '</sup>'    => '</text:span>',
74   '<br/>'     => '<text:line-break/>',
75   '<br>'      => '<text:line-break/>',
76 );
77
78 sub _format_html {
79   my ($self, $content, %params) = @_;
80
81   my $in_p        = 0;
82   my $p_start_tag = qq|<text:p text:style-name="@{[ $self->{current_text_style} ]}">|;
83   my $prefix      = '';
84   my $suffix      = '';
85
86   my (@tags_to_open, @tags_to_close);
87   for (my $idx = scalar(@{ $self->{tag_stack} }) - 1; $idx >= 0; --$idx) {
88     my $tag = $self->{tag_stack}->[$idx];
89
90     next if $tag =~ m{/>$};
91     last if $tag =~ m{^<table};
92
93     if ($tag =~ m{^<text:p}) {
94       $in_p        = 1;
95       $p_start_tag = $tag;
96       last;
97
98     } else {
99       $suffix  =  "${tag}${suffix}";
100       $tag     =~ s{ .*>}{>};
101       $prefix .=  '</' . substr($tag, 1);
102     }
103   }
104
105   $content            =~ s{ ^<p> | </p>$ }{}gx if $in_p;
106   $content            =~ s{ \r+ }{}gx;
107   $content            =~ s{ \n+ }{ }gx;
108   $content            =~ s{ (?:\&nbsp;|\s)+ }{ }gx;
109
110   my $ul_start_tag    = qq|<text:list xml:id="list@{[ int rand(9999999999999999) ]}" text:style-name="LKIVITENDOitemize@{[ $self->{current_text_style} ]}">|;
111   my $ol_start_tag    = qq|<text:list xml:id="list@{[ int rand(9999999999999999) ]}" text:style-name="LKIVITENDOenumerate@{[ $self->{current_text_style} ]}">|;
112   my $ul_li_start_tag = qq|<text:list-item><text:p text:style-name="PKIVITENDOitemize@{[ $self->{current_text_style} ]}">|;
113   my $ol_li_start_tag = qq|<text:list-item><text:p text:style-name="PKIVITENDOenumerate@{[ $self->{current_text_style} ]}">|;
114
115   my @parts = map {
116     if (substr($_, 0, 1) eq '<') {
117       s{ +}{}g;
118       if ($_ eq '</p>') {
119         $in_p--;
120         $in_p == 0 ? '</text:p>' : '';
121
122       } elsif ($_ eq '<p>') {
123         $in_p++;
124         $in_p == 1 ? $p_start_tag : '';
125
126       } elsif ($_ eq '<ul>') {
127         $self->{used_list_styles}->{itemize}->{$self->{current_text_style}}   = 1;
128         $html_replace{'<li>'}                                                 = $ul_li_start_tag;
129         $ul_start_tag;
130
131       } elsif ($_ eq '<ol>') {
132         $self->{used_list_styles}->{enumerate}->{$self->{current_text_style}} = 1;
133         $html_replace{'<li>'}                                                 = $ol_li_start_tag;
134         $ol_start_tag;
135
136       } else {
137         $html_replace{$_} || '';
138       }
139
140     } else {
141       $::locale->quote_special_chars('Template/OpenDocument', HTML::Entities::decode_entities($_));
142     }
143   } split(m{(<.*?>)}x, $content);
144
145   my $out  = join('', $prefix, @parts, $suffix);
146
147   # $::lxdebug->dump(0, "prefix parts suffix", [ $prefix, join('', @parts), $suffix ]);
148
149   return $out;
150 }
151
152 my %formatters = (
153   html => \&_format_html,
154   text => \&_format_text,
155 );
156
157 sub new {
158   my $type = shift;
159
160   my $self = $type->SUPER::new(@_);
161
162   $self->set_tag_style('&lt;%', '%&gt;');
163   $self->{quot_re} = '&quot;';
164
165   return $self;
166 }
167
168 sub parse_foreach {
169   my ($self, $var, $text, $start_tag, $end_tag, @indices) = @_;
170
171   my ($form, $new_contents) = ($self->{"form"}, "");
172
173   my $ary = $self->_get_loop_variable($var, 1, @indices);
174
175   for (my $i = 0; $i < scalar(@{$ary || []}); $i++) {
176     $form->{"__first__"} = $i == 0;
177     $form->{"__last__"} = ($i + 1) == scalar(@{$ary});
178     $form->{"__odd__"} = (($i + 1) % 2) == 1;
179     $form->{"__counter__"} = $i + 1;
180     my $new_text = $self->parse_block($text, (@indices, $i));
181     return undef unless (defined($new_text));
182     $new_contents .= $start_tag . $new_text . $end_tag;
183   }
184   map({ delete($form->{"__${_}__"}); } qw(first last odd counter));
185
186   return $new_contents;
187 }
188
189 sub find_end {
190   my ($self, $text, $pos, $var, $not) = @_;
191
192   my $depth = 1;
193   $pos = 0 unless ($pos);
194
195   while ($pos < length($text)) {
196     $pos++;
197
198     next if (substr($text, $pos - 1, 5) ne '&lt;%');
199
200     if ((substr($text, $pos + 4, 2) eq 'if') || (substr($text, $pos + 4, 3) eq 'for')) {
201       $depth++;
202
203     } elsif ((substr($text, $pos + 4, 4) eq 'else') && (1 == $depth)) {
204       if (!$var) {
205         $self->{error} = '<%else%> outside of <%if%> / <%ifnot%>.';
206         return undef;
207       }
208
209       my $block = substr($text, 0, $pos - 1);
210       substr($text, 0, $pos - 1) = "";
211       $text =~ s!^\&lt;\%[^\%]+\%\&gt;!!;
212       $text = '&lt;%if' . ($not ?  " " : "not ") . $var . '%&gt;' . $text;
213
214       return ($block, $text);
215
216     } elsif (substr($text, $pos + 4, 3) eq 'end') {
217       $depth--;
218       if ($depth == 0) {
219         my $block = substr($text, 0, $pos - 1);
220         substr($text, 0, $pos - 1) = "";
221         $text =~ s!^\&lt;\%[^\%]+\%\&gt;!!;
222
223         return ($block, $text);
224       }
225     }
226   }
227
228   return undef;
229 }
230
231 sub parse_block {
232   $main::lxdebug->enter_sub();
233
234   my ($self, $contents, @indices) = @_;
235
236   my $new_contents = "";
237
238   while ($contents ne "") {
239     if (substr($contents, 0, 1) eq "<") {
240       $contents =~ m|^(<[^>]+>)|;
241       my $tag = $1;
242       substr($contents, 0, length($1)) = "";
243
244       $self->{current_text_style} = $1 if $tag =~ m|text:style-name\s*=\s*"([^"]+)"|;
245
246       push @{ $self->{tag_stack} }, $tag;
247
248       if ($tag =~ m|<table:table-row|) {
249         $contents =~ m|^(.*?)(</table:table-row[^>]*>)|;
250         my $table_row = $1;
251         my $end_tag = $2;
252
253         if ($table_row =~ m|\&lt;\%foreachrow\s+(.*?)\%\&gt;|) {
254           my $var = $1;
255
256           $contents =~ m|^(.*?)(\&lt;\%foreachrow\s+.*?\%\&gt;)|;
257           substr($contents, length($1), length($2)) = "";
258
259           ($table_row, $contents) = $self->find_end($contents, length($1));
260           if (!$table_row) {
261             $self->{error} = "Unclosed <\%foreachrow\%>." unless ($self->{"error"});
262             $main::lxdebug->leave_sub();
263             return undef;
264           }
265
266           $contents   =~ m|^(.*?)(</table:table-row[^>]*>)|;
267           $table_row .=  $1;
268           $end_tag    =  $2;
269
270           substr $contents, 0, length($1) + length($2), '';
271
272           my $new_text = $self->parse_foreach($var, $table_row, $tag, $end_tag, @indices);
273           if (!defined($new_text)) {
274             $main::lxdebug->leave_sub();
275             return undef;
276           }
277           $new_contents .= $new_text;
278
279         } else {
280           substr($contents, 0, length($table_row) + length($end_tag)) = "";
281           my $new_text = $self->parse_block($table_row, @indices);
282           if (!defined($new_text)) {
283             $main::lxdebug->leave_sub();
284             return undef;
285           }
286           $new_contents .= $tag . $new_text . $end_tag;
287         }
288
289       } else {
290         $new_contents .= $tag;
291       }
292
293       if ($tag =~ m{^</ | />$}x) {
294         # $::lxdebug->message(0, "popping top tag is $tag top " . $self->{tag_stack}->[-1]);
295         pop @{ $self->{tag_stack} };
296       }
297
298     } else {
299       $contents =~ /^([^<]+)/;
300       my $text = $1;
301
302       my $pos_if = index($text, '&lt;%if');
303       my $pos_foreach = index($text, '&lt;%foreach');
304
305       if ((-1 == $pos_if) && (-1 == $pos_foreach)) {
306         substr($contents, 0, length($text)) = "";
307         $new_contents .= $self->substitute_vars($text, @indices);
308         next;
309       }
310
311       if ((-1 == $pos_if) || ((-1 != $pos_foreach) && ($pos_if > $pos_foreach))) {
312         $new_contents .= $self->substitute_vars(substr($contents, 0, $pos_foreach), @indices);
313         substr($contents, 0, $pos_foreach) = "";
314
315         if ($contents !~ m|^(\&lt;\%foreach (.*?)\%\&gt;)|) {
316           $self->{error} = "Malformed <\%foreach\%>.";
317           $main::lxdebug->leave_sub();
318           return undef;
319         }
320
321         my $var = $2;
322
323         substr($contents, 0, length($1)) = "";
324
325         my $block;
326         ($block, $contents) = $self->find_end($contents);
327         if (!$block) {
328           $self->{error} = "Unclosed <\%foreach\%>." unless ($self->{error});
329           $main::lxdebug->leave_sub();
330           return undef;
331         }
332
333         my $new_text = $self->parse_foreach($var, $block, "", "", @indices);
334         if (!defined($new_text)) {
335           $main::lxdebug->leave_sub();
336           return undef;
337         }
338         $new_contents .= $new_text;
339
340       } else {
341         if (!$self->_parse_block_if(\$contents, \$new_contents, $pos_if, @indices)) {
342           $main::lxdebug->leave_sub();
343           return undef;
344         }
345       }
346     }
347   }
348
349   $main::lxdebug->leave_sub();
350
351   return $new_contents;
352 }
353
354 sub parse {
355   $main::lxdebug->enter_sub();
356   my $self = $_[0];
357
358   local *OUT = $_[1];
359   my $form = $self->{form};
360
361   close(OUT);
362
363   my $is_qr_bill = $::instance_conf->get_create_qrbill_invoices &&
364                    ($form->{formname} eq 'invoice' ||
365                     $form->{formname} eq 'invoice_for_advance_payment') &&
366                    $form->{template_meta}->{printer}->{template_code} =~ m/qr/ ?
367                    1 : 0;
368
369   my $qr_image_path;
370   if ($is_qr_bill) {
371     # the biller account information, biller address and the reference number,
372     # are needed in the template aswell as in the qr-code generation, therefore
373     # assemble these and add to $::form
374     $qr_image_path = $self->generate_qr_code;
375   }
376
377   my $file_name;
378   if ($form->{IN} =~ m|^/|) {
379     $file_name = $form->{IN};
380   } else {
381     $file_name = $form->{templates} . "/" . $form->{IN};
382   }
383
384   my $zip = Archive::Zip->new();
385   if (Archive::Zip->AZ_OK != $zip->read($file_name)) {
386     $self->{error} = "File not found/is not a OpenDocument file.";
387     $main::lxdebug->leave_sub();
388     return 0;
389   }
390
391   my $contents = Encode::decode('utf-8-strict', $zip->contents("content.xml"));
392   if (!$contents) {
393     $self->{error} = "File is not a OpenDocument file.";
394     $main::lxdebug->leave_sub();
395     return 0;
396   }
397
398   $self->{current_text_style} =  '';
399   $self->{used_list_styles}   =  {
400     itemize                   => {},
401     enumerate                 => {},
402   };
403
404   my $new_contents;
405   if ($self->{use_template_toolkit}) {
406     my $additional_params = $::form;
407
408     $::form->template->process(\$contents, $additional_params, \$new_contents) || die $::form->template->error;
409   } else {
410     $self->{tag_stack} = [];
411     $new_contents = $self->parse_block($contents);
412   }
413   if (!defined($new_contents)) {
414     $main::lxdebug->leave_sub();
415     return 0;
416   }
417
418   my $new_styles = SL::Template::OpenDocument::Styles->get_style('text_basic');
419
420   foreach my $type (qw(itemize enumerate)) {
421     foreach my $parent (sort { $a cmp $b } keys %{ $self->{used_list_styles}->{$type} }) {
422       $new_styles .= SL::Template::OpenDocument::Styles->get_style('text_list_item', TYPE => $type, PARENT => $parent)
423                    .  SL::Template::OpenDocument::Styles->get_style("list_${type}",  TYPE => $type, PARENT => $parent);
424     }
425   }
426
427   # $::lxdebug->dump(0, "new_Styles", $new_styles);
428
429   $new_contents =~ s|</office:automatic-styles>|${new_styles}</office:automatic-styles>|;
430   $new_contents =~ s|[\n\r]||gm;
431
432 #   $new_contents =~ s|>|>\n|g;
433
434   $zip->contents("content.xml", Encode::encode('utf-8-strict', $new_contents));
435
436   my $styles = Encode::decode('utf-8-strict', $zip->contents("styles.xml"));
437   if ($contents) {
438     my $new_styles = $self->parse_block($styles);
439     if (!defined($new_contents)) {
440       $main::lxdebug->leave_sub();
441       return 0;
442     }
443     $zip->contents("styles.xml", Encode::encode('utf-8-strict', $new_styles));
444   }
445
446   if ($is_qr_bill) {
447     # get placeholder path from odt XML
448     my $qr_placeholder_path;
449     my $dom = XML::LibXML->load_xml(string => $contents);
450     my @nodelist = $dom->getElementsByTagName("draw:frame");
451     for my $node (@nodelist) {
452       my $attr = $node->getAttribute('draw:name');
453       if ($attr eq 'QRCodePlaceholder') {
454         my @children = $node->getChildrenByTagName('draw:image');
455         $qr_placeholder_path = $children[0]->getAttribute('xlink:href');
456       }
457     }
458     if (!defined($qr_placeholder_path)) {
459       $::form->error($::locale->text('QR-Code placeholder image: QRCodePlaceholder not found in template.'));
460     }
461     # replace QR-Code Placeholder Image in zip file (odt) with generated one
462     $zip->updateMember(
463      $qr_placeholder_path,
464      $qr_image_path
465     );
466   }
467
468   $zip->writeToFileNamed($form->{tmpfile}, 1);
469
470   my $res = 1;
471   if ($form->{format} =~ /pdf/) {
472     $res = $self->convert_to_pdf();
473   }
474
475   $main::lxdebug->leave_sub();
476   return $res;
477 }
478
479 sub generate_qr_code {
480   $main::lxdebug->enter_sub();
481   my $self = $_[0];
482   my $form = $self->{form};
483
484   # assemble data for QR-Code
485
486   if (!$form->{qrbill_iban}) {
487     $::form->error($::locale->text('No bank account flagged for QRBill usage was found.'));
488   }
489
490   my %biller_information = (
491     iban => $form->{qrbill_iban}
492   );
493
494   if (!$form->{qrbill_biller_countrycode}) {
495     $::form->error($::locale->text('Error mapping biller countrycode.'));
496   }
497   my %biller_data = (
498     address_type => 'S',
499     company => $::instance_conf->get_company(),
500     street => get_street_name_from_address_line($::instance_conf->get_address_street1()),
501     street_no => get_building_number_from_address_line($::instance_conf->get_address_street1()),
502     postalcode => $::instance_conf->get_address_zipcode(),
503     city => $::instance_conf->get_address_city(),
504     countrycode => $form->{qrbill_biller_countrycode},
505   );
506
507   my ($amount, $amount_formatted);
508   if ($form->{qrbill_without_amount}) {
509     $amount = '';
510     $amount_formatted = '';
511   } else {
512     $amount = $form->{qrbill_amount};
513
514     # format amount for template
515     $amount_formatted = get_amount_formatted($amount);
516     if (!$amount_formatted) {
517       $::form->error($::locale->text('Amount has wrong format.'));
518     }
519   }
520
521   my %payment_information = (
522     amount => $amount,
523     currency => $form->{currency},
524   );
525
526   # get address data from billing address if given, otherwise from invoice
527   my $street_name = get_street_name_from_address_line($form->{billing_address_id} ?
528       $form->{billing_address_street} :
529       $form->{street});
530   my $building_number = get_building_number_from_address_line($form->{billing_address_id} ?
531       $form->{billing_address_street} :
532       $form->{street});
533   my $postalcode = $form->{billing_address_id} ?
534       $form->{billing_address_zipcode} :
535       $form->{zipcode};
536   my $city = $form->{billing_address_id} ?
537       $form->{billing_address_city} :
538       $form->{city};
539
540   # validate address data
541   if ($postalcode !~ m/^\d{4,}$/) {
542     $::form->error($::locale->text('Zipcode missing or wrong format.'));
543   }
544   if (!$city) {
545     $::form->error($::locale->text('No city given.'));
546   }
547   if (!$form->{qrbill_customer_countrycode}) {
548     $::form->error($::locale->text('Error mapping customer countrycode.'));
549   }
550
551   my %invoice_recipient_data = (
552     address_type => 'S',
553     name => $form->{billing_address_id} ?
554               $form->{billing_address_name} :
555               $form->{name},
556     street => $street_name,
557     street_no => $building_number,
558     postalcode => $postalcode,
559     city => $city,
560     countrycode => $form->{qrbill_customer_countrycode},
561   );
562
563   my %ref_nr_data;
564   if ($::instance_conf->get_create_qrbill_invoices == 1) {
565     # fill reference number with zeros when printing preview (before booking)
566     my $reference_number = $form->{id} ? $form->{qr_reference} : '0' x 27;
567
568     %ref_nr_data = (
569       type => 'QRR',
570       ref_number => $reference_number,
571     );
572     # get ref. number/iban formatted with spaces and set into form for template
573     # processing
574     $form->{ref_number} = $reference_number;
575     $form->{ref_number_formatted} = get_ref_number_formatted($reference_number);
576   } elsif ($::instance_conf->get_create_qrbill_invoices == 2) {
577     %ref_nr_data = (
578       type => 'NON',
579       ref_number => '',
580     );
581   } else {
582     $::form->error($::locale->text('Error getting QR-Bill type.'));
583   }
584
585   my %additional_information = (
586     unstructured_message => $form->{qr_unstructured_message}
587   );
588
589   # set into form for template processing
590   $form->{biller_information} = \%biller_information;
591   $form->{biller_data} = \%biller_data;
592   $form->{iban_formatted} = get_iban_formatted($form->{qrbill_iban});
593   $form->{amount_formatted} = $amount_formatted;
594   $form->{unstructured_message} = $form->{qr_unstructured_message};
595
596   # set outfile
597   my $outfile = $form->{tmpdir} . '/' . 'qr-code.png';
598
599   # generate QR-Code Image
600   eval {
601    my $qr_image = SL::Helper::QrBill->new(
602      \%biller_information,
603      \%biller_data,
604      \%payment_information,
605      \%invoice_recipient_data,
606      \%ref_nr_data,
607      \%additional_information
608    );
609    $qr_image->generate($outfile);
610   } or do {
611    local $_ = $@; chomp; my $error = $_;
612    $::form->error($::locale->text('QR-Image generation failed: ' . $error));
613   };
614
615   $main::lxdebug->leave_sub();
616   return $outfile;
617 }
618
619 sub _run_python_uno {
620   my ($self, @args) = @_;
621
622   local $ENV{PYTHONPATH};
623   $ENV{PYTHONPATH} = $::lx_office_conf{environment}->{python_uno_path} . ':' . $ENV{PYTHONPATH} if $::lx_office_conf{environment}->{python_uno_path};
624   my $cmd          = $::lx_office_conf{applications}->{python_uno} . ' ' . join(' ', @args);
625   return `$cmd`;
626 }
627
628 sub is_openoffice_running {
629   my ($self) = @_;
630
631   $main::lxdebug->enter_sub();
632
633   my $output = $self->_run_python_uno('./scripts/oo-uno-test-conn.py', $::lx_office_conf{print_templates}->{openofficeorg_daemon_port}, ' 2> /dev/null');
634   chomp $output;
635
636   my $res = ($? == 0) || $output;
637   $main::lxdebug->message(LXDebug->DEBUG2(), "  is_openoffice_running(): res $res\n");
638
639   $main::lxdebug->leave_sub();
640
641   return $res;
642 }
643
644 sub spawn_openoffice {
645   $main::lxdebug->enter_sub();
646
647   my ($self) = @_;
648
649   $main::lxdebug->message(LXDebug->DEBUG2(), "spawn_openoffice()\n");
650
651   my ($try, $spawned_oo, $res);
652
653   $res = 0;
654   for ($try = 0; $try < 15; $try++) {
655     if ($self->is_openoffice_running()) {
656       $res = 1;
657       last;
658     }
659
660     if ($::dispatcher->interface_type eq 'FastCGI') {
661       $::dispatcher->{request}->Detach;
662     }
663
664     if (!$spawned_oo) {
665       my $pid = fork();
666       if (0 == $pid) {
667         $main::lxdebug->message(LXDebug->DEBUG2(), "  Child daemonizing\n");
668
669         if ($::dispatcher->interface_type eq 'FastCGI') {
670           $::dispatcher->{request}->Finish;
671           $::dispatcher->{request}->LastCall;
672         }
673         chdir('/');
674         open(STDIN, '/dev/null');
675         open(STDOUT, '>/dev/null');
676         my $new_pid = fork();
677         exit if ($new_pid);
678         my $ssres = setsid();
679         $main::lxdebug->message(LXDebug->DEBUG2(), "  Child execing\n");
680         my @cmdline = ($::lx_office_conf{applications}->{openofficeorg_writer},
681                        "--minimized", "--norestore", "--nologo", "--nolockcheck",
682                        "--headless",
683                        "--accept=socket,host=localhost,port=" .
684                        $::lx_office_conf{print_templates}->{openofficeorg_daemon_port} . ";urp;");
685         exec(@cmdline);
686       } else {
687         # parent
688         if ($::dispatcher->interface_type eq 'FastCGI') {
689           $::dispatcher->{request}->Attach;
690         }
691       }
692
693       $main::lxdebug->message(LXDebug->DEBUG2(), "  Parent after fork\n");
694       $spawned_oo = 1;
695       sleep(3);
696     }
697
698     sleep($try >= 5 ? 2 : 1);
699   }
700
701   if (!$res) {
702     $self->{error} = "Conversion from OpenDocument to PDF failed because " .
703       "OpenOffice could not be started.";
704   }
705
706   $main::lxdebug->leave_sub();
707
708   return $res;
709 }
710
711 sub convert_to_pdf {
712   $main::lxdebug->enter_sub();
713
714   my ($self) = @_;
715
716   my $form = $self->{form};
717
718   my $filename = $form->{tmpfile};
719   $filename =~ s/.odt$//;
720   if (substr($filename, 0, 1) ne "/") {
721     $filename = getcwd() . "/${filename}";
722   }
723
724   if (substr($self->{userspath}, 0, 1) eq "/") {
725     $ENV{HOME} = $self->{userspath};
726   } else {
727     $ENV{HOME} = getcwd() . "/" . $self->{userspath};
728   }
729
730   my $outdir = dirname($filename);
731
732   if (!$::lx_office_conf{print_templates}->{openofficeorg_daemon}) {
733     if (system($::lx_office_conf{applications}->{openofficeorg_writer},
734                "--minimized", "--norestore", "--nologo", "--nolockcheck", "--headless",
735                "--convert-to", "pdf", "--outdir", $outdir,
736                "file:${filename}.odt") == -1) {
737       die "system call to $::lx_office_conf{applications}->{openofficeorg_writer} failed: $!";
738     }
739   } else {
740     if (!$self->spawn_openoffice()) {
741       $main::lxdebug->leave_sub();
742       return 0;
743     }
744
745     $self->_run_python_uno('./scripts/oo-uno-convert-pdf.py', $::lx_office_conf{print_templates}->{openofficeorg_daemon_port}, "${filename}.odt");
746   }
747
748   my $res = $?;
749   if ((0 == $?) || (-f "${filename}.pdf" && -s "${filename}.pdf")) {
750     $form->{tmpfile} =~ s/odt$/pdf/;
751
752     unlink($filename . ".odt");
753
754     $main::lxdebug->leave_sub();
755     return 1;
756
757   }
758
759   unlink($filename . ".odt", $filename . ".pdf");
760   $self->{error} = "Conversion from OpenDocument to PDF failed. " .
761     "Exit code: $res";
762
763   $main::lxdebug->leave_sub();
764   return 0;
765 }
766
767 sub format_string {
768   my ($self, $content, $variable) = @_;
769
770   my $formatter =
771        $formatters{ $self->{variable_content_types}->{$variable} }
772     // $formatters{ $self->{default_content_type} }
773     // $formatters{ text };
774
775   return $formatter->($self, $content, variable => $variable);
776 }
777
778 sub get_mime_type() {
779   my ($self) = @_;
780
781   if ($self->{form}->{format} =~ /pdf/) {
782     return "application/pdf";
783   } else {
784     return "application/vnd.oasis.opendocument.text";
785   }
786 }
787
788 sub uses_temp_file {
789   return 1;
790 }
791
792 1;