1 package SL::Helper::CreatePDF;
7 use English qw(-no_match_vars);
11 use File::Copy qw(move);
12 use List::MoreUtils qw(uniq);
13 use List::Util qw(first);
14 use String::ShellQuote ();
22 use SL::Template::LaTeX;
24 use Exporter 'import';
25 our @EXPORT_OK = qw(create_pdf merge_pdfs find_template);
31 my ($class, %params) = @_;
33 return __PACKAGE__->create_parsed_file(
35 template_type => 'LaTeX',
40 sub create_parsed_file {
41 my ($class, %params) = @_;
43 my $userspath = $::lx_office_conf{paths}->{userspath};
44 my $vars = $params{variables} || {};
45 my $form = Form->new('');
46 $form->{$_} = $vars->{$_} for keys %{$vars};
47 $form->{format} = lc($params{format} || 'pdf');
48 $form->{cwd} = getcwd();
49 $form->{templates} = $::instance_conf->get_templates;
50 $form->{IN} = $params{template};
51 $form->{tmpdir} = $form->{cwd} . '/' . $userspath;
52 my $tmpdir = $form->{tmpdir};
53 my ($suffix) = $params{template} =~ m{\.(.+)};
55 my ($temp_fh, $tmpfile) = File::Temp::tempfile(
56 'kivitendo-printXXXXXX',
57 SUFFIX => ".${suffix}",
58 DIR => $form->{tmpdir},
60 ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
63 $form->{tmpfile} = $tmpfile;
64 (undef, undef, $form->{template_meta}{tmpfile}) = File::Spec->splitpath($tmpfile);
66 my $parser = SL::Template::create(
67 type => ($params{template_type} || 'LaTeX'),
68 source => $form->{IN},
70 myconfig => \%::myconfig,
72 variable_content_types => $params{variable_content_types},
75 my $result = $parser->parse($temp_fh);
82 die $parser->get_error;
85 # SL::Template:** modify $form->{tmpfile} by removing its
86 # $form->{userspath} prefix. They also store the final file's actual
87 # file name in $form->{tmpfile} – but it is now relative to
88 # $form->{userspath}. Other modules return the full file name…
89 my ($volume, $directory, $file_name) = File::Spec->splitpath($form->{tmpfile});
90 my $full_file_name = File::Spec->catfile($tmpdir, $file_name);
91 if (($params{return} || 'content') eq 'file_name') {
92 my $new_name = File::Spec->catfile($tmpdir, 'keep-' . $form->{tmpfile});
93 rename $full_file_name, $new_name;
100 my $content = File::Slurp::read_file($full_file_name);
108 # Alternativen zu pdfinfo wären (aber wesentlich langamer):
110 # gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=/dev/null $filename | grep 'Processing pages'
111 # my (undef,undef,undef,undef,$pages) = split / +/,$shell_out;
113 # gs -dBATCH -dNOPAUSE -q -dNODISPLAY -c "($filename) (r) file runpdfbegin pdfpagecount = quit"
118 my ($class, $filename) = @_;
119 return 0 unless -f $filename;
120 my $shell_out = `pdfinfo $filename | grep 'Pages:'`;
121 my ($label, $pages) = split / +/, $shell_out;
126 my ($class, %params) = @_;
127 my $filecount = scalar(@{ $params{file_names} });
129 if ($params{inp_content}) {
130 return $params{inp_content} if $filecount == 0 && !$params{out_path};
132 elsif ($params{out_path}) {
133 return 0 if $filecount == 0;
134 if ($filecount == 1) {
135 if (!rename($params{file_names}->[0], $params{out_path})) {
136 # special filesystem or cross filesystem etc
137 move($params{file_names}->[0], $params{out_path});
143 return '' if $filecount == 0;
144 return scalar(File::Slurp::read_file($params{file_names}->[0])) if $filecount == 1;
147 my ($temp_fh, $temp_name) = File::Temp::tempfile(
148 'kivitendo-printXXXXXX',
150 DIR => $::lx_office_conf{paths}->{userspath},
151 UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
155 my $input_names = '';
158 if ($params{bothsided}) {
159 $emptypage = $::instance_conf->get_templates . '/emptyPage.pdf';
160 unless (-f $emptypage) {
162 delete $params{bothsided};
165 if ($params{inp_content}) {
166 my ($temp_fh, $inp_name) = File::Temp::tempfile(
167 'kivitendo-contentXXXXXX',
169 DIR => $::lx_office_conf{paths}->{userspath},
171 $::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files}
177 print $temp_fh $params{inp_content};
179 $input_names = $inp_name . ' ';
181 ($params{bothsided} && __PACKAGE__->has_odd_pages($inp_name)
186 foreach (@{ $params{file_names} }) {
187 $input_names .= $emptypage . ' ' if $hasodd;
188 $input_names .= String::ShellQuote::shell_quote($_) . ' ';
190 ($params{bothsided} && __PACKAGE__->has_odd_pages($_)
195 my $exe = $::lx_office_conf{applications}->{ghostscript} || 'gs';
197 `$exe -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=${temp_name} ${input_names} 2>&1`;
199 die "Executing gs failed: $ERRNO" if !defined $output;
200 die $output if $? != 0;
202 if ($params{out_path}) {
203 if (!rename($temp_name, $params{out_path})) {
205 # special filesystem or cross filesystem etc
206 move($temp_name, $params{out_path});
210 return scalar File::Slurp::read_file($temp_name);
214 my ($class, %params) = @_;
216 $params{name} or croak "Missing parameter 'name'";
218 my $path = $::instance_conf->get_templates;
219 my $extension = $params{extension} || "tex";
220 my ($printer, $language) = ('', '');
222 if ($params{printer} || $params{printer_id}) {
223 if ($params{printer} && !ref $params{printer}) {
224 $printer = '_' . $params{printer};
227 $printer = $params{printer} || SL::DB::Printer->new(id => $params{printer_id})->load;
228 $printer = $printer->template_code ? '_' . $printer->template_code : '';
232 if ($params{language} || $params{language_id}) {
233 if ($params{language} && !ref $params{language}) {
234 $language = '_' . $params{language};
237 $language = $params{language} || SL::DB::Language->new(id => $params{language_id})->load;
238 $language = $language->template_code ? '_' . $language->template_code : '';
242 my @template_files = (
243 $params{name} . "${language}${printer}",
244 $params{name} . "${language}",
249 if ($params{email}) {
250 unshift @template_files,
252 $params{name} . "_email${language}${printer}",
253 $params{name} . "_email${language}",
257 @template_files = map { "${_}.${extension}" } uniq grep { $_ } @template_files;
259 my $template = first { -f ($path . "/$_") } @template_files;
261 return wantarray ? ($template, @template_files) : $template;
273 SL::Helper::CreatePDF - A helper for creating PDFs from template files
277 # Retrieve a sales order from the database and create a PDF for
279 my $order = SL::DB::Order->new(id => …)->load;
280 my $print_form = Form->new('');
281 $print_form->{type} = 'invoice';
282 $print_form->{formname} = 'invoice',
283 $print_form->{format} = 'pdf',
284 $print_form->{media} = 'file';
286 $order->flatten_to_form($print_form, format_amounts => 1);
287 $print_form->prepare_for_printing;
289 my $pdf = SL::Helper::CreatePDF->create_pdf(
290 template => 'sales_order',
291 variables => $print_form,
298 =item C<create_pdf %params>
300 Parses a LaTeX template file, creates a PDF for it and returns either
301 its content or its file name. The recognized parameters are the same
302 as the ones for L</create_parsed_file> with C<format> and
303 C<template_type> being pre-set.
305 =item C<create_parsed_file %params>
307 Parses a template file and returns either its content or its file
308 name. The recognized parameters are:
312 =item * C<template> – mandatory. The template file name relative to
313 the users' templates directory. Must be an existing file name,
314 e.g. one retrieved by L</find_template>.
316 =item * C<variables> – optional hash reference containing variables
317 available to the template.
319 =item * C<return> – optional scalar containing either C<content> (the
320 default) or C<file_name>. If it is set to C<file_name> then the file
321 name of the temporary file containing the PDF is returned, and the
322 caller is responsible for deleting it. Otherwise a scalar containing
323 the PDF itself is returned and all temporary files have already been
324 deleted by L</create_pdf>.
326 =item * C<format> – optional, defaults to C<pdf> and determines the
327 output format. Can be set to C<html> for HTML output if
328 C<template_type> is set to C<HTML> as well.
330 =item * C<template_type> – optional, defaults to C<LaTeX> and
331 determines the template's format. Can be set to C<HTML> for HTML
332 output if C<format> is set to C<html> as well.
336 =item C<find_template %params>
338 Searches the user's templates directory for a template file name to
339 use. The file names considered depend on the parameters; they can
340 contain a template base name and suffixes for email, language and
341 printers. As a fallback the name C<default.$extension> is also
344 The return value depends on the context. In scalar context the
345 template file name that matches the given parameters is returned. It's
346 a file name relative to the user's templates directory. If no template
347 file is found then C<undef> is returned.
349 In list context the first element is the same value as in scalar
350 context. Additionally a list of considered template file names is
353 The recognized parameters are:
357 =item * C<name> – mandatory. The template's file name basis
358 without any additional suffix or extension, e.g. C<sales_quotation>.
360 =item * C<extension> – optional file name extension to use without the
361 dot. Defaults to C<tex>.
363 =item * C<email> – optional flag indicating whether or not the
364 template is to be sent via email. If set to true then template file
365 names containing C<_email> are considered as well.
367 =item * C<language> and C<language_id> – optional parameters
368 indicating the language to be used. C<language> can be either a string
369 containing the language code to use or an instance of
370 C<SL::DB::Language>. C<language_id> can contain the ID of the
371 C<SL::DB:Language> instance to load and use. If given template file
372 names containing C<_language_template_code> are considered as well.
374 =item * C<printer> and C<printer_id> – optional parameters indicating
375 the printer to be used. C<printer> can be either a string containing
376 the printer code to use or an instance of
377 C<SL::DB::Printer>. C<printer_id> can contain the ID of the
378 C<SL::DB:Printer> instance to load and use. If given template file
379 names containing C<_printer_template_code> are considered as well.
383 =item C<merge_pdfs %params>
385 Merges two or more PDFs into a single PDF by using the external
386 application ghostscript.
388 Normally the function returns the contents of the resulting PDF.
389 if The parameter C<out_path> is set the resulting PDF is in this file
390 and the return value is 1 if it successful or 0 if not.
392 The recognized parameters are:
396 =item * C<file_names> – mandatory array reference containing the file
399 =item * C<inp_content> – optional, contents of first file to merge with C<file_names>.
401 =item * C<out_path> – optional, returns not the merged contents but wrote him into this file
405 Note that this function relies on the presence of the external
406 application ghostscript. The executable to use is configured via
407 kivitendo's configuration file setting C<application.ghostscript>.
417 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>