File Coverage

blib/lib/Text/Amuse/Compile/Fonts/Selected.pm
Criterion Covered Total %
statement 92 96 95.8
branch 35 42 83.3
condition 5 6 83.3
subroutine 11 11 100.0
pod 2 2 100.0
total 145 157 92.3


line stmt bran cond sub pod time code
1             package Text::Amuse::Compile::Fonts::Selected;
2 59     59   435 use utf8;
  59         160  
  59         349  
3 59     59   1881 use strict;
  59         162  
  59         1152  
4 59     59   314 use warnings;
  59         143  
  59         1528  
5 59     59   702 use Moo;
  59         165  
  59         423  
6 59     59   22574 use Types::Standard qw/InstanceOf Enum Bool/;
  59         210  
  59         589  
7              
8             =head1 NAME
9              
10             Text::Amuse::Compile::Fonts::Selected - simple class to hold selected fonts
11              
12             =head1 ACCESSORS
13              
14             All are read-only instances of L.
15              
16             =head2 main
17              
18             =head2 sans
19              
20             =head2 mono
21              
22             =head2 size
23              
24             =head2 luatex
25              
26             Boolean if running under luatex
27              
28             =head2 all_fonts
29              
30             The instance of L carrying all available
31             fonts.
32              
33             =head1 METHODS
34              
35             =head2 compose_polyglossia_fontspec_stanza(lang => 'english', others => [qw/russian farsi/], bidi => 1)
36              
37             The place to produce this stanza is a bit weird, but fontspec and
38             polyglossia are tighly coupled.
39              
40             Named arguments:
41              
42             =over 4
43              
44             =item lang
45              
46             The main language.
47              
48             =item others
49              
50             The other languages as arrayref
51              
52             =item bidi
53              
54             Boolean if bidirectional
55              
56             =item main_is_rtl
57              
58             Boolean if main language is RTL
59              
60             =item is_slide
61              
62             Boolean if for beamer
63              
64             =item captions
65              
66             Custom locale strings. See L
67              
68             =back
69              
70             =head2 families
71              
72             Return an arrayref with the C, C and C
objects.
73              
74             =cut
75              
76             has mono => (is => 'ro', required => 1, isa => InstanceOf['Text::Amuse::Compile::Fonts::Family']);
77             has sans => (is => 'ro', required => 1, isa => InstanceOf['Text::Amuse::Compile::Fonts::Family']);
78             has main => (is => 'ro', required => 1, isa => InstanceOf['Text::Amuse::Compile::Fonts::Family']);
79             has size => (is => 'ro', default => sub { 10 }, isa => Enum[9..14]);
80             has all_fonts => (is => 'ro', required => 1, isa => InstanceOf['Text::Amuse::Compile::Fonts']);
81             has luatex => (is => 'ro', default => sub { 0 }, isa => Bool);
82              
83             sub compose_polyglossia_fontspec_stanza {
84 257     257 1 102649 my ($self, %args) = @_;
85              
86 257         728 my @out;
87              
88 257         940 push @out, <<'STANDARD';
89             \usepackage{microtype}
90             \usepackage{graphicx}
91             \usepackage{alltt}
92             \usepackage{verbatim}
93             \usepackage[shortlabels]{enumitem}
94             \usepackage{tabularx}
95             \usepackage[normalem]{ulem}
96             \def\hsout{\bgroup \ULdepth=-.55ex \ULset}
97             % https://tex.stackexchange.com/questions/22410/strikethrough-in-section-title
98             % Unclear if \protect \hsout is needed. Doesn't looks so
99             \DeclareRobustCommand{\sout}[1]{\texorpdfstring{\hsout{#1}}{#1}}
100             \usepackage{wrapfig}
101              
102             % avoid breakage on multiple

and avoid the next [] to be eaten
103             \newcommand*{\forcelinebreak}{\strut\\*{}}
104              
105             \newcommand*{\hairline}{%
106             \bigskip%
107             \noindent \hrulefill%
108             \bigskip%
109             }
110              
111             % reverse indentation for biblio and play
112              
113             \newenvironment*{amusebiblio}{
114             \leftskip=\parindent
115             \parindent=-\parindent
116             \smallskip
117             \indent
118             }{\smallskip}
119              
120             \newenvironment*{amuseplay}{
121             \leftskip=\parindent
122             \parindent=-\parindent
123             \smallskip
124             \indent
125             }{\smallskip}
126              
127             \newcommand*{\Slash}{\slash\hspace{0pt}}
128              
129             STANDARD
130              
131 257 100       1176 unless($args{is_slide}) {
132 248         855 push @out, <<'HYPERREF';
133             % http://tex.stackexchange.com/questions/3033/forcing-linebreaks-in-url
134             \PassOptionsToPackage{hyphens}{url}\usepackage[hyperfootnotes=false,hidelinks,breaklinks=true]{hyperref}
135             \usepackage{bookmark}
136             HYPERREF
137             }
138 257   100     1335 my $main_lang = $args{lang} || 'english';
139 257 100       691 my @langs = (@{ $args{others} || [] }, $main_lang);
  257         1568  
140 257         1295 my $babel_langs = join(',', @langs) . ",shorthands=off";
141 257         886 my $bidi_schema = 'basic';
142 257 100       1692 unless ($self->luatex) {
143 252 100       1219 $bidi_schema = $args{main_is_rtl} ? 'bidi-r' : 'bidi-l';
144             }
145 257 100       1268 my $bidi = $args{bidi} ? ", bidi=$bidi_schema" : "";
146             BABELFONTS: {
147 257 100       717 if (Text::Amuse::Utils::has_babel_ldf($main_lang)) {
  257         1561  
148             # one or more is missing, load the main from ldf, others from ini
149 238 100       14990 if (grep { !Text::Amuse::Utils::has_babel_ldf($_) } @{ $args{others} || []}) {
  16 100       1118  
  238         1562  
150 8         808 push @out, "\\usepackage[$babel_langs,provide+=*${bidi}]{babel}";
151             }
152             else {
153             # load everything with the standard ldf
154 230         1148 push @out, "\\usepackage[${babel_langs}${bidi}]{babel}";
155             }
156             }
157             else {
158 19         3417 push @out, "\\usepackage[$babel_langs,provide*=*${bidi}]{babel}";
159             }
160 257         1920 my %slots = (qw/main rm
161             mono tt
162             sans sf/);
163 257         1959 foreach my $slot (sort keys %slots) {
164             # check all the available fonts if there are language specific
165 771         7674 foreach my $lang (reverse @langs) {
166 858         4111 my $font = $self->_font_for_slot_and_lang($slot, $lang);
167 858 100       3149 my @font_opts = $slot eq 'main' ? () : (qw/Scale MatchLowercase/);
168 858 100       2176 if ($lang eq $main_lang) {
169             push @out, sprintf("\\babelfont{%s}[%s]{%s}",
170 771         3244 $slots{$slot},
171             $font->babel_font_options(@font_opts),
172             $font->babel_font_name);
173             }
174             else {
175             push @out, sprintf("\\babelfont[%s]{%s}[%s]{%s}",
176             $lang,
177 87         303 $slots{$slot},
178             $font->babel_font_options(@font_opts),
179             $font->babel_font_name);
180             }
181             }
182             }
183             }
184 257         5026 my %cjk = (
185             japanese => 1,
186             korean => 1,
187             chinese => 1,
188             );
189              
190 257 100       1144 if ($cjk{$main_lang}) {
191             # these will die with luatex. Too bad.
192             # right now we’re using Song for sans and Kai for sf
193             # https://github.com/adobe-fonts/source-han-serif/releases/download/2.000R/SourceHanSerifCN.zip
194             # https://github.com/adobe-fonts/source-han-sans/releases/download/2.004R/SourceHanSansCN.zip
195             # load all languages with ini files
196 3         13 push @out, "\\usepackage{xeCJK}";
197 3         10 foreach my $slot (qw/main mono sans/) {
198             # original lang
199 9         29 my $font = $self->_font_for_slot_and_lang($slot, $main_lang);
200 9         38 push @out, sprintf("\\setCJK${slot}font{%s}[%s]",
201             $font->babel_font_name,
202             $font->babel_font_options,
203             );
204             }
205             }
206 257 50       1016 if (my $custom = $args{captions}) {
207 0 0       0 if (my $base = delete $custom->{_base_}) {
208 0         0 foreach my $k (sort keys %$custom) {
209 0         0 push @out, "\\setlocalecaption{$base}{$k}{$custom->{$k}}";
210             }
211             }
212             }
213 257 100       900 if ($args{has_ruby}) {
214 2         6 push @out, "\\usepackage{ruby}";
215             }
216 257         803 push @out, '';
217 257         4030 return join("\n", @out);
218             }
219              
220             sub _shape_mapping {
221             return +{
222 9     9   66 bold => 'BoldFont',
223             italic => 'ItalicFont',
224             bolditalic => 'BoldItalicFont',
225             };
226             }
227              
228             has definitions => (is => 'lazy');
229              
230             sub _build_definitions {
231 1     1   415 my $self = shift;
232 1         3 my %definitions;
233 1         3 foreach my $slot (qw/mono sans main/) {
234 3         8 my $font = $self->$slot;
235 3 100       17 my %definition = (
236             name => $font->name,
237             attr => { $slot eq 'main' ? () : (Scale => 'MatchLowercase' ) },
238             );
239 3 50       46 if ($font->has_files) {
240 3         67 $definition{name} = $font->regular->basename_and_ext;
241              
242 3         51 my $dirname = $font->regular->dirname;
243              
244             # if $dirname have spaces, etc., skip it, and let's hope
245             # tex will find them anyway.
246 3 50       22 if ($font->regular->dirname =~ m/\A([A-Za-z0-9\.\/_-]+)\z/) {
247 3         29 $definition{attr}{Path} = $1;
248             }
249             else {
250 0         0 warn $font->regular->dirname . " does not look like a path which can be embedded." .
251             " Please make sure the fonts are installed in a standard TeX location\n";
252             }
253              
254 3         5 my %map = %{$self->_shape_mapping};
  3         8  
255 3         13 foreach my $method (keys %map) {
256 9         132 $definition{attr}{$map{$method}} = $font->$method->basename_and_ext;
257             }
258             }
259 3         61 $definitions{$slot} = \%definition;
260             }
261 1         9 return \%definitions;
262             }
263              
264             sub _fontspec_args {
265 6     6   1284 my ($self, $slot, $language) = @_;
266 6   50     15 $language ||= 'english';
267 6         24 my %scripts = (
268             macedonian => 'Cyrillic',
269             russian => 'Cyrillic',
270             farsi => 'Arabic',
271             arabic => 'Arabic',
272             hebrew => 'Hebrew',
273             greek => 'Greek',
274             );
275 6 50       123 my $def = $self->definitions->{$slot} or die "bad usage, can't find $slot";
276 6   100     70 my $script = $scripts{$language} || 'Latin';
277 6         13 my @list = ("Ligatures=TeX");
278 6         7 my @shapes = sort values %{ $self->_shape_mapping };
  6         13  
279 6         18 foreach my $att (qw/Scale Path/, @shapes) {
280 30 100       57 if (my $v = $def->{attr}->{$att}) {
281 28         53 push @list, "$att=$v";
282             }
283             }
284 6         42 return sprintf('{%s}[%s]', $def->{name}, join(",%\n ", @list));
285             }
286              
287             sub families {
288 134     134 1 1328237 my $self = shift;
289 134         1378 return [ $self->main, $self->mono, $self->sans ];
290             }
291              
292             sub _font_for_slot_and_lang {
293 867     867   2243 my ($self, $slot, $lang) = @_;
294 867         3741 my $font = $self->$slot;
295 867 100       4323 if (my @language_specific = $self->all_fonts->fonts_for_language($slot, $lang)) {
296             # there are other fonts setting the lang
297 15 50       48 unless ($font->for_babel_language($lang)) {
298 15         131 $font = $language_specific[0];
299             }
300             }
301 867         2658 return $font;
302             }
303              
304              
305             1;