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 ();
20 use SL::System::Process;
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 $keep_temp_files = $::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files};
44 my $userspath = SL::System::Process::exe_dir() . "/" . $::lx_office_conf{paths}->{userspath};
45 my $temp_dir = File::Temp->newdir(
46 "kivitendo-print-XXXXXX",
48 CLEANUP => !$keep_temp_files,
51 my $vars = $params{variables} || {};
52 my $form = Form->new('');
53 $form->{$_} = $vars->{$_} for keys %{$vars};
54 $form->{format} = lc($params{format} || 'pdf');
55 $form->{cwd} = SL::System::Process::exe_dir();
56 $form->{templates} = $::instance_conf->get_templates;
57 $form->{IN} = $params{template};
58 $form->{tmpdir} = $temp_dir->dirname;
59 my $tmpdir = $form->{tmpdir};
60 my ($suffix) = $params{template} =~ m{\.(.+)};
62 my ($temp_fh, $tmpfile) = File::Temp::tempfile(
63 'kivitendo-printXXXXXX',
64 SUFFIX => ".${suffix}",
65 DIR => $form->{tmpdir},
66 UNLINK => !$keep_temp_files,
69 $form->{tmpfile} = $tmpfile;
70 (undef, undef, $form->{template_meta}{tmpfile}) = File::Spec->splitpath($tmpfile);
72 my $parser = SL::Template::create(
73 type => ($params{template_type} || 'LaTeX'),
74 source => $form->{IN},
76 myconfig => \%::myconfig,
78 variable_content_types => $params{variable_content_types},
81 my $result = $parser->parse($temp_fh);
88 die $parser->get_error;
91 # SL::Template:** modify $form->{tmpfile} by removing its
92 # $form->{userspath} prefix. They also store the final file's actual
93 # file name in $form->{tmpfile} – but it is now relative to
94 # $form->{userspath}. Other modules return the full file name…
95 my ($volume, $directory, $file_name) = File::Spec->splitpath($form->{tmpfile});
96 my $full_file_name = File::Spec->catfile($tmpdir, $file_name);
97 if (($params{return} || 'content') eq 'file_name') {
98 my $new_name = File::Spec->catfile($userspath, 'keep-' . $form->{tmpfile});
99 rename $full_file_name, $new_name;
106 my $content = File::Slurp::read_file($full_file_name);
114 # Alternativen zu pdfinfo wären (aber wesentlich langamer):
116 # gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=/dev/null $filename | grep 'Processing pages'
117 # my (undef,undef,undef,undef,$pages) = split / +/,$shell_out;
119 # gs -dBATCH -dNOPAUSE -q -dNODISPLAY -c "($filename) (r) file runpdfbegin pdfpagecount = quit"
124 my ($class, $filename) = @_;
125 return 0 unless -f $filename;
126 my $shell_out = `pdfinfo $filename | grep 'Pages:'`;
127 my ($label, $pages) = split / +/, $shell_out;
132 my ($class, %params) = @_;
133 my $filecount = scalar(@{ $params{file_names} });
135 if ($params{inp_content}) {
136 return $params{inp_content} if $filecount == 0 && !$params{out_path};
137 } elsif ($params{out_path}) {
138 return 0 if $filecount == 0;
139 if ($filecount == 1) {
140 if (!rename($params{file_names}->[0], $params{out_path})) {
141 # special filesystem or cross filesystem etc
142 move($params{file_names}->[0], $params{out_path});
147 return '' if $filecount == 0;
148 return scalar(File::Slurp::read_file($params{file_names}->[0])) if $filecount == 1;
151 my ($temp_fh, $temp_name) = File::Temp::tempfile(
152 'kivitendo-printXXXXXX',
154 DIR => $::lx_office_conf{paths}->{userspath},
155 UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
159 my $input_names = '';
162 if ($params{bothsided}) {
163 $emptypage = $::instance_conf->get_templates . '/emptyPage.pdf';
164 unless (-f $emptypage) {
166 delete $params{bothsided};
169 if ($params{inp_content}) {
170 my ($temp_fh, $inp_name) = File::Temp::tempfile(
171 'kivitendo-contentXXXXXX',
173 DIR => $::lx_office_conf{paths}->{userspath},
174 UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
177 print $temp_fh $params{inp_content};
179 $input_names = $inp_name . ' ';
180 $hasodd = $params{bothsided} && __PACKAGE__->has_odd_pages($inp_name);
182 foreach (@{ $params{file_names} }) {
183 $input_names .= $emptypage . ' ' if $hasodd;
184 $input_names .= String::ShellQuote::shell_quote($_) . ' ';
185 $hasodd = $params{bothsided} && __PACKAGE__->has_odd_pages($_);
187 my $exe = $::lx_office_conf{applications}->{ghostscript} || 'gs';
189 `$exe -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=${temp_name} ${input_names} 2>&1`;
191 die "Executing gs failed: $ERRNO" if !defined $output;
192 die $output if $? != 0;
194 if ($params{out_path}) {
195 if (!rename($temp_name, $params{out_path})) {
197 # special filesystem or cross filesystem etc
198 move($temp_name, $params{out_path});
202 return scalar File::Slurp::read_file($temp_name);
206 my ($class, %params) = @_;
208 $params{name} or croak "Missing parameter 'name'";
210 my $path = $::instance_conf->get_templates;
211 my $extension = $params{extension} || "tex";
212 my ($printer, $language) = ('', '');
214 if ($params{printer} || $params{printer_id}) {
215 if ($params{printer} && !ref $params{printer}) {
216 $printer = '_' . $params{printer};
218 $printer = $params{printer} || SL::DB::Printer->new(id => $params{printer_id})->load;
219 $printer = $printer->template_code ? '_' . $printer->template_code : '';
223 if ($params{language} || $params{language_id}) {
224 if ($params{language} && !ref $params{language}) {
225 $language = '_' . $params{language};
227 $language = $params{language} || SL::DB::Language->new(id => $params{language_id})->load;
228 $language = $language->template_code ? '_' . $language->template_code : '';
232 my @template_files = (
233 $params{name} . "${language}${printer}",
234 $params{name} . "${language}",
239 if ($params{email}) {
240 unshift @template_files, (
241 $params{name} . "_email${language}${printer}",
242 $params{name} . "_email${language}",
246 @template_files = map { "${_}.${extension}" } uniq grep { $_ } @template_files;
248 my $template = first { -f ($path . "/$_") } @template_files;
250 return wantarray ? ($template, @template_files) : $template;
262 SL::Helper::CreatePDF - A helper for creating PDFs from template files
266 # Retrieve a sales order from the database and create a PDF for
268 my $order = SL::DB::Order->new(id => …)->load;
269 my $print_form = Form->new('');
270 $print_form->{type} = 'invoice';
271 $print_form->{formname} = 'invoice',
272 $print_form->{format} = 'pdf',
273 $print_form->{media} = 'file';
275 $order->flatten_to_form($print_form, format_amounts => 1);
276 $print_form->prepare_for_printing;
278 my $pdf = SL::Helper::CreatePDF->create_pdf(
279 template => 'sales_order',
280 variables => $print_form,
287 =item C<create_pdf %params>
289 Parses a LaTeX template file, creates a PDF for it and returns either
290 its content or its file name. The recognized parameters are the same
291 as the ones for L</create_parsed_file> with C<format> and
292 C<template_type> being pre-set.
294 =item C<create_parsed_file %params>
296 Parses a template file and returns either its content or its file
297 name. The recognized parameters are:
301 =item * C<template> – mandatory. The template file name relative to
302 the users' templates directory. Must be an existing file name,
303 e.g. one retrieved by L</find_template>.
305 =item * C<variables> – optional hash reference containing variables
306 available to the template.
308 =item * C<return> – optional scalar containing either C<content> (the
309 default) or C<file_name>. If it is set to C<file_name> then the file
310 name of the temporary file containing the PDF is returned, and the
311 caller is responsible for deleting it. Otherwise a scalar containing
312 the PDF itself is returned and all temporary files have already been
313 deleted by L</create_pdf>.
315 =item * C<format> – optional, defaults to C<pdf> and determines the
316 output format. Can be set to C<html> for HTML output if
317 C<template_type> is set to C<HTML> as well.
319 =item * C<template_type> – optional, defaults to C<LaTeX> and
320 determines the template's format. Can be set to C<HTML> for HTML
321 output if C<format> is set to C<html> as well.
325 =item C<find_template %params>
327 Searches the user's templates directory for a template file name to
328 use. The file names considered depend on the parameters; they can
329 contain a template base name and suffixes for email, language and
330 printers. As a fallback the name C<default.$extension> is also
333 The return value depends on the context. In scalar context the
334 template file name that matches the given parameters is returned. It's
335 a file name relative to the user's templates directory. If no template
336 file is found then C<undef> is returned.
338 In list context the first element is the same value as in scalar
339 context. Additionally a list of considered template file names is
342 The recognized parameters are:
346 =item * C<name> – mandatory. The template's file name basis
347 without any additional suffix or extension, e.g. C<sales_quotation>.
349 =item * C<extension> – optional file name extension to use without the
350 dot. Defaults to C<tex>.
352 =item * C<email> – optional flag indicating whether or not the
353 template is to be sent via email. If set to true then template file
354 names containing C<_email> are considered as well.
356 =item * C<language> and C<language_id> – optional parameters
357 indicating the language to be used. C<language> can be either a string
358 containing the language code to use or an instance of
359 C<SL::DB::Language>. C<language_id> can contain the ID of the
360 C<SL::DB:Language> instance to load and use. If given template file
361 names containing C<_language_template_code> are considered as well.
363 =item * C<printer> and C<printer_id> – optional parameters indicating
364 the printer to be used. C<printer> can be either a string containing
365 the printer code to use or an instance of
366 C<SL::DB::Printer>. C<printer_id> can contain the ID of the
367 C<SL::DB:Printer> instance to load and use. If given template file
368 names containing C<_printer_template_code> are considered as well.
372 =item C<merge_pdfs %params>
374 Merges two or more PDFs into a single PDF by using the external
375 application ghostscript.
377 Normally the function returns the contents of the resulting PDF.
378 if The parameter C<out_path> is set the resulting PDF is in this file
379 and the return value is 1 if it successful or 0 if not.
381 The recognized parameters are:
385 =item * C<file_names> – mandatory array reference containing the file
388 =item * C<inp_content> – optional, contents of first file to merge with C<file_names>.
390 =item * C<out_path> – optional, returns not the merged contents but wrote him into this file
394 Note that this function relies on the presence of the external
395 application ghostscript. The executable to use is configured via
396 kivitendo's configuration file setting C<application.ghostscript>.
406 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>