File Coverage

blib/lib/PDF/API3/Compat/API2/Resource/Font/BdFont.pm
Criterion Covered Total %
statement 30 173 17.3
branch 0 40 0.0
condition 0 47 0.0
subroutine 10 13 76.9
pod 2 3 66.6
total 42 276 15.2


line stmt bran cond sub pod time code
1             #=======================================================================
2             # ____ ____ _____ _ ____ ___ ____
3             # | _ \| _ \| ___| _ _ / \ | _ \_ _| |___ \
4             # | |_) | | | | |_ (_) (_) / _ \ | |_) | | __) |
5             # | __/| |_| | _| _ _ / ___ \| __/| | / __/
6             # |_| |____/|_| (_) (_) /_/ \_\_| |___| |_____|
7             #
8             # A Perl Module Chain to faciliate the Creation and Modification
9             # of High-Quality "Portable Document Format (PDF)" Files.
10             #
11             # Copyright 1999-2005 Alfred Reibenschuh .
12             #
13             #=======================================================================
14             #
15             # This library is free software; you can redistribute it and/or
16             # modify it under the terms of the GNU Lesser General Public
17             # License as published by the Free Software Foundation; either
18             # version 2 of the License, or (at your option) any later version.
19             #
20             # This library is distributed in the hope that it will be useful,
21             # but WITHOUT ANY WARRANTY; without even the implied warranty of
22             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23             # Lesser General Public License for more details.
24             #
25             # You should have received a copy of the GNU Lesser General Public
26             # License along with this library; if not, write to the
27             # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28             # Boston, MA 02111-1307, USA.
29             #
30             # $Id: BdFont.pm,v 2.0 2005/11/16 02:18:14 areibens Exp $
31             #
32             #=======================================================================
33             package PDF::API3::Compat::API2::Resource::Font::BdFont;
34            
35             BEGIN {
36            
37 1     1   5 use utf8;
  1         2  
  1         10  
38 1     1   30 use Encode qw(:all);
  1         2  
  1         359  
39            
40 1     1   6 use vars qw( @ISA $VERSION $BmpNum);
  1         2  
  1         61  
41 1     1   38 use PDF::API3::Compat::API2::Resource::Font;
  1         3  
  1         24  
42 1     1   5 use PDF::API3::Compat::API2::Util;
  1         2  
  1         209  
43 1     1   5 use PDF::API3::Compat::API2::Basic::PDF::Utils;
  1         3  
  1         119  
44 1     1   5 use Math::Trig;
  1         3  
  1         253  
45 1     1   1544 use Unicode::UCD 'charinfo';
  1         87661  
  1         187  
46            
47 1     1   28 @ISA=qw(PDF::API3::Compat::API2::Resource::Font);
48            
49 1         17 ( $VERSION ) = sprintf '%i.%03i', split(/\./,('$Revision: 2.0 $' =~ /Revision: (\S+)\s/)[0]); # $Date: 2005/11/16 02:18:14 $
50            
51 1         38 $BmpNum=0;
52            
53             }
54 1     1   18 no warnings qw[ deprecated recursion uninitialized ];
  1         3  
  1         2506  
