1 package Font::TTF::Coverage;
5 Font::TTF::Coverage - Opentype coverage and class definition objects
9 Coverage tables and class definition objects are virtually identical concepts
10 in OpenType. Their difference comes purely in their storage. Therefore we can
11 say that a coverage table is a class definition in which the class definition
12 for each glyph is the corresponding index in the coverage table. The resulting
13 data structure is that a Coverage table has the following fields:
17 A boolean to indicate whether this table is a coverage table (TRUE) or a
18 class definition (FALSE)
22 A hash of glyph ids against values (either coverage index or class value)
26 The storage format used is given here, but is recalculated when the table
31 A count of the elements in a coverage table for use with add. Each subsequent
32 addition is added with the current count and increments the count.
38 =head2 new($isCover [, vals])
40 Creates a new coverage table or class definition table, depending upon the
41 value of $isCover. if $isCover then vals may be a list of glyphs to include in order.
42 If no $isCover, then vals is a hash of glyphs against class values.
49 my ($isCover) = shift;
52 $self->{'cover'} = $isCover;
58 { $self->{'val'}{$v} = $self->{'count'}++; }
62 $self->{'val'} = {@_};
63 foreach (values %{$self->{'val'}}) {$self->{'max'} = $_ if $_ > $self->{'max'}}
71 Reads the coverage/class table from the given file handle
78 my ($dat, $fmt, $num, $i, $c);
81 ($fmt, $num) = unpack("n2", $dat);
82 $self->{'fmt'} = $fmt;
88 $fh->read($dat, $num << 1);
89 map {$self->{'val'}{$_} = $i++} unpack("n*", $dat);
92 $fh->read($dat, $num * 6);
93 for ($i = 0; $i < $num; $i++)
95 ($first, $last, $c) = unpack("n3", substr($dat, $i * 6, 6));
96 map {$self->{'val'}{$_} = $c++} ($first .. $last);
99 $self->{'count'} = $num;
104 ($num) = unpack("n", $dat);
105 $fh->read($dat, $num << 1);
106 map {$self->{'val'}{$first++} = $_; $self->{'max'} = $_ if ($_ > $self->{'max'})} unpack("n*", $dat);
109 $fh->read($dat, $num * 6);
110 for ($i = 0; $i < $num; $i++)
112 ($first, $last, $c) = unpack("n3", substr($dat, $i * 6, 6));
113 map {$self->{'val'}{$_} = $c} ($first .. $last);
114 $self->{'max'} = $c if ($c > $self->{'max'});
121 =head2 out($fh, $state)
123 Writes the coverage/class table to the given file handle. If $state is 1 then
124 the output string is returned rather than being output to a filehandle.
130 my ($self, $fh, $state) = @_;
131 my ($g, $eff, $grp, $out);
132 my ($shipout) = ($state ? sub {$out .= $_[0];} : sub {$fh->print($_[0]);});
133 my (@gids) = sort {$a <=> $b} keys %{$self->{'val'}};
135 $fmt = 1; $grp = 1; $eff = 0;
136 for ($i = 1; $i <= $#gids; $i++)
138 if ($self->{'val'}{$gids[$i]} < $self->{'val'}{$gids[$i-1]} && $self->{'cover'})
142 } elsif ($gids[$i] == $gids[$i-1] + 1 && ($self->{'cover'} || $self->{'val'}{$gids[$i]} == $self->{'val'}{$gids[$i-1]}))
147 $eff += $gids[$i] - $gids[$i-1] if (!$self->{'cover'});
150 # if ($self->{'cover'})
151 { $fmt = 2 if ($eff / $grp > 3); }
153 # { $fmt = 2 if ($grp > 1); }
155 if ($fmt == 1 && $self->{'cover'})
158 &$shipout(pack('n2', 1, scalar @gids));
159 &$shipout(pack('n*', @gids));
162 my ($last) = $gids[0];
163 &$shipout(pack("n3", 1, $last, $gids[-1] - $last + 1));
167 { &$shipout(pack('n*', (0) x ($g - $last - 1))); }
168 &$shipout(pack('n', $self->{'val'}{$g}));
173 my ($start, $end, $ind, $numloc, $endloc, $num);
174 &$shipout(pack("n2", 2, 0));
175 $numloc = $fh->tell() - 2 unless $state;
177 $start = 0; $end = 0; $num = 0;
178 while ($end < $#gids)
180 if ($gids[$end + 1] == $gids[$end] + 1
181 && $self->{'val'}{$gids[$end + 1]}
182 == $self->{'val'}{$gids[$end]}
183 + ($self->{'cover'} ? 1 : 0))
189 &$shipout(pack("n3", $gids[$start], $gids[$end],
190 $self->{'val'}{$gids[$start]}));
195 &$shipout(pack("n3", $gids[$start], $gids[$end],
196 $self->{'val'}{$gids[$start]}));
199 { substr($out, 2, 2) = pack('n', $num); }
202 $endloc = $fh->tell();
203 $fh->seek($numloc, 0);
204 $fh->print(pack("n", $num));
205 $fh->seek($endloc, 0);
208 return ($state ? $out : $self);
212 =head2 $c->add($glyphid[, $class])
214 Adds a glyph id to the coverage table incrementing the count so that each subsequent addition
215 has the next sequential number. Returns the index number of the glyphid added
221 my ($self, $gid, $class) = @_;
223 return $self->{'val'}{$gid} if (defined $self->{'val'}{$gid});
224 if ($self->{'cover'})
226 $self->{'val'}{$gid} = $self->{'count'};
227 return $self->{'count'}++;
231 $self->{'val'}{$gid} = $class || '0';
232 $self->{'max'} = $class if ($class > $self->{'max'});
240 Returns a vector of all the glyph ids covered by this coverage table or class
247 my ($vec, $range, $size);
251 if ($self->{'cover'})
252 { $range = 1; $size = 1; }
255 $range = $self->{'max'};
260 $range = $range >> 1;
262 $range = $self->{'max'} + 1;
264 foreach (keys %{$self->{'val'}})
265 { vec($vec, $_, $size) = $self->{'val'}{$_} > $range ? $range : $self->{'val'}{$_}; }
266 length($vec) . ":" . $vec;
268 $vec = join(";", map{"$_,$self->{'val'}{$_}"} keys %{$self->{'val'}});
273 Sorts the coverage table so that indexes are in ascending order of glyphid.
274 Returns a map such that $map[$new_index]=$old_index.
283 foreach (sort {$a <=> $b} keys %{$self->{'val'}})
285 push(@res, $self->{'val'}{$_});
286 $self->{'val'}{$_} = $i++;
291 =head2 $c->out_xml($context)
293 Outputs this coverage/class in XML
299 my ($self, $context, $depth) = @_;
300 my ($fh) = $context->{'fh'};
302 $fh->print("$depth<" . ($self->{'cover'} ? 'coverage' : 'class') . ">\n");
303 foreach $gid (sort {$a <=> $b} keys %{$self->{'val'}})
305 $fh->printf("$depth$context->{'indent'}<gref glyph='%s' val='%s'/>\n", $gid, $self->{'val'}{$gid});
307 $fh->print("$depth</" . ($self->{'cover'} ? 'coverage' : 'class') . ">\n");
317 Martin Hosken Martin_Hosken@sil.org. See L<Font::TTF::Font> for copyright and