package PDF::API2::Resource::Font::BdFont;
use base 'PDF::API2::Resource::Font';
use strict;
use warnings;
our $VERSION = '2.043'; # VERSION
use PDF::API2::Util;
use PDF::API2::Basic::PDF::Utils;
our $BmpNum = 0;
=head1 NAME
PDF::API2::Resource::Font::BdFont - Module for using bitmapped Fonts.
=head1 SYNOPSIS
#
use PDF::API2;
#
$pdf = PDF::API2->new;
$sft = $pdf->bdfont($file);
#
=head1 METHODS
=over 4
=cut
=item $font = PDF::API2::Resource::Font::BdFont->new $pdf, $font, %options
Returns a BmpFont object.
=cut
=pod
Valid %options are:
I<-encode>
... changes the encoding of the font from its default.
See I<perl's Encode> for the supported values.
I<-pdfname> ... changes the reference-name of the font from its default.
The reference-name is normally generated automatically and can be
retrieved via $pdfname=$font->name.
=cut
sub new {
my ($class, $pdf, $file, %opts) = @_;
$class = ref($class) if ref($class);
my $name = sprintf('%s+Bdf%02i', pdfkey(), ++$BmpNum) . '~' . time();
my $self = $class->SUPER::new($pdf, $name);
$pdf->new_obj($self) unless $self->is_obj($pdf);
# Adobe Bitmap Distribution Font
$self->{' data'} = $self->readBDF($file);
my $first = 1;
my $last = 255;
$self->{'Subtype'} = PDFName('Type3');
$self->{'FirstChar'} = PDFNum($first);
$self->{'LastChar'} = PDFNum($last);
$self->{'FontMatrix'} = PDFArray(map { PDFNum($_) } 0.001, 0, 0, 0.001, 0, 0);
$self->{'FontBBox'} = PDFArray(map { PDFNum($_) } $self->fontbbox());
my $xo = PDFDict();
$self->{'Encoding'} = $xo;
$xo->{'Type'} = PDFName('Encoding');
$xo->{'BaseEncoding'} = PDFName('WinAnsiEncoding');
$xo->{'Differences'} = PDFArray(PDFNum('0'),
map { PDFName($_ or '.notdef') }
@{$self->data->{'char'}});
my $procs = PDFDict();
$pdf->new_obj($procs);
$self->{'CharProcs'} = $procs;
$self->{'Resources'} = PDFDict();
$self->{'Resources'}->{'ProcSet'} = PDFArray(map { PDFName($_) }
qw(PDF Text ImageB ImageC ImageI));
foreach my $w ($first .. $last) {
$self->data->{'uni'}->[$w] = uniByName($self->data->{'char'}->[$w]);
$self->data->{'u2e'}->{$self->data->{'uni'}->[$w]} = $w;
}
my @widths;
foreach my $w (@{$self->data->{'char2'}}) {
$widths[$w->{'ENCODING'}] = $self->data->{'wx'}->{$w->{'NAME'}};
my @bbx = @{$w->{'BBX'}};
my $stream = pack('H*', $w->{'hex'});
my $y = $bbx[1];
my $char = PDFDict();
$char->{'Filter'} = PDFArray(PDFName('FlateDecode'));
# $char->{' stream'} = $widths[$w->{'ENCODING'}] . ' 0 ' . join(' ', map { int($_) } $self->fontbbox()) . " d1\n";
$char->{' stream'} = $widths[$w->{'ENCODING'}] . " 0 d0\n";
$char->{'Comment'} = PDFStr(join(' ',
"N='" . $w->{'NAME'} . "'",
"C=(" . $w->{'ENCODING'} . ")"));
$procs->{$w->{'NAME'}} = $char;
@bbx = map { $_ * 1000 / $self->data->{'upm'} } @bbx;
if ($y == 0) {
$char->{' stream'} .= "q Q\n";
}
else {
my $x = 8 * length($stream) / $y; # q $x 0 0 $y 50 50 cm
my $dict = join('',
"/Interpolate true",
"/Mask[0 0.1]",
"/Decode[1 0]",
"/H $y",
"/W $x",
"/BPC 1",
"/CS /G");
my $img = qq|BI\n$dict\nID $stream\nEI\n|;
$procs->{$self->data->{'char'}->[$w]} = $char;
$char->{' stream'} .= "$bbx[0] 0 0 $bbx[1] $bbx[2] $bbx[3] cm\n";
$char->{' stream'} .= $img . "\n";
}
$pdf->new_obj($char);
}
$procs->{'.notdef'} = $procs->{$self->data->{'char'}->[32]};
delete $procs->{''};
$self->{'Widths'} = PDFArray(map { PDFNum($widths[$_] or 0) }
$first .. $last);
$self->data->{'e2n'} = $self->data->{'char'};
$self->data->{'e2u'} = $self->data->{'uni'};
$self->data->{'u2c'} = {};
$self->data->{'u2e'} = {};
$self->data->{'u2n'} = {};
$self->data->{'n2c'} = {};
$self->data->{'n2e'} = {};
$self->data->{'n2u'} = {};
my $data = $self->data();
foreach my $n (reverse 0 .. 255) {
$data->{'n2c'}->{$data->{'char'}->[$n] or '.notdef'} //= $n;
$data->{'n2e'}->{$data->{'e2n'}->[$n] or '.notdef'} //= $n;
$data->{'n2u'}->{$data->{'e2n'}->[$n] or '.notdef'} //= $data->{'e2u'}->[$n];
$data->{'n2u'}->{$data->{'char'}->[$n] or '.notdef'} //= $data->{'uni'}->[$n];
$data->{'u2c'}->{$data->{'uni'}->[$n]} //= $n;
$data->{'u2e'}->{$data->{'e2u'}->[$n]} //= $n;
$data->{'u2n'}->{$data->{'e2u'}->[$n]} //= ($data->{'e2n'}->[$n] or '.notdef');
$data->{'u2n'}->{$data->{'uni'}->[$n]} //= ($data->{char}->[$n] or '.notdef');
}
return $self;
}
sub readBDF {
my ($self, $file) = @_;
my $data = {};
$data->{'char'} = [];
$data->{'char2'} = [];
$data->{'wx'} = {};
die "file='$file' doesn't exist" unless -e $file;
open(my $afmf, '<', $file) or die "Can't find the BDF file for $file";
local $/ = "\n"; # ensure correct $INPUT_RECORD_SEPARATOR
local $_ = undef;
while ($_ = <$afmf>) {
chomp($_);
if (/^STARTCHAR/ .. /^ENDCHAR/) {
if (/^STARTCHAR\s+(\S+)/) {
my $name = $1;
$name =~ s/^(\d+.*)$/X_$1/;
push @{$data->{'char2'}}, { 'NAME' => $name };
}
elsif (/^BITMAP/ .. /^ENDCHAR/) {
next if /^BITMAP/;
if (/^ENDCHAR/) {
$data->{'char2'}->[-1]->{'NAME'} ||= 'E_' . $data->{'char2'}->[-1]->{'ENCODING'};
$data->{'char'}->[$data->{'char2'}->[-1]->{'ENCODING'}] = $data->{'char2'}->[-1]->{'NAME'};
($data->{'wx'}->{$data->{'char2'}->[-1]->{'NAME'}}) = split(/\s+/, $data->{'char2'}->[-1]->{'SWIDTH'});
$data->{'char2'}->[-1]->{'BBX'} = [split(/\s+/, $data->{'char2'}->[-1]->{'BBX'})];
}
else {
$data->{'char2'}->[-1]->{'hex'} .= $_;
}
}
else {
m/^(\S+)\s+(.+)$/;
$data->{'char2'}->[-1]->{uc($1)} .= $2;
}
# } elsif (/^STARTPROPERTIES/ .. /^ENDPROPERTIES/) {
}
else {
m/^(\S+)\s+(.+)$/;
$data->{uc($1)} .= $2;
}
}
close($afmf);
unless (exists $data->{'wx'}->{'.notdef'}) {
$data->{'wx'}->{'.notdef'} = 0;
$data->{'bbox'}{'.notdef'} = [0, 0, 0, 0];
}
$data->{'fontname'} = pdfkey() . pdfkey() . '~' . time();
$data->{'apiname'} = $data->{'fontname'};
$data->{'flags'} = 34;
$data->{'fontbbox'} = [ split(/\s+/, $data->{'FONTBOUNDINGBOX'}) ];
$data->{'upm'} = ($data->{'PIXEL_SIZE'}
or ($data->{'fontbbox'}->[1] - $data->{'fontbbox'}->[3]));
@{$data->{'fontbbox'}} = (map { int($_ * 1000 / $data->{'upm'}) }
@{$data->{'fontbbox'}});
foreach my $n (0 .. 255) {
$data->{'char'}->[$n] ||= '.notdef';
# $data->{'wx'}->{$data->{'char'}->[$n]}
# = int($data->{'wx'}->{$data->{'char'}->[$n]} * 1000 / $data->{'upm'});
}
$data->{'uni'} ||= [];
foreach my $n (0 .. 255) {
$data->{'uni'}->[$n] = uniByName($data->{'char'}->[$n] or '.notdef') || 0;
}
$data->{'ascender'} = $data->{'RAW_ASCENT'}
|| int($data->{'FONT_ASCENT'} * 1000 / $data->{'upm'});
$data->{'descender'} = $data->{'RAW_DESCENT'}
|| int($data->{'FONT_DESCENT'} * 1000 / $data->{'upm'});
$data->{'type'} = 'Type3';
$data->{'capheight'} = 1000;
$data->{'iscore'} = 0;
$data->{'issymbol'} = 0;
$data->{'isfixedpitch'} = 0;
$data->{'italicangle'} = 0;
$data->{'missingwidth'} = $data->{'AVERAGE_WIDTH'}
|| int($data->{'FONT_AVERAGE_WIDTH'} * 1000 / $data->{'upm'})
|| $data->{'RAW_AVERAGE_WIDTH'}
|| 500;
$data->{'underlineposition'} = -200;
$data->{'underlinethickness'} = 10;
$data->{'xheight'} = $data->{'RAW_XHEIGHT'}
|| int($data->{'FONT_XHEIGHT'} * 1000 / $data->{'upm'})
|| int($data->{'ascender'} / 2);
$data->{'firstchar'} = 1;
$data->{'lastchar'} = 255;
delete $data->{'wx'}->{''};
return $data;
}
=back
=cut
1;