File Coverage

blib/lib/Dancer2/Plugin/FontSubset.pm
Criterion Covered Total %
statement 138 479 28.8
branch 27 202 13.3
condition 8 125 6.4
subroutine 29 45 64.4
pod 0 22 0.0
total 202 873 23.1


line stmt bran cond sub pod time code
1             package Dancer2::Plugin::FontSubset;
2             our $AUTHORITY = 'cpan:YANICK';
3             # ABSTRACT: Generate font subsets on-the-fly
4             $Dancer2::Plugin::FontSubset::VERSION = '0.0.1';
5              
6 2     2   1513120 use 5.12.0;
  2         18  
7              
8 2     2   834 use List::AllUtils qw/ uniq /;
  2         17544  
  2         124  
9 2     2   809 use Font::TTF::Font;
  2         5321  
  2         49  
10 2     2   653 use Font::TTF::Scripts::Name;
  2         1362  
  2         83  
11              
12 2     2   386 use Dancer2::Plugin;
  2         10085  
  2         18  
13              
14             has fonts_dir => (
15             is => 'ro',
16             lazy => 1,
17             from_config => sub { 'fonts' },
18             );
19              
20             has font_base_url => (
21             is => 'ro',
22             lazy => 1,
23             from_config => sub { '/font' },
24             );
25              
26             has use_cache => (
27             is => 'ro',
28             lazy => 1,
29             from_config => sub { 0 },
30             );
31              
32             has cache => (
33             is => 'ro',
34             lazy => 1,
35             default => sub {
36             $_[0]->find_plugin('Dancer2::Plugin::Cache::CHI');
37             },
38             );
39              
40             sub BUILD {
41 2     2 0 3819 my $self = shift;
42              
43 2         35 $self->app->add_route(
44             method => 'get',
45             regexp => $self->font_base_url . '/subset.js',
46             code => $self->_subset_route,
47             );
48              
49 2         6773 $self->app->add_route(
50             method => 'get',
51             regexp => $self->font_base_url . '/:fontname',
52             code => $self->_font_route,
53             );
54            
55             }
56              
57             sub font_path {
58 4     4 0 61 $_[0]->fonts_dir . '/' . $_[1];
59             }
60              
61             sub _subset_route {
62 2     2   5 my $self = shift;
63              
64 2     0   13 return sub { sprintf <<'END_JS', $self->font_base_url };
  0         0  
65             $(function(){
66             $('.subfont').each(function(){
67             var characters = $(this).text().split('').sort();
68             characters = characters.filter(function(e,i,a){
69             return characters.lastIndexOf(e) == i
70             }).join('');
71             var family = $(this).attr('data-font') + '-' + characters;
72             var style = "@font-face { font-family: " + family
73             + "; src: url('%s/" + $(this).attr('data-font') + ".ttf?t=" + characters
74             + "'); }";
75             $('body').append( "" );
76             $(this).css('font-family', family);
77             });
78             });
79             END_JS
80              
81             }
82              
83             sub _font_route {
84 2     2   17 my $plugin = shift;
85              
86             return sub {
87 4     4   424214 my $app = shift;
88              
89 4         25 my $fontname = $app->request->params->{fontname};
90              
91 4         49 $fontname =~ s#\.\.##g; # just to be safe
92              
93 4         52 my $path = $plugin->font_path( $fontname );
94              
95             # $app->send_error( 'font not found', 404 ) unless -f $path;
96              
97             # no text? No job to do
98             my $text = $app->request->params->{t}
99 4   100     171 // return $app->send_file( $path );
100              
101 3         57 my @chars = map { ord } sort { $a cmp $b } uniq split //, $text;
  6         18  
  3         13  
102              
103 3         74 $path = join '/', $plugin->app->config->{public_dir}, $path;
104              
105 3         38 my $output = $plugin->generate_subfont( $path, @chars );
106              
107 3         475 return $app->send_file( \$output, content_type => 'application/x-ttf' );
108             }
109 2         13 };
110              
111             sub _generate_subfont {
112 2     2   8 my( $self, $path, @chars ) = @_;
113              
114 2         23 my $f = Font::TTF::Font->open($path);
115              
116 2         111015 my $cmap = $f->{'cmap'}->read->find_ms;
117 2         6204 my $subsetter = Font::TTF::Scripts::SubSetter->new;
118              
119 2         5 for my $char ( @chars ) {
120             $subsetter->add_glyph( $cmap->{val}{$char} )
121 4 50       14 if $cmap->{val}{$char};
122             }
123              
124 2         4 my $canchangegids = 1;
125 2         6 my $numg = $f->{'maxp'}{'numGlyphs'};
126 2     28   27 $f->tables_do(sub {$canchangegids |= $_[0]->canchangegids();});
  28         207  
127 2 50       20 $numg = $subsetter->creategidmap($f) if ($canchangegids);
128              
129 2         16 $f->{'loca'}->subset($subsetter);
130 2     28   18 $f->tables_do(sub {$_[0]->subset($subsetter);});
  28         252  
131 2         21 $f->{'maxp'}{'numGlyphs'} = $subsetter->{'gcount'};
132 2     28   15 $f->tables_do(sub {$_[0]->update;});
  28         5130  
133 2         153 open my $fh, '>', \my $output;
134              
135 2         15 $f->out($fh);
136              
137 2         13920 return $output;
138             };
139              
140             sub generate_subfont {
141 3     3 0 11 my( $self, @args ) = @_;
142              
143 3 100       47 return $self->_generate_subfont(@args) unless $self->use_cache;
144              
145             return $self->cache->cache->compute(
146             'font-' . $args[0] . '-' . join( '', @args[1..$#args] ), sub {
147 1     1   205 $self->_generate_subfont(@args);
148             }
149 2         69 );
150             }
151              
152             1;
153              
154             # all shamelessly ripped off from 'ttfsubset' from
155             # TTF::Font::TTF
156              
157             package
158             Font::TTF::Scripts::SubSetter;
159              
160             sub new
161             {
162 2     2   5 my ($class) = @_;
163 2         6 my ($self) = {};
164 2         6 $self->{'glyphs'} = '';
165 2         4 $self->{'remaps'} = {};
166 2   33     20 bless $self, $class || ref $class;
167 2         9 foreach (0..2) { $self->add_glyph($_); }
  6         14  
168 2         4 return $self;
169             }
170              
171             sub add_glyph
172             {
173 10     10   18 my ($self, $n, $private) = @_;
174 10 50 33     73 if (($private && !$self->{'gidmap'}[$n]) || (!$private && !vec($self->{'glyphs'}, $n, 1)))
      33        
      33        
175             {
176 10         24 vec($self->{'glyphs'}, $n, 1) = 1; # unless ($private);
177 10 50       27 $self->{'gidmap'}[$n] = $self->{'gcount'}++ if (defined $self->{'gidmap'});
178 10         14 return 1;
179             }
180             else
181 0         0 { return 0; }
182             }
183              
184             sub keep_glyph
185             {
186 2416     2416   3223 my ($self, $n) = @_;
187 2416         4113 return vec($self->{'glyphs'}, $n, 1);
188             }
189              
190             sub remap
191             {
192 0     0   0 my ($self, $u, $n) = @_;
193 0         0 $self->{'remaps'}{$u} = $n;
194             }
195              
196             sub langlist
197             {
198 0     0   0 my ($self, @dat) = @_;
199 0         0 $self->{'langs'} = { map {$_=>1} @dat };
  0         0  
200             }
201              
202             sub scriptlist
203             {
204 0     0   0 my ($self, @dat) = @_;
205 0         0 $self->{'scripts'} = { map {$_=>1} @dat };
  0         0  
206             }
207              
208             sub creategidmap
209             {
210 2     2   6 my ($self, $font) = @_;
211 2         6 my ($numg) = $font->{'maxp'}{'numGlyphs'};
212 2         5 my ($count) = 0;
213              
214 2         6 $self->{'gidmap'} = [];
215 2         5 $self->{'gcount'} = 0;
216 2         8 foreach my $i (0 .. $numg - 1)
217 406 100       378 { push (@{$self->{'gidmap'}}, vec($self->{'glyphs'}, $i, 1) ? $self->{'gcount'}++ : 0); }
  406         601  
218 2         6 return $self->{'gcount'};
219             }
220              
221             sub map_glyph
222             {
223 448     448   509 my ($self, $g) = @_;
224             # no glyph remapping yet
225 448 50       524 if ($self->{'gidmap'})
226 448         679 { return $self->{'gidmap'}[$g]; }
227             else
228 0         0 { return $g; }
229             }
230              
231             package
232             Font::TTF::Table;
233              
234             sub canchangegids
235 28     28 0 42 { 1; }
236              
237             sub subset
238             {
239 30     30 0 53 my ($self, $subsetter) = @_;
240 30 100       127 return 0 if ($self->{' subsetdone'});
241 28         59 $self->{' subsetdone'} = 1;
242 28         128 $self->read;
243 28         29937 $self->dirty;
244 28         218 return 1;
245             }
246              
247             package
248             Font::TTF::Loca;
249              
250             sub subset
251             {
252 4     4 0 10 my ($self, $subsetter) = @_;
253 4         18 my ($res) = [];
254 4         8 my ($i, $vec);
255              
256 4 100       31 return unless ($self->SUPER::subset($subsetter));
257 2         9 for ($i = 0; $i < @{$self->{'glyphs'}}; $i++)
  406         1791  
258             {
259 404 100       518 if ($subsetter->keep_glyph($i))
260 10         22 { $self->outglyph($subsetter, $res, $i); }
261             }
262 2         114 $self->{'glyphs'} = $res;
263             }
264              
265             sub outglyph
266             {
267 10     10 0 25 my ($self, $subsetter, $res, $n) = @_;
268              
269 10         23 $res->[$subsetter->map_glyph($n)] = $self->{'glyphs'}[$n];
270 10 50 66     36 if (defined $self->{'glyphs'}[$n] && $self->{'glyphs'}[$n]->read()->{'numberOfContours'} < 0)
271             {
272 0         0 my ($g) = $self->{'glyphs'}[$n]->read_dat();
273 0         0 foreach my $c (@{$g->{'comps'}})
  0         0  
274             {
275 0 0       0 if ($subsetter->add_glyph($c->{'glyph'}, 1))
276 0         0 { $self->outglyph($subsetter, $res, $c->{'glyph'}); }
277 0         0 $c->{'glyph'} = $subsetter->map_glyph($c->{'glyph'});
278             }
279 0         0 $g->{' isDirty'} = 1;
280             }
281             }
282              
283             package
284             Font::TTF::Ttopen;
285              
286             sub subset
287             {
288 0     0 0 0 my ($self, $subsetter) = @_;
289 0 0       0 return unless ($self->SUPER::subset($subsetter));
290              
291 0         0 my ($l, $count, @lmap, @lookups, $lkvec, $res, $nlookup);
292 0         0 $lkvec = "";
293 0         0 $nlookup = $#{$self->{'LOOKUP'}};
  0         0  
294             # process non-contextual lookups
295 0         0 foreach $l (0 .. $nlookup)
296             {
297 0         0 my ($type) = $self->{'LOOKUP'}[$l]{'TYPE'};
298 0 0 0     0 next if ($type >= $self->extension() - 2 && $type < $self->extension());
299 0         0 $res = $self->subset_lookup($self->{'LOOKUP'}[$l]);
300              
301 0 0       0 if (!@{$res})
  0         0  
302             {
303 0         0 delete $self->{'LOOKUP'}[$l];
304 0         0 vec($lkvec, $l, 1) = 0;
305             }
306             else
307             {
308 0         0 $self->{'LOOKUP'}[$l]{'SUB'} = $res;
309 0         0 vec($lkvec, $l, 1) = 1;
310             }
311             }
312             # now process contextual lookups knowing whether the other lookup is there
313             # also collect the complete lookup list now
314 0         0 foreach $l (0 .. $nlookup)
315             {
316 0 0       0 if (defined $self->{'LOOKUP'}[$l])
317             {
318 0         0 my ($type) = $self->{'LOOKUP'}[$l]{'TYPE'};
319 0 0 0     0 if ($type >= $self->extension() - 2 && $type < $self->extension())
320             {
321 0         0 $res = $self->subset_lookup($self->{'LOOKUP'}[$l], $lkvec);
322 0 0       0 if (!@{$res})
  0         0  
323             {
324 0         0 delete $self->{'LOOKUP'}[$l];
325 0         0 vec($lkvec, $l, 1) = 0;
326             }
327             else
328             {
329 0         0 $self->{'LOOKUP'}[$l]{'SUB'} = $res;
330 0         0 vec($lkvec, $l, 1) = 1;
331             }
332             }
333             }
334 0 0       0 if (vec($lkvec, $l, 1))
335             {
336 0         0 push (@lookups, $self->{'LOOKUP'}[$l]);
337 0         0 push (@lmap, $count++);
338             }
339             else
340 0         0 { push (@lmap, -1); }
341             }
342            
343 0         0 $self->{'LOOKUP'} = \@lookups;
344 0         0 foreach $l (@lookups)
345 0         0 { $self->fixcontext($l, \@lmap); }
346              
347 0         0 foreach my $t (@{$self->{'FEATURES'}{'FEAT_TAGS'}})
  0         0  
348             {
349 0         0 my $f = $self->{'FEATURES'}{$t};
350 0         0 foreach $l (0 .. $#{$f->{'LOOKUPS'}})
  0         0  
351             {
352 0         0 my ($v) = $lmap[$f->{'LOOKUPS'}[$l]];
353 0 0       0 if ($v < 0)
354 0         0 { delete $f->{'LOOKUPS'}[$l]; }
355             else
356 0         0 { $f->{'LOOKUPS'}[$l] = $v; }
357             }
358 0 0       0 if (!@{$f->{'LOOKUPS'}})
  0         0  
359 0         0 { delete $self->{'FEATURES'}{$t}; }
360             else
361 0         0 { $f->{'LOOKUPS'} = [grep {defined $_} @{$f->{'LOOKUPS'}}]; }
  0         0  
  0         0  
362             }
363 0         0 $self->{'FEATURES'}{'FEAT_TAGS'} = [grep {defined $self->{'FEATURES'}{$_}} @{$self->{'FEATURES'}{'FEAT_TAGS'}}];
  0         0  
  0         0  
364              
365 0         0 my ($isEmpty) = 1;
366 0         0 foreach my $s (keys %{$self->{'SCRIPTS'}})
  0         0  
367             {
368 0         0 foreach $l (-1 .. $#{$self->{'SCRIPTS'}{$s}{'LANG_TAGS'}})
  0         0  
369             {
370 0         0 my $lang;
371 0 0       0 if ($l < 0)
372 0         0 { $lang = $self->{'SCRIPTS'}{$s}{'DEFAULT'}; }
373             else
374 0         0 { $lang = $self->{'SCRIPTS'}{$s}{$self->{'SCRIPTS'}{$s}{'LANG_TAGS'}[$l]}; }
375              
376 0 0       0 if (defined $lang->{'FEATURES'})
377             {
378 0         0 foreach my $i (0 .. @{$lang->{'FEATURES'}})
  0         0  
379             {
380 0 0       0 if (!defined $self->{'FEATURES'}{$lang->{'FEATURES'}[$i]})
381 0         0 { delete $lang->{'FEATURES'}[$i]; }
382             }
383 0         0 $lang->{'FEATURES'} = [grep {$_} @{$lang->{'FEATURES'}}];
  0         0  
  0         0  
384             }
385 0 0 0     0 if (defined $lang->{'DEFAULT'} && $lang->{'DEFAULT'} >= 0)
386             {
387 0         0 my ($found) = 0;
388 0         0 foreach my $f (@{$self->{'FEATURES'}{'FEAT_TAGS'}})
  0         0  
389             {
390 0 0       0 if ($self->{'FEATURES'}{$f}{'INDEX'} == $lang->{'DEFAULT'})
391             {
392 0         0 $found = 1;
393 0         0 last;
394             }
395             }
396 0 0       0 if (!$found)
397 0         0 { $lang->{'DEFAULT'} = -1; }
398             }
399 0 0 0     0 if (($l >= 0 && defined $subsetter->{'langs'}
      0        
      0        
      0        
      0        
      0        
400             && !defined $subsetter->{'langs'}{$self->{'SCRIPTS'}{$s}{'LANG_TAGS'}[$l]})
401             || ((!defined $lang->{'FEATURES'} || !@{$lang->{'FEATURES'}})
402             && (!defined $lang->{'DEFAULT'} || $lang->{'DEFAULT'} < 0)))
403             {
404 0 0       0 if ($l < 0)
405 0         0 { delete $self->{'SCRIPTS'}{$s}{'DEFAULT'}; }
406             else
407             {
408 0         0 delete $self->{'SCRIPTS'}{$s}{$self->{'SCRIPTS'}{$s}{'LANG_TAGS'}[$l]};
409 0         0 delete $self->{'SCRIPTS'}{$s}{'LANG_TAGS'}[$l];
410             }
411             }
412             }
413 0 0 0     0 if ((defined $subsetter->{'scripts'} && !defined $subsetter->{'scripts'}{$s})
      0        
      0        
414             || (!@{$self->{'SCRIPTS'}{$s}{'LANG_TAGS'}} && !defined $self->{'SCRIPTS'}{$s}{'DEFAULT'}))
415             {
416 0         0 delete $self->{'SCRIPTS'}{$s};
417 0         0 next;
418             }
419             else
420 0         0 { $isEmpty = 0; }
421             }
422 0 0       0 if ($isEmpty)
423             {
424 0         0 my ($k, $v);
425 0         0 while (($k, $v) = each %{$self->{' PARENT'}})
  0         0  
426             {
427 0 0       0 if ($v eq $self)
428             {
429 0         0 delete $self->{' PARENT'}{$k};
430 0         0 last;
431             }
432             }
433             }
434             }
435              
436             sub subset_lookup
437             {
438 0     0 0 0 my ($self, $lookup, $lkvec) = @_;
439 0         0 my ($s, $l);
440 0         0 my ($res) = [];
441              
442 0         0 foreach $s (@{$lookup->{'SUB'}})
  0         0  
443             {
444 0 0 0     0 if (!$self->subset_subtable(undef, $s, $lookup, $lkvec)
      0        
445 0         0 || !defined $s->{'RULES'} || !@{$s->{'RULES'}})
446 0         0 { next; }
447 0         0 $s->{'RULES'} = [grep {$_} @{$s->{'RULES'}}];
  0         0  
  0         0  
448             # remove unused coverage indices
449 0 0       0 if ($s->{'COVERAGE'})
450             {
451 0         0 my $c = $s->{'COVERAGE'}{'val'};
452 0         0 my $i = 0;
453 0         0 foreach my $k (sort {$c->{$a} <=> $c->{$b}} keys %{$c})
  0         0  
  0         0  
454 0         0 { $c->{$k} = $i++; }
455             }
456 0         0 push (@{$res}, $s);
  0         0  
457             }
458 0         0 return $res;
459             }
460              
461              
462             sub subset_class
463             {
464 0     0 0 0 my ($self, $subsetter, $classdef, $noremap) = @_;
465 0         0 my ($res) = [];
466 0         0 my ($count) = 0;
467 0         0 my ($class) = $classdef->{'val'};
468              
469 0         0 foreach (sort {$a <=> $b} keys %{$class})
  0         0  
  0         0  
470             {
471 0 0       0 if (!$subsetter->keep_glyph($_))
472 0         0 { delete $class->{$_}; }
473             else
474             {
475 0         0 my $g = $subsetter->map_glyph($_);
476 0         0 $class->{$g} = delete $class->{$_};
477 0 0       0 $res->[$class->{$g}] = ++$count unless (defined $res->[$class->{$g}])
478             }
479             }
480             # remap the class
481 0 0       0 unless ($noremap)
482             {
483 0         0 foreach (keys %{$class})
  0         0  
484 0         0 { $class->{$_} = $res->[$class->{$_}]; }
485             }
486 0 0       0 if (@{$res})
  0         0  
487 0         0 { return $res; }
488             else
489 0         0 { return undef; }
490             }
491              
492             sub subset_cover
493             {
494 0     0 0 0 my ($self, $subsetter, $coverage, $rules) = @_;
495 0 0       0 return $coverage if (defined $coverage->{'isremapped'});
496 0         0 my $isEmpty = 1;
497 0         0 my $cover = $coverage->{'val'};
498 0         0 foreach (sort {$a <=> $b} keys %{$cover})
  0         0  
  0         0  
499             {
500 0 0       0 if (!$subsetter->keep_glyph($_))
501             {
502 0 0       0 delete $rules->[$cover->{$_}] if $rules;
503 0         0 delete $cover->{$_};
504             }
505             else
506             {
507 0         0 $cover->{$subsetter->map_glyph($_)} = delete $cover->{$_};
508 0         0 $isEmpty = 0;
509             }
510             }
511 0 0       0 if ($isEmpty)
512 0         0 { return undef; }
513             else
514             {
515 0         0 $coverage->{'isremapped'} = 1;
516 0         0 return $coverage;
517             }
518             }
519              
520             sub subset_string
521             {
522 0     0 0 0 my ($self, $subsetter, $string, $fmt, $classvals) = @_;
523 0         0 my ($test) = 1;
524              
525 0 0 0     0 return 0 if ($fmt == 2 && !$classvals);
526 0         0 foreach (@{$string})
  0         0  
527             {
528 0 0 0     0 if ($fmt == 1 && $subsetter->keep_glyph($_))
    0 0        
    0 0        
529 0         0 { $_ = $subsetter->map_glyph($_); }
530             elsif ($fmt == 2 && defined $classvals->[$_])
531 0         0 { $_ = $classvals->[$_]; }
532             elsif ($fmt == 3 && $self->subset_cover($subsetter, $_, undef))
533             { }
534             else
535             {
536 0         0 $test = 0;
537 0         0 last;
538             }
539             }
540 0         0 return $test;
541             }
542              
543             sub subset_context
544             {
545 0     0 0 0 my ($self, $subsetter, $sub, $type, $lkvec) = @_;
546 0         0 my ($fmt) = $sub->{'FORMAT'};
547 0         0 my ($classvals, $prevals, $postvals, $i, $j, @gids);
548              
549 0 0 0     0 return 0 if (defined $sub->{'COVERAGE'} && !$self->subset_cover($subsetter, $sub->{'COVERAGE'}, $fmt < 2 ? $sub->{'RULES'} : undef));
    0          
550 0         0 while (my ($k, $v) = each %{$sub->{'COVERAGE'}{'val'}})
  0         0  
551 0         0 { $gids[$v] = $k; }
552 0 0 0     0 return 0 if (defined $sub->{'CLASS'} && !($classvals = $self->subset_class($subsetter, $sub->{'CLASS'})));
553 0 0 0     0 return 0 if (defined $sub->{'PRE_CLASS'} && !($prevals = $self->subset_class($subsetter, $sub->{'PRE_CLASS'})));
554 0 0 0     0 return 0 if (defined $sub->{'POST_CLASS'} && !($postvals = $self->subset_class($subsetter, $sub->{'POST_CLASS'})));
555             # tidy up coverage tables that contain glyphs not in the matching class
556             # if (defined $sub->{'CLASS'})
557             # {
558             # foreach $i (0 .. $#gids)
559             # {
560             # if (defined $gids[$i] && !defined $sub->{'CLASS'}{'val'}{$gids[$i]})
561             # {
562             # delete $sub->{'COVERAGE'}{'val'}{$gids[$i]};
563             # delete $gids[$i];
564             # }
565             # }
566             # @gids = grep {defined $_} @gids;
567             # }
568             # return 0 unless (@gids);
569              
570              
571 0         0 foreach $i (0 .. @{$sub->{'RULES'}})
  0         0  
572             {
573 0         0 my ($isEmpty) = 1;
574 0 0       0 if ($sub->{'RULES'}[$i])
575             {
576 0         0 foreach $j (0 .. $#{$sub->{'RULES'}[$i]})
  0         0  
577             {
578 0         0 my ($r) = $sub->{'RULES'}[$i][$j];
579 0         0 my ($test) = 1;
580 0 0       0 if ($type == 4)
581             {
582 0 0       0 if ($subsetter->keep_glyph($r->{'ACTION'}[0]))
583 0         0 { $r->{'ACTION'}[0] = $subsetter->map_glyph($r->{'ACTION'}[0]); }
584             else
585 0         0 { $test = 0; }
586             }
587             else
588             {
589 0         0 foreach my $k (0 .. $#{$sub->{'RULES'}[$i][$j]{'ACTION'}})
  0         0  
590             {
591 0         0 my $a = $sub->{'RULES'}[$i][$j]{'ACTION'}[$k];
592 0 0       0 if (!vec($lkvec, $a->[1], 1))
593 0         0 { delete $sub->{'RULES'}[$i][$j]{'ACTION'}[$k]; }
594             }
595 0         0 $test = (@{$sub->{'RULES'}[$i][$j]{'ACTION'}} != 0);
  0         0  
596             }
597 0 0 0     0 if ($test && $type == 6 && defined $r->{'PRE'})
      0        
598 0         0 { $test = $self->subset_string($subsetter, $r->{'PRE'}, $fmt, $prevals); }
599 0 0 0     0 if ($test && $type == 6 && defined $r->{'POST'})
      0        
600 0         0 { $test = $self->subset_string($subsetter, $r->{'POST'}, $fmt, $postvals); }
601 0 0       0 if ($test)
602 0         0 { $test = $self->subset_string($subsetter, $r->{'MATCH'}, $fmt, $classvals); }
603 0 0       0 if (!$test)
604 0         0 { delete $sub->{'RULES'}[$i][$j]; }
605             else
606 0         0 { $isEmpty = 0; }
607             }
608 0         0 $sub->{'RULES'}[$i] = [grep {$_} @{$sub->{'RULES'}[$i]}];
  0         0  
  0         0  
609             }
610 0 0       0 if ($isEmpty)
611             {
612 0         0 delete $sub->{'RULES'}[$i];
613 0 0       0 delete $sub->{'COVERAGE'}{'val'}{$gids[$i]} if ($fmt < 2); # already remapped
614             }
615             }
616 0         0 return 1;
617             }
618              
619             sub fixcontext
620             {
621 0     0 0 0 my ($self, $l, $lmap) = @_;
622              
623 0 0 0     0 return if ($l->{'TYPE'} < $self->extension() - 2 || $l->{'TYPE'} >= $self->extension());
624 0         0 foreach my $s (@{$l->{'SUB'}})
  0         0  
625             {
626 0         0 foreach my $r (@{$s->{'RULES'}})
  0         0  
627             {
628 0         0 foreach my $p (@{$r})
  0         0  
629             {
630 0         0 foreach my $b (@{$p->{'ACTION'}})
  0         0  
631 0         0 { $b->[1] = $lmap->[$b->[1]]; }
632             }
633             }
634             }
635             }
636              
637              
638              
639             package
640             Font::TTF::GSUB;
641              
642             sub subset_subtable
643             {
644 0     0 0 0 my ($self, $subsetter, $sub, $lookup, $lkvec) = @_;
645 0         0 my ($type) = $lookup->{'TYPE'};
646 0         0 my ($fmt) = $sub->{'FORMAT'};
647 0         0 my ($r, $i, $j, @gids, $k, $v);
648              
649 0 0 0     0 return 0 if ($type < 4 && !$self->subset_cover($subsetter, $sub->{'COVERAGE'}, $sub->{'RULES'}));
650              
651 0         0 while (($k, $v) = each %{$sub->{'COVERAGE'}{'val'}})
  0         0  
652 0         0 { $gids[$v] = $k; }
653              
654 0 0 0     0 if (($type == 1 && $fmt > 1) || $type == 2)
    0 0        
    0 0        
655             {
656 0         0 foreach $i (0 .. $#{$sub->{'RULES'}})
  0         0  
657             {
658 0 0       0 next unless (defined $sub->{'RULES'}[$i]);
659 0         0 foreach my $k (0 .. $#{$sub->{'RULES'}[$i][0]{'ACTION'}})
  0         0  
660             {
661 0         0 $j = $sub->{'RULES'}[$i][0]{'ACTION'}[$k];
662 0 0       0 if (!$subsetter->keep_glyph($j))
663             {
664 0         0 delete $sub->{'RULES'}[$i];
665 0         0 delete $sub->{'COVERAGE'}{'val'}{$gids[$i]}; # already remapped
666 0         0 last;
667             }
668             else
669 0         0 { $sub->{'RULES'}[$i][0]{'ACTION'}[$k] = $subsetter->map_glyph($j); }
670             }
671             }
672             }
673             elsif ($type == 3)
674             {
675 0         0 foreach $i (0 .. $#{$sub->{'RULES'}})
  0         0  
676             {
677 0 0       0 if (!defined $sub->{'RULES'}[$i])
678             {
679 0         0 delete $sub->{'COVERAGE'}{'val'}{$gids[$i]};
680 0         0 next;
681             }
682 0         0 my $res = [];
683 0         0 foreach $j (@{$sub->{'RULES'}[$i][0]{'ACTION'}})
  0         0  
684             {
685 0 0       0 if ($subsetter->keep_glyph($j))
686 0         0 { push (@{$res}, $subsetter->map_glyph($j)); }
  0         0  
687             }
688 0 0       0 if (@{$res})
  0         0  
689 0         0 { $sub->{'RULES'}[$i][0]{'ACTION'} = $res; }
690             else
691             {
692 0         0 delete $sub->{'RULES'}[$i];
693 0         0 delete $sub->{'COVERAGE'}{'val'}{$gids[$i]}; # already remapped
694             }
695             }
696             }
697             elsif ($type >=4 && $type <= 6)
698 0         0 { return $self->subset_context($subsetter, $sub, $type, $lkvec); }
699 0         0 return 1;
700             }
701              
702             package
703             Font::TTF::GPOS;
704              
705             sub subset_subtable
706             {
707 0     0 0 0 my ($self, $subsetter, $sub, $lookup, $lkvec) = @_;
708 0         0 my ($type) = $lookup->{'TYPE'};
709 0         0 my ($fmt) = $sub->{'FORMAT'};
710 0         0 my (@gids) = sort { $a <=> $b} keys %{$sub->{'COVERAGE'}{'val'}};
  0         0  
  0         0  
711 0         0 my ($i, $j, $k);
712              
713 0 0 0     0 return 0 if ($type <= 6 && !$self->subset_cover($subsetter, $sub->{'COVERAGE'}, $sub->{'RULES'}));
714 0 0 0     0 if ($type == 2 && $fmt == 1)
    0 0        
    0 0        
    0 0        
715             {
716 0         0 foreach $i (0 .. $#{$sub->{'RULES'}})
  0         0  
717             {
718 0         0 foreach $j (0 .. $#{$sub->{'RULES'}[$i]})
  0         0  
719             {
720 0         0 my ($r) = $sub->{'RULES'}[$i][$j];
721 0 0       0 if (!$subsetter->keep_glyph($r->{'MATCH'}[0]))
722 0         0 { delete $sub->{'RULES'}[$i][$j]; }
723             else
724 0         0 { $r->{'MATCH'}[0] = $subsetter->map_glyph($r->{'MATCH'}[0]); }
725             }
726 0 0       0 if (!@{$sub->{'RULES'}[$i]})
  0         0  
727 0         0 { delete $sub->{'RULES'}[$i]; }
728             else
729 0         0 { $sub->{'RULES'}[$i] = [grep {$_} @{$sub->{'RULES'}[$i]}]; }
  0         0  
  0         0  
730             }
731             }
732             elsif ($type == 2 && $fmt == 2)
733             {
734 0         0 my ($c1vals) = $self->subset_class($subsetter, $sub->{'CLASS'});
735 0         0 my ($c2vals) = $self->subset_class($subsetter, $sub->{'MATCH'}[0]);
736 0         0 my ($nrules) = [];
737            
738 0         0 foreach $i (0 .. $#{$sub->{'RULES'}})
  0         0  
739             {
740 0 0       0 if (!$c1vals->[$i])
741 0         0 { delete $sub->{'RULES'}[$i]; }
742             else
743             {
744 0         0 my (@nrule);
745 0         0 foreach $j (0 .. $#{$sub->{'RULES'}[$i]})
  0         0  
746             {
747 0 0       0 if (!defined $c2vals->[$j])
748 0         0 { delete $sub->{'RULES'}[$i][$j]; }
749             else
750 0         0 { $nrule[$c2vals->[$j]] = $sub->{'RULES'}[$i][$j]; }
751             }
752 0 0       0 if (@nrule)
753 0         0 { $nrules->[$c1vals->[$i]] = [grep {$_} @nrule]; }
  0         0  
754             }
755             }
756 0 0       0 if (@{$nrules})
  0         0  
757 0         0 { $sub->{'RULES'} = $nrules; }
758             else
759 0         0 { return 0; }
760             }
761             elsif ($type >= 4 && $type <= 6)
762 0 0       0 { return $self->subset_cover($subsetter, $sub->{'MATCH'}[0], $sub->{'MARKS'}) ? 1 : 0; }
763             elsif ($type >=7 && $type <= 8)
764 0         0 { return $self->subset_context($subsetter, $sub, $type - 2, $lkvec); }
765 0         0 return 1;
766             }
767              
768             package
769             Font::TTF::GDEF;
770              
771             sub subset
772             {
773 0     0 0 0 my ($self, $subsetter) = @_;
774              
775 0 0       0 return unless ($self->SUPER::subset($subsetter));
776 0 0       0 if (defined $self->{'GLYPH'})
777 0 0       0 { delete $self->{'GLYPH'} unless (Font::TTF::Ttopen->subset_class($subsetter, $self->{'GLYPH'}, 1)); }
778 0 0       0 if (defined $self->{'ATTACH'})
779 0 0       0 { delete $self->{'ATTACH'} unless (Font::TTF::Ttopen->subset_cover($subsetter, $self->{'ATTACH'}{'COVERAGE'}, $self->{'ATTACH'}{'POINTS'})); }
780 0 0       0 if (defined $self->{'LIG'})
781 0 0       0 { delete $self->{'LIG'} unless (Font::TTF::Ttopen->subset_cover($subsetter, $self->{'LIG'}{'COVERAGE'}, $self->{'LIG'}{'POINTS'})); }
782 0 0       0 if (defined $self->{'MARKS'})
783 0 0       0 { delete $self->{'MARKS'} unless (Font::TTF::Ttopen->subset_cover($subsetter, $self->{'MARKS'}, undef)); }
784             }
785              
786             package
787             Font::TTF::Cmap;
788              
789             sub subset
790             {
791 2     2 0 6 my ($self, $subsetter) = @_;
792              
793 2 50       10 return unless ($self->SUPER::subset($subsetter));
794 2         4 foreach my $i (0 .. $#{$self->{'Tables'}})
  2         9  
795             {
796 6         33 my ($t) = $self->{'Tables'}[$i]{'val'};
797 6         9 foreach my $k (keys %{$t})
  6         202  
798             {
799 1198 100       1871 if ($subsetter->keep_glyph($t->{$k}))
800 20         41 { $t->{$k} = $subsetter->map_glyph($t->{$k}); }
801             else
802 1178         1815 { delete $t->{$k}; }
803             }
804 6 100       77 if ($self->is_unicode($i))
805             {
806 4         45 foreach my $k (keys %{$subsetter->{'remaps'}})
  4         22  
807 0         0 { $t->{$k} = $subsetter->map_glyph($subsetter->{'remaps'}{$k}); }
808             }
809             }
810             }
811              
812             package
813             Font::TTF::Post;
814              
815 2     2   13169 no warnings;
  2         5  
  2         647  
816              
817             sub subset
818             {
819 2     2 0 6 my ($self, $subsetter) = @_;
820 2         6 my ($res) = [];
821              
822 2 50       12 return unless ($self->SUPER::subset($subsetter));
823             # need to rewrite for real glyph remapping
824 2         6 foreach my $i (0 .. @{$self->{'VAL'}})
  2         9  
825 408 100       461 { $res->[$subsetter->map_glyph($i)] = $subsetter->keep_glyph($i) ? $self->{'VAL'}[$i] : ".notdef"; }
826 2         21 $self->{'VAL'} = $res;
827             }
828              
829             package
830             Font::TTF::Hmtx;
831              
832             sub subset
833             {
834 2     2 0 8 my ($self, $subsetter) = @_;
835 2         5 my ($adv) = [];
836 2         7 my ($lsb) = [];
837              
838 2 50       14 return unless ($self->SUPER::subset($subsetter));
839 2         7 for (my $i = 0; $i < @{$self->{'advance'}}; $i++)
  408         735  
840             {
841 406 100       612 if ($subsetter->keep_glyph($i))
842             {
843 10         19 my ($g) = $subsetter->map_glyph($i);
844 10         19 $adv->[$g] = $self->{'advance'}[$i];
845 10         20 $lsb->[$g] = $self->{'lsb'}[$i];
846             }
847             }
848 2         15 $self->{'advance'} = $adv;
849 2         15 $self->{'lsb'} = $lsb;
850             }
851              
852             package
853             Font::TTF::LTSH;
854              
855             sub subset
856             {
857 0     0 0   my ($self, $subsetter) = @_;
858 0           my ($res) = [];
859              
860 0 0         return unless ($self->SUPER::subset($subsetter));
861 0           for (my $i = 0; $i < @{$self->{'glyphs'}}; $i++)
  0            
862             {
863 0 0         if ($subsetter->keep_glyph($i))
864 0           { $res->[$subsetter->map_glyph($i)] = $self->{'glyphs'}[$i]; }
865             }
866 0           $self->{'glyphs'} = $res;
867 0           $self->{'Num'} = $subsetter->{'gcount'};
868             }
869              
870              
871             package
872             Font::TTF::Gloc;
873              
874 0     0 0   sub canchangegids { 0; }
875              
876             1;
877              
878             __END__