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   87835 use strict;
  59         157  
  59         1938  
4 59     59   332 use warnings;
  59         141  
  59         1575  
5 59     59   348 use utf8;
  59         153  
  59         492  
6 59     59   1830 use Text::Amuse;
  59         35357  
  59         1655  
7 59     59   776 use Text::Amuse::Functions qw/muse_format_line/;
  59         2320  
  59         3893  
8 59     59   974 use Text::Amuse::Compile::Templates;
  59         159  
  59         1948  
9 59     59   838 use Template::Tiny;
  59         1452  
  59         120801  
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 26162 my ($class, %args) = @_;
58 19         69 my $files = delete $args{files};
59 19         66 my $include_paths = delete $args{include_paths};
60 19 50 33     151 die "Missing files" unless $files && @$files;
61 19         44 my @docs;
62 19         83 my (%languages, %language_codes);
63 19         0 my ($main_lang, $main_lang_code);
64 19         65 foreach my $file (@$files) {
65 59         859 my %args;
66 59 100       562 if (ref($file)) {
67 53         250 %args = $file->text_amuse_constructor;
68             }
69             else {
70 6         24 %args = (file => $file);
71             }
72 59   100     549 my $doc = Text::Amuse->new(%args,
73             include_paths => $include_paths || [],
74             );
75 59         5549 push @docs, $doc;
76              
77 59         248 my $current_lang_code = $doc->language_code;
78 59         50075 my $current_lang = $doc->language;
79              
80             # the first file determine the main language
81 59   66     7134 $main_lang ||= $current_lang;
82 59   66     237 $main_lang_code ||= $current_lang_code;
83              
84 59 100       197 if ($main_lang ne $current_lang) {
85 27         107 $languages{$current_lang}++;
86 27         80 $language_codes{$current_lang_code}++;
87             }
88 59 100       111 foreach my $other (@{ $doc->other_languages || [] }) {
  59         220  
89 12 100       143401 if ($main_lang ne $other) {
90 6         29 $languages{$other}++;
91             }
92             }
93 59 100       943392 foreach my $other (@{ $doc->other_language_codes || [] }) {
  59         259  
94 12 100       332 if ($main_lang_code ne $other) {
95 6         29 $language_codes{$other}++;
96             }
97             }
98             }
99 19         432 my (%html_headers, %latex_headers);
100 19         95 foreach my $k (keys %args) {
101 24         4849 $html_headers{$k} = muse_format_line(html => $args{$k});
102 24         15059 $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       3485 is_bidi => scalar(grep { $_->is_rtl || $_->is_bidi } @docs),
122 19   100     9211 has_ruby => scalar(grep { $_->has_ruby } @docs),
  59         1406  
123             include_paths => $include_paths || [],
124             };
125 19         623 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 78 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 148 return shift->{language};
186             }
187              
188             sub language_code {
189             return shift->{language_code},
190 69     69 1 1790 }
191              
192             sub hyphenation {
193             return shift->{hyphenation},
194 19     19 1 6269657 }
195              
196             sub other_language_codes {
197 1     1 1 4 my $self = shift;
198 1         3 my %langs = %{ $self->{other_language_codes} };
  1         5  
199 1 50       6 if (%langs) {
200 1         10 return [ sort keys %langs ];
201             }
202             else {
203 0         0 return;
204             }
205             }
206              
207             sub other_languages {
208 17     17 1 43 my $self = shift;
209 17         45 my %langs = %{ $self->{other_languages} };
  17         90  
210 17 100       73 if (%langs) {
211 9         108 return [ sort keys %langs ];
212             }
213             else {
214 8         96 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 306 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   86 my ($self, %opts) = @_;
250 21         44 my @out;
251 21         54 my $counter = 0;
252 21         84 foreach my $doc ($self->docs) {
253 56         795 $counter++;
254             # we need to add a title page for each fragment
255 56         140 my $title_page = '';
256 56         212 $self->tt->process($self->templates->title_page_html,
257             { doc => $doc },
258             \$title_page);
259              
260             # add a prefix to disambiguate anchors
261 56         582446 my $prefix = sprintf('piece%06d', $counter);
262 56         232 my @pieces = $doc->as_splat_html;
263 56         549955 foreach my $piece (@pieces) {
264 166         1691 $piece =~ s/(
265             (?:class="text-amuse-link"\x{20} href="\#
266             |id=")
267             text-amuse-label)/$1-$prefix/gx;
268             }
269 56 100       231 if ($opts{attrs}) {
270             push @out, map {
271 37         106 +{
272 145         3548 text => $_,
273             language_code => $doc->language_code,
274             html_direction => $doc->html_direction,
275             }
276             } ($title_page, @pieces);
277             }
278             else {
279 19         97 push @out, $title_page, @pieces;
280             }
281             }
282 21         505 return @out;
283             }
284              
285             sub as_splat_html_with_attrs {
286 11     11 1 61 return shift->_as_splat_html(attrs => 1);
287             }
288              
289             sub as_splat_html {
290 10     10 1 60 return shift->_as_splat_html;
291             }
292              
293             sub as_html {
294 8     8 1 1545 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 3880 my $self = shift;
305 13         34 my @out;
306 13         37 my $index = 0;
307 13         53 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         20138 my @pieces = $doc->as_splat_html;
318 40         375412 my @toc = $doc->raw_html_toc;
319 40         400333 my $missing = scalar(@pieces) - scalar(@toc);
320 40 50       146 die "This shouldn't happen: missing pieces: $missing" if $missing;
321             # main loop
322 40         106 foreach my $entry (@toc) {
323             push @out, {
324             index => $index++,
325             level => $entry->{level},
326             string => $entry->{string},
327 121         542 };
328             }
329             }
330 13         143 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 40 my $self = shift;
341 11         26 my %out;
342 11         49 foreach my $doc ($self->docs) {
343 36         581 foreach my $attachment ($doc->attachments) {
344 12         244 $out{$attachment} = 1;
345             }
346             }
347 11         305 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 53 my $self = shift;
374 17         121 my @out;
375 17         78 my $current_language = $self->language;
376 17         40 my $counter = 0;
377 17         77 foreach my $doc ($self->docs) {
378 49         152 $counter++;
379 49         276 my $prefix = sprintf('piece%06d', $counter);
380 49         131 my $output = "\n\n";
381              
382 49         196 my $doc_language = $doc->language;
383              
384 49 100       825 if ($doc_language ne $current_language) {
385 26         126 $output .= sprintf('\selectlanguage{%s}', $doc_language) . "\n\n";
386 26         52 $current_language = $doc_language;
387             }
388              
389 49         123 my $template_output = '';
390 49         163 $self->tt->process($self->templates->bare_latex,
391             { doc => $doc },
392             \$template_output);
393             # disambiguate the refs names when merging
394 49         392343 $template_output =~ s/(
395             \\hyper(def|ref\{\})
396             \{
397             )
398             amuse
399             (\})
400             /$1${prefix}amuse$3/gx;
401 49         265 $output .= $template_output;
402 49         181 push @out, $output;
403             }
404 17         284 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 62 sub wants_toc { return 1; }
423              
424 25     25 1 5222 sub wants_postamble { return 1; }
425              
426 69     69 1 168650 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 24238 return { %{ shift->{latex_headers} } };
  164         704  
451             }
452              
453             sub header_as_html {
454 27     27 1 525 return { %{ shift->{html_headers} } };
  27         207  
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 19512 my $self = shift;
465 337 100       853 unless (defined $self->{_header_defined_hashref}) {
466 17         44 my %fields;
467 17         80 my %headers = $self->headers;
468 17         76 foreach my $k (keys %headers) {
469 24 50 33     210 if (defined($headers{$k}) and length($headers{$k})) {
470 24         83 $fields{$k} = 1;
471             }
472             }
473 17         79 $self->{_header_defined_hashref} = \%fields;
474             }
475 337         544 return { %{ $self->{_header_defined_hashref} } };
  337         1534  
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 806 return @{ shift->{docs} };
  80         376  
506             }
507              
508             sub files {
509 1     1 1 563 return @{ shift->{files} };
  1         9  
510             }
511              
512             sub headers {
513 37     37 1 266 return %{ shift->{headers} };
  37         309  
514             }
515              
516             sub tt {
517 105     105 1 400 return shift->{tt};
518             }
519              
520             sub templates {
521 105     105 1 542 return shift->{templates};
522             }
523              
524              
525             1;
526