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   1072 use utf8;
  61         134  
  61         394  
3 61     61   1783 use strict;
  61         169  
  61         1111  
4 61     61   285 use warnings;
  61         126  
  61         1655  
5 61     61   1740 use Types::Standard qw/ArrayRef InstanceOf/;
  61         193967  
  61         519  
6 61     61   62158 use JSON::MaybeXS qw/decode_json/;
  61         227022  
  61         3402  
7 61     61   23813 use Text::Amuse::Compile::Fonts::Family;
  61         186  
  61         2157  
8 61     61   26227 use Text::Amuse::Compile::Fonts::File;
  61         179  
  61         1980  
9 61     61   462 use Moo;
  61         120  
  61         241  
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 2153 my $self = shift;
163 1064         1647 return @{$self->list};
  1064         6031  
164             }
165              
166             sub serif_fonts {
167 493     493 1 36584 my $self = shift;
168 493         974 return grep { $_->is_serif } @{$self->list};
  24528         37287  
  493         2152  
169             }
170              
171             sub mono_fonts {
172 493     493 1 34982 my $self = shift;
173 493         948 return grep { $_->is_mono } @{$self->list};
  24528         37398  
  493         2048  
174             }
175              
176             sub sans_fonts {
177 491     491 1 35403 my $self = shift;
178 491         965 return grep { $_->is_sans } @{$self->list};
  24424         37577  
  491         1895  
179             }
180              
181             sub all_fonts_with_files {
182 1     1 1 245 my $self = shift;
183 1         2 return grep { $_->has_files } @{$self->list};
  3         98  
  1         6  
184             }
185              
186             sub serif_fonts_with_files {
187 1     1 1 266 my $self = shift;
188 1 100       3 return grep { $_->is_serif && $_->has_files } @{$self->list};
  3         14  
  1         5  
189             }
190              
191             sub sans_fonts_with_files {
192 1     1 1 291 my $self = shift;
193 1 100       4 return grep { $_->is_sans && $_->has_files } @{$self->list};
  3         16  
  1         5  
194             }
195              
196             sub mono_fonts_with_files {
197 1     1 1 252 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 2052 my ($self, $slot, $babel_lang) = @_;
203 840 50 33     3408 die "Missing arguments" unless $slot && $babel_lang;
204 840 100       2214 if ($slot eq 'main') {
205 279         774 $slot = 'serif';
206             }
207             # print Dumper([$self->all_fonts]);
208             return grep {
209 840 100 100     2240 $_->type eq $slot and $_->has_languages and $_->for_babel_language($babel_lang)
  39825         240323  
210             } $self->all_fonts;
211             }
212              
213              
214             sub BUILDARGS {
215 1019     1019 1 98072 my ($class, $arg) = @_;
216 1019         1876 my $list;
217 1019 100       3020 if ($arg) {
218 19 100       67 if (my $ref = ref($arg)) {
219 15 50       46 if ($ref eq 'ARRAY') {
220 15         35 $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         9 eval {
228 4 50       159 open (my $fh, '<', $arg) or die "Cannot open $arg $!";
229 4         30 local $/ = undef;
230 4         126 my $body = <$fh>;
231 4         45 close $fh;
232 4         113 $list = decode_json($body);
233             };
234 4 50       22 if ($@) {
235 0         0 warn $@;
236 0         0 $list = undef;
237             }
238             }
239             }
240 1019   66     5223 $list ||= $class->default_font_list;
241 1019         1958 my @out;
242 1019         2640 foreach my $fontref (@$list) {
243 51058         2629902 my $font = { %$fontref }; # do a copy do avoid mangling the argument.
244 51058 50 33     168382 if ($font->{name} and $font->{type}) {
245 51058   66     87740 $font->{desc} ||= $font->{name};
246 51058         75276 foreach my $type (qw/regular bold italic bolditalic/) {
247 204228 100       337099 if (my $file = delete $font->{$type}) {
248 143         2070 my $obj = Text::Amuse::Compile::Fonts::File->new(file => $file,
249             shape => $type
250             );
251 141         3665 $font->{$type} = $obj;
252             }
253             }
254 51056         798098 push @out, Text::Amuse::Compile::Fonts::Family->new(%$font);
255             }
256             }
257 1016         122506 return { list => \@out };
258             }
259              
260             sub default_font_list {
261             return [
262             {
263 1000     1000 1 62223 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;