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   2022 use strict;
  2         6  
  2         77  
4 2     2   12 use warnings;
  2         5  
  2         152  
5              
6             our $VERSION = '3.025'; # VERSION
7             our $LAST_UPDATE = '3.024'; # manually update whenever code is changed
8              
9 2     2   15 use Carp;
  2         4  
  2         216  
10 2     2   12 use Encode qw(:all);
  2         7  
  2         2999  
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 2 my $class = shift();
62 1 50       5 $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         7 push @fonts, shift() while ref($_[0]);
74              
75 1         5 my %options = @_;
76             # copy dashed option names to preferred undashed names
77 1 50 33     4 if (defined $options{'-encode'} && !defined $options{'encode'}) { $options{'encode'} = delete($options{'-encode'}); }
  0         0  
78              
79 1 50       11 $self->{'encode'} = $options{'encode'} if defined $options{'encode'};
80             # note that self->encode is undefined if encode not given!
81              
82 1         3 my $font_number = 0;
83 1         3 foreach my $font (@fonts) {
84 2 100       20 if (ref($font) eq 'ARRAY') {
    50          
85 1         2 push @{$self->{'fonts'}}, shift(@$font);
  1         2  
86            
87 1         7 while (defined $font->[0]) {
88 1         3 my $blockspec = shift @$font;
89 1 50       4 if (ref($blockspec)) {
90 1         3 foreach my $block ($blockspec->[0] .. $blockspec->[-1]) {
91 1         6 $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         3 push @{$self->{'fonts'}}, $font;
  1         4  
127 1         4 foreach my $block (0 .. 255) {
128 256         466 $self->{'block'}->{$block} = $font_number;
129             }
130             }
131 2         7 $font_number++;
132             }
133              
134 1         5 return $self;
135             }
136              
137             sub isvirtual {
138 2     2 0 8 return 1;
139             }
140              
141             sub fontlist {
142 11     11 0 19 my $self = shift;
143              
144 11         14 return [@{ $self->{'fonts'} }];
  11         61  
145             }
146              
147             sub width {
148 4     4 0 10 my ($self, $text) = @_;
149              
150 4 50       9 if (defined $self->{'encode'}) { # is self->encode guaranteed set?
151 4 100       17 $text = decode($self->{'encode'}, $text) unless utf8::is_utf8($text);
152             }
153 4         304 my $width = 0;
154 4         8 my @blocks = ();
155              
156 4         21 foreach my $u (unpack('U*', $text)) {
157 18         27 my $font_number = 0;
158 18 50       52 if (defined $self->{'code'}->{$u}) {
    50          
159 0         0 $font_number = $self->{'code'}->{$u};
160             } elsif (defined $self->{'block'}->{($u >> 8)}) {
161 18         31 $font_number = $self->{'block'}->{($u >> 8)};
162             } else {
163 0         0 $font_number = 0;
164             }
165 18 100 100     53 if (scalar @blocks == 0 or $blocks[-1]->[0] != $font_number) {
166 6         26 push @blocks, [$font_number, pack('U', $u)];
167             } else {
168 12         34 $blocks[-1]->[1] .= pack('U', $u);
169             }
170             }
171 4         9 foreach my $block (@blocks) {
172 6         12 my ($font_number, $string) = @$block;
173 6         13 $width += $self->fontlist()->[$font_number]->width($string);
174             }
175              
176 4         13 return $width;
177             }
178              
179             sub text {
180 2     2 0 14 my ($self, $text, $size, $indent) = @_;
181              
182 2 50       6 if (defined $self->{'encode'}) { # is self->encode guaranteed to be defined?
183 2 100       8 $text = decode($self->{'encode'}, $text) unless utf8::is_utf8($text);
184             }
185 2 50       54 croak 'Font size not specified' unless defined $size;
186              
187 2         4 my $newtext = '';
188 2         4 my $last_font_number;
189             my @codes;
190              
191 2         6 foreach my $u (unpack('U*', $text)) {
192 9         23 my $font_number = 0;
193 9 50       29 if (defined $self->{'code'}->{$u}) {
    50          
194 0         0 $font_number = $self->{'code'}->{$u};
195             } elsif (defined $self->{'block'}->{($u >> 8)}) {
196 9         17 $font_number = $self->{'block'}->{($u >> 8)};
197             }
198              
199 9 100 100     31 if (defined $last_font_number and
200             $font_number != $last_font_number) {
201 1         8 my $font = $self->fontlist()->[$last_font_number];
202 1         4 $newtext .= '/' . $font->name() . ' ' . $size. ' Tf ';
203 1         6 $newtext .= $font->text(pack('U*', @codes), $size, $indent) . ' ';
204 1         3 $indent = undef;
205 1         3 @codes = ();
206             }
207              
208 9         18 push @codes, $u;
209 9         17 $last_font_number = $font_number;
210             }
211              
212 2 50       7 if (scalar @codes > 0) {
213 2         5 my $font = $self->fontlist()->[$last_font_number];
214 2         8 $newtext .= '/' . $font->name() . ' ' . $size . ' Tf ';
215 2         16 $newtext .= $font->text(pack('U*', @codes), $size, $indent);
216             }
217              
218 2         8 return $newtext;
219             }
220              
221             =back
222              
223             =cut
224              
225             1;