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   81832 use strict;
  58         126  
  58         1603  
4 58     58   276 use warnings;
  58         113  
  58         1305  
5 58     58   274 use utf8;
  58         116  
  58         463  
6 58     58   1604 use Text::Amuse;
  58         31497  
  58         1554  
7 58     58   871 use Text::Amuse::Functions qw/muse_format_line/;
  58         2027  
  58         3144  
8 58     58   944 use Text::Amuse::Compile::Templates;
  58         145  
  58         1515  
9 58     58   896 use Template::Tiny;
  58         1255  
  58         100849  
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 20360 my ($class, %args) = @_;
58 19         63 my $files = delete $args{files};
59 19         46 my $include_paths = delete $args{include_paths};
60 19 50 33     124 die "Missing files" unless $files && @$files;
61 19         41 my @docs;
62 19         63 my (%languages, %language_codes);
63 19         0 my ($main_lang, $main_lang_code);
64 19         56 foreach my $file (@$files) {
65 59         776 my %args;
66 59 100       212 if (ref($file)) {
67 53         228 %args = $file->text_amuse_constructor;
68             }
69             else {
70 6         16 %args = (file => $file);
71             }
72 59   100     513 my $doc = Text::Amuse->new(%args,
73             include_paths => $include_paths || [],
74             );
75 59         5378 push @docs, $doc;
76              
77 59         233 my $current_lang_code = $doc->language_code;
78 59         43334 my $current_lang = $doc->language;
79              
80             # the first file determine the main language
81 59   66     5515 $main_lang ||= $current_lang;
82 59   66     201 $main_lang_code ||= $current_lang_code;
83              
84 59 100       177 if ($main_lang ne $current_lang) {
85 27         76 $languages{$current_lang}++;
86 27         59 $language_codes{$current_lang_code}++;
87             }
88 59 100       99 foreach my $other (@{ $doc->other_languages || [] }) {
  59         205  
89 12 100       99761 if ($main_lang ne $other) {
90 6         14 $languages{$other}++;
91             }
92             }
93 59 100       802884 foreach my $other (@{ $doc->other_language_codes || [] }) {
  59         264  
94 12 100       200 if ($main_lang_code ne $other) {
95 6         20 $language_codes{$other}++;
96             }
97             }
98             }
99 19         424 my (%html_headers, %latex_headers);
100 19         96 foreach my $k (keys %args) {
101 24         4865 $html_headers{$k} = muse_format_line(html => $args{$k});
102 24         12450 $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       2859 is_bidi => scalar(grep { $_->is_rtl || $_->is_bidi } @docs),
122 19   100     6940 has_ruby => scalar(grep { $_->has_ruby } @docs),
  59         1099  
123             include_paths => $include_paths || [],
124             };
125 19         641 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 90 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 150 return shift->{language};
186             }
187              
188             sub language_code {
189             return shift->{language_code},
190 53     53 1 1412 }
191              
192             sub hyphenation {
193             return shift->{hyphenation},
194 19     19 1 4929388 }
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       3 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 40 my $self = shift;
209 17         32 my %langs = %{ $self->{other_languages} };
  17         90  