55            
56             =head1 NAME
57            
58             PDF::API3::Compat::API2::Resource::Font::BdFont - Module for using bitmapped Fonts.
59            
60             =head1 SYNOPSIS
61            
62             #
63             use PDF::API3::Compat::API2;
64             #
65             $pdf = PDF::API3::Compat::API2->new;
66             $sft = $pdf->bdfont($file);
67             #
68            
69             =head1 METHODS
70            
71             =over 4
72            
73             =cut
74            
75             =item $font = PDF::API3::Compat::API2::Resource::Font::BdFont->new $pdf, $font, %options
76            
77             Returns a BmpFont object.
78            
79             =cut
80            
81             =pod
82            
83             Valid %options are:
84            
85             I<-encode>
86             ... changes the encoding of the font from its default.
87             See I for the supported values.
88            
89             I<-pdfname> ... changes the reference-name of the font from its default.
90             The reference-name is normally generated automatically and can be
91             retrived via $pdfname=$font->name.
92            
93             =cut
94            
95             sub new {
96 0     0 1   my ($class,$pdf,$file,@opts) = @_;
97 0           my ($self,$data);
98 0           my %opts=@opts;
99            
100 0 0         $class = ref $class if ref $class;
101 0           $self = $class->SUPER::new($pdf, sprintf('%s+Bdf%02i',pdfkey(),++$BmpNum).'~'.time());
102 0 0         $pdf->new_obj($self) unless($self->is_obj($pdf));
103            
104             # adobe bitmap distribution font
105 0           $self->{' data'}=$self->readBDF($file);
106            
107 0           my $first=1;
108 0           my $last=255;
109            
110 0           $self->{'Subtype'} = PDFName('Type3');
111 0           $self->{'FirstChar'} = PDFNum($first);
112 0           $self->{'LastChar'} = PDFNum($last);
113 0           $self->{'FontMatrix'} = PDFArray(map { PDFNum($_) } ( 0.001, 0, 0, 0.001, 0, 0 ) );
  0            
114 0           $self->{'FontBBox'} = PDFArray(map { PDFNum($_) } ( $self->fontbbox ) );
  0            
115            
116 0           my $xo=PDFDict();
117 0           $self->{'Encoding'}=$xo;
118 0           $xo->{Type}=PDFName('Encoding');
119 0           $xo->{BaseEncoding}=PDFName('WinAnsiEncoding');
120 0   0       $xo->{Differences}=PDFArray(PDFNum('0'),(map { PDFName($_||'.notdef') } @{$self->data->{char}}));
  0            
  0            
121            
122 0           my $procs=PDFDict();
123 0           $pdf->new_obj($procs);
124 0           $self->{'CharProcs'} = $procs;
125            
126 0           $self->{Resources}=PDFDict();
127 0           $self->{Resources}->{ProcSet}=PDFArray(map { PDFName($_) } qw(PDF Text ImageB ImageC ImageI));
  0            
128 0           foreach my $w ($first..$last) {
129 0           $self->data->{uni}->[$w]=uniByName($self->data->{char}->[$w]);
130 0           $self->data->{u2e}->{$self->data->{uni}->[$w]}=$w;
131             }
132 0           my @widths=();
133 0           foreach my $w (@{$self->data->{char2}}) {
  0            
134 0           $widths[$w->{ENCODING}]=$self->data->{wx}->{$w->{NAME}};
135 0           my @bbx=@{$w->{BBX}};
  0            
136 0           my $stream=pack('H*',$w->{hex});
137 0           my $y=$bbx[1];
138 0           my $char=PDFDict();
139 0           $char->{Filter}=PDFArray(PDFName('FlateDecode'));
140             ## $char->{' stream'}=$widths[$w->{ENCODING}]." 0 ".join(' ',map { int($_) } $self->fontbbox)." d1\n";
141 0           $char->{' stream'}=$widths[$w->{ENCODING}]." 0 d0\n";
142 0           $char->{Comment}=PDFStr("N='$w->{NAME}' C=($w->{ENCODING})");
143 0           $procs->{$w->{NAME}}=$char;
144 0           @bbx=map { $_*1000/$self->data->{upm} } @bbx;
  0            
145 0 0         if($y==0) {
146 0           $char->{' stream'}.="q Q\n";
147             } else {
148 0           my $x=8*length($stream)/$y; # q $x 0 0 $y 50 50 cm
149 0           my $img=qq|BI\n/Interpolate true/Mask[0 0.1]/Decode[1 0]/H $y/W $x/BPC 1/CS/G\nID $stream\nEI\n|;
150 0           $procs->{$self->data->{char}->[$w]}=$char;
151 0           $char->{' stream'}.="$bbx[0] 0 0 $bbx[1] $bbx[2] $bbx[3] cm\n$img\n";
152             }
153 0           $pdf->new_obj($char);
154             }
155 0           $procs->{'.notdef'}=$procs->{$self->data->{char}->[32]};
156 0           delete $procs->{''};
157 0   0       $self->{Widths}=PDFArray(map { PDFNum($widths[$_]||0) } ($first..$last));
  0            
158 0           $self->data->{e2n}=$self->data->{char};
159 0           $self->data->{e2u}=$self->data->{uni};
160            
161 0           $self->data->{u2c}={};
162 0           $self->data->{u2e}={};
163 0           $self->data->{u2n}={};
164 0           $self->data->{n2c}={};
165 0           $self->data->{n2e}={};
166 0           $self->data->{n2u}={};
167            
168 0           foreach my $n (reverse 0..255) {
169 0 0 0       $self->data->{n2c}->{$self->data->{char}->[$n] || '.notdef'}=$n unless(defined $self->data->{n2c}->{$self->data->{char}->[$n] || '.notdef'});
      0        
170 0 0 0       $self->data->{n2e}->{$self->data->{e2n}->[$n] || '.notdef'}=$n unless(defined $self->data->{n2e}->{$self->data->{e2n}->[$n] || '.notdef'});
      0        
171            
172 0 0 0       $self->data->{n2u}->{$self->data->{e2n}->[$n] || '.notdef'}=$self->data->{e2u}->[$n] unless(defined $self->data->{n2u}->{$self->data->{e2n}->[$n] || '.notdef'});
      0        
173 0 0 0       $self->data->{n2u}->{$self->data->{char}->[$n] || '.notdef'}=$self->data->{uni}->[$n] unless(defined $self->data->{n2u}->{$self->data->{char}->[$n] || '.notdef'});
      0        
174            
175 0 0         $self->data->{u2c}->{$self->data->{uni}->[$n]}=$n unless(defined $self->data->{u2c}->{$self->data->{uni}->[$n]});
176 0 0         $self->data->{u2e}->{$self->data->{e2u}->[$n]}=$n unless(defined $self->data->{u2e}->{$self->data->{e2u}->[$n]});
177            
178 0 0 0       $self->data->{u2n}->{$self->data->{e2u}->[$n]}=($self->data->{e2n}->[$n] || '.notdef') unless(defined $self->data->{u2n}->{$self->data->{e2u}->[$n]});
179 0 0 0       $self->data->{u2n}->{$self->data->{uni}->[$n]}=($self->data->{char}->[$n] || '.notdef') unless(defined $self->data->{u2n}->{$self->data->{uni}->[$n]});
180             }
181            
182 0           return($self);
183             }
184            
185            
186             =item $font = PDF::API3::Compat::API2::Resource::Font::BdFont->new_api $api, %options
187            
188             Returns a BdFont object. This method is different from 'new' that
189             it needs an PDF::API3::Compat::API2-object rather than a PDF::API3::Compat::API2::PDF::File-object.
190            
191             =cut
192            
193             sub new_api {
194 0     0 1   my ($class,$api,@opts)=@_;
195            
196 0           my $obj=$class->new($api->{pdf},@opts);
197            
198 0 0         $api->{pdf}->new_obj($obj) unless($obj->is_obj($api->{pdf}));
199            
200 0           $api->{pdf}->out_obj($api->{pages});
201 0           return($obj);
202             }
203            
204             sub readBDF {
205 0     0 0   my ($self,$file)=@_;
206 0           my $data={};
207 0           $data->{char}=[];
208 0           $data->{char2}=[];
209 0           $data->{wx}={};
210            
211 0 0         if(! -e $file) {die "file='$file' not existant.";}
  0            
212 0 0         open(AFMF, $file) or die "Can't find the BDF file for $file";
213 0           local($/, $_) = ("\n", undef); # ensure correct $INPUT_RECORD_SEPARATOR
214 0           while ($_=) {
215 0           chomp($_);
216 0 0         if (/^STARTCHAR/ .. /^ENDCHAR/) {
217 0 0         if (/^STARTCHAR\s+(\S+)/) {
    0          
218 0           my $name=$1;
219 0           $name=~s|^(\d+.*)$|X_$1|;
220 0           push @{$data->{char2}},{'NAME'=>$name};
  0            
221             } elsif (/^BITMAP/ .. /^ENDCHAR/) {
222 0 0         next if(/^BITMAP/);
223 0 0         if(/^ENDCHAR/){
224 0   0       $data->{char2}->[-1]->{NAME}||='E_'.$data->{char2}->[-1]->{ENCODING};
225 0           $data->{char}->[$data->{char2}->[-1]->{ENCODING}]=$data->{char2}->[-1]->{NAME};
226 0           ($data->{wx}->{$data->{char2}->[-1]->{NAME}})=split(/\s+/,$data->{char2}->[-1]->{SWIDTH});
227 0           $data->{char2}->[-1]->{BBX}=[split(/\s+/,$data->{char2}->[-1]->{BBX})];
228             } else {
229 0           $data->{char2}->[-1]->{hex}.=$_;
230             }
231             } else {
232 0           m|^(\S+)\s+(.+)$|;
233 0           $data->{char2}->[-1]->{uc($1)}.=$2;
234             }
235             ## } elsif(/^STARTPROPERTIES/ .. /^ENDPROPERTIES/) {
236             } else {
237 0           m|^(\S+)\s+(.+)$|;
238 0           $data->{uc($1)}.=$2;
239             }
240             }
241 0           close(AFMF);
242 0 0         unless (exists $data->{wx}->{'.notdef'}) {
243 0           $data->{wx}->{'.notdef'} = 0;
244 0           $data->{bbox}{'.notdef'} = [0, 0, 0, 0];
245             }
246            
247 0           $data->{fontname}=pdfkey().pdfkey().'~'.time();
248 0           $data->{apiname}=$data->{fontname};
249 0           $data->{flags} = 34;
250 0           $data->{fontbbox} = [ split(/\s+/,$data->{FONTBOUNDINGBOX}) ];
251 0   0       $data->{upm}=$data->{PIXEL_SIZE} || ($data->{fontbbox}->[1] - $data->{fontbbox}->[3]);
252 0           @{$data->{fontbbox}} = map { int($_*1000/$data->{upm}) } @{$data->{fontbbox}};
  0            
  0            
  0            
253            
254 0           foreach my $n (0..255) {
255 0   0       $data->{char}->[$n]||='.notdef';
256             # $data->{wx}->{$data->{char}->[$n]}=int($data->{wx}->{$data->{char}->[$n]}*1000/$data->{upm});
257             }
258            
259 0   0       $data->{uni}||=[];
260 0           foreach my $n (0..255) {
261 0   0       $data->{uni}->[$n]=uniByName($data->{char}->[$n] || '.notdef') || 0;
262             }
263 0   0       $data->{ascender}=$data->{RAW_ASCENT}
264             || int($data->{FONT_ASCENT}*1000/$data->{upm});
265 0   0       $data->{descender}=$data->{RAW_DESCENT}
266             || int($data->{FONT_DESCENT}*1000/$data->{upm});
267            
268 0           $data->{type}='Type3';
269 0           $data->{capheight}=1000;
270 0           $data->{iscore}=0;
271 0           $data->{issymbol} = 0;
272 0           $data->{isfixedpitch}=0;
273 0           $data->{italicangle}=0;
274 0   0       $data->{missingwidth}=$data->{AVERAGE_WIDTH}
275             || int($data->{FONT_AVERAGE_WIDTH}*1000/$data->{upm})
276             || $data->{RAW_AVERAGE_WIDTH}
277             || 500;
278 0           $data->{underlineposition}=-200;
279 0           $data->{underlinethickness}=10;
280 0   0       $data->{xheight}=$data->{RAW_XHEIGHT}
281             || int($data->{FONT_XHEIGHT}*1000/$data->{upm})
282             || int($data->{ascender}/2);
283 0           $data->{firstchar}=1;
284 0           $data->{lastchar}=255;
285            
286 0           delete $data->{wx}->{''};
287            
288 0           return($data);
289             }
290            
291             1;
292            
293             __END__