Bug 8375: (follow-up) adjust StrWidth to account for TTF fonts
[koha.git] / C4 / Creators / PDF.pm
1 package C4::Creators::PDF;
2
3 # Copyright 2009 Foundations Bible College.
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 use strict;
21 use warnings;
22 use PDF::Reuse;
23 use PDF::Reuse::Barcode;
24 use File::Temp;
25 use List::Util qw/first/;
26
27 BEGIN {
28     use version; our $VERSION = qv('3.07.00.049');
29 }
30
31 sub _InitVars {
32     my $self = shift;
33     my $param = shift;
34     prInitVars($param);
35 }
36
37 sub new {
38     my $invocant = shift;
39     my $type = ref($invocant) || $invocant;
40     my %opts = @_;
41     my $self = {};
42     _InitVars() if ($opts{InitVars} == 0);
43     _InitVars($opts{InitVars}) if ($opts{InitVars} > 0);
44     delete($opts{InitVars});
45     prDocDir($opts{'DocDir'}) if $opts{'DocDir'};
46     delete($opts{'DocDir'});
47
48     my $fh = File::Temp->new( UNLINK => 0, SUFFIX => '.pdf' );
49     $opts{Name} = $self->{filename} = "$fh"; # filename
50     close $fh; # we need just filename
51
52     prFile(\%opts);
53     bless ($self, $type);
54     return $self;
55 }
56
57 sub End {
58     my $self = shift;
59
60     prEnd();
61
62     # slurp temporary filename and print it out for plack to pick up
63     local $/ = undef;
64     open(my $fh, '<', $self->{filename}) || die "$self->{filename}: $!";
65     print <$fh>;
66     close $fh;
67     unlink $self->{filename};
68 }
69
70 sub Add {
71     my $self = shift;
72     my $string = shift;
73     prAdd($string);
74 }
75
76 sub Bookmark {
77     my $self = shift;
78     my $reference = shift;
79     prBookmark($reference);
80 }
81
82 sub Compress {
83     my $self = shift;
84     my $directive = shift;
85     prCompress($directive);
86 }
87
88 sub Doc {
89     my $self = shift;
90     my %params = @_;
91     prDoc(%params);
92 }
93
94 sub DocForm {
95     my $self = shift;
96     my %params = @_;
97     return prDocForm(%params);
98 }
99
100 sub Extract {
101     my $self = shift;
102     my ($pdfFile, $pageNo, $oldInternalName) = @_;
103     return prExtract($pdfFile, $pageNo, $oldInternalName);
104 }
105
106 sub Field {
107     my $self = shift;
108     my ($fieldName, $value) = @_;
109     prField($fieldName, $value);
110 }
111
112 sub Font {
113     my $self = shift;
114     my $fontName = shift;
115
116     my $ttf = C4::Context->config('ttf');
117
118     if ( $ttf ) {
119         my $ttf_path = first { $_->{type} eq $fontName } @{ $ttf->{font} };
120         if ( -e $ttf_path->{content} ) {
121             return prTTFont($ttf_path->{content});
122         } else {
123             warn "ERROR in koha-conf.xml -- missing <font type=\"$fontName\">/path/to/font.ttf</font>";
124         }
125     }
126     return prFont($fontName);
127 }
128
129 sub FontSize {
130     my $self = shift;
131     my $size = shift;
132     return prFontSize($size);
133 }
134
135 sub Form {
136     my $self = shift;
137     my %params = @_;
138     return prForm(%params);
139 }
140
141 sub GetLogBuffer {
142     my $self = shift;
143     return prGetLogBuffer();
144 }
145
146 sub GraphState {
147     my $self = shift;
148     my $string = shift;
149     prGraphState($string);
150 }
151
152 sub Image {
153     my $self = shift;
154     my %params = @_;
155     return prImage(%params);
156 }
157
158 sub Init {
159     my $self = shift;
160     my ($string, $duplicateCode) = @_;
161     prInit($string, $duplicateCode);
162 }
163
164 sub AltJpeg {
165     my $self = shift;
166     my ($imageData, $width, $height, $imageFormat, $altImageData, $altImageWidth, $altImageHeight, $altImageFormat) = @_;
167     return prAltJpeg($imageData, $width, $height, $imageFormat, $altImageData, $altImageWidth, $altImageHeight, $altImageFormat);
168 }
169
170 sub Jpeg {
171     my $self = shift;
172     my ($imageData, $width, $height, $imageFormat) = @_;
173     return prJpegBlob($imageData, $width, $height, $imageFormat);
174 }
175
176 # FIXME: This magick foo is an absolute hack until the maintainer of PDF::Reuse releases the next version which will include these features
177
178 sub prAltJpeg
179 {  my ($iData, $iWidth, $iHeight, $iFormat,$aiData, $aiWidth, $aiHeight, $aiFormat) = @_;
180    my ($namnet, $utrad);
181    if (! $PDF::Reuse::pos)                    # If no output is active, it is no use to continue
182    {   return;
183    }
184    prJpegBlob($aiData, $aiWidth, $aiHeight, $aiFormat);
185    my $altObjNr = $PDF::Reuse::objNr;
186    $PDF::Reuse::imageNr++;
187    $namnet = 'Ig' . $PDF::Reuse::imageNr;
188    $PDF::Reuse::objNr++;
189    $PDF::Reuse::objekt[$PDF::Reuse::objNr] = $PDF::Reuse::pos;
190    $utrad = "$PDF::Reuse::objNr 0 obj\n" .
191             "[ << /Image $altObjNr 0 R\n" .
192             "/DefaultForPrinting true\n" .
193             ">>\n" .
194             "]\n" .
195             "endobj\n";
196    $PDF::Reuse::pos += syswrite *PDF::Reuse::UTFIL, $utrad;
197    if ($PDF::Reuse::runfil)
198    {  $PDF::Reuse::log .= "Jpeg~AltImage\n";
199    }
200    $PDF::Reuse::objRef{$namnet} = $PDF::Reuse::objNr;
201    $namnet = prJpegBlob($iData, $iWidth, $iHeight, $iFormat, $PDF::Reuse::objNr);
202    if (! $PDF::Reuse::pos)
203    {  errLog("No output file, you have to call prFile first");
204    }
205    return $namnet;
206 }
207
208 sub prJpegBlob
209 {  my ($iData, $iWidth, $iHeight, $iFormat, $altArrayObjNr) = @_;
210    my ($iLangd, $namnet, $utrad);
211    if (! $PDF::Reuse::pos)                    # If no output is active, it is no use to continue
212    {   return;
213    }
214    my $checkidOld = $PDF::Reuse::checkId;
215    if (!$iFormat)
216    {   my ($iFile, $checkId) = findGet($iData, $checkidOld);
217        if ($iFile)
218        {  $iLangd = (stat($iFile))[7];
219           $PDF::Reuse::imageNr++;
220           $namnet = 'Ig' . $PDF::Reuse::imageNr;
221           $PDF::Reuse::objNr++;
222           $PDF::Reuse::objekt[$PDF::Reuse::objNr] = $PDF::Reuse::pos;
223           open (my $fh, '<', "$iFile") || errLog("Couldn't open $iFile, $!, aborts");
224           binmode $fh;
225           my $iStream;
226           sysread $fh, $iStream, $iLangd;
227           $utrad = "$PDF::Reuse::objNr 0 obj\n<</Type/XObject/Subtype/Image/Name/$namnet" .
228                     "/Width $iWidth /Height $iHeight /BitsPerComponent 8 " .
229                     ($altArrayObjNr ? "/Alternates $altArrayObjNr 0 R " : "") .
230                     "/Filter/DCTDecode/ColorSpace/DeviceRGB"
231                     . "/Length $iLangd >>stream\n$iStream\nendstream\nendobj\n";
232           close $fh;
233           $PDF::Reuse::pos += syswrite $PDF::Reuse::UTFIL, $utrad;
234           if ($PDF::Reuse::runfil)
235           {  $PDF::Reuse::log .= "Cid~$PDF::Reuse::checkId\n";
236              $PDF::Reuse::log .= "Jpeg~$iFile~$iWidth~$iHeight\n";
237           }
238           $PDF::Reuse::objRef{$namnet} = $PDF::Reuse::objNr;
239        }
240        undef $checkId;
241    }
242    elsif ($iFormat == 1)
243    {  my $iBlob = $iData;
244       $iLangd = length($iBlob);
245       $PDF::Reuse::imageNr++;
246       $namnet = 'Ig' . $PDF::Reuse::imageNr;
247       $PDF::Reuse::objNr++;
248       $PDF::Reuse::objekt[$PDF::Reuse::objNr] = $PDF::Reuse::pos;
249       $utrad = "$PDF::Reuse::objNr 0 obj\n<</Type/XObject/Subtype/Image/Name/$namnet" .
250                 "/Width $iWidth /Height $iHeight /BitsPerComponent 8 " .
251                 ($altArrayObjNr ? "/Alternates $altArrayObjNr 0 R " : "") .
252                 "/Filter/DCTDecode/ColorSpace/DeviceRGB"
253                 . "/Length $iLangd >>stream\n$iBlob\nendstream\nendobj\n";
254       $PDF::Reuse::pos += syswrite *PDF::Reuse::UTFIL, $utrad;
255       if ($PDF::Reuse::runfil)
256       {  $PDF::Reuse::log .= "Jpeg~Blob~$iWidth~$iHeight\n";
257       }
258       $PDF::Reuse::objRef{$namnet} = $PDF::Reuse::objNr;
259    }
260    if (! $PDF::Reuse::pos)
261    {  errLog("No output file, you have to call prFile first");
262    }
263    return $namnet;
264 }
265
266 sub Js {
267     my $self = shift;
268     my $string_or_fileName = shift;
269     prJs($string_or_fileName);
270 }
271
272 sub Link {
273     my $self = shift;
274     my %params = @_;
275     prLink(%params);
276 }
277
278 sub Log {
279     my $self = shift;
280     my $string = shift;
281     prLog($string);
282 }
283
284 sub LogDir {
285     my $self = shift;
286     my $directory = shift;
287     prLogDir($directory);
288 }
289
290 sub Mbox {
291     my $self = shift;
292     my ($lowerLeftX, $lowerLeftY, $upperRightX, $upperRightY) = @_;
293     prMbox($lowerLeftX, $lowerLeftY, $upperRightX, $upperRightY);
294 }
295
296 sub Page {
297     my $self = shift;
298     my $noLog = shift;
299     prPage($noLog);
300 }
301
302 sub SinglePage {
303     my $self = shift;
304     my ($file, $pageNumber) = @_;
305     return prSinglePage($file, $pageNumber);
306 }
307
308 sub StrWidth {
309     my $self = shift;
310     my ($string, $font, $fontSize) = @_;
311
312     # replace font code with path to TTF font file if need be
313     my $ttf = C4::Context->config('ttf');
314     if ( $ttf ) {
315         my $ttf_path = first { $_->{type} eq $font } @{ $ttf->{font} };
316         if ( -e $ttf_path->{content} ) {
317             $font = $ttf_path->{content};
318         } else {
319             warn "ERROR in koha-conf.xml -- missing <font type=\"$font\">/path/to/font.ttf</font>";
320         }
321     }
322
323     return prStrWidth($string, $font, $fontSize);
324 }
325
326 sub Text {
327     my $self = shift;
328     my ($x, $y, $string, $align, $rotation) = @_;
329     return prText($x, $y, $string, $align, $rotation);
330 }
331
332 sub TTFont {
333     my $self = shift;
334     my $path = shift;
335     return prTTFont($path);
336 }
337
338 sub Code128 {
339     my $self = shift;
340     my %opts = @_;
341     PDF::Reuse::Barcode::Code128(%opts);
342 }
343
344 sub Code39 {
345     my $self = shift;
346     my %opts = @_;
347     PDF::Reuse::Barcode::Code39(%opts);
348 }
349
350 sub COOP2of5 {
351     my $self = shift;
352     my %opts = @_;
353     PDF::Reuse::Barcode::COOP2of5(%opts);
354 }
355
356 sub EAN13 {
357     my $self = shift;
358     my %opts = @_;
359     PDF::Reuse::Barcode::EAN13(%opts);
360 }
361
362 sub EAN8 {
363     my $self = shift;
364     my %opts = @_;
365     PDF::Reuse::Barcode::EAN8(%opts);
366 }
367
368 sub IATA2of5 {
369     my $self = shift;
370     my %opts = @_;
371     PDF::Reuse::Barcode::IATA2of5(%opts);
372 }
373
374 sub Industrial2of5 {
375     my $self = shift;
376     my %opts = @_;
377     PDF::Reuse::Barcode::Industrial2of5(%opts);
378 }
379
380 sub ITF {
381     my $self = shift;
382     my %opts = @_;
383     PDF::Reuse::Barcode::ITF(%opts);
384 }
385
386 sub Matrix2of5 {
387     my $self = shift;
388     my %opts = @_;
389     PDF::Reuse::Barcode::Matrix2of5(%opts);
390 }
391
392 sub NW7 {
393     my $self = shift;
394     my %opts = @_;
395     PDF::Reuse::Barcode::NW7(%opts);
396 }
397
398 sub UPCA {
399     my $self = shift;
400     my %opts = @_;
401     PDF::Reuse::Barcode::UPCA(%opts);
402 }
403
404 sub UPCE {
405     my $self = shift;
406     my %opts = @_;
407     PDF::Reuse::Barcode::UPCE(%opts);
408 }
409
410 1;
411 __END__
412
413
414 =head1 NAME
415
416 C4::Creators::PDF -   A class wrapper for PDF::Reuse and PDF::Reuse::Barcode to allow usage as a psuedo-object. For usage see
417                     PDF::Reuse documentation and C4::Creators::PDF code.
418
419 =cut
420
421 =head1 AUTHOR
422
423 Chris Nighswonger <cnighswonger AT foundations DOT edu>
424
425 =cut