Skip to content

Commit

Permalink
add shape() method
Browse files Browse the repository at this point in the history
  • Loading branch information
dwarring committed Jan 21, 2024
1 parent f78d145 commit 9f1bd04
Show file tree
Hide file tree
Showing 24 changed files with 187 additions and 111 deletions.
11 changes: 10 additions & 1 deletion Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{{$NEXT}}
- Add auto-loading of type-1 AFM metrics
- Add auto-loading of type-1 AFM metrics. Look for a
sibling *.afm file when loading type-1 *.pfa or *.pfb
files and attach it.
- Add shape() method:
-- integrate HarfBuzz for TrueType and OpenType shaping
-- fallback to kerning + typographic ligature substitution
for other fonts
-- Add allocate-cid() and encode-cids() method to encoders.
To support HarfBuzz shaping, which may return non-unicode mapped
glyphs.

0.7.8 2023-11-13T06:38:11+13:00
- Further streamline CMAP encoding. Remove redundant code-point checks.
Expand Down
6 changes: 3 additions & 3 deletions META6.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"build-depends": [
],
"depends": [
"Font::AFM:ver<1.24.9+>",
"Font::FreeType:ver<0.4.3+>",
"HarfBuzz",
"Font::AFM:ver<1.24.10+>",
"Font::FreeType:ver<0.5.5+>",
"HarfBuzz:ver<0.1.3+>",
"HarfBuzz::Font::FreeType",
"Hash::int",
"Native::Packing",
Expand Down
6 changes: 3 additions & 3 deletions lib/PDF/Font/Loader/Enc.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ has Bool $.encoding-updated is rw;
has PDF::COS::Stream $.cmap is rw; # /ToUnicode CMap
has Font::AFM $.core-metrics is rw;
has Lock $.lock handles<protect> .= new;
has Glyph %!glyphs{Int};
has Glyph %.glyphs-seen{Int} is built;
# internal font units to 1000ths
submethod TWEAK(:$widths) {
@!widths = .map(*.Int) with $widths;
Expand Down Expand Up @@ -84,7 +84,7 @@ method local-glyph-name($cid) {
}

method glyph($cid is raw) {
self.protect: { %!glyphs{$cid} //= self!make-glyph($cid); }
self.protect: { %!glyphs-seen{$cid} //= self!make-glyph($cid); }
}

multi method get-glyphs(Str:D $text) {
Expand Down Expand Up @@ -215,7 +215,7 @@ sub codepoint-to-hex(UInt $_) {
!! $s; # 1-2 bytes
}

sub ligature-to-hex(UInt @codes) {
sub ligature-to-hex(@codes) {
my $buf = @codes>>.chr.join.encode("utf16");
$buf>>.fmt('%04X').join;
}
Expand Down
35 changes: 20 additions & 15 deletions lib/PDF/Font/Loader/Enc/CMap.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -290,24 +290,29 @@ my constant %PreferredEnc = do {
%win;
}
has UInt $!next-cid = 0;
has %!used-cid;
method use-cid($_) { %!used-cid{$_}++ }
method add-encoding($ord) {
my $cid := %PreferredEnc{$ord};
if $cid && !@!to-unicode[$cid] && !%!used-cid{$cid} && !self!skip-cid-block($cid) {
self.set-encoding($ord, $cid);
has @!used-cid;
method use-cid($_) { @!used-cid[$_] = 1 }
method allocate-cid {
my UInt $cid;
repeat {
++$!next-cid;
} while @!used-cid[$!next-cid] || @!to-unicode[$!next-cid] || self!skip-cid-block($!next-cid);
if $!next-cid >= 2 ** ($.is-wide ?? 16 !! 8) {
has $!out-of-gas //= warn "CID code-range is exhausted";
}
else {
# sequential allocation
repeat {
} while %!used-cid{$!next-cid} || @!to-unicode[++$!next-cid] || self!skip-cid-block($!next-cid) ;
$cid := $!next-cid;
if $cid >= 2 ** ($.is-wide ?? 16 !! 8) {
has $!out-of-gas //= warn "CID code-range is exhausted";
}
else {
self.set-encoding($ord, $cid);
}
}
@!used-cid[$cid] = 1;
$cid;
}
method add-encoding($ord) {
my $cid = %PreferredEnc{$ord};
if $cid && !@!to-unicode[$cid] && !@!used-cid[$cid] && !self!skip-cid-block($cid) {
self.set-encoding($ord, $cid);
}
elsif $cid = self.allocate-cid {
self.set-encoding($ord, $cid);
}
$cid;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/PDF/Font/Loader/Enc/Glyphic.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ role PDF::Font::Loader::Enc::Glyphic
}

if $gid {
$glyph-name = $!face.glyph-name-from-index($gid) // callsame() // $ord.chr.uniname;
$glyph-name = $!face.glyph-name-from-index($gid) || callsame() || $ord.chr.uniname;
# Not sure what glyph names are universally supported. This is conservative.
$.encoding-updated = True
unless $glyph-name ~~ %Font::AFM::Glyphs{$ord.chr};
Expand All @@ -37,8 +37,8 @@ role PDF::Font::Loader::Enc::Glyphic
}

# Callback for unmapped glyphs
method cid-map-glyph($glyph-name, $cid) {
if $!face.index-from-glyph-name($glyph-name) -> $gid {
method cid-map-glyph($glyph-name, $cid, $gid = $!face.index-from-glyph-name($glyph-name)) {
if $gid {
$.cid-to-gid-map[$cid] ||= $gid;
}
}
Expand Down
69 changes: 45 additions & 24 deletions lib/PDF/Font/Loader/Enc/Identity16.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,30 @@ also does PDF::Content::Font::Encoder;

has Font::FreeType::Face $.face is required;
has FT_Face $!raw;
has uint32 @!to-unicode;
has UInt %.charset{UInt};
has UInt $.min-index;
has UInt $.max-index;
has atomicint $!init = 0;
has uint32 @.to-unicode is built;

submethod TWEAK {
$!raw = $!face.raw;
if self.cid-to-gid-map -> $cid-gid {
my $gid-uni:= $!face.index-to-unicode;
for $cid-gid.kv -> $cid, $gid {
if $gid {
my $cp := $gid-uni[$gid];
if $cp {
@!to-unicode[$cid] = $cp
}
else {
@!used-cid[$cid] = 1;
}
}
}
}
else {
@!to-unicode = $!face.index-to-unicode;
}
}

method is-wide {True}
Expand All @@ -33,7 +49,22 @@ also does PDF::Content::Font::Encoder;
}

method add-encoding(UInt:D $ord) {
self.set-encoding: $ord, $!raw.FT_Get_Char_Index($ord);
my $gid = $!raw.FT_Get_Char_Index($ord);
my $cid;
if self.cid-to-gid-map -> $map {
if $ord <= $!raw.num-glyphs && !@!to-unicode[$ord] && !@!used-cid[$ord] {
$cid = $ord;
}
else {
$cid = self.allocate-cid;
}
$map[$cid] = $gid
if $cid;
}
else {
$cid = $gid;
}
self.set-encoding: $ord, $cid
}

multi method encode(Str $text, :cids($)!) {
Expand All @@ -44,6 +75,17 @@ also does PDF::Content::Font::Encoder;
}
}

has UInt $!next-cid = 0;
has uint8 @!used-cid;
method allocate-cid {
repeat {
$!next-cid++;
} while @!used-cid[$!next-cid] || @!to-unicode[$!next-cid] && $!next-cid <= $!raw.num-glyphs;
my $cid = $!next-cid <= $!raw.num-glyphs ?? $!next-cid !! 0;
@!used-cid[$cid] = 1;
$cid;
}

method encode-cids(@cids is raw) {
my blob8 $buf := pack(@cids, 16);
$buf.decode: 'latin-1';
Expand All @@ -53,27 +95,6 @@ also does PDF::Content::Font::Encoder;
self.encode-cids: self.encode($text, :cids);
}

method !setup-decoding {
$.lock.protect: {
unless $!init {
my FT_Face $struct = $!face.raw;
my FT_UInt $glyph-idx;
my FT_ULong $char-code = $struct.FT_Get_First_Char( $glyph-idx);
@!to-unicode[$!face.num-glyphs] = 0;
while $glyph-idx {
@!to-unicode[ $glyph-idx ] = $char-code;
$char-code = $struct.FT_Get_Next_Char( $char-code, $glyph-idx);
}
$!init= 1;
}
}
}

method to-unicode {
$!init || self!setup-decoding();
@!to-unicode;
}

multi method decode(Str $encoded, :cids($)!) {
$encoded.ords.map: -> \hi, \lo {hi +< 8 + lo};
}
Expand Down
12 changes: 7 additions & 5 deletions lib/PDF/Font/Loader/Enc/Type1.rakumod
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#| Implements a Type1 single byte encoding scheme, such as win, mac, or std
use PDF::Content::Font::Enc::Type1;
use PDF::Font::Loader::Enc;
class PDF::Font::Loader::Enc::Type1
is PDF::Content::Font::Enc::Type1
is PDF::Font::Loader::Enc {
class PDF::Font::Loader::Enc::Type1 {

use PDF::Content::Font::Enc::Type1;
also is PDF::Content::Font::Enc::Type1;

use PDF::Font::Loader::Enc;
also is PDF::Font::Loader::Enc;

use PDF::Font::Loader::Enc::Glyphic;
also does PDF::Font::Loader::Enc::Glyphic;
Expand Down
Loading

0 comments on commit 9f1bd04

Please sign in to comment.