Post without Account — your post will be reviewed, and if appropriate, posted under Anonymous.

RT 120048 - The "synthetic fonts" feature is broken (and probably fixed...)

  • 2 Replies

Offline Phil

  • Global Moderator
  • Sr. Member
  • *****
  • 356
    • View Profile
Tue Jan 31 04:34:43 2017 futuramedium [...] - Ticket created
Subject:    The "synthetic fonts" feature is broken (and probably fixed...)
Code: [Select]
use strict;
use warnings;
use PDF::API2;

my $pdf  = PDF::API2-> new;
my $font = $pdf-> ttfont( 'calibri.ttf' );
my $sft  = $pdf-> synfont( $font );

Can't locate object method "name" via package "PDF::API2::Resource::CIDFont::TrueType=HASH(0x1e067cc)" (perhaps you forgot to load "PDF::API2::Resource::CIDFont::TrueType=HASH(0x1e067cc)"?) at C:/Strawberry/perl/site/lib/PDF/API2/Resource/Font/ line 92.


2 lines for a fix, the 3d line to add "ToUnicode" by default (a PDF file without it is a bad idea, I think).

Further, these fonts can't be used with anything but basic latin. The '-encode' option is mentioned in POD, but not implemented in code. See SynFont.diff patch. Tested with TTF fonts and CP1251 (Latin/Cyrillic) encoding, should work for any "simple" (fitting into single byte) encoding.
Subject:    API2.diff
Code: [Select]
--- Thu Jan 26 22:30:03 2017
+++ Tue Jan 31 11:58:13 2017
@@ -1883,13 +1883,13 @@
 sub synfont {
-    my ($self, %opts) = @_;
+    my ($self, $font, %opts) = @_;
     require PDF::API2::Resource::Font::SynFont;
-    my $obj = PDF::API2::Resource::Font::SynFont->new($self->{'pdf'}, %opts);
+    my $obj = PDF::API2::Resource::Font::SynFont->new($self->{'pdf'}, $font, %opts);
-    $obj->tounicodemap() if $opts{-unicodemap} == 1;
+    $obj->tounicodemap unless defined $opts{-unicodemap} and !$opts{-unicodemap};
     return $obj;
Subject: SynFont.diff

--- Thu Jan 26 22:30:03 2017
+++ Tue Jan 31 12:20:39 2017
@@ -82,11 +82,7 @@
     my $space=$opts{-space}||'0';
     my $bold=($opts{-bold}||0)*10; # convert to em
-    $self->{' slant'}=$slant;
-    $self->{' oblique'}=$oblique;
-    $self->{' bold'}=$bold;
-    $self->{' boldmove'}=0.001;
-    $self->{' space'}=$space;
+    $font-> encodeByName( $opts{ -encode }) if $opts{ -encode };
     $class = ref $class if ref $class;
     $self = $class->SUPER::new($pdf,
@@ -178,7 +174,10 @@
         my $char=PDFDict();
-        my $wth=int($font->width(chr($w))*1000*$slant+2*$space);
+        my $uni = $self->data->{uni}->[$w];
+        my $wth = int($font->width(chr($uni))*1000*$slant+2*$space);
         $char->{' stream'}=$wth." 0 ".join(' ',map { int($_) } $self->fontbbox)." d1\n";
@@ -196,16 +195,15 @@
             $char->{' stream'}.="/FSN 800 Tf\n";
             $char->{' stream'}.=($slant*110)." Tz\n";
             $char->{' stream'}.=" [ -$space ] TJ\n" if($space);
-            my $ch=$self->encByUni(hex($ci->{upper}));
-            $wth=int($font->width(chr($ch))*800*$slant*1.1+2*$space);
-            $char->{' stream'}.=$font->text(chr($ch));
+            $wth=int($font->width(uc chr($uni))*800*$slant*1.1+2*$space);
+            $char->{' stream'}.=$font->text(uc chr($uni));
             $char->{' stream'}.="/FSN 1000 Tf\n";
             $char->{' stream'}.=($slant*100)." Tz\n" if($slant!=1);
             $char->{' stream'}.=" [ -$space ] TJ\n" if($space);
-            $char->{' stream'}.=$font->text(chr($w));
+            $char->{' stream'}.=$font-> text( chr( $uni ));
         $char->{' stream'}.=" Tj\nET\n";
         push @widths,$wth;
Code: [Select]
use strict;
use warnings;
use utf8;
use PDF::API2;
use Encode qw/ encode /;

my $pdf  = PDF::API2-> new;
my $page = $pdf-> page;

$page-> mediabox( 'A4' );

my $text = $page-> text;
my $font = $pdf-> ttfont( 'calibri.ttf' );
my $str  = 'Hello World! Привет, мир!';
my %opts = ( -encode => 'CP1251' );

my $sft = $pdf-> synfont( $font, %opts );
$text-> font( $sft, 20 );
$text-> translate( 100, 700 );
$text-> text( $str );

my $sft1 = $pdf-> synfont( $font, %opts = ( %opts, -caps => 1 ));
$text-> font( $sft1, 20 );
$text-> translate( 100, 650 );
$text-> text( $str );

my $sft2 = $pdf-> synfont( $font, %opts, -bold => 4 );
$text-> font( $sft2, 20 );
$text-> translate( 100, 600 );
$text-> text( $str );

my $sft3 = $pdf-> synfont( $font, %opts, -oblique => 30 );
$text-> font( $sft3, 20 );
$text-> translate( 100, 550 );
$text-> text( $str );

my $sft4 = $pdf-> synfont( $font, %opts, -slant => 0.5, -space => 100 );
$text-> font( $sft4, 20 );
$text-> translate( 100, 500 );
$text-> text( $str );

$pdf-> saveas( 'test.pdf' );
<originally file included a BOM -- Mod.>
« Last Edit: July 02, 2017, 09:28:43 PM by Phil »


Offline Phil

  • Global Moderator
  • Sr. Member
  • *****
  • 356
    • View Profile
Sun Jul 02 17:48:37 2017 steve [...] - Correspondence added

Thanks for the patches.  I've applied the first part (getting synfont to work at all) and copied the unicodemap code from ttfont(), which does the same thing as you suggested.  I've also added a test to ensure it keeps working in the future.

In your second patch (encoding), in the third hunk, you've changed $ci->{upper} to uc chr($uni).  Is that always going to be equivalent?
Sun Jul 02 17:48:38 2017 The RT System itself - Status changed from 'new' to 'open'
Sun Jul 02 17:48:38 2017 steve [...] - Status changed from 'open' to 'stalled'


Offline Phil

  • Global Moderator
  • Sr. Member
  • *****
  • 356
    • View Profile
 PhilterPaper commented on Jul 12

Wed Jul 12 10:50:38 2017 futuramedium [...] - Correspondence added


in my patch, both instances of "uc chr($uni)" can be replaced with "chr hex($ci->{upper})". They should always be equivalent, I think. As to style/consistency/readability, at the time of writing, it seemed slightly better that way.

 PhilterPaper commented on Oct 5

The changes seem to be unnecessary -- the missing $font was added a few releases ago, and $opts{'-unicodemap'} is guaranteed to exist (default 1), so it's not necessary to check if it's defined.

As for the rest of the changes, I'll have to look at what Steve has in it already, and run the test case.

 PhilterPaper commented on Oct 11

I have made some progress on this, and put it in GitHub, but I think there is still work to be done on synthetic fonts. The code appears to have only been tested with a TrueType font, as there is no encodeByName() method in core fonts or Type1 fonts. The call didn't seem to make any difference even for TrueType fonts, so I commented it out. If it turns out it's needed, something will have to be done for core and T1 fonts -- possibly just a dummy encodeByName() method. The author ("futuramedium") stated above that uc and ->{'upper'} should behave the same, but in my testing, uc didn't seem to handle non-ASCII characters (such as accented Latin letters). So, I reverted to the upper code.

Other than that, the new code seems to work fairly well, so I am going to go ahead and commit it. There are still open issues that I want to look at some more, so I won't close this report yet.

  • Restricted to single byte encodings.
  • T1 and TT fonts gave some odd listings in 021_synfonts (some characters in wrong places), so I need to figure out what's going on, and whether it can be fixed or needs to be documented restrictions. Could this be the missing encodeByName()?
  • automap() does not work for TT fonts, limiting 021_synfonts display to the basic 256 characters of an encoding.
  • Some characters (especially ligatures), such as eszet (sharp s), 'n, fi, and fl; do not have upper case equivalents and "small caps" for them are unchanged. Currently this is documented as a caution to users, but consideration should be given (if there are not too many such cases) of automatically replacing these characters with multiple simple characters, so that they upper case properly. For example, replace a single eszet by two s's (lower case), so small caps shows SS, or an "fi" ligature by two letters f and i, so small caps shows FI.
  • Consider extending 021_synfonts to gracefully handle T1 and TT fonts (currently only core).

 PhilterPaper commented on Nov 3

I am going to close this issue, and offload a few items to #79 and #81.

1, 3 See #81 for further work in this area.

2 This appears to be limited to one PS (Type1) font (URW Palladio L), which may be a bad internal font encoding map supplied with the font. That's my assumption, anyway. I can't see anything that can be done about it on the PDF::Builder side. If more fonts show up with misplaced characters in the Synthetic Fonts (or elsewhere), this will have to be revisited.

4 See #79 for further discussion on this.

5 Done.