package PDF::Builder::Resource::UniFont; use strict; use warnings; our $VERSION = '3.026'; # VERSION our $LAST_UPDATE = '3.026'; # manually update whenever code is changed use Carp; use Encode qw(:all); =head1 NAME PDF::Builder::Resource::UniFont - Unicode Font Support =head1 METHODS =head2 new $font = PDF::Builder::Resource::UniFont->new($pdf, @fontspecs, %options) =over Returns a uni-font object. B fonts can be registered using the following hash-ref: { font => $fontobj, # the font to be registered blocks => $blockspec, # the unicode blocks the font is being registered for codes => $codespec, # the unicode codepoints, -"- } B [ $block1, $block3, # register font for block 1 + 3 [$blockA, $blockZ], # register font for blocks A .. Z ] B [ $cp1, $cp3, # register font for codepoint 1 + 3 [$cpA, $cpZ], # register font for codepoints A .. Z ] B if you want to register a font for the entire unicode space (ie. U+0000 .. U+FFFF), then simply specify a font-object without the hash-ref. Valid %options are: 'encode' ... changes the encoding of the font from its default. (see "perldoc Encode" for a list of valid tags) =back =cut sub new { my $class = shift(); $class = ref($class) if ref($class); my $self = { 'fonts' => [], 'block' => {}, 'code' => {}, 'pdf' => shift(), }; bless $self, $class; my @fonts; push @fonts, shift() while ref($_[0]); my %options = @_; # copy dashed option names to preferred undashed names if (defined $options{'-encode'} && !defined $options{'encode'}) { $options{'encode'} = delete($options{'-encode'}); } $self->{'encode'} = $options{'encode'} if defined $options{'encode'}; # note that self->encode is undefined if encode not given! my $font_number = 0; foreach my $font (@fonts) { if (ref($font) eq 'ARRAY') { push @{$self->{'fonts'}}, shift(@$font); while (defined $font->[0]) { my $blockspec = shift @$font; if (ref($blockspec)) { foreach my $block ($blockspec->[0] .. $blockspec->[-1]) { $self->{'block'}->{$block} = $font_number; } } else { $self->{'block'}->{$blockspec} = $font_number; } } } elsif (ref($font) eq 'HASH') { push @{$self->{'fonts'}}, $font->{'font'}; if (defined $font->{'blocks'} and ref($font->{'blocks'}) eq 'ARRAY') { foreach my $blockspec (@{$font->{'blocks'}}) { if (ref($blockspec)) { foreach my $block ($blockspec->[0] .. $blockspec->[-1]) { $self->{'block'}->{$block} = $font_number; } } else { $self->{'block'}->{$blockspec} = $font_number; } } } if (defined $font->{'codes'} and ref($font->{'codes'}) eq 'ARRAY') { foreach my $codespec (@{$font->{'codes'}}) { if (ref($codespec)) { foreach my $code ($codespec->[0] .. $codespec->[-1]) { $self->{'code'}->{$code} = $font_number; } } else { $self->{'code'}->{$codespec} = $font_number; } } } } else { push @{$self->{'fonts'}}, $font; foreach my $block (0 .. 255) { $self->{'block'}->{$block} = $font_number; } } $font_number++; } return $self; } =head2 isvirtual $flag = $font->isvirtual() =over (No Information) =back =cut sub isvirtual { return 1; } =head2 fontlist $font->fontlist() =over (No Information) =back =cut sub fontlist { my $self = shift; return [@{ $self->{'fonts'} }]; } =head2 width $w = $font->width($string) =over (No Information) =back =cut sub width { my ($self, $text) = @_; if (defined $self->{'encode'}) { # is self->encode guaranteed set? $text = decode($self->{'encode'}, $text) unless utf8::is_utf8($text); } my $width = 0; my @blocks = (); foreach my $u (unpack('U*', $text)) { my $font_number = 0; if (defined $self->{'code'}->{$u}) { $font_number = $self->{'code'}->{$u}; } elsif (defined $self->{'block'}->{($u >> 8)}) { $font_number = $self->{'block'}->{($u >> 8)}; } else { $font_number = 0; } if (scalar @blocks == 0 or $blocks[-1]->[0] != $font_number) { push @blocks, [$font_number, pack('U', $u)]; } else { $blocks[-1]->[1] .= pack('U', $u); } } foreach my $block (@blocks) { my ($font_number, $string) = @$block; $width += $self->fontlist()->[$font_number]->width($string); } return $width; } =head2 text $font->text($string, $size, $indent) =over (No Information) =back =cut sub text { my ($self, $text, $size, $indent) = @_; if (defined $self->{'encode'}) { # is self->encode guaranteed to be defined? $text = decode($self->{'encode'}, $text) unless utf8::is_utf8($text); } croak 'Font size not specified' unless defined $size; my $newtext = ''; my $last_font_number; my @codes; foreach my $u (unpack('U*', $text)) { my $font_number = 0; if (defined $self->{'code'}->{$u}) { $font_number = $self->{'code'}->{$u}; } elsif (defined $self->{'block'}->{($u >> 8)}) { $font_number = $self->{'block'}->{($u >> 8)}; } if (defined $last_font_number and $font_number != $last_font_number) { my $font = $self->fontlist()->[$last_font_number]; $newtext .= '/' . $font->name() . ' ' . $size. ' Tf '; $newtext .= $font->text(pack('U*', @codes), $size, $indent) . ' '; $indent = undef; @codes = (); } push @codes, $u; $last_font_number = $font_number; } if (scalar @codes > 0) { my $font = $self->fontlist()->[$last_font_number]; $newtext .= '/' . $font->name() . ' ' . $size . ' Tf '; $newtext .= $font->text(pack('U*', @codes), $size, $indent); } return $newtext; } 1;