File Coverage

blib/lib/PDF/FromHTML.pm
Criterion Covered Total %
statement 27 30 90.0
branch 0 2 0.0
condition n/a
subroutine 10 11 90.9
pod n/a
total 37 43 86.0


line stmt bran cond sub pod time code
1             package PDF::FromHTML;
2              
3 1     1   918 use 5.006;
  1         4  
  1         44  
4 1     1   7 use strict;
  1         3  
  1         42  
5 1     1   15 use warnings;
  1         2  
  1         81  
6              
7             our $VERSION = '0.31';
8              
9             BEGIN {
10 1     1   2 foreach my $method ( qw( pdf twig tidy args ) ) {
11 1     1   5 no strict 'refs';
  1         2  
  1         81  
12 4 0   0   44 *$method = sub { $#_ ? ($_[0]{$method} = $_[1]) : $_[0]{$method} };
  0         0  
13             }
14             }
15              
16 1     1   6 use Cwd;
  1         1  
  1         92  
17 1     1   2891 use File::Temp;
  1         26837  
  1         107  
18 1     1   10 use File::Basename;
  1         3  
  1         106  
19              
20 1     1   1256 use PDF::Writer;
  1         344  
  1         7  
21 1     1   837 use PDF::FromHTML::Twig;
  0            
  0            
22             use PDF::FromHTML::Template;
23              
24             use constant HAS_UNICODE_SUPPORT => ($] >= 5.008);
25              
26             use constant PDF_WRITER_BACKEND => do {
27             local $@;
28              
29             # For Perl 5.6.x, we have to use pdflib
30             PDF::Writer->import('pdflib')
31             unless HAS_UNICODE_SUPPORT();
32              
33             eval { ref(PDF::Writer->new) }
34             or die( "Please install PDF::API2 (preferred) or pdflib_pl first" );
35             };
36             use constant HAS_HTML_TIDY => do {
37             local $@;
38             eval { require HTML::Tidy; 1 } or do {
39             unless ( eval { require XML::Clean; 1 } ) {
40             die( "Please install HTML::Tidy (preferred) or XML::Clean first" );
41             }
42             0; # Has XML::Clean but no HTML::Tidy
43             };
44             };
45              
46             =head1 NAME
47              
48             PDF::FromHTML - Convert HTML documents to PDF
49              
50             =head1 SYNOPSIS
51              
52             my $pdf = PDF::FromHTML->new( encoding => 'utf-8' );
53              
54             # Loading from a file:
55             $pdf->load_file('source.html');
56              
57             # Or from a scalar reference:
58             # $pdf->load_file(\$input);
59              
60             # Perform the actual conversion:
61             $pdf->convert(
62             # With PDF::API2, font names such as 'traditional' also works
63             Font => 'font.ttf',
64             LineHeight => 10,
65             Landscape => 1,
66             );
67              
68             # Write to a file:
69             $pdf->write_file('target.pdf');
70              
71             # Or to a scalar reference:
72             # $pdf->write_file(\$output);
73              
74             =head1 DESCRIPTION
75              
76             This module transforms HTML into PDF, using an assortment of XML
77             transformations implemented in L.
78              
79             There is also a command-line utility, L, that comes
80             with this distribution.
81              
82             =head1 PUBLIC METHODS
83              
84             =cut
85              
86             sub new {
87             my $class = shift;
88             bless({
89             twig => PDF::FromHTML::Twig->new,
90             args => { @_ },
91             }, $class);
92             }
93              
94             sub load_file {
95             my ($self, $file) = @_;
96             $self->{file} = $file;
97             }
98              
99             sub parse_file {
100             my $self = shift;
101             my $file = $self->{file};
102             my $content = '';
103              
104             my $dir = Cwd::getcwd();
105              
106             if (!ref $file) {
107             open my $fh, '<', $file or die $!;
108             chdir File::Basename::dirname($file);
109             $content = do { local $/; <$fh> };
110             }
111             else {
112             $content = $$file;
113             }
114              
115             my $encoding = ($self->args->{encoding} || 'utf8');
116             if (HAS_UNICODE_SUPPORT() and $self->args) {
117             require Encode;
118             $content = Encode::decode($encoding, $content, Encode::FB_XMLCREF());
119             }
120              
121             $content =~ s{ }{}g;
122             $content =~ s{}{}gs;
123              
124             if (HAS_HTML_TIDY()) {
125             if (HAS_UNICODE_SUPPORT()) {
126             $content = Encode::encode( ascii => $content, Encode::FB_XMLCREF());
127             }
128             $content = HTML::Tidy->new->clean(
129             '',
130             '',
131             $content,
132             );
133             }
134             else {
135             $content =~ s{&#(\d+);}{chr $1}eg;
136             $content =~ s{&#x([\da-fA-F]+);}{chr hex $1}eg;
137             $content = XML::Clean::clean($content, '1.0', { encoding => 'UTF-8' });
138             $content =~ s{<(/?\w+)}{<\L$1}g;
139             }
140              
141             $self->twig->parse( $content );
142              
143             chdir $dir;
144             }
145              
146             =head2 convert(%params)
147              
148             Convert the loaded file to PDF. Valid parameters are:
149              
150             PageWidth 640
151             PageResolution 540
152             FontBold 'HelveticaBold'
153             FontOblique 'HelveticaOblique'
154             FontBoldOblique 'HelveticaBoldOblique'
155             LineHeight 12
156             FontUnicode 'Helvetica'
157             Font (same as FontUnicode)
158             PageSize 'A4'
159             Landscape 0
160              
161             =cut
162              
163             sub convert {
164             my ($self, %args) = @_;
165              
166             {
167             # import arguments into Twig parameters
168             no strict 'refs';
169             ${"PDF::FromHTML::Twig::$_"} = $args{$_} foreach keys %args;
170             }
171              
172             $self->parse_file;
173              
174             my ($fh, $filename) = File::Temp::tempfile(
175             SUFFIX => '.xml',
176             UNLINK => 1,
177             );
178              
179             binmode($fh);
180             if (HAS_UNICODE_SUPPORT()) {
181             binmode($fh, ':utf8');
182             }
183              
184             # use File::Copy;
185             # copy($filename => '/tmp/foo.xml');
186              
187             # XXX HACK! XXX
188             my $text = $self->twig->sprint;
189             $text =~ s{\$(__[A-Z_]+__)}{}g;
190             print $fh $text;
191             close $fh;
192              
193             # print STDERR "==> Temp file written to $filename\n";
194              
195             local $@;
196             local $^W;
197             $self->pdf(eval { PDF::FromHTML::Template->new( filename => $filename ) })
198             or die "$filename: $@";
199             $self->pdf->param(@_);
200             }
201              
202             sub write_file {
203             my $self = shift;
204              
205             local $^W;
206             if (@_ and ref($_[0]) eq 'SCALAR') {
207             ${$_[0]} = $self->pdf->get_buffer;
208             }
209             else {
210             $self->pdf->write_file(@_);
211             }
212             }
213              
214             1;
215              
216             =head1 HINTS & TIPS
217              
218             =head2 EimgE tags
219              
220             Add the height and width attributes if you are creating the source HTML,
221             it keeps PDF::FromHTML from having to open and read the source image file
222             to get the real size. Less file I/O means faster processing.
223              
224             =head1 CAVEATS
225              
226             Although B will work with both HTML and XHTML formats,
227             it is not designed to utilise CSS.
228              
229             This means any HTML using external or inline CSS for design and
230             layout, including but not limited to: images, backgrounds, colours,
231             fonts etc... will not be converted into the PDF.
232              
233             To get an idea of the likely resulting PDF, you may wish to use an
234             non-CSS capable browser for testing first.
235              
236             There is currently no plan to adapt this module to utilise CSS.
237             (Patches welcome, though!)
238              
239             =head1 SEE ALSO
240              
241             L is a simple command-line interface to this module.
242              
243             L, L, L.
244              
245             =head1 CONTRIBUTORS
246              
247             Charleston Software Associates Einfo@charletonsw.comE
248              
249             =head1 AUTHORS
250              
251             唐鳳 Ecpan@audreyt.orgE
252              
253             =head1 CC0 1.0 Universal
254              
255             To the extent possible under law, 唐鳳 has waived all copyright and related
256             or neighboring rights to PDF-FromHTML.
257              
258             This work is published from Taiwan.
259              
260             L
261              
262             =cut