Epub: fixes #4164, #4166
[librarian.git] / src / librarian / font-optimizer / ext / Font-TTF / lib / Font / TTF / GDEF.pm
1 package Font::TTF::GDEF;
2
3 =head1 NAME
4
5 Font::TTF::GDEF - Opentype GDEF table support
6
7 =head1 DESCRIPTION
8
9 The GDEF table contains various global lists of information which are apparantly
10 used in other places in an OpenType renderer. But precisely where is open to
11 speculation...
12
13 =head1 INSTANCE VARIABLES
14
15 There are 4 tables in the GDEF table, each with their own structure:
16
17 =over 4
18
19 =item GLYPH
20
21 This is an L<Font::TTF::Coverage> Class Definition table containing information
22 as to what type each glyph is.
23
24 =item ATTACH
25
26 The attach table consists of a coverage table and then attachment points for
27 each glyph in the coverage table:
28
29 =over 8
30
31 =item COVERAGE
32
33 This is a coverage table
34
35 =item POINTS
36
37 This is an array of point elements. Each element is an array of curve points
38 corresponding to the attachment points on that glyph. The order of the curve points
39 in the array corresponds to the attachment point number specified in the MARKS
40 coverage table (see below).
41
42 =back
43
44 =item LIG
45
46 This contains the ligature caret positioning information for ligature glyphs
47
48 =over 8
49
50 =item COVERAGE
51
52 A coverage table to say which glyphs are ligatures
53
54 =item LIGS
55
56 An array of elements for each ligature. Each element is an array of information
57 for each caret position in the ligature (there being number of components - 1 of
58 these, generally)
59
60 =over 12
61
62 =item FMT
63
64 This is the format of the information and is important to provide the semantics
65 for the value. This value must be set correctly before output
66
67 =item VAL
68
69 The value which has meaning according to FMT
70
71 =item DEVICE
72
73 For FMT = 3, a device table is also referenced which is stored here
74
75 =back
76
77 =back
78
79 =item MARKS
80
81 Due to confusion in the GDEF specification, this field is currently withdrawn until
82 the confusion is resolved. That way, perhaps this stuff will work!
83
84 This class definition table stores the mark attachment point numbers for each
85 attachment mark, to indicate which attachment point the mark attaches to on its
86 base glyph.
87
88 =back
89
90
91 =head1 METHODS
92
93 =cut
94
95 use strict;
96 use Font::TTF::Table;
97 use Font::TTF::Utils;
98 use Font::TTF::Ttopen;
99 use vars qw(@ISA $new_gdef);
100
101 @ISA = qw(Font::TTF::Table);
102 $new_gdef = 1;
103
104 =head2 $t->read
105
106 Reads the table into the data structure
107
108 =cut
109
110 sub read
111 {
112     my ($self) = @_;
113     my ($fh) = $self->{' INFILE'};
114     my ($boff) = $self->{' OFFSET'};
115     my ($dat, $goff, $loff, $aoff, $moff, $r, $s, $bloc);
116
117     $self->SUPER::read or return $self;
118     $bloc = $fh->tell();
119     if ($new_gdef)
120     {
121         $fh->read($dat, 12);
122         ($self->{'Version'}, $goff, $aoff, $loff, $moff) = TTF_Unpack('fS4', $dat);
123     }
124     else
125     {
126         $fh->read($dat, 10);
127         ($self->{'Version'}, $goff, $aoff, $loff) = TTF_Unpack('fS3', $dat);
128     }
129
130     if ($goff > 0)
131     {
132         $fh->seek($goff + $boff, 0);
133         $self->{'GLYPH'} = Font::TTF::Coverage->new(0)->read($fh);
134     }
135
136     if ($new_gdef && $moff > 0 && $moff < $self->{' LENGTH'})
137     {
138         $fh->seek($moff + $boff, 0);
139         $self->{'MARKS'} = Font::TTF::Coverage->new(0)->read($fh);
140     }
141
142     if ($aoff > 0)
143     {
144         my ($off, $gcount, $pcount);
145         
146         $fh->seek($aoff + $boff, 0);
147         $fh->read($dat, 4);
148         ($off, $gcount) = TTF_Unpack('SS', $dat);
149         $fh->read($dat, $gcount << 1);
150
151         $fh->seek($aoff + $boff +  $off, 0);
152         $self->{'ATTACH'}{'COVERAGE'} = Font::TTF::Coverage->new(1)->read($fh);
153
154         foreach $r (TTF_Unpack('S*', $dat))
155         {
156             unless ($r)
157             {
158                 push (@{$self->{'ATTACH'}{'POINTS'}}, []);
159                 next;
160             }
161             $fh->seek($aoff + $boff + $r, 0);
162             $fh->read($dat, 2);
163             $pcount = TTF_Unpack('S', $dat);
164             $fh->read($dat, $pcount << 1);
165             push (@{$self->{'ATTACH'}{'POINTS'}}, [TTF_Unpack('S*', $dat)]);
166         }
167     }
168
169     if ($loff > 0)
170     {
171         my ($lcount, $off, $ccount, $srec, $comp);
172
173         $fh->seek($loff + $boff, 0);
174         $fh->read($dat, 4);
175         ($off, $lcount) = TTF_Unpack('SS', $dat);
176         $fh->read($dat, $lcount << 1);
177
178         $fh->seek($off + $loff + $boff, 0);
179         $self->{'LIG'}{'COVERAGE'} = Font::TTF::Coverage->new(1)->read($fh);
180
181         foreach $r (TTF_Unpack('S*', $dat))
182         {
183             $fh->seek($r + $loff + $boff, 0);
184             $fh->read($dat, 2);
185             $ccount = TTF_Unpack('S', $dat);
186             $fh->read($dat, $ccount << 1);
187
188             $srec = [];
189             foreach $s (TTF_Unpack('S*', $dat))
190             {
191                 $comp = {};
192                 $fh->seek($s + $r + $loff + $boff, 0);
193                 $fh->read($dat, 4);
194                 ($comp->{'FMT'}, $comp->{'VAL'}) = TTF_Unpack('S*', $dat);
195                 if ($comp->{'FMT'} == 3)
196                 {
197                     $fh->read($dat, 2);
198                     $off = TTF_Unpack('S', $dat);
199                     if (defined $self->{' CACHE'}{$off + $s + $r})
200                     { $comp->{'DEVICE'} = $self->{' CACHE'}{$off + $s + $r}; }
201                     else
202                     {
203                         $fh->seek($off + $s + $r + $loff + $boff, 0);
204                         $comp->{'DEVICE'} = Font::TTF::Delta->new->read($fh);
205                         $self->{' CACHE'}{$off + $s + $r} = $comp->{'DEVICE'};
206                     }
207                 }
208                 push (@$srec, $comp);
209             }
210             push (@{$self->{'LIG'}{'LIGS'}}, $srec);
211         }
212     }
213
214     $self;
215 }
216
217
218 =head2 $t->out($fh)
219
220 Writes out this table.
221
222 =cut
223
224 sub out
225 {
226     my ($self, $fh) = @_;
227     my ($goff, $aoff, $loff, $moff, @offs, $loc1, $coff, $loc);
228
229     return $self->SUPER::out($fh) unless $self->{' read'};
230
231     $loc = $fh->tell();
232     if ($new_gdef)
233     { $fh->print(TTF_Pack('fSSSS', $self->{'Version'}, 0, 0, 0, 0)); }
234     else
235     { $fh->print(TTF_Pack('fSSS', $self->{'Version'}, 0, 0, 0)); }
236
237     if (defined $self->{'GLYPH'})
238     {
239         $goff = $fh->tell() - $loc;
240         $self->{'GLYPH'}->out($fh);
241     }
242
243     if (defined $self->{'ATTACH'})
244     {
245         my ($r);
246         
247         $aoff = $fh->tell() - $loc;
248         $fh->print(pack('n*', (0) x ($#{$self->{'ATTACH'}{'POINTS'}} + 3)));
249         foreach $r (@{$self->{'ATTACH'}{'POINTS'}})
250         {
251             push (@offs, $fh->tell() - $loc - $aoff);
252             $fh->print(pack('n*', $#{$r} + 1, @$r));
253         }
254         $coff = $fh->tell() - $loc - $aoff;
255         $self->{'ATTACH'}{'COVERAGE'}->out($fh);
256         $loc1 = $fh->tell();
257         $fh->seek($aoff + $loc, 0);
258         $fh->print(pack('n*', $coff, $#offs + 1, @offs));
259         $fh->seek($loc1, 0);
260     }
261
262     if (defined $self->{'LIG'})
263     {
264         my (@reftables, $ltables, $i, $j, $out, $r, $s);
265
266         $ltables = {};
267         $loff = $fh->tell() - $loc;
268         $out = pack('n*',
269                         Font::TTF::Ttopen::ref_cache($self->{'LIG'}{'COVERAGE'}, $ltables, 0),
270                         $#{$self->{'LIG'}{'LIGS'}} + 1,
271                         (0) x ($#{$self->{'LIG'}{'LIGS'}} + 1));
272         push (@reftables, [$ltables, 0]);
273         $i = 0;
274         foreach $r (@{$self->{'LIG'}{'LIGS'}})
275         {
276             $ltables = {};
277             $loc1 = length($out);
278             substr($out, ($i << 1) + 4, 2) = TTF_Pack('S', $loc1);
279             $out .= pack('n*', $#{$r} + 1, (0) x ($#{$r} + 1));
280             @offs = (); $j = 0;
281             foreach $s (@$r)
282             {
283                 substr($out, ($j << 1) + 2 + $loc1, 2) =
284                         TTF_Pack('S', length($out) - $loc1);
285                 $out .= TTF_Pack('SS', $s->{'FMT'}, $s->{'VAL'});
286                 $out .= pack('n', Font::TTF::Ttopen::ref_cache($s->{'DEVICE'},
287                         $ltables, length($out))) if ($s->{'FMT'} == 3);
288                 $j++;
289             }
290             push (@reftables, [$ltables, $loc1]);
291             $i++;
292         }
293         Font::TTF::Ttopen::out_final($fh, $out, \@reftables);
294     }
295
296     if ($new_gdef && defined $self->{'MARKS'})
297     {
298         $moff = $fh->tell() - $loc;
299         $self->{'MARKS'}->out($fh);
300     }
301
302     $loc1 = $fh->tell();
303     $fh->seek($loc + 4, 0);
304     if ($new_gdef)
305     { $fh->print(TTF_Pack('S4', $goff, $aoff, $loff, $moff)); }
306     else
307     { $fh->print(TTF_Pack('S3', $goff, $aoff, $loff)); }
308     $fh->seek($loc1, 0);
309     $self;
310 }
311
312 1;
313