1#======================================================================= 2# ____ ____ _____ _ ____ ___ ____ 3# | _ \| _ \| ___| _ _ / \ | _ \_ _| |___ \ 4# | |_) | | | | |_ (_) (_) / _ \ | |_) | | __) | 5# | __/| |_| | _| _ _ / ___ \| __/| | / __/ 6# |_| |____/|_| (_) (_) /_/ \_\_| |___| |_____| 7# 8# A Perl Module Chain to faciliate the Creation and Modification 9# of High-Quality "Portable Document Format (PDF)" Files. 10# 11#======================================================================= 12# 13# THIS IS A REUSED PERL MODULE, FOR PROPER LICENCING TERMS SEE BELOW: 14# 15# 16# Copyright Martin Hosken <Martin_Hosken@sil.org> 17# 18# No warranty or expression of effectiveness, least of all regarding 19# anyone's safety, is implied in this software or documentation. 20# 21# This specific module is licensed under the Perl Artistic License. 22# 23# 24# $Id: Coverage.pm,v 2.0 2005/11/16 02:16:00 areibens Exp $ 25# 26#======================================================================= 27package PDF::API3::Compat::API2::Basic::TTF::Coverage; 28 29=head1 TITLE 30 31PDF::API3::Compat::API2::Basic::TTF::Coverage - Opentype coverage and class definition objects 32 33=head1 DESCRIPTION 34 35Coverage tables and class definition objects are virtually identical concepts 36in OpenType. Their difference comes purely in their storage. Therefore we can 37say that a coverage table is a class definition in which the class definition 38for each glyph is the corresponding index in the coverage table. The resulting 39data structure is that a Coverage table has the following fields: 40 41=item cover 42 43A boolean to indicate whether this table is a coverage table (TRUE) or a 44class definition (FALSE) 45 46=item val 47 48A hash of glyph ids against values (either coverage index or class value) 49 50=item fmt 51 52The storage format used is given here, but is recalculated when the table 53is written out. 54 55=item count 56 57A count of the elements in a coverage table for use with add. Each subsequent 58addition is added with the current count and increments the count. 59 60=head1 METHODS 61 62=cut 63 64=head2 new($isCover [, vals]) 65 66Creates a new coverage table or class definition table, depending upon the 67value of $isCover. if $isCover then vals may be a list of glyphs to include in order. 68If no $isCover, then vals is a hash of glyphs against class values. 69 70=cut 71 72sub new 73{ 74 my ($class) = shift; 75 my ($isCover) = shift; 76 my ($self) = {}; 77 78 $self->{'cover'} = $isCover; 79 $self->{'count'} = 0; 80 if ($isCover) 81 { 82 my ($v); 83 foreach $v (@_) 84 { $self->{'val'}{$v} = $self->{'count'}++; } 85 } 86 else 87 { $self->{'val'} = {@_}; } 88 bless $self, $class; 89} 90 91 92=head2 read($fh) 93 94Reads the coverage/class table from the given file handle 95 96=cut 97 98sub read 99{ 100 my ($self, $fh) = @_; 101 my ($dat, $fmt, $num, $i, $c); 102 103 $fh->read($dat, 4); 104 ($fmt, $num) = unpack("n2", $dat); 105 $self->{'fmt'} = $fmt; 106 107 if ($self->{'cover'}) 108 { 109 if ($fmt == 1) 110 { 111 $fh->read($dat, $num << 1); 112 map {$self->{'val'}{$_} = $i++} unpack("n*", $dat); 113 } elsif ($fmt == 2) 114 { 115 $fh->read($dat, $num * 6); 116 for ($i = 0; $i < $num; $i++) 117 { 118 ($first, $last, $c) = unpack("n3", substr($dat, $i * 6, 6)); 119 map {$self->{'val'}{$_} = $c++} ($first .. $last); 120 } 121 } 122 } elsif ($fmt == 1) 123 { 124 $fh->read($dat, 2); 125 $first = $num; 126 ($num) = unpack("n", $dat); 127 $fh->read($dat, $num << 1); 128 map {$self->{'val'}{$first++} = $_} unpack("n*", $dat); 129 } elsif ($fmt == 2) 130 { 131 $fh->read($dat, $num * 6); 132 for ($i = 0; $i < $num; $i++) 133 { 134 ($first, $last, $c) = unpack("n3", substr($dat, $i * 6, 6)); 135 map {$self->{'val'}{$_} = $c} ($first .. $last); 136 } 137 } 138 $self; 139} 140 141 142=head2 out($fh, $state) 143 144Writes the coverage/class table to the given file handle. If $state is 1 then 145the output string is returned rather than being output to a filehandle. 146 147=cut 148 149sub out 150{ 151 my ($self, $fh, $state) = @_; 152 my ($g, $eff, $grp, $out); 153 my ($shipout) = ($state ? sub {$out .= $_[0];} : sub {$fh->print($_[0]);}); 154 my (@gids) = sort {$a <=> $b} keys %{$self->{'val'}}; 155 156 $fmt = 1; $grp = 1; 157 for ($i = 1; $i <= $#gids; $i++) 158 { 159 if ($self->{'val'}{$gids[$i]} < $self->{'val'}{$gids[$i-1]} && $self->{'cover'}) 160 { 161 $fmt = 2; 162 last; 163 } elsif ($gids[$i] == $gids[$i-1] + 1) 164 { $eff++; } 165 else 166 { $grp++; } 167 } 168 if ($self->{'cover'}) 169 { $fmt = 2 if ($eff / $grp > 4); } 170 else 171 { $fmt = 2 if ($grp > 1); } 172 173 if ($fmt == 1 && $self->{'cover'}) 174 { 175 my ($last) = 0; 176 &$shipout(pack('n2', 1, scalar @gids)); 177 &$shipout(pack('n*', @gids)); 178 } elsif ($fmt == 1) 179 { 180 my ($last) = $gids[0]; 181 &$shipout(pack("n3", 1, $last, $gids[-1] - $last + 1)); 182 foreach $g (@gids) 183 { 184 if ($g > $last + 1) 185 { &$shipout(pack('n*', 0 x ($g - $last - 1))); } 186 &$shipout(pack('n', $self->{'val'}{$g})); 187 $last = $g; 188 } 189 } else 190 { 191 my ($start, $end, $ind, $numloc, $endloc, $num); 192 &$shipout(pack("n2", 2, 0)); 193 $numloc = $fh->tell() - 2 unless $state; 194 195 $start = 0; $end = 0; $num = 0; 196 while ($end < $#gids) 197 { 198 if ($gids[$end + 1] == $gids[$end] + 1 199 && $self->{'val'}{$gids[$end + 1]} 200 == $self->{'val'}{$gids[$end]} 201 + ($self->{'cover'} ? 1 : 0)) 202 { 203 $end++; 204 next; 205 } 206 207 &$shipout(pack("n3", $gids[$start], $gids[$end], 208 $self->{'val'}{$gids[$start]})); 209 $start = $end + 1; 210 $end++; 211 $num++; 212 } 213 &$shipout(pack("n3", $gids[$start], $gids[$end], 214 $self->{'val'}{$gids[$start]})); 215 $num++; 216 if ($state) 217 { substr($out, 2, 2) = pack('n', $num); } 218 else 219 { 220 $endloc = $fh->tell(); 221 $fh->seek($numloc, 0); 222 $fh->print(pack("n", $num)); 223 $fh->seek($endloc, 0); 224 } 225 } 226 return ($state ? $out : $self); 227} 228 229 230=head2 $c->add($glyphid) 231 232Adds a glyph id to the coverage table incrementing the count so that each subsequent addition 233has the next sequential number. Returns the index number of the glyphid added 234 235=cut 236 237sub add 238{ 239 my ($self, $gid) = @_; 240 241 return $self->{'val'}{$gid} if (defined $self->{'val'}{$gid}); 242 $self->{'val'}{$gid} = $self->{'count'}; 243 return $self->{'count'}++; 244} 245 246 247=head2 $c->out_xml($context) 248 249Outputs this coverage/class in XML 250 251=cut 252 253sub out_xml 254{ 255 my ($self, $context, $depth) = @_; 256 my ($fh) = $context->{'fh'}; 257 258 $fh->print("$depth<" . ($self->{'cover'} ? 'coverage' : 'class') . ">\n"); 259 foreach $gid (sort {$a <=> $b} keys %{$self->{'val'}}) 260 { 261 $fh->printf("$depth$context->{'indent'}<gref glyph='%s' val='%s'/>\n", $gid, $self->{'val'}{$gid}); 262 } 263 $fh->print("$depth</" . ($self->{'cover'} ? 'coverage' : 'class') . ">\n"); 264 $self; 265} 266 267sub release 268{ return( $_[0] ); } 269 270 271=head1 AUTHOR 272 273Martin Hosken Martin_Hosken@sil.org. See L<PDF::API3::Compat::API2::Basic::TTF::Font> for copyright and 274licensing. 275 276=cut 277 2781; 279 280