1 package Font::TTF::GrFeat;
 
   5 Font::TTF::GrFeat - Graphite Font Features
 
   9 =head1 INSTANCE VARIABLES
 
  17 An array of hashes of the following form
 
  27 name index in name table
 
  35 the default setting number
 
  39 hash of setting number against name string index
 
  54 require Font::TTF::Table;
 
  56 @ISA = qw(Font::TTF::Table);
 
  60 Reads the features from the TTF file into memory
 
  67         my ($featureCount, $features);
 
  69         return $self if $self->{' read'};
 
  70         $self->SUPER::read_dat or return $self;
 
  72         ($self->{'version'}, $featureCount) = TTF_Unpack("vS", $self->{' dat'});
 
  75         foreach (1 .. $featureCount) {
 
  76                 my ($feature, $nSettings, $settingTable, $featureFlags, $nameIndex, $reserved);
 
  77                 if ($self->{'version'} == 1)
 
  79                         ($feature, $nSettings, $settingTable, $featureFlags, $nameIndex)
 
  80                                 = TTF_Unpack("SSLSS", substr($self->{' dat'}, $_ * 12, 12));
 
  81                         #The version 1 Feat table ends with a feature (id 1) named NoName
 
  82                         #with zero settings but with an offset to the last entry in the setting
 
  83                         #array. This last setting has id 0 and an invalid name id. This last
 
  84                         #feature is changed to have one setting.
 
  85                         if ($_ == $featureCount && $nSettings == 0) {$nSettings = 1;}
 
  88                         {($feature, $nSettings, $reserved, $settingTable, $featureFlags, $nameIndex)
 
  89                                 = TTF_Unpack("LSSLSS", substr($self->{' dat'}, 12 + ($_ - 1) * 16, 16))};
 
  92                                 'feature'       => $feature,
 
  96                 #interpret the featureFlags & store settings
 
  97                 $feature->{'exclusive'} = (($featureFlags & 0x8000) != 0);
 
  99                 my @settings = TTF_Unpack("S*", substr($self->{' dat'}, $settingTable, $nSettings * 4));
 
 100                 if ($featureFlags & 0x4000)
 
 101                         {$feature->{'default'} = $featureFlags & 0x00FF;}
 
 103                         {$feature->{'default'} = @settings[0];}
 
 104                 $feature->{'settings'} = {@settings};
 
 106                 push(@$features, $feature);
 
 109         $self->{'features'} = $features;
 
 111         delete $self->{' dat'}; # no longer needed, and may become obsolete
 
 112         $self->{' read'} = 1;
 
 118 Writes the features to a TTF file
 
 124         my ($self, $fh) = @_;
 
 125         my ($features, $numFeatures, $settings, $featureFlags, $featuresData, $settingsData);
 
 127         return $self->SUPER::out($fh) unless $self->{' read'};
 
 129         $features = $self->{'features'};
 
 130         $numFeatures = @$features;
 
 131         $featuresData, $settingsData = ('', '');
 
 133         foreach (@$features) {
 
 134                 $settings = $_->{'settings'};
 
 135                 $featureFlags = ($_->{'exclusive'} ? 0x8000 : 0x0000);
 
 137 #               output default setting first instead of using the featureFlags (as done below)
 
 138 #               $featureFlags = ($_->{'exclusive'} ? 0x8000 : 0x0000) |
 
 139 #                                                               ($_->{'default'} != 0 ? 0x4000 | ($_->{'default'} & 0x00FF) 
 
 141                 if ($self->{'version'} == 1)
 
 143                         $featuresData .= TTF_Pack("SSLSS",
 
 145                                                                                 scalar keys %$settings,
 
 146                                                                                 12 + 12 * $numFeatures + length $settingsData,
 
 152                         $featuresData .= TTF_Pack("LSSLSS",
 
 154                                                                                 scalar keys %$settings,
 
 156                                                                                 12 + 16 * $numFeatures + length $settingsData,
 
 161                 #output default setting first
 
 162                 #the settings may not be in their original order
 
 163                 my $defaultSetting = $_->{'default'};
 
 164                 $settingsData .= TTF_Pack("SS", $defaultSetting, $settings->{$defaultSetting});
 
 165                 foreach (sort {$a <=> $b} keys %$settings) {
 
 166                         if ($_ == $defaultSetting) {next;} #skip default setting
 
 167                         $settingsData .= TTF_Pack("SS", $_, $settings->{$_});
 
 171         $fh->print(TTF_Pack("vSSL", $self->{'version'}, $numFeatures, 0, 0));
 
 172         $fh->print($featuresData);
 
 173         $fh->print($settingsData);
 
 178 =head2 $t->print($fh)
 
 180 Prints a human-readable representation of the table
 
 186         my ($self, $fh) = @_;
 
 187         my ($names, $features, $settings);
 
 191         $names = $self->{' PARENT'}->{'name'};
 
 194         $fh = 'STDOUT' unless defined $fh;
 
 196         $features = $self->{'features'};
 
 197         foreach (@$features) {
 
 198                 $fh->printf("Feature %d, %s, default: %d name %d # '%s'\n",
 
 200                                         ($_->{'exclusive'} ? "exclusive" : "additive"),
 
 203                                         $names->{'strings'}[$_->{'name'}][3][1]{1033});
 
 204                 $settings = $_->{'settings'};
 
 205                 foreach (sort { $a <=> $b } keys %$settings) {
 
 206                         $fh->printf("\tSetting %d, name %d # '%s'\n",
 
 207                                                 $_, $settings->{$_}, $names->{'strings'}[$settings->{$_}][3][1]{1033});
 
 216         my ($self, $feature, $setting) = @_;
 
 220         my $names = $self->{' PARENT'}->{'name'};
 
 223         my $features = $self->{'features'};
 
 224         my ($featureEntry) = grep { $_->{'feature'} == $feature } @$features;
 
 225         my $featureName = $names->{'strings'}[$featureEntry->{'name'}][3][1]{1033};
 
 226         my $settingName = $featureEntry->{'exclusive'}
 
 227                         ? $names->{'strings'}[$featureEntry->{'settings'}->{$setting}][3][1]{1033}
 
 228                         : $names->{'strings'}[$featureEntry->{'settings'}->{$setting & ~1}][3][1]{1033}
 
 229                                 . (($setting & 1) == 0 ? " On" : " Off");
 
 231         ($featureName, $settingName);
 
 238 The version 1 Feat table ends with a feature (id 1) named NoName
 
 239 with zero settings but with an offset to the last entry in the setting
 
 240 array. This last setting has id 0 and an invalid name id. This last
 
 241 feature is changed to have one setting.
 
 245 Alan Ward (derived from Jonathan Kew's Feat.pm).
 
 246 See L<Font::TTF::Font> for copyright and licensing.