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 ();
21 use SL::Template::LaTeX;
23 use Exporter 'import';
24 our @EXPORT_OK = qw(create_pdf merge_pdfs find_template);
30 my ($class, %params) = @_;
32 return __PACKAGE__->create_parsed_file(
34 template_type => 'LaTeX',
39 sub create_parsed_file {
40 my ($class, %params) = @_;
42 my $userspath = $::lx_office_conf{paths}->{userspath};
43 my $vars = $params{variables} || {};
44 my $form = Form->new('');
45 $form->{$_} = $vars->{$_} for keys %{$vars};
46 $form->{format} = lc($params{format} || 'pdf');
47 $form->{cwd} = getcwd();
48 $form->{templates} = $::instance_conf->get_templates;
49 $form->{IN} = $params{template};
50 $form->{tmpdir} = $form->{cwd} . '/' . $userspath;
51 my $tmpdir = $form->{tmpdir};
52 my ($suffix) = $params{template} =~ m{\.(.+)};
54 my ($temp_fh, $tmpfile) = File::Temp::tempfile(
55 'kivitendo-printXXXXXX',
56 SUFFIX => ".${suffix}",
57 DIR => $form->{tmpdir},
58 UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
61 $form->{tmpfile} = $tmpfile;
62 (undef, undef, $form->{template_meta}{tmpfile}) = File::Spec->splitpath($tmpfile);
64 my $parser = SL::Template::create(
65 type => ($params{template_type} || 'LaTeX'),
66 source => $form->{IN},
68 myconfig => \%::myconfig,
70 variable_content_types => $params{variable_content_types},
73 my $result = $parser->parse($temp_fh);
80 die $parser->get_error;
83 # SL::Template:** modify $form->{tmpfile} by removing its
84 # $form->{userspath} prefix. They also store the final file's actual
85 # file name in $form->{tmpfile} – but it is now relative to
86 # $form->{userspath}. Other modules return the full file name…
87 my ($volume, $directory, $file_name) = File::Spec->splitpath($form->{tmpfile});
88 my $full_file_name = File::Spec->catfile($tmpdir, $file_name);
89 if (($params{return} || 'content') eq 'file_name') {
90 my $new_name = File::Spec->catfile($tmpdir, 'keep-' . $form->{tmpfile});
91 rename $full_file_name, $new_name;
98 my $content = File::Slurp::read_file($full_file_name);
106 # Alternativen zu pdfinfo wären (aber wesentlich langamer):
108 # gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=/dev/null $filename | grep 'Processing pages'
109 # my (undef,undef,undef,undef,$pages) = split / +/,$shell_out;
111 # gs -dBATCH -dNOPAUSE -q -dNODISPLAY -c "($filename) (r) file runpdfbegin pdfpagecount = quit"
116 my ($class, $filename) = @_;
117 return 0 unless -f $filename;
118 my $shell_out = `pdfinfo $filename | grep 'Pages:'`;
119 my ($label, $pages) = split / +/, $shell_out;
124 my ($class, %params) = @_;
125 my $filecount = scalar(@{ $params{file_names} });
127 if ($params{inp_content}) {
128 return $params{inp_content} if $filecount == 0 && !$params{out_path};
129 } elsif ($params{out_path}) {
130 return 0 if $filecount == 0;
131 if ($filecount == 1) {
132 if (!rename($params{file_names}->[0], $params{out_path})) {
133 # special filesystem or cross filesystem etc
134 move($params{file_names}->[0], $params{out_path});
139 return '' if $filecount == 0;
140 return scalar(File::Slurp::read_file($params{file_names}->[0])) if $filecount == 1;
143 my ($temp_fh, $temp_name) = File::Temp::tempfile(
144 'kivitendo-printXXXXXX',
146 DIR => $::lx_office_conf{paths}->{userspath},
147 UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
151 my $input_names = '';
154 if ($params{bothsided}) {
155 $emptypage = $::instance_conf->get_templates . '/emptyPage.pdf';
156 unless (-f $emptypage) {
158 delete $params{bothsided};
161 if ($params{inp_content}) {
162 my ($temp_fh, $inp_name) = File::Temp::tempfile(
163 'kivitendo-contentXXXXXX',
165 DIR => $::lx_office_conf{paths}->{userspath},
166 UNLINK => ($::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files})? 0 : 1,
169 print $temp_fh $params{inp_content};
171 $input_names = $inp_name . ' ';
172 $hasodd = $params{bothsided} && __PACKAGE__->has_odd_pages($inp_name);
174 foreach (@{ $params{file_names} }) {
175 $input_names .= $emptypage . ' ' if $hasodd;
176 $input_names .= String::ShellQuote::shell_quote($_) . ' ';
177 $hasodd = $params{bothsided} && __PACKAGE__->has_odd_pages($_);
179 my $exe = $::lx_office_conf{applications}->{ghostscript} || 'gs';
181 `$exe -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=${temp_name} ${input_names} 2>&1`;
183 die "Executing gs failed: $ERRNO" if !defined $output;
184 die $output if $? != 0;
186 if ($params{out_path}) {
187 if (!rename($temp_name, $params{out_path})) {
189 # special filesystem or cross filesystem etc
190 move($temp_name, $params{out_path});
194 return scalar File::Slurp::read_file($temp_name);
198 my ($class, %params) = @_;
200 $params{name} or croak "Missing parameter 'name'";
202 my $path = $::instance_conf->get_templates;
203 my $extension = $params{extension} || "tex";
204 my ($printer, $language) = ('', '');
206 if ($params{printer} || $params{printer_id}) {
207 if ($params{printer} && !ref $params{printer}) {
208 $printer = '_' . $params{printer};
210 $printer = $params{printer} || SL::DB::Printer->new(id => $params{printer_id})->load;
211 $printer = $printer->template_code ? '_' . $printer->template_code : '';
215 if ($params{language} || $params{language_id}) {
216 if ($params{language} && !ref $params{language}) {
217 $language = '_' . $params{language};
219 $language = $params{language} || SL::DB::Language->new(id => $params{language_id})->load;
220 $language = $language->template_code ? '_' . $language->template_code : '';
224 my @template_files = (
225 $params{name} . "${language}${printer}",
226 $params{name} . "${language}",
231 if ($params{email}) {
232 unshift @template_files, (
233 $params{name} . "_email${language}${printer}",
234 $params{name} . "_email${language}",
238 @template_files = map { "${_}.${extension}" } uniq grep { $_ } @template_files;
240 my $template = first { -f ($path . "/$_") } @template_files;
242 return wantarray ? ($template, @template_files) : $template;
254 SL::Helper::CreatePDF - A helper for creating PDFs from template files
258 # Retrieve a sales order from the database and create a PDF for
260 my $order = SL::DB::Order->new(id => …)->load;
261 my $print_form = Form->new('');
262 $print_form->{type} = 'invoice';
263 $print_form->{formname} = 'invoice',
264 $print_form->{format} = 'pdf',
265 $print_form->{media} = 'file';
267 $order->flatten_to_form($print_form, format_amounts => 1);
268 $print_form->prepare_for_printing;
270 my $pdf = SL::Helper::CreatePDF->create_pdf(
271 template => 'sales_order',
272 variables => $print_form,
279 =item C<create_pdf %params>
281 Parses a LaTeX template file, creates a PDF for it and returns either
282 its content or its file name. The recognized parameters are the same
283 as the ones for L</create_parsed_file> with C<format> and
284 C<template_type> being pre-set.
286 =item C<create_parsed_file %params>
288 Parses a template file and returns either its content or its file
289 name. The recognized parameters are:
293 =item * C<template> – mandatory. The template file name relative to
294 the users' templates directory. Must be an existing file name,
295 e.g. one retrieved by L</find_template>.
297 =item * C<variables> – optional hash reference containing variables
298 available to the template.
300 =item * C<return> – optional scalar containing either C<content> (the
301 default) or C<file_name>. If it is set to C<file_name> then the file
302 name of the temporary file containing the PDF is returned, and the
303 caller is responsible for deleting it. Otherwise a scalar containing
304 the PDF itself is returned and all temporary files have already been
305 deleted by L</create_pdf>.
307 =item * C<format> – optional, defaults to C<pdf> and determines the
308 output format. Can be set to C<html> for HTML output if
309 C<template_type> is set to C<HTML> as well.
311 =item * C<template_type> – optional, defaults to C<LaTeX> and
312 determines the template's format. Can be set to C<HTML> for HTML
313 output if C<format> is set to C<html> as well.
317 =item C<find_template %params>
319 Searches the user's templates directory for a template file name to
320 use. The file names considered depend on the parameters; they can
321 contain a template base name and suffixes for email, language and
322 printers. As a fallback the name C<default.$extension> is also
325 The return value depends on the context. In scalar context the
326 template file name that matches the given parameters is returned. It's
327 a file name relative to the user's templates directory. If no template
328 file is found then C<undef> is returned.
330 In list context the first element is the same value as in scalar
331 context. Additionally a list of considered template file names is
334 The recognized parameters are:
338 =item * C<name> – mandatory. The template's file name basis
339 without any additional suffix or extension, e.g. C<sales_quotation>.
341 =item * C<extension> – optional file name extension to use without the
342 dot. Defaults to C<tex>.
344 =item * C<email> – optional flag indicating whether or not the
345 template is to be sent via email. If set to true then template file
346 names containing C<_email> are considered as well.
348 =item * C<language> and C<language_id> – optional parameters
349 indicating the language to be used. C<language> can be either a string
350 containing the language code to use or an instance of
351 C<SL::DB::Language>. C<language_id> can contain the ID of the
352 C<SL::DB:Language> instance to load and use. If given template file
353 names containing C<_language_template_code> are considered as well.
355 =item * C<printer> and C<printer_id> – optional parameters indicating
356 the printer to be used. C<printer> can be either a string containing
357 the printer code to use or an instance of
358 C<SL::DB::Printer>. C<printer_id> can contain the ID of the
359 C<SL::DB:Printer> instance to load and use. If given template file
360 names containing C<_printer_template_code> are considered as well.
364 =item C<merge_pdfs %params>
366 Merges two or more PDFs into a single PDF by using the external
367 application ghostscript.
369 Normally the function returns the contents of the resulting PDF.
370 if The parameter C<out_path> is set the resulting PDF is in this file
371 and the return value is 1 if it successful or 0 if not.
373 The recognized parameters are:
377 =item * C<file_names> – mandatory array reference containing the file
380 =item * C<inp_content> – optional, contents of first file to merge with C<file_names>.
382 =item * C<out_path> – optional, returns not the merged contents but wrote him into this file
386 Note that this function relies on the presence of the external
387 application ghostscript. The executable to use is configured via
388 kivitendo's configuration file setting C<application.ghostscript>.
398 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>