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