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.