package Pastel
::Font
::TTF
;
use Pastel
::Mixin
::Mixin
;
@ISA = qw(Pastel::Mixin::Mixin);
my $class = ref($arg) || $arg;
$self->{subfamily
} = undef;
my $mod = Pastel
::Font
::TTF
->new();
my ($path, $file) = $mod->_rearrange(["PATH","FILE"],@args);
if(defined($path) || defined($file)){
$mod->set_file_handle($path);
$mod->set_file_handle($file);
croak
"Supply filename in Pastel::Font::TTF::create_from_file()\n";
my $fh = IO
::File
->new();
if($fh->open("< $path")){
croak
"Could not open $path in Pastel::Font::TTF::set_file_handle\n";
$self->make_directory_entry();
if (defined($self->{_fh
})){
sub make_directory_entry
{
my $fh = $self->get_file_handle();
eval{read($fh, $buf, 12)};
croak
"Read error in Pastel::Font::TTF::make_directory_entry\n";
my ($version, $number) = unpack("Nn", $buf);
#print "Version = $version, Number of tables = $number\n";
# print "\nTABLE\tOFFSET\tLENGTH\n";
for(my $i = 0; $i < $number; $i++){
my ( $table, $offset, $length) = unpack("a4x4NN", $buf);
$self->{table
}->{$table} = $offset;
#print "$table\t$offset\t$length\n";
#print $self->{table}->{'OS/2'};
# my $fh = $self->get_file_handle();
# my $PLATFORM_ID = $self->{platform};
# my $ENCODING_ID = $self->{encoding};
# if ( $self->{platform} =="1" && $self->{encoding} =="0"){
# my $add = $self->get_table_address('name');
# my ( $num , $offset) = unpack ("x2nn", $buf);
# #print "*******NAME : Number of records, $num, Offset: $offset\n";
# my ($copyright_offset, $font_family_name_offset,
# $subfamily_offset, $id_offset,$full_name_offset,
# $version_string_offset, $postscript_offset, $trademark_offset);
# my ($copyright_length, $font_family_length,
# $subfamily_length, $id_length, $full_name_length,
# $version_length, $postscript_length, $trademark_length);
# for (my $i = 0; $i < $num; $i++){
# my ($id, $encoding, $language, $name_id, $length, $string_offset)
# #print "****NAMERECORDS: $id, $encoding, $language, $name_id, $length, $string_offset\n";
# if ( ($id == $PLATFORM_ID)&& # Windows??
# ($encoding == $ENCODING_ID) && #UGL??
# ($language == $LANGUAGE_ID)
# if ($name_id == 0 ) { #Copyright
# $copyright_offset = $string_offset;
# $copyright_length = $length;
# if ($name_id == 1 ) { # Familyname
# $font_family_name_offset = $string_offset;
# $font_family_length = $length;
# if ($name_id == 2 ) { # Subfamily
# $subfamily_offset = $string_offset;
# $subfamily_length = $length;
# if ($name_id == 3 ) { # Identifier
# $id_offset = $string_offset;
# if ($name_id == 4 ) { # Full name
# $full_name_offset = $string_offset;
# $full_name_length = $length;
# if ($name_id == 5 ) { #version string
# $version_string_offset = $string_offset;
# $version_length = $length;
# if ( $name_id == 6) { # Postscript name
# $postscript_offset = $string_offset;
# $postscript_length = $length;
# if ($name_id == 7 ) { # Trademark
# $trademark_offset = $string_offset;
# $trademark_length = $length;
# seek ( $fh, $add + $offset + $copyright_offset, 0);
# read ($fh, $buf, $copyright_length);
# # print "COPYRIGHT: $buf\n\n";
# seek ($fh, $add + $offset + $font_family_name_offset, 0 );
# read ( $fh, $buf, $font_family_length);
## print "FAMILY: $buf\n\n";
# seek ($fh, $add + $offset + $subfamily_offset, 0);
# read ($fh, $buf, $subfamily_length);
# #print "SUBFAMILY: $buf\n\n";
# seek ( $fh, $add + $offset +$id_offset, 0);
# read ($fh, $buf, $id_length);
# seek ( $fh, $add + $offset +$full_name_offset, 0);
# read ($fh, $buf, $full_name_length);
# #print "FULL NAME: $buf\n\n";
# seek ( $fh, $add + $offset +$version_string_offset, 0);
# read ($fh, $buf, $version_length);
# #print "VERSION: $buf\n\n";
# seek ( $fh, $add + $offset +$postscript_offset, 0);
# read ($fh, $buf, $postscript_length);
# #print "Postscript: $buf\n\n";
# seek ( $fh, $add + $offset +$trademark_offset, 0);
# read ($fh, $buf, $trademark_length);
# #print "TRADEMARK: $buf\n\n";
if (defined($self->{table
}->{$table_name})){
return $self->{table
}->{$table_name};
croak
"Undefined table address in Pastel::Font::TTF::get_table_address()\n";
my $fh = $self->get_file_handle();
my $PLATFORM_ID = $self->{platform
};
my $ENCODING_ID = $self->{encoding
};
# Glyph indices are stored in "cmap" table. We get the offset of the
# "cmap" table from the %table hash
my $cmap = $self->get_table_address('cmap');
#'cmap' table starts with
# USHORT Table version number
# USHORT Number of encoding tables
#Get number of tables and skip the version number
my ($num) = unpack ("x2n", $buf);
# Read the tables. There will $num tables
# Each one for a specific encoding and platform id
# There are three most important id and encoding-
# Windows : ID=3 Encoding = 1
# Windows symbol : ID=3 Encoding = 0
# Mac/Poscript : ID=1 Encoding = 0
# USHORT Platform specific encoding ID
# ULONG Byte ofset from the begining of the 'cmap' table
for(my $i = 0; $i < $num; $i++){
my($id, $encoding, $off) = unpack("nnN", $buf);
if($id == $PLATFORM_ID && $encoding == $ENCODING_ID){
seek($fh, $cmap + $offset, 0);
# Mac/Poscript table with encoding 0 use the following format
# USHORT version starts at 0
# BYTE glyphIdArray[256] There is no trick here just read the whole
if ($PLATFORM_ID =="1" && $ENCODING_ID=="0"){
# Skip the format, length and version information
#print (unpack("nnn", $buf));
# Now read the 256 element array directly
for (my $i =0; $i < 256; $i++){
$self->{glyphs
}->[$i] = unpack("C", $buf);
#print $self->{glyphs}->[$i];
print "Char $i\t\t-> Index $self->{glyphs}->[$i]\n";
# Windows table with encoding 1 use the following format FORMAT 4
# USHORT format Format number is set to 4.
# USHORT length Length in bytes.
# USHORT version Version number (starts at 0).
# USHORT segCountX2 2 x segCount.
# USHORT searchRange 2 x (2**floor(log2(segCount)))
# USHORT entrySelector log2(searchRange/2)
# USHORT rangeShift 2 x segCount - searchRange
# USHORT endCount[segCount] End characterCode for each segment,
# USHORT reservedPad Set to 0.
# USHORT startCount[segCount] Start character code for each segment.
# USHORT idDelta[segCount] Delta for all character codes in segment.
# USHORT idRangeOffset[segCount]Offsets into glyphIdArray or 0
# USHORT glyphIdArray[ ] Glyph index array (arbitrary length)
if ( $PLATFORM_ID == "3" && $ENCODING_ID =="1"){
my ($format, $length, $version) = unpack("nnn", $buf);
#print "Format: $format\tLength: $length\tVersion: $version\n\n";
my ($seg_countX2, $search_range, $entry_selector, $range_shift)
my $seg_count = $seg_countX2 / 2;
# print "SegcountX2:\t\t$seg_countX2\n";
# print "Search Range:\t$search_range\n";
# print "Entry:\t$entry_selector\n";
# print "Range Shift:\t$range_shift\n";
read($fh, $buf, 2 * $seg_count);
my(@end_count) = unpack("n" x
$seg_count, $buf);
#print "EndCount: ", join("\t",@end_count), "\n";
my $reserve_pad = unpack("n", $buf);
#print "Reserve Pad: $reserve_pad\n";
read($fh, $buf, 2 * $seg_count);
my(@start_count) = unpack("n" x
$seg_count, $buf);
#print "Start Count: ", join("\t",@start_count), "\n";
read($fh, $buf, 2 * $seg_count);
my(@id_delta) = unpack("n" x
$seg_count, $buf);
#print "idDelta: ", join("\t",@id_delta), "\n";
read($fh, $buf, 2 * $seg_count);
my(@id_range_offset) = unpack("n" x
$seg_count, $buf);
#print "idRangeOffset: ", join("\t",@id_range_offset), "\n";
my $num = read($fh, $buf, $length - ($seg_count * 8) - 16);
my (@glyph_id) = unpack("n" x
($num / 2), $buf);
# for(my $i = 0; $i <@end_count; $i++){
# if ( ($INDEX <= $end_count[$i]) && ($INDEX >= $start_count[$i]) ){
# if ($id_range_offset[$i] == 0){
# return $INDEX + $id_delta[$i] -($id_delta[$i]> 32767 ? 65536: 0);
# my $offset = $INDEX - $start_count[$i];
# print "OFFSET ===== $offset\n";
# if ($glyph_id[$offset] == 0){
# return $glyph_id[$offset] + $id_delta[$i] - ($id_delta[$i]> 32767 ? 65536: 0);
for ( my $i = 0; $i < 256; $i++){
for (my $j = 0; $j <$seg_count; $j++){
if ($end_count[$j] >= $i && $start_count[$j] <= $i){
#print "ID RANGE OFFSET $id_range_offset[$j]", "\n";
if ($id_range_offset[$j] != 0){
$self->{glyphs
}->[$i] = $glyph_id[$id_range_offset[$j]/2 + ($i - $start_count[$j]) - ($seg_count - $j)];
$self->{glyphs
}->[$i] = ($id_delta[$j] + $i) % 65536;
if (!defined($self->{glyphs
}->[$i])){
#$self->{glyphs}->[$i] = $glyph_id[0];
$self->{glyphs
}->[$i] = 0;
#print "Char $i -> \t\t $self->{glyphs}->[$i]\n";
my $fh = $self->get_file_handle();
seek($fh, $self->{table
}->{"hhea"}, 0);
read($fh, $buf, 36) == 36 || die "reading hhea table";
my($h_num) = unpack("x34n", $buf);
seek($fh, $self->{table
}->{"hmtx"}, 0);
read($fh, $buf, 4 * $num) == 4 * $num || die "reading hmtx table";
my (@h_temp) = unpack("n" x
(2 * $num), $buf);
# print "******@h_temp\n";
for (my $i = 0; $i < @h_temp; $i +=2){
push (@advanced_width,$h_temp[$i]);
for (my $i = 1; $i < @h_temp; $i +=2){
#print @advanced_width, "\n";
if ($index > @lsb){$index = @lsb;}
my $a = $advanced_width[$index] - ($advanced_width[$index] > 32768 ?
65536 : 0);
my $l = $lsb[$index] - ($lsb[$index] > 32768 ?
65536 :0);
if (defined($self->{leading
})){
#$self->{leading} = $self->_get_leading();
my $fh = $self->get_file_handle();
# Get the adress of the OS/2 table
my $add = $self->get_table_address('OS/2');
#Leading is sTypoLineGap in OS/2 table
read($fh, $buf, 74) == 74 || die "reading OS/2 table";
my ($leading) = unpack("x72n", $buf);
#print join(" ",@panose), "\n";
return $leading - ($leading > 32768 ?
65536 : 0);
my $add = $self->get_table_address("head");
my $fh = $self->get_file_handle();
read($fh, $buf, 54) == 54 || die "reading head table";
my($units_per_em, $index_to_loc) = unpack("x18nx30n", $buf);
# print "Unit/EM: $units_per_em\tIndex_to_loc: $index_to_loc\n\n";
if (defined($self->{ascent
})){
#$self->{ascent} = $self->_get_ascent();
my $fh = $self->get_file_handle();
# Get the adress of the OS/2 table
my $add = $self->get_table_address('OS/2');
# Ascent is is sTypoAscender in OS/2 table
read($fh, $buf, 70) == 70 || die "reading OS/2 table";
my ($ascent) = unpack("x68n", $buf);
#print join(" ",@panose), "\n";
return $ascent - ($ascent > 32768 ?
65536 : 0);
if (defined($self->{descent
})){
#$self->{descent} = $self->_get_descent();
my $fh = $self->get_file_handle();
my $add = $self->get_table_address('OS/2');
read($fh, $buf, 74) == 74 || die "reading OS/2 table";
my ($ascent, $descent, $leading) =
$self->{ascent
} = $ascent - ($ascent > 32768 ?
65536 : 0);
$self->{descent
} = $descent - ($descent > 32768 ?
65536 :0);
$self->{leading
} = $leading - ($leading > 32768 ?
65536 :0);
if ( defined($self->{family
}) ){
$self->_parse_name_table();
if ( defined( $self->{subfamily
})){
return $self->{subfamily
};
$self->_parse_name_table();
return $self->{subfamily
};
my $fh = $self->get_file_handle();
if ( $PLATFORM_ID =="1" && $ENCODING_ID =="0"){
my $add = $self->get_table_address("name");
my ( $num , $offset) = unpack ("x2nn", $buf);
#print "*******NAME : Number of records, $num, Offset: $offset\n";
my ($copyright_offset, $font_family_name_offset,
$subfamily_offset, $id_offset,$full_name_offset,
$version_string_offset, $postscript_offset, $trademark_offset);
my ($copyright_length, $font_family_length,
$subfamily_length, $id_length, $full_name_length,
$version_length, $postscript_length, $trademark_length);
for (my $i = 0; $i < $num; $i++){
my ($id, $encoding, $language, $name_id, $length, $string_offset)
#print "****NAMERECORDS: $id, $encoding, $language, $name_id, $length, $string_offset\n";
if ( ($id == $PLATFORM_ID)&& # Windows??
($encoding == $ENCODING_ID) && #UGL??
($language == $LANGUAGE_ID)
if ($name_id == 0 ) { #Copyright
$copyright_offset = $string_offset;
$copyright_length = $length;
if ($name_id == 1 ) { # Familyname
$font_family_name_offset = $string_offset;
$font_family_length = $length;
if ($name_id == 2 ) { # Subfamily
$subfamily_offset = $string_offset;
$subfamily_length = $length;
if ($name_id == 3 ) { # Identifier
$id_offset = $string_offset;
if ($name_id == 4 ) { # Full name
$full_name_offset = $string_offset;
$full_name_length = $length;
if ($name_id == 5 ) { #version string
$version_string_offset = $string_offset;
$version_length = $length;
if ( $name_id == 6) { # Postscript name
$postscript_offset = $string_offset;
$postscript_length = $length;
if ($name_id == 7 ) { # Trademark
$trademark_offset = $string_offset;
$trademark_length = $length;
seek ( $fh, $self->get_table_address("name") + $offset + $copyright_offset, 0);
read ($fh, $buf, $copyright_length);
# print "COPYRIGHT: $buf\n\n";
seek ($fh, $self->get_table_address("name") + $offset + $font_family_name_offset, 0 );
read ( $fh, $buf, $font_family_length);
$self->{family
}= $self->_remove_white_space($buf, $font_family_length);
#print "\n****", "@char", "*****\n";
# print "FAMILY: $buf\n\n";
seek ($fh, $self->get_table_address('name') + $offset + $subfamily_offset, 0);
read ($fh, $buf, $subfamily_length);
#print "SUBFAMILY: $buf\n\n";
$self->{subfamily
} = $self->_remove_white_space($buf, $subfamily_length);
seek ( $fh, $self->get_table_address('name') + $offset +$id_offset, 0);
read ($fh, $buf, $id_length);
seek ( $fh, $self->get_table_address('name') + $offset +$full_name_offset, 0);
read ($fh, $buf, $full_name_length);
#print "FULL NAME: $buf\n\n";
seek ( $fh, $self->get_table_address('name') + $offset +$version_string_offset, 0);
read ($fh, $buf, $version_length);
#print "VERSION: $buf\n\n";
seek ( $fh, $self->get_table_address('name') + $offset +$postscript_offset, 0);
read ($fh, $buf, $postscript_length);
#print "Postscript: $buf\n\n";
seek ( $fh, $self->get_table_address('name') + $offset +$trademark_offset, 0);
read ($fh, $buf, $trademark_length);
#print "TRADEMARK: $buf\n\n";
sub _remove_white_space
{
my $font_family_length = shift;
my @char = unpack("C*",$buf);
my $i = $font_family_length;
if (defined $char[$j+1]){
$s .= pack("C",$char[$j+1]);}