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 58     58   71793 use strict;
  58         129  
  58         1640  
4 58     58   630 use warnings;
  58         136  
  58         1328  
5 58     58   294 use utf8;
  58         122  
  58         402  
6 58     58   1563 use Text::Amuse;
  58         28324  
  58         1343  
7 58     58   662 use Text::Amuse::Functions qw/muse_format_line/;
  58         1817  
  58         3268  
8 58     58   826 use Text::Amuse::Compile::Templates;
  58         144  
  58         1491  
9 58     58   756 use Template::Tiny;
  58         1182  
  58         104717  
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 23235 my ($class, %args) = @_;
58 19         63 my $files = delete $args{files};
59 19         62 my $include_paths = delete $args{include_paths};
60 19 50 33     134 die "Missing files" unless $files && @$files;
61 19         49 my @docs;
62 19         66 my (%languages, %language_codes);
63 19         0 my ($main_lang, $main_lang_code);
64 19         58 foreach my $file (@$files) {
65 59         861 my %args;
66 59 100       221 if (ref($file)) {
67 53         233 %args = $file->text_amuse_constructor;
68             }
69             else {
70 6         21 %args = (file => $file);
71             }
72 59   100     565 my $doc = Text::Amuse->new(%args,
73             include_paths => $include_paths || [],
74             );
75 59         5633 push @docs, $doc;
76              
77 59         274 my $current_lang_code = $doc->language_code;
78 59         45545 my $current_lang = $doc->language;
79              
80             # the first file determine the main language
81 59   66     5934 $main_lang ||= $current_lang;
82 59   66     228 $main_lang_code ||= $current_lang_code;
83              
84 59 100       205 if ($main_lang ne $current_lang) {
85 27         73 $languages{$current_lang}++;
86 27         56 $language_codes{$current_lang_code}++;
87             }
88 59 100       100 foreach my $other (@{ $doc->other_languages || [] }) {
  59         229  
89 12 100       121492 if ($main_lang ne $other) {
90 6         25 $languages{$other}++;
91             }
92             }
93 59 100       812484 foreach my $other (@{ $doc->other_language_codes || [] }) {
  59         279  
94 12 100       259 if ($main_lang_code ne $other) {
95 6         28 $language_codes{$other}++;
96             }
97             }
98             }
99 19         446 my (%html_headers, %latex_headers);
100 19         88 foreach my $k (keys %args) {
101 24         5701 $html_headers{$k} = muse_format_line(html => $args{$k});
102 24         13256 $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       3024 is_bidi => scalar(grep { $_->is_rtl || $_->is_bidi } @docs),
122 19   100     6148 has_ruby => scalar(grep { $_->has_ruby } @docs),
  59         1214  
123             include_paths => $include_paths || [],
124             };
125 19         583 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 88 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 177 return shift->{language};
186             }
187              
188             sub language_code {
189             return shift->{language_code},
190 53     53 1 1572 }
191              
192             sub hyphenation {
193             return shift->{hyphenation},
194 19     19 1 5349606 }
195              
196             sub other_language_codes {
197 1     1 1 3 my $self = shift;
198 1         2 my %langs = %{ $self->{other_language_codes} };
  1         4  
199 1 50       5 if (%langs) {
200 1         6 return [ sort keys %langs ];
201             }
202             else {
203 0         0 return;
204             }
205             }
206              
207             sub other_languages {
208 17     17 1 49 my $self = shift;
209 17         38 my %langs = %{ $self->{other_languages} };
  17         91  
210 17 100       72 if (%langs) {
211 9         107 return [ sort keys %langs ];
212             }
213             else {
214 8         92 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 96 return shift->{is_bidi};
224             }
225              
226             sub html_direction {
227 18     18 1 265 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   74 my ($self, %opts) = @_;
250 21         55 my @out;
251 21         50 my $counter = 0;
252 21         111 foreach my $doc ($self->docs) {
253 56         709 $counter++;
254             # we need to add a title page for each fragment
255 56         121 my $title_page = '';
256 56         232 $self->tt->process($self->templates->title_page_html,
257             { doc => $doc },
258             \$title_page);
259              
260             # add a prefix to disambiguate anchors
261 56         498579 my $prefix = sprintf('piece%06d', $counter);
262 56         291 my @pieces = $doc->as_splat_html;
263 56         474658 foreach my $piece (@pieces) {
264 166         1551 $piece =~ s/(
265             (?:class="text-amuse-link"\x{20} href="\#
266             |id=")
267             text-amuse-label)/$1-$prefix/gx;
268             }
269 56 100       245 if ($opts{attrs}) {
270             push @out, map {
271 37         119 +{
272 145         3363 text => $_,
273             language_code => $doc->language_code,
274             html_direction => $doc->html_direction,
275             }
276             } ($title_page, @pieces);
277             }
278             else {
279 19         89 push @out, $title_page, @pieces;
280             }
281             }
282 21         467 return @out;
283             }
284              
285             sub as_splat_html_with_attrs {
286 11     11 1 62 return shift->_as_splat_html(attrs => 1);
287             }
288              
289             sub as_splat_html {
290 10     10 1 55 return shift->_as_splat_html;
291             }
292              
293             sub as_html {
294 8     8 1 1372 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 3101 my $self = shift;
305 13         24 my @out;
306 13         31 my $index = 0;
307 13         58 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         191 };
315              
316             # do the same thing we do in the File.pm
317 40         16238 my @pieces = $doc->as_splat_html;
318 40         319588 my @toc = $doc->raw_html_toc;
319 40         339546 my $missing = scalar(@pieces) - scalar(@toc);
320 40 50       210 die "This shouldn't happen: missing pieces: $missing" if $missing;
321             # main loop
322 40         107 foreach my $entry (@toc) {
323             push @out, {
324             index => $index++,
325             level => $entry->{level},
326             string => $entry->{string},
327 121         509 };
328             }
329             }
330 13         120 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 33 my $self = shift;
341 11         27 my %out;
342 11         52 foreach my $doc ($self->docs) {
343 36         544 foreach my $attachment ($doc->attachments) {
344 12         241 $out{$attachment} = 1;
345             }
346             }
347 11         290 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 48 my $self = shift;
374 17         38 my @out;
375 17         135 my $current_language = $self->language;
376 17         42 my $counter = 0;
377 17         82 foreach my $doc ($self->docs) {
378 49         101 $counter++;
379 49         359 my $prefix = sprintf('piece%06d', $counter);
380 49         114 my $output = "\n\n";
381              
382 49         219 my $doc_language = $doc->language;
383              
384 49 100       851 if ($doc_language ne $current_language) {
385 26         109 $output .= sprintf('\selectlanguage{%s}', $doc_language) . "\n\n";
386 26         51 $current_language = $doc_language;
387             }
388              
389 49         105 my $template_output = '';
390 49         187 $self->tt->process($self->templates->bare_latex,
391             { doc => $doc },
392             \$template_output);
393             # disambiguate the refs names when merging
394 49         335271 $template_output =~ s/(
395             \\hyper(def|ref\{\})
396             \{
397             )
398             amuse
399             (\})
400             /$1${prefix}amuse$3/gx;
401 49         299 $output .= $template_output;
402 49         220 push @out, $output;
403             }
404 17         264 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 78 sub wants_toc { return 1; }
423              
424 25     25 1 4648 sub wants_postamble { return 1; }
425              
426 69     69 1 146606 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 20919 return { %{ shift->{latex_headers} } };
  164         636  
451             }
452              
453             sub header_as_html {
454 27     27 1 488 return { %{ shift->{html_headers} } };
  27         202  
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 16532 my $self = shift;
465 337 100       868 unless (defined $self->{_header_defined_hashref}) {
466 17         51 my %fields;
467 17         88 my %headers = $self->headers;
468 17         77 foreach my $k (keys %headers) {
469 24 50 33     180 if (defined($headers{$k}) and length($headers{$k})) {
470 24         82 $fields{$k} = 1;
471             }
472             }
473 17         105 $self->{_header_defined_hashref} = \%fields;
474             }
475 337         437 return { %{ $self->{_header_defined_hashref} } };
  337         1417  
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 653 return @{ shift->{docs} };
  80         354  
506             }
507              
508             sub files {
509 1     1 1 505 return @{ shift->{files} };
  1         6  
510             }
511              
512             sub headers {
513 37     37 1 311 return %{ shift->{headers} };
  37         323  
514             }
515              
516             sub tt {
517 105     105 1 413 return shift->{tt};
518             }
519              
520             sub templates {
521 105     105 1 652 return shift->{templates};
522             }
523              
524              
525             1;
526