File Coverage

blib/lib/Font/TTF/Glyph.pm
Criterion Covered Total %
statement 106 351 30.2
branch 40 196 20.4
condition 3 65 4.6
subroutine 10 18 55.5
pod 11 14 78.5
total 170 644 26.4


line stmt bran cond sub pod time code
1             package Font::TTF::Glyph;
2              
3             =head1 NAME
4              
5             Font::TTF::Glyph - Holds a information for a single glyph
6              
7             =head1 DESCRIPTION
8              
9             This is a single glyph description as held in a TT font. On creation only its
10             header is read. Thus you can get the bounding box of each glyph without having
11             to read all the other information.
12              
13             =head1 INSTANCE VARIABLES
14              
15             In addition to the named variables in a glyph header (C etc.), there are
16             also all capital instance variables for holding working information, mostly
17             from the location table.
18              
19             =head2 Variables for all glyphs:
20              
21             The standard attributes each glyph has are:
22              
23             =over 4
24              
25             =item numberOfContours
26              
27             For simple glyphs this will be the count of contours. For compound glyphs this will be -1.
28              
29             =item xMin
30              
31             =item yMin
32              
33             =item xMax
34              
35             =item yMax
36              
37             These identify the bounding box of the glyph.
38              
39             =back
40              
41             There are also other, derived, instance variables for each glyph which are read
42             when the whole glyph is read (via C):
43              
44             =over 4
45              
46             =item instLen
47              
48             Number of bytes in the hinting instructions (Warning this variable is deprecated,
49             use C{'hints'})> instead).
50              
51             =item hints
52              
53             The string containing the hinting code for the glyph
54              
55             =back
56              
57             =head2 Variables for simple glyphs (numberOfContours E= 0):
58              
59             =over 4
60              
61             =item endPoints
62              
63             An array of endpoints for each contour in the glyph. There are
64             C contours in a glyph. The number of points in a glyph is
65             equal to the highest endpoint of a contour.
66              
67             =item numPoints
68              
69             This is a generated value which contains the total number of points for this simple glyph.
70              
71             =back
72              
73             There are also a number of arrays indexed by point number:
74              
75             =over 4
76              
77             =item flags
78              
79             The flags associated with reading this point. The flags for a point are
80             recalculated for a point when it is Cd. Thus the flags are not very
81             useful. The only important bit is bit 0 which indicates whether the point is
82             an 'on' curve point, or an 'off' curve point.
83              
84             =item x
85              
86             The absolute x co-ordinate of the point.
87              
88             =item y
89              
90             The absolute y co-ordinate of the point
91              
92             =back
93              
94             =head2 Variables for compound glyphs (numberOfContours == -1):
95              
96             =over 4
97              
98             =item metric
99              
100             This holds the component number (not its glyph number) of the component from
101             which the metrics for this glyph should be taken.
102              
103             =item comps
104              
105             This is an array of hashes for each component. Each hash has a number of
106             elements:
107              
108             =over 4
109              
110             =item glyph
111              
112             The glyph number of the glyph which comprises this component of the composite.
113             NOTE: In some badly generated fonts, C may contain a numerical value
114             but that glyph might not actually exist in the font file. This could
115             occur in any glyph, but is particularly likely for glyphs that have
116             no strokes, such as SPACE, U+00A0 NO-BREAK SPACE, or
117             U+200B ZERO WIDTH SPACE.
118              
119             =item args
120              
121             An array of two arguments which may be an x, y co-ordinate or two attachment
122             points (one on the base glyph the other on the component). See flags for details.
123              
124             =item flag
125              
126             The flag for this component
127              
128             =item scale
129              
130             A 4 number array for component scaling. This allows stretching, rotating, etc.
131             Note that scaling applies to placement co-ordinates (rather than attachment points)
132             before locating rather than after.
133              
134             =back
135              
136             =item numPoints
137              
138             This is a generated value which contains the number of components read in for this
139             compound glyph.
140              
141             =back
142              
143             =head2 Private instance variables:
144              
145             =over 4
146              
147             =item INFILE (P)
148              
149             The input file form which to read any information
150              
151             =item LOC (P)
152              
153             Location relative to the start of the glyf table in the read file
154              
155             =item BASE (P)
156              
157             The location of the glyf table in the read file
158              
159             =item LEN (P)
160              
161             This is the number of bytes required by the glyph. It should be kept up to date
162             by calling the C method whenever any of the glyph content changes.
163              
164             =item OUTLOC (P)
165              
166             Location relative to the start of the glyf table. This variable is only active
167             whilst the output process is going on. It is used to inform the location table
168             where the glyph is located, since the glyf table is output before the loca
169             table due to alphabetical ordering.
170              
171             =item OUTLEN (P)
172              
173             This indicates the length of the glyph data when it is output. This more
174             accurately reflects the internal memory form than the C variable which
175             only reflects the read file length. The C variable is only set after
176             calling C or C.
177              
178             =back
179              
180             =head2 Editing
181              
182             If you want to edit a glyph in some way, then you should read_dat the glyph, then
183             make your changes and then update the glyph or set the $g->{' isDirty'} variable.
184             The application must ensure that the following instance variables are
185             correct, from which update will calculate the rest, including the bounding box
186             information.
187              
188             numPoints
189             numberOfContours
190             endPoints
191             x, y, flags (only flags bit 0)
192             instLen
193             hints
194              
195             For components, the numPoints, x, y, endPoints & flags are not required but
196             the following information is required for each component.
197              
198             flag (bits 2, 10, 11, 12)
199             glyph
200             args
201             scale
202             metric (glyph instance variable)
203            
204              
205             =head1 METHODS
206              
207             =cut
208              
209 1     1   4 use strict;
  1         1  
  1         23  
