Slight change in api - returning one value from element handler will mean not to...
[librarian.git] / librarian / font-optimizer / ext / Font-TTF / lib / Font / TTF / Delta.pm
1 package Font::TTF::Delta;
2
3 =head1 NAME 
4
5 Font::TTF::Delta - Opentype Device tables
6
7 =head1 DESCRIPTION
8
9 Each device table corresponds to a set of deltas for a particular point over
10 a range of ppem values.
11
12 =item first
13
14 The first ppem value in the range
15
16 =item last
17
18 The last ppem value in the range
19
20 =item val
21
22 This is an array of deltas corresponding to each ppem in the range between
23 first and last inclusive.
24
25 =item fmt
26
27 This is the fmt used (log2 of number bits per value) when the device table was
28 read. It is recalculated on output.
29
30 =head1 METHODS
31
32 =cut
33
34 use strict;
35 use Font::TTF::Utils;
36
37 =head2 new
38
39 Creates a new device table
40
41 =cut
42
43 sub new
44 {
45     my ($class) = @_;
46     my ($self) = {};
47
48     bless $self, $class;
49 }
50
51
52 =head2 read
53
54 Reads a device table from the given IO object at the current location
55
56 =cut
57
58 sub read
59 {
60     my ($self, $fh) = @_;
61     my ($dat, $fmt, $num, $i, $j, $mask);
62
63     $fh->read($dat, 6);
64     ($self->{'first'}, $self->{'last'}, $fmt) = TTF_Unpack("S3", $dat);
65     $self->{'fmt'} = $fmt;
66
67     $fmt = 1 << $fmt;
68     $num = ((($self->{'last'} - $self->{'first'} + 1) * $fmt) + 15) >> 8;
69     $fh->read($dat, $num);
70
71     $mask = (0xffff << (16 - $fmt)) & 0xffff;
72     $j = 0;
73     for ($i = $self->{'first'}; $i <= $self->{'last'}; $i++)
74     {
75         if ($j == 0)
76         {
77             $num = TTF_Unpack("S", substr($dat, 0, 2));
78             substr($dat, 0, 2) = '';
79         }
80         push (@{$self->{'val'}}, ($num & $mask) >> (16 - $fmt));
81         $num <<= $fmt;
82         $j += $fmt;
83         $j = 0 if ($j >= 16);
84     }
85     $self;
86 }
87
88
89 =head2 out($fh, $style)
90
91 Outputs a device table to the given IO object at the current location, or just
92 returns the data to be output if $style != 0
93
94 =cut
95
96 sub out
97 {
98     my ($self, $fh, $style) = @_;
99     my ($dat, $fmt, $num, $mask, $j, $f, $out);
100
101     foreach $f (@{$self->{'val'}})
102     {
103         my ($tfmt) = $f > 0 ? $f + 1 : -$f;
104         $fmt = $tfmt if $tfmt > $fmt;
105     }
106
107     if ($fmt > 8)
108     { $fmt = 3; }
109     elsif ($fmt > 2)
110     { $fmt = 2; }
111     else
112     { $fmt = 1; }
113
114     $out = TTF_Pack("S3", $self->{'first'}, $self->{'last'}, $fmt);
115
116     $fmt = 1 << $fmt;
117     $mask = 0xffff >> (16 - $fmt);
118     $j = 0; $dat = 0;
119     foreach $f (@{$self->{'val'}})
120     {
121         $dat |= ($f & $mask) << (16 - $fmt - $j);
122         $j += $fmt;
123         if ($j >= 16)
124         {
125             $j = 0;
126             $out .= TTF_Pack("S", $dat);
127             $dat = 0;
128         }
129     }
130     $out .= pack('n', $dat) if ($j > 0);
131     $fh->print($out) unless $style;
132     $out;
133 }
134
135 =head2 $d->signature()
136
137 Returns a content based identifying string for this delta for
138 compression purposes
139
140 =cut
141
142 sub signature
143 {
144     my ($self) = @_;
145     return join (",", $self->{'first'}, $self->{'last'}, @{$self->{'val'}});
146 }
147
148
149 =head2 $d->out_xml($context)
150
151 Outputs a delta in XML
152
153 =cut
154
155 sub out_xml
156 {
157     my ($self, $context, $depth) = @_;
158     my ($fh) = $context->{'fh'};
159
160     $fh->printf("%s<delta first='%s' last='%s'>\n", $depth, $self->{'first'}, $self->{'last'});
161     $fh->print("$depth$context->{'indent'}" . join (' ', @{$self->{'val'}}) . "\n") if defined ($self->{'val'});
162     $fh->print("$depth</delta>\n");
163 }
164
165 =head1 AUTHOR
166
167 Martin Hosken Martin_Hosken@sil.org. See L<Font::TTF::Font> for copyright and
168 licensing.
169
170 =cut
171
172 1;
173