File Coverage

blib/lib/Text/Amuse/Compile/Merged.pm
Criterion Covered Total %
statement 155 166 93.3
branch 26 30 86.6
condition 10 16 62.5
subroutine 34 39 87.1
pod 31 31 100.0
total 256 282 90.7


line stmt bran cond sub pod time code
1             package Text::Amuse::Compile::Merged;
2              
3 59     59   126895 use strict;
  59         149  
  59         2655  
4 59     59   328 use warnings;
  59         133  
  59         3583  
5 59     59   374 use utf8;
  59         131  
  59         535  
6 59     59   2587 use Text::Amuse;
  59         44401  
  59         2410  
7 59     59   739 use Text::Amuse::Functions qw/muse_format_line/;
  59         1898  
  59         4775  
8 59     59   855 use Text::Amuse::Compile::Templates;
  59         121  
  59         2230  
9 59     59   683 use Template::Tiny;
  59         1168  
  59         161529  
10              
11             =encoding utf8
12              
13             =head1 NAME
14              
15             Text::Amuse::Compile::Merged - Merging muse files together.
16              
17             =head2
18              
19             =head1 SYNOPSIS
20              
21             my $doc = Text::Amuse::Compile::Merged->new( files => ([ file1, file2, ..]);
22             $doc->as_html;
23             $doc->as_splat_html;
24             $doc->as_latex;
25             $doc->header_as_html;
26             $doc->header_as_latex;
27              
28             This module emulates a L document merging files together,
29             and so it can be passed to Text::Amuse::Compile::File and have the
30             thing produced seemlessly.
31              
32             =head1 METHODS
33              
34             =head2 new(files => [qw/file1 file2/], title => 'blabl', ...)
35              
36             The constructor requires the C argument. Any other option is
37             considered part of the header of this virtual L document.
38              
39             On creation, the module will store in the object a list of
40             L objects, which will be merged together.
41              
42             When asking for header_as_html, you get the constructor options (save
43             for the C option) properly formatted.
44              
45             The headers of the individual merged files go into the body.
46              
47             The first file determine the main language of the whole document.
48              
49             Anyway, if it's a multilanguage text, hyphenation is supposed to
50             switch properly.
51              
52             Optionally, C can be passed here.
53              
54             =cut
55              
56             sub new {
57 19     19 1 450925 my ($class, %args) = @_;
58 19         62 my $files = delete $args{files};
59 19         56 my $include_paths = delete $args{include_paths};
60 19 50 33     168 die "Missing files" unless $files && @$files;
61 19         63 my @docs;
62 19         78 my (%languages, %language_codes);
63 19         0 my ($main_lang, $main_lang_code);
64 19         59 foreach my $file (@$files) {
65 59         1653 my %args;
66 59 100       260 if (ref($file)) {
67 53         293 %args = $file->text_amuse_constructor;
68             }
69             else {
70 6         20 %args = (file => $file);
71             }
72 59   100     709 my $doc = Text::Amuse->new(%args,
73             include_paths => $include_paths || [],
74             );
75 59         6870 push @docs, $doc;
76              
77 59         291 my $current_lang_code = $doc->language_code;
78 59         56314 my $current_lang = $doc->language;
79              
80             # the first file determine the main language
81 59   66     9322 $main_lang ||= $current_lang;
82 59   66     274 $main_lang_code ||= $current_lang_code;
83              
84 59 100       238 if ($main_lang ne $current_lang) {
85 27         94 $languages{$current_lang}++;
86 27         81 $language_codes{$current_lang_code}++;
87             }
88 59 100       112 foreach my $other (@{ $doc->other_languages || [] }) {
  59         255  
89 12 100       182249 if ($main_lang ne $other) {
90 6         30 $languages{$other}++;
91             }
92             }
93 59 100       1133492 foreach my $other (@{ $doc->other_language_codes || [] }) {
  59         322  
94 12 100       332 if ($main_lang_code ne $other) {
95 6         63 $language_codes{$other}++;
96             }
97             }
98             }
99 19         487 my (%html_headers, %latex_headers);
100 19         72 foreach my $k (keys %args) {
101 24         5561 $html_headers{$k} = muse_format_line(html => $args{$k});
102 24         15363 $latex_headers{$k} = muse_format_line(ltx => $args{$k});
103             }
104              
105             my $self = {
106             headers => { %args },
107             html_headers => \%html_headers,
108             latex_headers => \%latex_headers,
109             files => [ @$files ],
110             docs => \@docs,
111             hyphenation => $docs[0]->hyphenation, # use the first
112             language => $main_lang,
113             language_code => $main_lang_code,
114             other_languages => \%languages,
115             other_language_codes => \%language_codes,
116             tt => Template::Tiny->new,
117             templates => Text::Amuse::Compile::Templates->new,
118             font_script => $docs[0]->font_script,
119             html_direction => $docs[0]->html_direction,
120             is_rtl => $docs[0]->is_rtl,
121 59 100       3752 is_bidi => scalar(grep { $_->is_rtl || $_->is_bidi } @docs),
122 19   100     8622 has_ruby => scalar(grep { $_->has_ruby } @docs),
  59         1365  
123             include_paths => $include_paths || [],
124             };
125 19         808 bless $self, $class;
126             }
127              
128             =head2 language
129              
130             Return the english name of the main language
131              
132             =head2 language_code
133              
134             Return the code of the main language
135              
136             =head2 other_languages
137              
138             If it's a multilingual merged text, return an arrayref of the other
139             language names, undef otherwise.
140              
141             =head2 other_language_codes
142              
143             If it's a multilingual merged text, return an arrayref of the other
144             language codes, undef otherwise.
145              
146             =head2 hyphenation
147              
148             Return the hyphenation of the first text.
149              
150             =head2 font_script
151              
152             The font script of the first text.
153              
154             =head2 html_direction
155              
156             The direction (rtl or ltr) of the first text
157              
158             =head2 is_rtl
159              
160             Return true if the first text is RTL.
161              
162             =head2 is_bidi
163              
164             Return true if any of the text is RTL or bidirectional.
165              
166             =head2 include_paths
167              
168             Return the include paths set in the object.
169              
170             =head2 has_ruby
171              
172             Return true if any of the pieces needs ruby
173              
174             =cut
175              
176             sub has_ruby {
177 16     16 1 63 shift->{has_ruby};
178             }
179              
180             sub include_paths {
181 0     0 1 0 return @{shift->{include_paths}}
  0         0  
182             }
183              
184             sub language {
185 34     34 1 168 return shift->{language};
186             }
187              
188             sub language_code {
189             return shift->{language_code},
190 69     69 1 2263 }
191              
192             sub hyphenation {
193             return shift->{hyphenation},
194 19     19 1 9305846 }
195              
196             sub other_language_codes {
197 1     1 1 2 my $self = shift;
198 1         2 my %langs = %{ $self->{other_language_codes} };
  1         4  
199 1 50       3 if (%langs) {
200 1         5 return [ sort keys %langs ];
201             }
202             else {
203 0         0 return;
204             }
205             }
206              
207             sub other_languages {
208 17     17 1 42 my $self = shift;
209 17         37 my %langs = %{ $self->{other_languages} };
  17         81  
210 17 100       73 if (%langs) {
211 9         108 return [ sort keys %langs ];
212             }
213             else {
214 8         87 return;
215             }
216             }
217              
218             sub font_script {
219 0     0 1 0 return shift->{font_script};
220             }
221              
222             sub is_bidi {
223 16     16 1 104 return shift->{is_bidi};
224             }
225              
226             sub html_direction {
227 18     18 1 341 return shift->{html_direction};
228             }
229             sub is_rtl {
230 0     0 1 0 return shift->{is_rtl};
231             }
232              
233             =head2 as_splat_html
234              
235             Return a list of HTML fragments.
236              
237             =head2 as_splat_html_with_attrs
238              
239             Return a list of tokens for the minimal html template
240              
241             =head2 as_html
242              
243             As as as_splat_html but return a single string. This is invalid HTML
244             and it should be used only for debugging.
245              
246             =cut
247              
248             sub _as_splat_html {
249 21     21   88 my ($self, %opts) = @_;
250 21         282 my @out;
251 21         63 my $counter = 0;
252 21         90 foreach my $doc ($self->docs) {
253 56         943 $counter++;
254             # we need to add a title page for each fragment
255 56         132 my $title_page = '';
256 56         288 $self->tt->process($self->templates->title_page_html,
257             { doc => $doc },
258             \$title_page);
259              
260             # add a prefix to disambiguate anchors
261 56         752851 my $prefix = sprintf('piece%06d', $counter);
262 56         337 my @pieces = $doc->as_splat_html;
263 56         646324 foreach my $piece (@pieces) {
264 166         1783 $piece =~ s/(
265             (?:class="text-amuse-link"\x{20} href="\#
266             |id=")
267             text-amuse-label)/$1-$prefix/gx;
268             }
269 56 100       321 if ($opts{attrs}) {
270             push @out, map {
271 37         122 +{
272 145         4360 text => $_,
273             language_code => $doc->language_code,
274             html_direction => $doc->html_direction,
275             }
276             } ($title_page, @pieces);
277             }
278             else {
279 19         108 push @out, $title_page, @pieces;
280             }
281             }
282 21         673 return @out;
283             }
284              
285             sub as_splat_html_with_attrs {
286 11     11 1 51 return shift->_as_splat_html(attrs => 1);
287             }
288              
289             sub as_splat_html {
290 10     10 1 56 return shift->_as_splat_html;
291             }
292              
293             sub as_html {
294 8     8 1 2251 return join("\n", shift->as_splat_html);
295             }
296              
297             =head2 raw_html_toc
298              
299             Implements the C from L
300              
301             =cut
302              
303             sub raw_html_toc {
304 13     13 1 5840 my $self = shift;
305 13         31 my @out;
306 13         31 my $index = 0;
307 13         67 foreach my $doc ($self->docs) {
308              
309             # push the title page
310             push @out, {
311             index => $index++,
312             level => 1,
313             string => $doc->header_as_html->{title},
314 40         254 };
315              
316             # do the same thing we do in the File.pm
317 40         26565 my @pieces = $doc->as_splat_html;
318 40         456056 my @toc = $doc->raw_html_toc;
319 40         464006 my $missing = scalar(@pieces) - scalar(@toc);
320 40 50       185 die "This shouldn't happen: missing pieces: $missing" if $missing;
321             # main loop
322 40         110 foreach my $entry (@toc) {
323             push @out, {
324             index => $index++,
325             level => $entry->{level},
326             string => $entry->{string},
327 121         669 };
328             }
329             }
330 13         147 return @out;
331             }
332              
333             =head2 attachments
334              
335             Implement the C method from C
336              
337             =cut
338              
339             sub attachments {
340 11     11 1 30 my $self = shift;
341 11         22 my %out;
342 11         50 foreach my $doc ($self->docs) {
343 36         584 foreach my $attachment ($doc->attachments) {
344 12         243 $out{$attachment} = 1;
345             }
346             }
347 11         316 return sort keys %out;
348             }
349              
350             =head2 included_files
351              
352             Implement the C method from C
353              
354             =cut
355              
356              
357             sub included_files {
358 0     0 1 0 my $self = shift;
359 0         0 my @out;
360 0         0 foreach my $doc ($self->docs) {
361 0         0 push @out, $doc->included_files;
362             }
363 0         0 return @out;
364             }
365              
366             =head2 as_latex
367              
368             Return the latex body
369              
370             =cut
371              
372             sub as_latex {
373 17     17 1 49 my $self = shift;
374 17         39 my @out;
375 17         76 my $current_language = $self->language;
376 17         42 my $counter = 0;
377 17         329 foreach my $doc ($self->docs) {
378 49         95 $counter++;
379 49         210 my $prefix = sprintf('piece%06d', $counter);
380 49         97 my $output = "\n\n";
381              
382 49         225 my $doc_language = $doc->language;
383              
384 49 100       913 if ($doc_language ne $current_language) {
385 26         68 $output .= sprintf('\selectlanguage{%s}', $doc_language) . "\n\n";
386 26         40 $current_language = $doc_language;
387             }
388              
389 49         93 my $template_output = '';
390 49         178 $self->tt->process($self->templates->bare_latex,
391             { doc => $doc },
392             \$template_output);
393             # disambiguate the refs names when merging
394 49         458324 $template_output =~ s/(
395             \\hyper(def|ref\{\})
396             \{
397             )
398             amuse
399             (\})
400             /$1${prefix}amuse$3/gx;
401 49         232 $output .= $template_output;
402 49         227 push @out, $output;
403             }
404 17         347 return join("\n\n", @out, "\n");
405             }
406              
407              
408             =head2 wants_toc
409              
410             Always returns true
411              
412             =head2 wants_postamble
413              
414             Always returns true
415              
416             =head2 wants_preamble
417              
418             Always returns true
419              
420             =cut
421              
422 16     16 1 84 sub wants_toc { return 1; }
423              
424 25     25 1 7491 sub wants_postamble { return 1; }
425              
426 69     69 1 234413 sub wants_preamble { return 1; }
427              
428             =head2 is_deleted
429              
430             Always returns false
431              
432             =cut
433              
434             sub is_deleted {
435 0     0 1 0 return 0;
436             }
437              
438              
439             =head2 header_as_latex
440              
441             Returns an hashref with the LaTeX-formatted info (passed to the constructor).
442              
443             =head2 header_as_html
444              
445             Same as above, but with HTML format.
446              
447             =cut
448              
449             sub header_as_latex {
450 164     164 1 30983 return { %{ shift->{latex_headers} } };
  164         724  
451             }
452              
453             sub header_as_html {
454 27     27 1 1820 return { %{ shift->{html_headers} } };
  27         195  
455             }
456              
457             =head2 header_defined
458              
459             Implements the C method of L.
460              
461             =cut
462              
463             sub header_defined {
464 337     337 1 24624 my $self = shift;
465 337 100       1022 unless (defined $self->{_header_defined_hashref}) {
466 17         41 my %fields;
467 17         95 my %headers = $self->headers;
468 17         76 foreach my $k (keys %headers) {
469 24 50 33     170 if (defined($headers{$k}) and length($headers{$k})) {
470 24         75 $fields{$k} = 1;
471             }
472             }
473 17         78 $self->{_header_defined_hashref} = \%fields;
474             }
475 337         576 return { %{ $self->{_header_defined_hashref} } };
  337         1596  
476             }
477              
478              
479              
480             =head1 INTERNALS
481              
482             =head2 docs
483              
484             Accessor to the list of L objects.
485              
486             =head2 files
487              
488             Accessor to the list of files.
489              
490             =head3 headers
491              
492             Accessor to the headers.
493              
494             =head3 tt
495              
496             Accessor to the L object.
497              
498             =head3 templates
499              
500             Accessor to the L object.
501              
502             =cut
503              
504             sub docs {
505 80     80 1 793 return @{ shift->{docs} };
  80         385  
506             }
507              
508             sub files {
509 1     1 1 412 return @{ shift->{files} };
  1         5  
510             }
511              
512             sub headers {
513 37     37 1 307 return %{ shift->{headers} };
  37         333  
514             }
515              
516             sub tt {
517 105     105 1 498 return shift->{tt};
518             }
519              
520             sub templates {
521 105     105 1 794 return shift->{templates};
522             }
523              
524              
525             1;
526