210 1     1   3 use vars qw(%fields @field_info);
  1         1  
  1         31  
211 1     1   3 use Font::TTF::Utils;
  1         1  
  1         45  
212 1     1   4 use Font::TTF::Table;
  1         1  
  1         2549  
213              
214             @field_info = (
215             'numberOfContours' => 's',
216             'xMin' => 's',
217             'yMin' => 's',
218             'xMax' => 's',
219             'yMax' => 's');
220              
221             sub init
222             {
223 1     1 0 1 my ($k, $v, $c, $i);
224 1         4 for ($i = 0; $i < $#field_info; $i += 2)
225             {
226 5         11 ($k, $v, $c) = TTF_Init_Fields($field_info[$i], $c, $field_info[$i + 1]);
227 5 50 33     17 next unless defined $k && $k ne "";
228 5         13 $fields{$k} = $v;
229             }
230             }
231              
232              
233             =head1 Font::TTF::Glyph->new(%parms)
234              
235             Creates a new glyph setting various instance variables
236              
237             =cut
238              
239             sub new
240             {
241 188     188 0 329 my ($class, %parms) = @_;
242 188         134 my ($self) = {};
243 188         113 my ($p);
244              
245 188         127 bless $self, $class;
246 188         256 foreach $p (keys %parms)
247 1316         1210 { $self->{" $p"} = $parms{$p}; }
248 188 100       268 init unless defined $fields{'xMin'};
249 188         386 $self;
250             }
251              
252              
253             =head2 $g->read
254              
255             Reads the header component of the glyph (numberOfContours and bounding box) and also the
256             glyph content, but into a data field rather than breaking it down into
257             its constituent structures. Use read_dat for this.
258              
259             =cut
260              
261             sub read
262             {
263 188     188 1 140 my ($self) = @_;
264 188         170 my ($fh) = $self->{' INFILE'};
265 188         118 my ($dat);
266              
267 188 50 33     273 return $self if (defined $self->{' read'} && $self->{' read'} > 0);
268 188         303 $self->{' read'} = 1;
269 188         402 $fh->seek($self->{' LOC'} + $self->{' BASE'}, 0);
270 188         1437 $fh->read($dat, $self->{' LEN'});
271 188         1824 TTF_Read_Fields($self, $self->{' DAT'} = $dat, \%fields);
272 188         180 $self;
273             }
274              
275              
276             =head2 $g->read_dat
277              
278             Reads the contents of the glyph (components and curves, etc.) from the memory
279             store C into structures within the object.
280              
281             =cut
282              
283             sub read_dat
284             {
285 188     188 1 439 my ($self) = @_;
286 188         130 my ($dat, $num, $max, $i, $flag, $len, $val, $val1, $fp);
287              
288 188 50 33     328 return $self if (defined $self->{' read'} && $self->{' read'} > 1);
289 188 50       369 $self->read unless $self->{' read'};
290 188         171 $dat = $self->{' DAT'};
291 188         121 $fp = 10;
292 188         155 $num = $self->{'numberOfContours'};
293 188 100       208 if ($num > 0)
    50          
294             {
295 184         399 $self->{'endPoints'} = [unpack("n*", substr($dat, $fp, $num << 1))];
296 184         166 $fp += $num << 1;
297 184         129 $max = 0;
298 184         134 foreach (@{$self->{'endPoints'}})
  184         251  
299 260 50       416 { $max = $_ if $_ > $max; }
300             # print STDERR join(",", unpack('C*', $self->{" DAT"}));
301             # printf STDERR ("(%d,%d in %d=%d @ %d)", scalar @{$self->{'endPoints'}}, $max, length($dat), $self->{' LEN'}, $fp);
302 184 50       129 $max++ if (@{$self->{'endPoints'}});
  184         268  
303 184         321 $self->{'numPoints'} = $max;
304 184         257 $self->{'instLen'} = unpack("n", substr($dat, $fp));
305 184         304 $self->{'hints'} = substr($dat, $fp + 2, $self->{'instLen'});
306 184         154 $fp += 2 + $self->{'instLen'};
307             # read the flags array
308 184         276 for ($i = 0; $i < $max; $i++)
309             {
310 5498         4801 $flag = unpack("C", substr($dat, $fp++));
311 5498         4118 $self->{'flags'}[$i] = $flag;
312 5498 100       8237 if ($flag & 8)
313             {
314 1474         1263 $len = unpack("C", substr($dat, $fp++));
315 1474         1650 while ($len-- > 0)
316             {
317 2976         1631 $i++;
318 2976         4529 $self->{'flags'}[$i] = $flag;
319             }
320             }
321             }
322             #read the x array
323 184         247 for ($i = 0; $i < $max; $i++)
324             {
325 8474         5452 $flag = $self->{'flags'}[$i];
326 8474 100       7748 if ($flag & 2)
    100          
327             {
328 6630         5830 $val = unpack("C", substr($dat, $fp++));
329 6630 100       7734 $val = -$val unless ($flag & 16);
330             } elsif ($flag & 16)
331 1310         842 { $val = 0; }
332             else
333             {
334 534         1027 $val = TTF_Unpack("s", substr($dat, $fp));
335 534         558 $fp += 2;
336             }
337 8474 100       15004 $self->{'x'}[$i] = $i == 0 ? $val : $self->{'x'}[$i - 1] + $val;
338             }
339             #read the y array
340 184         303 for ($i = 0; $i < $max; $i++)
341             {
342 8474         5409 $flag = $self->{'flags'}[$i];
343 8474 100       7721 if ($flag & 4)
    100          
344             {
345 6774         5648 $val = unpack("C", substr($dat, $fp++));
346 6774 100       8212 $val = -$val unless ($flag & 32);
347             } elsif ($flag & 32)
348 1074         737 { $val = 0; }
349             else
350             {
351 626         1107 $val = TTF_Unpack("s", substr($dat, $fp));
352 626         628 $fp += 2;
353             }
354 8474 100       15003 $self->{'y'}[$i] = $i == 0 ? $val : $self->{'y'}[$i - 1] + $val;
355             }
356             }
357            
358             # compound glyph
359             elsif ($num < 0)
360             {
361 4         4 $flag = 1 << 5; # cheat to get the loop going
362 4         9 for ($i = 0; $flag & 32; $i++)
363             {
364 8         19 ($flag, $self->{'comps'}[$i]{'glyph'}) = unpack("n2", substr($dat, $fp));
365 8         9 $fp += 4;
366 8         15 $self->{'comps'}[$i]{'flag'} = $flag;
367 8 100       10 if ($flag & 1) # ARGS1_AND_2_ARE_WORDS
368             {
369 4         10 $self->{'comps'}[$i]{'args'} = [TTF_Unpack("s2", substr($dat, $fp))];
370 4         7 $fp += 4;
371             } else
372             {
373 4         13 $self->{'comps'}[$i]{'args'} = [unpack("c2", substr($dat, $fp))];
374 4         5 $fp += 2;
375             }
376            
377 8 50       23 if ($flag & 8)
    50          
    50          
378             {
379 0         0 $val = TTF_Unpack("F", substr($dat, $fp));
380 0         0 $fp += 2;
381 0         0 $self->{'comps'}[$i]{'scale'} = [$val, 0, 0, $val];
382             } elsif ($flag & 64)
383             {
384 0         0 ($val, $val1) = TTF_Unpack("F2", substr($dat, $fp));
385 0         0 $fp += 4;
386 0         0 $self->{'comps'}[$i]{'scale'} = [$val, 0, 0, $val1];
387             } elsif ($flag & 128)
388             {
389 0         0 $self->{'comps'}[$i]{'scale'} = [TTF_Unpack("F4", substr($dat, $fp))];
390 0         0 $fp += 8;
391             }
392 8 50       19 $self->{'metric'} = $i if ($flag & 512);
393             }
394 4         11 $self->{'numPoints'} = $i;
395 4 50       8 if ($flag & 256) # HAVE_INSTRUCTIONS
396             {
397 0         0 $self->{'instLen'} = unpack("n", substr($dat, $fp));
398 0         0 $self->{'hints'} = substr($dat, $fp + 2, $self->{'instLen'});
399 0         0 $fp += 2 + $self->{'instLen'};
400             }
401             }
402 188 50       233 return undef if ($fp > length($dat));
403 188         172 $self->{' read'} = 2;
404 188         340 $self;
405             }
406              
407              
408             =head2 $g->out($fh)
409              
410             Writes the glyph data to outfile
411              
412             =cut
413              
414             sub out
415             {
416 188     188 1 113 my ($self, $fh) = @_;
417              
418 188 50       252 $self->read unless $self->{' read'};
419 188 50       193 $self->update if $self->{' isDirty'};
420 188         225 $fh->print($self->{' DAT'});
421 188         588 $self->{' OUTLEN'} = length($self->{' DAT'});
422 188         174 $self;
423             }
424              
425              
426             =head2 $g->out_xml($context, $depth)
427              
428             Outputs an XML description of the glyph
429              
430             =cut
431              
432             sub out_xml
433             {
434 0     0 1 0 my ($self, $context, $depth) = @_;
435 0         0 my ($addr) = ($self =~ m/\((.+)\)$/o);
436 0         0 my ($k, $ndepth);
437              
438 0 0       0 if ($context->{'addresses'}{$addr})
439             {
440 0         0 $context->{'fh'}->printf("%s\n", $depth, $context->{'gid'}, $context->{'addresses'}{$addr});
441 0         0 return $self;
442             }
443             else
444             {
445 0         0 $context->{'fh'}->printf("%s\n", $depth, $context->{'gid'});
446             }
447            
448 0         0 $ndepth = $depth . $context->{'indent'};
449 0         0 $self->read_dat;
450 0         0 foreach $k (sort grep {$_ !~ m/^\s/o} keys %{$self})
  0         0  
  0         0  
451             {
452 0         0 $self->XML_element($context, $ndepth, $k, $self->{$k});
453             }
454 0         0 $context->{'fh'}->print("$depth\n");
455 0         0 delete $context->{'done_points'};
456 0         0 $self;
457             }
458            
459              
460             sub XML_element
461             {
462 0     0 0 0 my ($self, $context, $depth, $key, $val) = @_;
463 0         0 my ($fh) = $context->{'fh'};
464 0         0 my ($dind) = $depth . $context->{'indent'};
465 0         0 my ($i);
466            
467 0 0 0     0 if ($self->{'numberOfContours'} >= 0 && ($key eq 'x' || $key eq 'y' || $key eq 'flags'))
    0 0        
468             {
469 0 0       0 return $self if ($context->{'done_points'});
470 0         0 $context->{'done_points'} = 1;
471              
472 0         0 $fh->print("$depth\n");
473 0         0 for ($i = 0; $i <= $#{$self->{'flags'}}; $i++)
  0         0  
474             { $fh->printf("%s\n", $dind,
475 0         0 $self->{'x'}[$i], $self->{'y'}[$i], $self->{'flags'}[$i]); }
476 0         0 $fh->print("$depth\n");
477             }
478             elsif ($key eq 'hints')
479             {
480 0         0 my ($dat);
481 0         0 $fh->print("$depth\n");
482             # Font::TTF::Utils::XML_hexdump($context, $depth . $context->{'indent'}, $self->{'hints'});
483 0   0     0 $dat = Font::TTF::Utils::XML_binhint($self->{'hints'}) || "";
484 0         0 $dat =~ s/\n(?!$)/\n$depth$context->{'indent'}/mg;
485 0         0 $fh->print("$depth$context->{'indent'}$dat");
486 0         0 $fh->print("$depth\n");
487             }
488             else
489 0         0 { return Font::TTF::Table::XML_element(@_); }
490              
491 0         0 $self;
492             }
493              
494             =head2 $g->dirty($val)
495              
496             This sets the dirty flag to the given value or 1 if no given value. It returns the
497             value of the flag
498              
499             =cut
500              
501             sub dirty
502             {
503 0     0 1 0 my ($self, $val) = @_;
504 0         0 my ($res) = $self->{' isDirty'};
505              
506 0 0       0 $self->{' isDirty'} = defined $val ? $val : 1;
507 0         0 $res;
508             }
509              
510             =head2 $g->update
511              
512             Generates a C<$self->{'DAT'}> from the internal structures, if the data has
513             been read into structures in the first place. If you are building a glyph
514             from scratch you will need to set the instance variable C<' isDirty'>.
515              
516             =cut
517              
518             sub update
519             {
520 188     188 1 112 my ($self) = @_;
521 188         106 my ($dat, $loc, $len, $flag, $x, $y, $i, $comp, $num, @rflags, $repeat);
522              
523 188 50       303 return $self unless ($self->{' isDirty'});
524 0           $self->read_dat->update_bbox;
525 0           $self->{' DAT'} = TTF_Out_Fields($self, \%fields, 10);
526 0           $num = $self->{'numberOfContours'};
527 0 0         if ($num > 0)
    0          
528             {
529 0           $self->{' DAT'} .= pack("n*", @{$self->{'endPoints'}});
  0            
530 0           $len = $self->{'instLen'};
531 0           $self->{' DAT'} .= pack("n", $len);
532 0 0         $self->{' DAT'} .= pack("a" . $len, substr($self->{'hints'}, 0, $len)) if ($len > 0);
533 0           $repeat = 0;
534 0           for ($i = 0; $i < $self->{'numPoints'}; $i++)
535             {
536 0           $flag = $self->{'flags'}[$i] & 1;
537 0 0         if ($i == 0)
538             {
539 0           $x = $self->{'x'}[$i];
540 0           $y = $self->{'y'}[$i];
541             } else
542             {
543 0           $x = $self->{'x'}[$i] - $self->{'x'}[$i - 1];
544 0           $y = $self->{'y'}[$i] - $self->{'y'}[$i - 1];
545             }
546 0 0         $flag |= 16 if ($x == 0);
547 0 0         $flag |= 32 if ($y == 0);
548 0 0 0       if (($flag & 16) == 0 && $x < 256 && $x > -256)
      0        
549             {
550 0           $flag |= 2;
551 0 0         $flag |= 16 if ($x >= 0);
552             }
553 0 0 0       if (($flag & 32) == 0 && $y < 256 && $y > -256)
      0        
554             {
555 0           $flag |= 4;
556 0 0         $flag |= 32 if ($y >= 0);
557             }
558 0 0 0       if ($i > 0 && $rflags[-1] == $flag && $repeat < 255)
      0        
559             {
560 0           $repeat++;
561             } else
562             {
563 0 0         if ($repeat)
564             {
565 0           $rflags[-1] |= 8;
566 0           push @rflags, $repeat;
567             }
568 0           push @rflags, $flag;
569 0           $repeat = 0;
570             }
571 0           $self->{'flags'}[$i] = $flag;
572             }
573             # Add final repeat if needed, then pack up the flag bytes:
574 0 0         if ($repeat)
575             {
576 0           $rflags[-1] |= 8;
577 0           push @rflags, $repeat;
578             }
579 0           $self->{' DAT'} .= pack("C*", @rflags);
580 0           for ($i = 0; $i < $self->{'numPoints'}; $i++)
581             {
582 0           $flag = $self->{'flags'}[$i];
583 0 0         $x = $self->{'x'}[$i] - (($i == 0) ? 0 : $self->{'x'}[$i - 1]);
584 0 0         if (($flag & 18) == 0)
    0          
    0          
585 0           { $self->{' DAT'} .= TTF_Pack("s", $x); }
586             elsif (($flag & 18) == 18)
587 0           { $self->{' DAT'} .= pack("C", $x); }
588             elsif (($flag & 18) == 2)
589 0           { $self->{' DAT'} .= pack("C", -$x); }
590             }
591 0           for ($i = 0; $i < $self->{'numPoints'}; $i++)
592             {
593 0           $flag = $self->{'flags'}[$i];
594 0 0         $y = $self->{'y'}[$i] - (($i == 0) ? 0 : $self->{'y'}[$i - 1]);
595 0 0         if (($flag & 36) == 0)
    0          
    0          
596 0           { $self->{' DAT'} .= TTF_Pack("s", $y); }
597             elsif (($flag & 36) == 36)
598 0           { $self->{' DAT'} .= pack("C", $y); }
599             elsif (($flag & 36) == 4)
600 0           { $self->{' DAT'} .= pack("C", -$y); }
601             }
602             }
603              
604             elsif ($num < 0)
605             {
606 0           for ($i = 0; $i <= $#{$self->{'comps'}}; $i++)
  0            
607             {
608 0           $comp = $self->{'comps'}[$i];
609 0           $flag = $comp->{'flag'} & 7158; # bits 2,10,11,12
610             $flag |= 1 unless ($comp->{'args'}[0] > -129 && $comp->{'args'}[0] < 128
611 0 0 0       && $comp->{'args'}[1] > -129 && $comp->{'args'}[1] < 128);
      0        
      0        
612 0 0         if (defined $comp->{'scale'})
613             {
614 0 0 0       if ($comp->{'scale'}[1] == 0 && $comp->{'scale'}[2] == 0)
615             {
616 0 0         if ($comp->{'scale'}[0] == $comp->{'scale'}[3])
617             { $flag |= 8 unless ($comp->{'scale'}[0] == 0
618 0 0 0       || $comp->{'scale'}[0] == 1); }
619             else
620 0           { $flag |= 64; }
621             } else
622 0           { $flag |= 128; }
623             }
624            
625 0 0 0       $flag |= 512 if (defined $self->{'metric'} && $self->{'metric'} == $i);
626 0 0         if ($i == $#{$self->{'comps'}})
  0            
627 0 0 0       { $flag |= 256 if (defined $self->{'instLen'} && $self->{'instLen'} > 0); }
628             else
629 0           { $flag |= 32; }
630            
631 0           $self->{' DAT'} .= pack("n", $flag);
632 0           $self->{' DAT'} .= pack("n", $comp->{'glyph'});
633 0           $comp->{'flag'} = $flag;
634              
635 0 0         if ($flag & 1)
636 0           { $self->{' DAT'} .= TTF_Pack("s2", @{$comp->{'args'}}); }
  0            
637             else
638 0           { $self->{' DAT'} .= pack("CC", @{$comp->{'args'}}); }
  0            
639              
640 0 0         if ($flag & 8)
    0          
    0          
641 0           { $self->{' DAT'} .= TTF_Pack("F", $comp->{'scale'}[0]); }
642             elsif ($flag & 64)
643 0           { $self->{' DAT'} .= TTF_Pack("F2", $comp->{'scale'}[0], $comp->{'scale'}[3]); }
644             elsif ($flag & 128)
645 0           { $self->{' DAT'} .= TTF_Pack("F4", @{$comp->{'scale'}}); }
  0            
646             }
647 0 0 0       if (defined $self->{'instLen'} && $self->{'instLen'} > 0)
648             {
649 0           $len = $self->{'instLen'};
650 0           $self->{' DAT'} .= pack("n", $len);
651 0           $self->{' DAT'} .= pack("a" . $len, substr($self->{'hints'}, 0, $len));
652             }
653             }
654 0           my ($olen) = length($self->{' DAT'});
655 0 0         $self->{' DAT'} .= ("\000") x (4 - ($olen & 3)) if ($olen & 3);
656 0           $self->{' OUTLEN'} = length($self->{' DAT'});
657 0           $self->{' read'} = 2; # changed from 1 to 2 so we don't read_dat() again
658             # we leave numPoints and instLen since maxp stats use this
659 0           $self;
660             }
661              
662              
663             =head2 $g->update_bbox
664              
665             Updates the bounding box for this glyph according to the points in the glyph
666              
667             =cut
668              
669             sub update_bbox
670             {
671 0     0 1   my ($self) = @_;
672 0           my ($num, $maxx, $minx, $maxy, $miny, $i, $comp, $x, $y, $compg);
673              
674 0 0 0       return $self unless (defined $self->{' read'} && $self->{' read'} > 1); # only if read_dat done
675 0           $miny = $minx = 65537; $maxx = $maxy = -65537;
  0            
676 0           $num = $self->{'numberOfContours'};
677 0 0         if ($num > 0)
    0          
678             {
679 0           for ($i = 0; $i < $self->{'numPoints'}; $i++)
680             {
681 0           ($x, $y) = ($self->{'x'}[$i], $self->{'y'}[$i]);
682              
683 0 0         $maxx = $x if ($x > $maxx);
684 0 0         $minx = $x if ($x < $minx);
685 0 0         $maxy = $y if ($y > $maxy);
686 0 0         $miny = $y if ($y < $miny);
687             }
688             }
689              
690             elsif ($num < 0)
691             {
692 0           foreach $comp (@{$self->{'comps'}})
  0            
693             {
694 0           my ($gnx, $gny, $gxx, $gxy);
695 0           my ($sxx, $sxy, $syx, $syy);
696            
697 0           my $otherg = $self->{' PARENT'}{'loca'}{'glyphs'}[$comp->{'glyph'}];
698             # work around bad fonts: see documentation for 'comps' above
699 0 0         next unless (defined $otherg);
700 0           $compg = $otherg->read->update_bbox;
701 0           ($gnx, $gny, $gxx, $gxy) = @{$compg}{'xMin', 'yMin', 'xMax', 'yMax'};
  0            
702 0 0 0       if (defined $comp->{'scale'})
    0          
703             {
704 0           ($sxx, $sxy, $syx, $syy) = @{$comp->{'scale'}};
  0            
705             ($gnx, $gny, $gxx, $gxy) = ($gnx*$sxx+$gny*$syx + $comp->{'args'}[0],
706             $gnx*$sxy+$gny*$syy + $comp->{'args'}[1],
707             $gxx*$sxx+$gxy*$syx + $comp->{'args'}[0],
708 0           $gxx*$sxy+$gxy*$syy + $comp->{'args'}[1]);
709             } elsif ($comp->{'args'}[0] || $comp->{'args'}[1])
710             {
711 0           $gnx += $comp->{'args'}[0];
712 0           $gny += $comp->{'args'}[1];
713 0           $gxx += $comp->{'args'}[0];
714 0           $gxy += $comp->{'args'}[1];
715             }
716 0 0         ($gnx, $gxx) = ($gxx, $gnx) if $gnx > $gxx;
717 0 0         ($gny, $gxy) = ($gxy, $gny) if $gny > $gxy;
718 0 0         $maxx = $gxx if $gxx > $maxx;
719 0 0         $minx = $gnx if $gnx < $minx;
720 0 0         $maxy = $gxy if $gxy > $maxy;
721 0 0         $miny = $gny if $gny < $miny;
722             }
723             }
724 0           $self->{'xMax'} = $maxx;
725 0           $self->{'xMin'} = $minx;
726 0           $self->{'yMax'} = $maxy;
727 0           $self->{'yMin'} = $miny;
728 0           $self;
729             }
730              
731            
732             =head2 $g->maxInfo
733              
734             Returns lots of information about a glyph so that the C table can update
735             itself. Returns array containing contributions of this glyph to maxPoints, maxContours,
736             maxCompositePoints, maxCompositeContours, maxSizeOfInstructions, maxComponentElements,
737             and maxComponentDepth.
738              
739             =cut
740              
741             sub maxInfo
742             {
743 0     0 1   my ($self) = @_;
744 0           my (@res, $i, @n);
745              
746 0           $self->read_dat; # make sure we've read some data
747 0 0         $res[4] = length($self->{'hints'}) if defined $self->{'hints'};
748 0           $res[6] = 1;
749 0 0         if ($self->{'numberOfContours'} > 0)
    0          
750             {
751 0           $res[0] = $self->{'numPoints'};
752 0           $res[1] = $self->{'numberOfContours'};
753             } elsif ($self->{'numberOfContours'} < 0)
754             {
755 0           for ($i = 0; $i <= $#{$self->{'comps'}}; $i++)
  0            
756             {
757             my $otherg =
758             $self->{' PARENT'}{'loca'}{'glyphs'}
759 0           [$self->{'comps'}[$i]{'glyph'}];
760            
761             # work around bad fonts: see documentation for 'comps' above
762 0 0         next unless (defined $otherg );
763            
764 0           @n = $otherg->maxInfo;
765              
766 0 0         $res[2] += $n[2] == 0 ? $n[0] : $n[2];
767 0 0         $res[3] += $n[3] == 0 ? $n[1] : $n[3];
768 0           $res[5]++;
769 0 0         $res[6] = $n[6] + 1 if ($n[6] >= $res[6]);
770             }
771             }
772 0           @res;
773             }
774              
775             =head2 $g->empty
776              
777             Empties the glyph of all information to the level of not having been read.
778             Useful for saving memory in apps with many glyphs being read
779              
780             =cut
781              
782             sub empty
783             {
784 0     0 1   my ($self) = @_;
785 0           my (%keep) = map {(" $_" => 1)} ('LOC', 'OUTLOC', 'PARENT', 'INFILE', 'BASE',
  0            
786             'OUTLEN', 'LEN');
787 0 0         map {delete $self->{$_} unless $keep{$_}} keys %$self;
  0            
788            
789 0           $self;
790             }
791              
792              
793             =head2 $g->get_points
794              
795             This method creates point information for a compound glyph. The information is
796             stored in the same place as if the glyph was not a compound, but since
797             numberOfContours is negative, the glyph is still marked as being a compound
798              
799             =cut
800              
801             sub get_points
802             {
803 0     0 1   my ($self) = @_;
804 0           my ($comp, $compg, $nump, $e, $i);
805              
806 0           $self->read_dat;
807 0 0         return undef unless ($self->{'numberOfContours'} < 0);
808              
809 0           foreach $comp (@{$self->{'comps'}})
  0            
810             {
811 0           $compg = $self->{' PARENT'}{'loca'}{'glyphs'}[$comp->{'glyph'}];
812             # work around bad fonts: see documentation for 'comps' above
813 0 0         next unless (defined $compg );
814 0           $compg->get_points;
815              
816 0           for ($i = 0; $i < $compg->{'numPoints'}; $i++)
817             {
818 0           my ($x, $y) = ($compg->{'x'}[$i], $compg->{'y'}[$i]);
819 0 0         if (defined $comp->{'scale'})
820             {
821             ($x, $y) = ($x * $comp->{'scale'}[0] + $y * $comp->{'scale'}[2],
822 0           $x * $comp->{'scale'}[1] + $y * $comp->{'scale'}[3]);
823             }
824 0 0         if (defined $comp->{'args'})
825 0           { ($x, $y) = ($x + $comp->{'args'}[0], $y + $comp->{'args'}[1]); }
826 0           push (@{$self->{'x'}}, $x);
  0            
827 0           push (@{$self->{'y'}}, $y);
  0            
828 0           push (@{$self->{'flags'}}, $compg->{'flags'}[$i]);
  0            
829             }
830 0           foreach $e (@{$compg->{'endPoints'}})
  0            
831 0           { push (@{$self->{'endPoints'}}, $e + $nump); }
  0            
832 0           $nump += $compg->{'numPoints'};
833             }
834 0           $self->{'numPoints'} = $nump;
835 0           $self;
836             }
837              
838              
839             =head2 $g->get_refs
840              
841             Returns an array of all the glyph ids that are used to make up this glyph. That
842             is all the compounds and their references and so on. If this glyph is not a
843             compound, then returns an empty array.
844              
845             Please note the warning about bad fonts that reference nonexistent glyphs
846             under INSTANCE VARIABLES above. This function will not attempt to
847             filter out nonexistent glyph numbers.
848              
849             =cut
850              
851             sub get_refs
852             {
853 0     0 1   my ($self) = @_;
854 0           my (@res, $g);
855              
856 0           $self->read_dat;
857 0 0         return unless ($self->{'numberOfContours'} < 0);
858 0           foreach $g (@{$self->{'comps'}})
  0            
859             {
860 0           push (@res, $g->{'glyph'});
861 0           my $otherg = $self->{' PARENT'}{'loca'}{'glyphs'}[$g->{'glyph'}];
862             # work around bad fonts: see documentation for 'comps' above
863 0 0         next unless (defined $otherg);
864 0           my @list = $otherg->get_refs;
865 0           push(@res, @list);
866             }
867 0           return @res;
868             }
869              
870             1;
871              
872             =head1 BUGS
873              
874             =over 4
875              
876             =item *
877              
878             The instance variables used here are somewhat clunky and inconsistent with
879             the other tables.
880              
881             =item *
882              
883             C doesn't re-calculate the bounding box or C.
884              
885             =back
886              
887             =head1 AUTHOR
888              
889             Martin Hosken L.
890              
891              
892             =head1 LICENSING
893              
894             Copyright (c) 1998-2016, SIL International (http://www.sil.org)
895              
896             This module is released under the terms of the Artistic License 2.0.
897             For details, see the full text of the license in the file LICENSE.
898              
899              
900              
901             =cut
902