File Coverage

blib/lib/Text/Amuse/Compile/Fonts.pm
Criterion Covered Total %
statement 87 90 96.6
branch 21 26 80.7
condition 9 15 60.0
subroutine 19 19 100.0
pod 11 11 100.0
total 147 161 91.3


line stmt bran cond sub pod time code
1             package Text::Amuse::Compile::Fonts;
2 61     61   855 use utf8;
  61         135  
  61         402  
3 61     61   1792 use strict;
  61         118  
  61         1090  
4 61     61   288 use warnings;
  61         114  
  61         1609  
5 61     61   1717 use Types::Standard qw/ArrayRef InstanceOf/;
  61         195595  
  61         500  
6 61     61   62581 use JSON::MaybeXS qw/decode_json/;
  61         217584  
  61         3183  
7 61     61   23230 use Text::Amuse::Compile::Fonts::Family;
  61         201  
  61         2004  
8 61     61   25138 use Text::Amuse::Compile::Fonts::File;
  61         196  
  61         1829  
9 61     61   399 use Moo;
  61         114  
  61         214  
10              
11             =head1 NAME
12              
13             Text::Amuse::Compile::Fonts - class for fonts management
14              
15             =head1 SYNOPSIS
16              
17             # hash to hold the fonts file, where $wd is the font directory
18             my %fontfiles = map { $_ => File::Spec->catfile($wd, $_ . '.otf') } (qw/regular italic
19             bold bolditalic/);
20             my $fonts = Text::Amuse::Compile::Fonts->new([
21             {
22             name => 'Example Serif',
23             type => 'serif',
24             desc => 'example font',
25             regular => $fontfiles{regular},
26             italic => $fontfiles{italic},
27             bold => $fontfiles{bold},
28             bolditalic => $fontfiles{bolditalic},
29             },
30             # more fonts here
31             ]);
32             # or you can pass the same structure if you got it serialized to
33             # json and saved to a file.
34             my $fonts = Text::Amuse::Compile::Fonts->new($json_file);
35             my @fonts = $fonts->all_fonts;
36              
37              
38             =head1 DESCRIPTION
39              
40             This class has the purpose to hold the list of available fonts, which
41             has to be provided to the constructor via a json file or as an
42             arrayref of L objects.
43              
44             To build a json file with some default fonts, you may want to try
45             L script installed with this
46             distribution.
47              
48             =head1 CONSTRUCTOR
49              
50             =head2 new($file_or_arrayref_with_fonts)
51              
52             The constructor accept either a file or an arrayref with fonts specifications.
53              
54             Each font specification is used to construct a
55             L object, which in turn may
56             contain L objects.
57              
58             Keys of the hashref inside the arrayref:
59              
60             =over 4
61              
62             =item name
63              
64             The name of the font. This is the system name, something that
65             fontconfig will understand. You can try with fc-list to see if you can
66             find it. Mandatory.
67              
68             =item type
69              
70             The type of the file. Can be either C, C or C.
71             Mandatory.
72              
73             =item desc
74              
75             An optional free form description.
76              
77             =item regular
78              
79             The path to the regular font file (.ttf or .otf or .woff)
80              
81             =item italic
82              
83             The path to the italic font file (.ttf or .otf or .woff)
84              
85             =item bold
86              
87             The path to the bold font file (.ttf or .otf or .woff)
88              
89             =item bolditalic
90              
91             The path to the bolditalic font file (.ttf or .otf or .woff)
92              
93             =back
94              
95             Please note that the paths to the files are optional. They are used
96             only for the EPUB generation, when the files are embedded in the final
97             file.
98              
99             Also note that the name of the fonts is not arbitrary. Fontconfig
100             needs to recognize it for a successful LaTeX compilation.
101              
102             =head1 ACCESSORS
103              
104             =head2 list
105              
106             The arrayref with the L objects.
107              
108             =head1 METHODS
109              
110             =head2 all_fonts
111              
112             Return the list of fonts, as a plain list
113              
114             =head2 serif_fonts
115              
116             As above, but only the serif fonts
117              
118             =head2 mono_fonts
119              
120             As above, but only the mono fonts
121              
122             =head2 sans_fonts
123              
124             As above, but only the sans fonts
125              
126             =head2 all_fonts_with_files
127              
128             Return the list of fonts which have the paths to the font file.
129              
130             =head2 serif_fonts_with_files
131              
132             As above, but only the serif fonts
133              
134             =head2 mono_fonts_with_files
135              
136             As above, but only the mono fonts
137              
138             =head2 sans_fonts_with_files
139              
140             As above, but only the sans fonts
141              
142             =head2 default_font_list
143              
144             Return an arrayref with the default font definitions
145              
146             =head2 fonts_for_language($babel_lang)
147              
148             Return the list of language-specific fonts.
149              
150             =head1 INTERNALS
151              
152             =head2 BUILDARGS
153              
154             Construct the font list from either the data structure or the file path.
155              
156             =cut
157              
158             has list => (is => 'ro',
159             isa => ArrayRef[InstanceOf['Text::Amuse::Compile::Fonts::Family']]);
160              
161             sub all_fonts {
162 1064     1064 1 2292 my $self = shift;
163 1064         1677 return @{$self->list};
  1064         6218  
164             }
165              
166             sub serif_fonts {
167 493     493 1 36177 my $self = shift;
168 493         970 return grep { $_->is_serif } @{$self->list};
  24528         36700  
  493         2117  
169             }
170              
171             sub mono_fonts {
172 493     493 1 34537 my $self = shift;
173 493         881 return grep { $_->is_mono } @{$self->list};
  24528         36941  
  493         2082  
174             }
175              
176             sub sans_fonts {
177 491     491 1 33858 my $self = shift;
178 491         929 return grep { $_->is_sans } @{$self->list};
  24424         36617  
  491         1811  
179             }
180              
181             sub all_fonts_with_files {
182 1     1 1 352 my $self = shift;
183 1         2 return grep { $_->has_files } @{$self->list};
  3         94  
  1         4  
184             }
185              
186             sub serif_fonts_with_files {
187 1     1 1 342 my $self = shift;
188 1 100       2 return grep { $_->is_serif && $_->has_files } @{$self->list};
  3         14  
  1         5  
189             }
190              
191             sub sans_fonts_with_files {
192 1     1 1 355 my $self = shift;
193 1 100       3 return grep { $_->is_sans && $_->has_files } @{$self->list};
  3         14  
  1         5  
194             }
195              
196             sub mono_fonts_with_files {
197 1     1 1 339 my $self = shift;
198 1 100       3 return grep { $_->is_mono && $_->has_files } @{$self->list};
  3         9  
  1         4  
199             }
200              
201             sub fonts_for_language {
202 840     840 1 2082 my ($self, $slot, $babel_lang) = @_;
203 840 50 33     3446 die "Missing arguments" unless $slot && $babel_lang;
204 840 100       2262 if ($slot eq 'main') {
205 279         1133 $slot = 'serif';
206             }
207             # print Dumper([$self->all_fonts]);
208             return grep {
209 840 100 100     2263 $_->type eq $slot and $_->has_languages and $_->for_babel_language($babel_lang)
  39825         235430  
210             } $self->all_fonts;
211             }
212              
213              
214             sub BUILDARGS {
215 1019     1019 1 96184 my ($class, $arg) = @_;
216 1019         1798 my $list;
217 1019 100       3035 if ($arg) {
218 19 100       63 if (my $ref = ref($arg)) {
219 15 50       39 if ($ref eq 'ARRAY') {
220 15         27 $list = $arg;
221             }
222             else {
223 0         0 die "Argument to ->new must be either a file or an arrayref";
224             }
225             }
226             else {
227 4         11 eval {
228 4 50       161 open (my $fh, '<', $arg) or die "Cannot open $arg $!";
229 4         27 local $/ = undef;
230 4         128 my $body = <$fh>;
231 4         52 close $fh;
232 4         105 $list = decode_json($body);
233             };
234 4 50       21 if ($@) {
235 0         0 warn $@;
236 0         0 $list = undef;
237             }
238             }
239             }
240 1019   66     5372 $list ||= $class->default_font_list;
241 1019         1995 my @out;
242 1019         2594 foreach my $fontref (@$list) {
243 51058         2599700 my $font = { %$fontref }; # do a copy do avoid mangling the argument.
244 51058 50 33     165400 if ($font->{name} and $font->{type}) {
245 51058   66     85503 $font->{desc} ||= $font->{name};
246 51058         72055 foreach my $type (qw/regular bold italic bolditalic/) {
247 204228 100       329898 if (my $file = delete $font->{$type}) {
248 143         2038 my $obj = Text::Amuse::Compile::Fonts::File->new(file => $file,
249             shape => $type
250             );
251 141         3523 $font->{$type} = $obj;
252             }
253             }
254 51056         782183 push @out, Text::Amuse::Compile::Fonts::Family->new(%$font);
255             }
256             }
257 1016         122558 return { list => \@out };
258             }
259              
260             sub default_font_list {
261             return [
262             {
263 1000     1000 1 65287 name => 'CMU Serif',
264             desc => 'Computer Modern',
265             type => 'serif',
266             },
267             {
268             name => 'DejaVu Serif',
269             name => 'DejaVu Serif',
270             type => 'serif',
271             },
272             {
273             name => 'FreeSerif',
274             name => 'FreeSerif',
275             type => 'serif',
276             },
277             {
278             name => 'Linux Libertine O',
279             desc => 'Linux Libertine',
280             type => 'serif',
281             },
282             {
283             name => 'TeX Gyre Termes',
284             desc => 'TeX Gyre Termes (Times)',
285             type => 'serif',
286             },
287             {
288             name => 'TeX Gyre Pagella',
289             desc => 'TeX Gyre Pagella (Palatino)',
290             type => 'serif',
291             },
292             {
293             name => 'TeX Gyre Schola',
294             desc => 'TeX Gyre Schola (Century)',
295             type => 'serif',
296             },
297             {
298             name => 'TeX Gyre Bonum',
299             desc => 'TeX Gyre Bonum (Bookman)',
300             type => 'serif',
301             },
302             { name => 'Coelacanth', desc => 'Coelacanth (no bolditalic)', type => 'serif' },
303             { name => 'Alegreya', desc => 'Alegreya', type => 'serif' },
304             { name => 'Arvo', desc => 'Arvo', type => 'serif' },
305             { name => 'Lora', desc => 'Lora', type => 'serif' },
306             { name => 'Merriweather', desc => 'Merriweather', type => 'serif' },
307             { name => 'Vollkorn', desc => 'Vollkorn', type => 'serif' },
308             # arabic
309             { name => 'Amiri', desc => 'Amiri', type => 'serif' },
310             { name => 'Scheherazade', desc => 'Scheherazade', type => 'serif' },
311             {
312             name => 'Antykwa Poltawskiego',
313             desc => 'Antykwa Półtawskiego',
314             type => 'serif',
315             },
316             {
317             name => 'Antykwa Torunska',
318             desc => 'Antykwa Toruńska',
319             type => 'serif',
320             },
321             {
322             name => 'Charis SIL',
323             desc => 'Charis SIL (Bitstream Charter)',
324             type => 'serif',
325             },
326             {
327             name => 'PT Serif',
328             desc => 'Paratype (cyrillic)',
329             type => 'serif',
330             },
331             {
332             name => 'Noto Serif',
333             desc => 'Noto Serif',
334             type => 'serif',
335             },
336             {
337             name => 'Gentium Book Basic',
338             desc => 'Gentium',
339             type => 'serif',
340             },
341             {
342             name => 'Cormorant Garamond',
343             desc => 'Garamond',
344             type => 'serif',
345             },
346             {
347             name => 'CMU Sans Serif',
348             desc => 'Computer Modern Sans Serif',
349             type => 'sans',
350             },
351             {
352             name => 'TeX Gyre Heros',
353             desc => 'TeX Gyre Heros (Helvetica)',
354             type => 'sans',
355             },
356             {
357             name => 'TeX Gyre Adventor',
358             desc => 'TeX Gyre Adventor (Avant Garde Gothic)',
359             type => 'sans',
360             },
361             {
362             name => 'Iwona',
363             desc => 'Iwona',
364             type => 'sans',
365             },
366             {
367             name => 'DejaVu Sans',
368             desc => 'DejaVu Sans',
369             type => 'sans',
370             },
371             {
372             name => 'PT Sans',
373             desc => 'PT Sans (cyrillic)',
374             type => 'sans',
375             },
376             {
377             name => 'Noto Sans',
378             desc => 'Noto Sans',
379             type => 'sans',
380             },
381             { name => 'Alegreya Sans', desc => 'Alegreya Sans', type => 'sans' },
382             { name => 'Archivo Narrow', desc => 'Archivo Narrow', type => 'sans' },
383             { name => 'Fira Sans', desc => 'Fira Sans', type => 'sans' },
384             { name => 'Karla', desc => 'Karla', type => 'sans' },
385             { name => 'Libre Franklin', desc => 'Libre Franklin', type => 'sans' },
386             { name => 'Poppins', desc => 'Poppins', type => 'sans' },
387             { name => 'Rubik', desc => 'Rubik', type => 'sans' },
388             { name => 'Source Sans Pro', desc => 'Source Sans Pro', type => 'sans' },
389             {
390             name => 'CMU Typewriter Text',
391             desc => 'Computer Modern Typewriter Text',
392             type => 'mono',
393             },
394             {
395             name => 'DejaVu Sans Mono',
396             desc => 'DejaVu Sans Mono',
397             type => 'mono',
398             },
399             {
400             name => 'TeX Gyre Cursor',
401             desc => 'TeX Gyre Cursor (Courier)',
402             type => 'mono',
403             },
404             { name => 'Anonymous Pro', desc => 'Anonymous Pro', type => 'mono' },
405             { name => 'Space Mono', desc => 'Space Mono', type => 'mono' },
406             {
407             "languages" => [ "zh" ],
408             "name" => "Source Han Serif SC",
409             "desc" => "Source Han Serif SC (Simplified Chinese)",
410             "type" => "serif"
411             },
412             {
413             "languages" => [ "ja" ],
414             "name" => "Source Han Serif",
415             "desc" => "Source Han Serif (Japanese)",
416             "type" => "serif"
417             },
418             {
419             "languages" => [ "ko" ],
420             "name" => "Source Han Serif K",
421             "desc" => "Source Han Serif K (Korean)",
422             "type" => "serif"
423             },
424             {
425             "languages" => [ "zh" ],
426             "name" => "Source Han Sans SC",
427             "desc" => "Source Han Sans SC (Simplified Chinese)",
428             "type" => "sans"
429             },
430             {
431             "languages" => [ "ja" ],
432             "name" => "Source Han Sans",
433             "desc" => "Source Han Sans (Japanese)",
434             "type" => "sans"
435             },
436             {
437             "languages" => [ "ko" ],
438             "name" => "Source Han Sans K",
439             "desc" => "Source Han Sans K (Korean)",
440             "type" => "sans"
441             },
442             {
443             "languages" => [ "zh" ],
444             "name" => "FandolSong",
445             "desc" => "FandolSong",
446             "type" => "sans"
447             },
448             {
449             "languages" => [ "zh" ],
450             "desc" => "FandolHei",
451             "name" => "FandolHei",
452             "type" => "serif"
453             },
454             ];
455             }
456              
457              
458             1;