File Coverage

blib/lib/PDF/Builder/Resource/UniFont.pm
Criterion Covered Total %
statement 80 101 79.2
branch 23 44 52.2
condition 7 15 46.6
subroutine 9 9 100.0
pod 1 5 20.0
total 120 174 68.9


line stmt bran cond sub pod time code
1             package PDF::Builder::Resource::UniFont;
2              
3 2     2   1649 use strict;
  2         6  
  2         63  
4 2     2   11 use warnings;
  2         5  
  2         116  
5              
6             our $VERSION = '3.024'; # VERSION
7             our $LAST_UPDATE = '3.024'; # manually update whenever code is changed
8              
9 2     2   10 use Carp;
  2         4  
  2         170  
10 2     2   12 use Encode qw(:all);
  2         3  
  2         2465  
11              
12             =head1 NAME
13              
14             PDF::Builder::Resource::UniFont - Unicode Font Support
15              
16             =head1 METHODS
17              
18             =over
19              
20             =item $font = PDF::Builder::Resource::UniFont->new($pdf, @fontspecs, %options)
21              
22             Returns a uni-font object.
23              
24             =cut
25              
26             =pod
27              
28             B fonts can be registered using the following hash-ref:
29              
30             {
31             font => $fontobj, # the font to be registered
32             blocks => $blockspec, # the unicode blocks the font is being registered for
33             codes => $codespec, # the unicode codepoints, -"-
34             }
35              
36             B
37              
38             [
39             $block1, $block3, # register font for block 1 + 3
40             [$blockA, $blockZ], # register font for blocks A .. Z
41             ]
42              
43             B
44              
45             [
46             $cp1, $cp3, # register font for codepoint 1 + 3
47             [$cpA, $cpZ], # register font for codepoints A .. Z
48             ]
49              
50             B if you want to register a font for the entire unicode space
51             (ie. U+0000 .. U+FFFF), then simply specify a font-object without the hash-ref.
52              
53             Valid %options are:
54              
55             'encode' ... changes the encoding of the font from its default.
56             (see "perldoc Encode" for a list of valid tags)
57              
58             =cut
59              
60             sub new {
61 1     1 1 3 my $class = shift();
62 1 50       4 $class = ref($class) if ref($class);
63              
64 1         4 my $self = {
65             'fonts' => [],
66             'block' => {},
67             'code' => {},
68             'pdf' => shift(),
69             };
70 1         4 bless $self, $class;
71              
72 1         2 my @fonts;
73 1         6 push @fonts, shift() while ref($_[0]);
74              
75 1         4 my %options = @_;
76             # copy dashed option names to preferred undashed names
77 1 50 33     6 if (defined $options{'-encode'} && !defined $options{'encode'}) { $options{'encode'} = delete($options{'-encode'}); }
  0         0  
78              
79 1 50       12 $self->{'encode'} = $options{'encode'} if defined $options{'encode'};
80             # note that self->encode is undefined if encode not given!
81              
82 1         2 my $font_number = 0;
83 1         2 foreach my $font (@fonts) {
84 2 100       8 if (ref($font) eq 'ARRAY') {
    50          
85 1         1 push @{$self->{'fonts'}}, shift(@$font);
  1         3  
86            
87 1         4 while (defined $font->[0]) {
88 1         2 my $blockspec = shift @$font;
89 1 50       4 if (ref($blockspec)) {
90 1         2 foreach my $block ($blockspec->[0] .. $blockspec->[-1]) {
91 1         4 $self->{'block'}->{$block} = $font_number;
92             }
93             } else {
94 0         0 $self->{'block'}->{$blockspec} = $font_number;
95             }
96             }
97             } elsif (ref($font) eq 'HASH') {
98 0         0 push @{$self->{'fonts'}}, $font->{'font'};
  0         0  
99              
100 0 0 0     0 if (defined $font->{'blocks'} and
101             ref($font->{'blocks'}) eq 'ARRAY') {
102 0         0 foreach my $blockspec (@{$font->{'blocks'}}) {
  0         0  
103 0 0       0 if (ref($blockspec)) {
104 0         0 foreach my $block ($blockspec->[0] .. $blockspec->[-1]) {
105 0         0 $self->{'block'}->{$block} = $font_number;
106             }
107             } else {
108 0         0 $self->{'block'}->{$blockspec} = $font_number;
109             }
110             }
111             }
112              
113 0 0 0     0 if (defined $font->{'codes'} and
114             ref($font->{'codes'}) eq 'ARRAY') {
115 0         0 foreach my $codespec (@{$font->{'codes'}}) {
  0         0  
116 0 0       0 if (ref($codespec)) {
117 0         0 foreach my $code ($codespec->[0] .. $codespec->[-1]) {
118 0         0 $self->{'code'}->{$code} = $font_number;
119             }
120             } else {
121 0         0 $self->{'code'}->{$codespec} = $font_number;
122             }
123             }
124             }
125             } else {
126 1         2 push @{$self->{'fonts'}}, $font;
  1         3  
127 1         4 foreach my $block (0 .. 255) {
128 256         412 $self->{'block'}->{$block} = $font_number;
129             }
130             }
131 2         4 $font_number++;
132             }
133              
134 1         4 return $self;
135             }
136              
137             sub isvirtual {
138 2     2 0 5 return 1;
139             }
140              
141             sub fontlist {
142 11     11 0 14 my $self = shift;
143              
144 11         17 return [@{ $self->{'fonts'} }];
  11         44  
145             }
146              
147             sub width {
148 4     4 0 15 my ($self, $text) = @_;
149              
150 4 50       10 if (defined $self->{'encode'}) { # is self->encode guaranteed set?
151 4 100       13 $text = decode($self->{'encode'}, $text) unless utf8::is_utf8($text);
152             }
153 4         281 my $width = 0;
154 4         7 my @blocks = ();
155              
156 4         17 foreach my $u (unpack('U*', $text)) {
157 18         23 my $font_number = 0;
158 18 50       45 if (defined $self->{'code'}->{$u}) {
    50          
159 0         0 $font_number = $self->{'code'}->{$u};
160             } elsif (defined $self->{'block'}->{($u >> 8)}) {
161 18         23 $font_number = $self->{'block'}->{($u >> 8)};
162             } else {
163 0         0 $font_number = 0;
164             }
165 18 100 100     44 if (scalar @blocks == 0 or $blocks[-1]->[0] != $font_number) {
166 6         21 push @blocks, [$font_number, pack('U', $u)];
167             } else {
168 12         25 $blocks[-1]->[1] .= pack('U', $u);
169             }
170             }
171 4         9 foreach my $block (@blocks) {
172 6         11 my ($font_number, $string) = @$block;
173 6         12 $width += $self->fontlist()->[$font_number]->width($string);
174             }
175              
176 4         11 return $width;
177             }
178              
179             sub text {
180 2     2 0 5 my ($self, $text, $size, $indent) = @_;
181              
182 2 50       6 if (defined $self->{'encode'}) { # is self->encode guaranteed to be defined?
183 2 100       7 $text = decode($self->{'encode'}, $text) unless utf8::is_utf8($text);
184             }
185 2 50       46 croak 'Font size not specified' unless defined $size;
186              
187 2         3 my $newtext = '';
188 2         3 my $last_font_number;
189             my @codes;
190              
191 2         7 foreach my $u (unpack('U*', $text)) {
192 9         10 my $font_number = 0;
193 9 50       26 if (defined $self->{'code'}->{$u}) {
    50          
194 0         0 $font_number = $self->{'code'}->{$u};
195             } elsif (defined $self->{'block'}->{($u >> 8)}) {
196 9         11 $font_number = $self->{'block'}->{($u >> 8)};
197             }
198              
199 9 100 100     24 if (defined $last_font_number and
200             $font_number != $last_font_number) {
201 1         5 my $font = $self->fontlist()->[$last_font_number];
202 1         5 $newtext .= '/' . $font->name() . ' ' . $size. ' Tf ';
203 1         6 $newtext .= $font->text(pack('U*', @codes), $size, $indent) . ' ';
204 1         3 $indent = undef;
205 1         2 @codes = ();
206             }
207              
208 9         15 push @codes, $u;
209 9         12 $last_font_number = $font_number;
210             }
211              
212 2 50       5 if (scalar @codes > 0) {
213 2         5 my $font = $self->fontlist()->[$last_font_number];
214 2         6 $newtext .= '/' . $font->name() . ' ' . $size . ' Tf ';
215 2         24 $newtext .= $font->text(pack('U*', @codes), $size, $indent);
216             }
217              
218 2         9 return $newtext;
219             }
220              
221             =back
222              
223             =cut
224              
225             1;