210 17 100       65 if (%langs) {
211 9         89 return [ sort keys %langs ];
212             }
213             else {
214 8         75 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 80 return shift->{is_bidi};
224             }
225              
226             sub html_direction {
227 18     18 1 248 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   70 my ($self, %opts) = @_;
250 21         37 my @out;
251 21         38 my $counter = 0;
252 21         70 foreach my $doc ($self->docs) {
253 56         674 $counter++;
254             # we need to add a title page for each fragment
255 56         110 my $title_page = '';
256 56         154 $self->tt->process($self->templates->title_page_html,
257             { doc => $doc },
258             \$title_page);
259              
260             # add a prefix to disambiguate anchors
261 56         469258 my $prefix = sprintf('piece%06d', $counter);
262 56         211 my @pieces = $doc->as_splat_html;
263 56         453342 foreach my $piece (@pieces) {
264 166         1455 $piece =~ s/(
265             (?:class="text-amuse-link"\x{20} href="\#
266             |id=")
267             text-amuse-label)/$1-$prefix/gx;
268             }
269 56 100       193 if ($opts{attrs}) {
270             push @out, map {
271 37         81 +{
272 145         2919 text => $_,
273             language_code => $doc->language_code,
274             html_direction => $doc->html_direction,
275             }
276             } ($title_page, @pieces);
277             }
278             else {
279 19         69 push @out, $title_page, @pieces;
280             }
281             }
282 21         406 return @out;
283             }
284              
285             sub as_splat_html_with_attrs {
286 11     11 1 49 return shift->_as_splat_html(attrs => 1);
287             }
288              
289             sub as_splat_html {
290 10     10 1 52 return shift->_as_splat_html;
291             }
292              
293             sub as_html {
294 8     8 1 1305 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 3332 my $self = shift;
305 13         26 my @out;
306 13         29 my $index = 0;
307 13         45 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         159 };
315              
316             # do the same thing we do in the File.pm
317 40         16322 my @pieces = $doc->as_splat_html;
318 40         302429 my @toc = $doc->raw_html_toc;
319 40         322637 my $missing = scalar(@pieces) - scalar(@toc);
320 40 50       154 die "This shouldn't happen: missing pieces: $missing" if $missing;
321             # main loop
322 40         88 foreach my $entry (@toc) {
323             push @out, {
324             index => $index++,
325             level => $entry->{level},
326             string => $entry->{string},
327 121         448 };
328             }
329             }
330 13         94 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 27 my $self = shift;
341 11         21 my %out;
342 11         44 foreach my $doc ($self->docs) {
343 36         448 foreach my $attachment ($doc->attachments) {
344 12         186 $out{$attachment} = 1;
345             }
346             }
347 11         262 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 42 my $self = shift;
374 17         37 my @out;
375 17         161 my $current_language = $self->language;
376 17         42 my $counter = 0;
377 17         68 foreach my $doc ($self->docs) {
378 49         83 $counter++;
379 49         266 my $prefix = sprintf('piece%06d', $counter);
380 49         91 my $output = "\n\n";
381              
382 49         190 my $doc_language = $doc->language;
383              
384 49 100       740 if ($doc_language ne $current_language) {
385 26         99 $output .= sprintf('\selectlanguage{%s}', $doc_language) . "\n\n";
386 26         55 $current_language = $doc_language;
387             }
388              
389 49         83 my $template_output = '';
390 49         158 $self->tt->process($self->templates->bare_latex,
391             { doc => $doc },
392             \$template_output);
393             # disambiguate the refs names when merging
394 49         318118 $template_output =~ s/(
395             \\hyper(def|ref\{\})
396             \{
397             )
398             amuse
399             (\})
400             /$1${prefix}amuse$3/gx;
401 49         223 $output .= $template_output;
402 49         176 push @out, $output;
403             }
404 17         220 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 60 sub wants_toc { return 1; }
423              
424 25     25 1 4201 sub wants_postamble { return 1; }
425              
426 69     69 1 129025 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 20142 return { %{ shift->{latex_headers} } };
  164         576  
451             }
452              
453             sub header_as_html {
454 27     27 1 478 return { %{ shift->{html_headers} } };
  27         173  
455             }
456              
457             =head2 header_defined
458              
459             Implements the C method of L.
460              
461             =cut
462              
463             sub header_defined {
464 303     303 1 13754 my $self = shift;
465 303 100       687 unless (defined $self->{_header_defined_hashref}) {
466 17         42 my %fields;
467 17         71 my %headers = $self->headers;
468 17         63 foreach my $k (keys %headers) {
469 24 50 33     137 if (defined($headers{$k}) and length($headers{$k})) {
470 24         69 $fields{$k} = 1;
471             }
472             }
473 17         76 $self->{_header_defined_hashref} = \%fields;
474             }
475 303         386 return { %{ $self->{_header_defined_hashref} } };
  303         1135  
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 884 return @{ shift->{docs} };
  80         326  
506             }
507              
508             sub files {
509 1     1 1 569 return @{ shift->{files} };
  1         6  
510             }
511              
512             sub headers {
513 37     37 1 298 return %{ shift->{headers} };
  37         282  
514             }
515              
516             sub tt {
517 105     105 1 348 return shift->{tt};
518             }
519              
520             sub templates {
521 105     105 1 503 return shift->{templates};
522             }
523              
524              
525             1;
526