1package Font::TTF::Post; 2 3=head1 NAME 4 5Font::TTF::Post - Holds the Postscript names for each glyph 6 7=head1 DESCRIPTION 8 9Holds the postscript names for glyphs. Note that they are not held as an 10array, but as indexes into two lists. The first list is the standard Postscript 11name list defined by the TrueType standard. The second comes from the font 12directly. 13 14Looking up a glyph from a Postscript name or a name from a glyph number is 15achieved through methods rather than variable lookup. 16 17This class handles PostScript table types of 1, 2, 2.5 & 3, but not version 4. 18Support for version 2.5 is as per Apple spec rather than MS. 19 20The way to look up Postscript names or glyphs is: 21 22 $pname = $f->{'post'}{'VAL'}[$gnum]; 23 $gnum = $f->{'post'}{'STRINGS'}{$pname}; 24 25=head1 INSTANCE VARIABLES 26 27Due to different systems having different limitations, there are various class 28variables available to control what post table types can be written. 29 30=over 4 31 32=item $Font::TTF::Post::no25 33 34If set tells Font::TTF::Post::out to use table type 2 instead of 2.5 in case apps 35cannot handle version 2.5. 36 37=item VAL 38 39Contains an array indexed by glyph number of Postscript names. This is used when 40writing out a font. 41 42=item STRINGS 43 44An associative array of Postscript names mapping to the highest glyph with that 45name. These may not be in sync with VAL. 46 47=back 48 49In addition there are the standard introductory variables defined in the 50standard: 51 52 FormatType 53 italicAngle 54 underlinePosition 55 underlineThickness 56 isFixedPitch 57 minMemType42 58 maxMemType42 59 minMemType1 60 maxMemType1 61 62=head1 METHODS 63 64=cut 65 66use strict; 67use vars qw(@ISA @base_set %base_set %fields $VERSION $no25 @field_info @base_set); 68require Font::TTF::Table; 69use Font::TTF::Utils; 70 71$no25 = 1; # officially deprecated format 2.5 tables in MS spec 1.3 72 73@ISA = qw(Font::TTF::Table); 74@field_info = ( 75 'FormatType' => 'f', 76 'italicAngle' => 'f', 77 'underlinePosition' => 's', 78 'underlineThickness' => 's', 79 'isFixedPitch' => 'L', 80 'minMemType42' => 'L', 81 'maxMemType42' => 'L', 82 'minMemType1' => 'L', 83 'maxMemType1' => 'L'); 84@base_set = qw(.notdef .null nonmarkingreturn space exclam quotedbl numbersign dollar percent ampersand quotesingle 85 parenleft parenright asterisk plus comma hyphen period slash zero one two three four five six 86 seven eight nine colon semicolon less equal greater question at A B C D E F G H I J K L M N O P Q 87 R S T U V W X Y Z bracketleft backslash bracketright asciicircum underscore grave a b c d e f g h 88 i j k l m n o p q r s t u v w x y z braceleft bar braceright asciitilde Adieresis Aring Ccedilla 89 Eacute Ntilde Odieresis Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute 90 egrave ecircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve ocircumflex 91 odieresis otilde uacute ugrave ucircumflex udieresis dagger degree cent sterling section bullet 92 paragraph germandbls registered copyright trademark acute dieresis notequal AE Oslash infinity 93 plusminus lessequal greaterequal yen mu partialdiff summation product pi integral ordfeminine 94 ordmasculine Omega ae oslash questiondown exclamdown logicalnot radical florin approxequal 95 Delta guillemotleft guillemotright ellipsis nonbreakingspace Agrave Atilde Otilde OE oe endash emdash 96 quotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis fraction currency 97 guilsinglleft guilsinglright fi fl daggerdbl periodcentered quotesinglbase quotedblbase perthousand 98 Acircumflex Ecircumflex Aacute Edieresis Egrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex 99 apple Ograve Uacute Ucircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent 100 ring cedilla hungarumlaut ogonek caron Lslash lslash Scaron scaron Zcaron zcaron brokenbar Eth eth 101 Yacute yacute Thorn thorn minus multiply onesuperior twosuperior threesuperior onehalf onequarter 102 threequarters franc Gbreve gbreve Idotaccent Scedilla scedilla Cacute cacute Ccaron ccaron dcroat); 103 104$VERSION = 0.01; # MJPH 5-AUG-1998 Re-organise data structures 105 106sub init 107{ 108 my ($k, $v, $c, $i); 109 for ($i = 0; $i < $#field_info; $i += 2) 110 { 111 ($k, $v, $c) = TTF_Init_Fields($field_info[$i], $c, $field_info[$i + 1]); 112 next unless defined $k && $k ne ""; 113 $fields{$k} = $v; 114 } 115 $i = 0; 116 %base_set = map {$_ => $i++} @base_set; 117} 118 119 120=head2 $t->read 121 122Reads the Postscript table into memory from disk 123 124=cut 125 126sub read 127{ 128 my ($self) = @_; 129 $self->SUPER::read or return $self; 130 131 my ($dat, $dat1, $i, $off, $c, $maxoff, $form, $angle, $numGlyphs); 132 my ($fh) = $self->{' INFILE'}; 133 134 $numGlyphs = $self->{' PARENT'}{'maxp'}{'numGlyphs'}; 135 init unless ($fields{'FormatType'}); 136 $fh->read($dat, 32); 137 TTF_Read_Fields($self, $dat, \%fields); 138 139 if (int($self->{'FormatType'} + .5) == 1) 140 { 141 for ($i = 0; $i < 258; $i++) 142 { 143 $self->{'VAL'}[$i] = $base_set[$i]; 144 $self->{'STRINGS'}{$base_set[$i]} = $i unless (defined $self->{'STRINGS'}{$base_set[$i]}); 145 } 146 } elsif (int($self->{'FormatType'} * 2 + .1) == 5) 147 { 148 $fh->read($dat, 2); 149 $numGlyphs = unpack("n", $dat); 150 $fh->read($dat, $numGlyphs); 151 for ($i = 0; $i < $numGlyphs; $i++) 152 { 153 $off = unpack("c", substr($dat, $i, 1)); 154 $self->{'VAL'}[$i] = $base_set[$i + $off]; 155 $self->{'STRINGS'}{$base_set[$i + $off]} = $i unless (defined $self->{'STRINGS'}{$base_set[$i + $off]}); 156 } 157 } elsif (int($self->{'FormatType'} + .5) == 2) 158 { 159 my (@strings); 160 161 $fh->read($dat, ($numGlyphs + 1) << 1); 162 for ($i = 0; $i < $numGlyphs; $i++) 163 { 164 $off = unpack("n", substr($dat, ($i + 1) << 1, 2)); 165 $maxoff = $off if (!defined $maxoff || $off > $maxoff); 166 } 167 for ($i = 0; $i < $maxoff - 257; $i++) 168 { 169 $fh->read($dat1, 1); 170 $off = unpack("C", $dat1); 171 $fh->read($dat1, $off); 172 $strings[$i] = $dat1; 173 } 174 for ($i = 0; $i < $numGlyphs; $i++) 175 { 176 $off = unpack("n", substr($dat, ($i + 1) << 1, 2)); 177 if ($off > 257) 178 { 179 $self->{'VAL'}[$i] = $strings[$off - 258]; 180 $self->{'STRINGS'}{$strings[$off - 258]} = $i; 181 } 182 else 183 { 184 $self->{'VAL'}[$i] = $base_set[$off]; 185 $self->{'STRINGS'}{$base_set[$off]} = $i unless (defined $self->{'STRINGS'}{$base_set[$off]}); 186 } 187 } 188 } 189 $self; 190} 191 192 193=head2 $t->out($fh) 194 195Writes out a new Postscript name table from memory or copies from disk 196 197=cut 198 199sub out 200{ 201 my ($self, $fh) = @_; 202 my ($i, $num); 203 204 return $self->SUPER::out($fh) unless $self->{' read'}; 205 206 $num = $self->{' PARENT'}{'maxp'}{'numGlyphs'}; 207 208 init unless ($fields{'FormatType'}); 209 210 for ($i = $#{$self->{'VAL'}}; !defined $self->{'VAL'}[$i] && $i > 0; $i--) 211 { pop(@{$self->{'VAL'}}); } 212 if ($#{$self->{'VAL'}} < 0) 213 { $self->{'FormatType'} = 3; } 214 else 215 { 216 $self->{'FormatType'} = 1; 217 for ($i = 0; $i < $num; $i++) 218 { 219 if (!defined $base_set{$self->{'VAL'}[$i]}) 220 { 221 $self->{'FormatType'} = 2; 222 last; 223 } 224 elsif ($base_set{$self->{'VAL'}[$i]} != $i) 225 { $self->{'FormatType'} = ($no25 ? 2 : 2.5); } 226 } 227 } 228 229 $fh->print(TTF_Out_Fields($self, \%fields, 32)); 230 231 return $self if (int($self->{'FormatType'} + .4) == 3); 232 233 if (int($self->{'FormatType'} + .5) == 2) 234 { 235 my (@ind); 236 my ($count) = 0; 237 238 $fh->print(pack("n", $num)); 239 for ($i = 0; $i < $num; $i++) 240 { 241 if (defined $base_set{$self->{'VAL'}[$i]}) 242 { $fh->print(pack("n", $base_set{$self->{'VAL'}[$i]})); } 243 else 244 { 245 $fh->print(pack("n", $count + 258)); 246 $ind[$count++] = $i; 247 } 248 } 249 for ($i = 0; $i < $count; $i++) 250 { 251 $fh->print(pack("C", length($self->{'VAL'}[$ind[$i]]))); 252 $fh->print($self->{'VAL'}[$ind[$i]]); 253 } 254 } elsif (int($self->{'FormatType'} * 2 + .5) == 5) 255 { 256 $fh->print(pack("n", $num)); 257 for ($i = 0; $i < $num; $i++) 258 { $fh->print(pack("c", defined $base_set{$self->{'VAL'}[$i]} ? 259 $base_set{$self->{'VAL'}[$i]} - $i : -$i)); } 260 } 261 262 $self; 263} 264 265 266=head2 $t->XML_element($context, $depth, $key, $val) 267 268Outputs the names as one block of XML 269 270=cut 271 272sub XML_element 273{ 274 my ($self) = shift; 275 my ($context, $depth, $key, $val) = @_; 276 my ($fh) = $context->{'fh'}; 277 my ($i); 278 279 return $self->SUPER::XML_element(@_) unless ($key eq 'STRINGS' || $key eq 'VAL'); 280 return unless ($key eq 'VAL'); 281 282 $fh->print("$depth<names>\n"); 283 for ($i = 0; $i <= $#{$self->{'VAL'}}; $i++) 284 { $fh->print("$depth$context->{'indent'}<name post='$self->{'VAL'}[$i]' gid='$i'/>\n"); } 285 $fh->print("$depth</names>\n"); 286 $self; 287} 288 289=head2 $t->minsize() 290 291Returns the minimum size this table can be. If it is smaller than this, then the table 292must be bad and should be deleted or whatever. 293 294=cut 295 296sub minsize 297{ 298 return 32; 299} 300 3011; 302 303=head1 BUGS 304 305=over 4 306 307=item * 308 309No support for type 4 tables 310 311=back 312 313=head1 AUTHOR 314 315Martin Hosken L<http://scripts.sil.org/FontUtils>. 316 317 318=head1 LICENSING 319 320Copyright (c) 1998-2016, SIL International (http://www.sil.org) 321 322This module is released under the terms of the Artistic License 2.0. 323For details, see the full text of the license in the file LICENSE. 324 325 326 327=cut 328 329 330