| blib/lib/Text/Amuse/Compile/File.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 558 | 663 | 84.1 |
| branch | 170 | 268 | 63.4 |
| condition | 40 | 63 | 63.4 |
| subroutine | 77 | 89 | 86.5 |
| pod | 33 | 33 | 100.0 |
| total | 878 | 1116 | 78.6 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Text::Amuse::Compile::File; | ||||||
| 2 | |||||||
| 3 | 59 | 59 | 273412 | use strict; | |||
| 59 | 129 | ||||||
| 59 | 2236 | ||||||
| 4 | 59 | 59 | 284 | use warnings; | |||
| 59 | 117 | ||||||
| 59 | 2959 | ||||||
| 5 | 59 | 59 | 366 | use utf8; | |||
| 59 | 212 | ||||||
| 59 | 422 | ||||||
| 6 | |||||||
| 7 | 59 | 59 | 2834 | use constant { DEBUG => $ENV{AMW_DEBUG} }; | |||
| 59 | 148 | ||||||
| 59 | 7754 | ||||||
| 8 | |||||||
| 9 | # core | ||||||
| 10 | # use Data::Dumper; | ||||||
| 11 | 59 | 59 | 1368 | use File::Copy qw/move/; | |||
| 59 | 8139 | ||||||
| 59 | 4189 | ||||||
| 12 | 59 | 59 | 33329 | use Encode qw/decode_utf8/; | |||
| 59 | 1063469 | ||||||
| 59 | 7339 | ||||||
| 13 | |||||||
| 14 | # needed | ||||||
| 15 | 59 | 59 | 30710 | use Template::Tiny; | |||
| 59 | 87261 | ||||||
| 59 | 3949 | ||||||
| 16 | 59 | 59 | 44167 | use Archive::Zip qw( :ERROR_CODES :CONSTANTS ); | |||
| 59 | 3333390 | ||||||
| 59 | 11171 | ||||||
| 17 | 59 | 59 | 50772 | use EBook::EPUB::Lite; | |||
| 59 | 19768147 | ||||||
| 59 | 6482 | ||||||
| 18 | 59 | 59 | 606 | use File::Copy; | |||
| 59 | 158 | ||||||
| 59 | 4960 | ||||||
| 19 | 59 | 59 | 460 | use File::Spec; | |||
| 59 | 133 | ||||||
| 59 | 2773 | ||||||
| 20 | 59 | 59 | 70049 | use IPC::Run qw(run timeout); | |||
| 59 | 2183013 | ||||||
| 59 | 4883 | ||||||
| 21 | 59 | 59 | 665 | use File::Basename (); | |||
| 59 | 1270 | ||||||
| 59 | 1152 | ||||||
| 22 | 59 | 59 | 57286 | use Path::Tiny (); | |||
| 59 | 1027749 | ||||||
| 59 | 2765 | ||||||
| 23 | |||||||
| 24 | # ours | ||||||
| 25 | 59 | 59 | 36440 | use PDF::Imposition; | |||
| 59 | 23565514 | ||||||
| 59 | 3461 | ||||||
| 26 | 59 | 59 | 1773 | use Text::Amuse; | |||
| 59 | 83049 | ||||||
| 59 | 2928 | ||||||
| 27 | 59 | 5812 | use Text::Amuse::Functions qw/muse_fast_scan_header | ||||
| 28 | muse_to_object | ||||||
| 29 | 59 | 59 | 1255 | muse_format_line/; | |||
| 59 | 4212 | ||||||
| 30 | 59 | 59 | 490 | use Text::Amuse::Utils; | |||
| 59 | 161 | ||||||
| 59 | 2417 | ||||||
| 31 | |||||||
| 32 | 59 | 59 | 44303 | use Text::Amuse::Compile::Templates; | |||
| 59 | 209 | ||||||
| 59 | 2827 | ||||||
| 33 | 59 | 59 | 37608 | use Text::Amuse::Compile::TemplateOptions; | |||
| 59 | 380 | ||||||
| 59 | 4095 | ||||||
| 34 | 59 | 59 | 42112 | use Text::Amuse::Compile::MuseHeader; | |||
| 59 | 358 | ||||||
| 59 | 3456 | ||||||
| 35 | 59 | 59 | 41384 | use Text::Amuse::Compile::Indexer; | |||
| 59 | 683 | ||||||
| 59 | 4100 | ||||||
| 36 | 59 | 59 | 654 | use Types::Standard qw/Int Str Bool Object Maybe CodeRef HashRef InstanceOf ArrayRef/; | |||
| 59 | 190 | ||||||
| 59 | 836 | ||||||
| 37 | 59 | 59 | 213537 | use Moo; | |||
| 59 | 152 | ||||||
| 59 | 450 | ||||||
| 38 | |||||||
| 39 | =encoding utf8 | ||||||
| 40 | |||||||
| 41 | =head1 NAME | ||||||
| 42 | |||||||
| 43 | Text::Amuse::Compile::File - Object for file scheduled for compilation | ||||||
| 44 | |||||||
| 45 | =head1 SYNOPSIS | ||||||
| 46 | |||||||
| 47 | Everything here is pretty much private. It's used by | ||||||
| 48 | Text::Amuse::Compile in a forked and chdir'ed environment. | ||||||
| 49 | |||||||
| 50 | =head1 ACCESSORS AND METHODS | ||||||
| 51 | |||||||
| 52 | =head2 new(name => $basename, suffix => $suffix, templates => $templates) | ||||||
| 53 | |||||||
| 54 | Constructor. Accepts the following named parameters: | ||||||
| 55 | |||||||
| 56 | =over 4 | ||||||
| 57 | |||||||
| 58 | =item name | ||||||
| 59 | |||||||
| 60 | =item virtual | ||||||
| 61 | |||||||
| 62 | If it's a virtual file which doesn't exit on the disk (a merged one) | ||||||
| 63 | |||||||
| 64 | =item suffix | ||||||
| 65 | |||||||
| 66 | =item ttdir | ||||||
| 67 | |||||||
| 68 | The directory with the custom templates. | ||||||
| 69 | |||||||
| 70 | =item fileobj | ||||||
| 71 | |||||||
| 72 | An optional L |
||||||
| 73 | |||||||
| 74 | =item standalone | ||||||
| 75 | |||||||
| 76 | When set to true, the tex output will obey bcor and twoside/oneside. | ||||||
| 77 | |||||||
| 78 | =item options | ||||||
| 79 | |||||||
| 80 | An hashref with the options to pass to the templates. | ||||||
| 81 | |||||||
| 82 | =item include_paths | ||||||
| 83 | |||||||
| 84 | Include paths arrayref. | ||||||
| 85 | |||||||
| 86 | =item run_timeout | ||||||
| 87 | |||||||
| 88 | Run timeout for latex/xindy run in seconds, default to 600 (which | ||||||
| 89 | should be plenty). | ||||||
| 90 | |||||||
| 91 | =back | ||||||
| 92 | |||||||
| 93 | =head1 INTERNALS | ||||||
| 94 | |||||||
| 95 | =over 4 | ||||||
| 96 | |||||||
| 97 | =item is_deleted | ||||||
| 98 | |||||||
| 99 | =item status_file | ||||||
| 100 | |||||||
| 101 | =item check_status | ||||||
| 102 | |||||||
| 103 | =item purged_extensions | ||||||
| 104 | |||||||
| 105 | =item muse_file | ||||||
| 106 | |||||||
| 107 | =item document | ||||||
| 108 | |||||||
| 109 | The L |
||||||
| 110 | |||||||
| 111 | =item tt | ||||||
| 112 | |||||||
| 113 | The L |
||||||
| 114 | |||||||
| 115 | =item logger | ||||||
| 116 | |||||||
| 117 | The logger subroutine set in the constructor. | ||||||
| 118 | |||||||
| 119 | =item cleanup | ||||||
| 120 | |||||||
| 121 | Remove auxiliary files (like the complete file and the status file) | ||||||
| 122 | |||||||
| 123 | =item luatex | ||||||
| 124 | |||||||
| 125 | Use luatex instead of xetex | ||||||
| 126 | |||||||
| 127 | =item fonts | ||||||
| 128 | |||||||
| 129 | The L |
||||||
| 130 | |||||||
| 131 | =item epub_embed_fonts | ||||||
| 132 | |||||||
| 133 | Boolean (default to true) which triggers the epub font embedding. | ||||||
| 134 | |||||||
| 135 | =item coverpage_only_if_toc | ||||||
| 136 | |||||||
| 137 | Boolean (default to false). Activates the conditional article output. | ||||||
| 138 | |||||||
| 139 | =item document_indexes | ||||||
| 140 | |||||||
| 141 | The raw, unparsed indexes found in the muse comments | ||||||
| 142 | |||||||
| 143 | =item indexes | ||||||
| 144 | |||||||
| 145 | If present, the parsed indexes are stored here | ||||||
| 146 | |||||||
| 147 | =back | ||||||
| 148 | |||||||
| 149 | =cut | ||||||
| 150 | |||||||
| 151 | has luatex => (is => 'ro', isa => Bool, default => sub { 0 }); | ||||||
| 152 | has name => (is => 'ro', isa => Str, required => 1); | ||||||
| 153 | has suffix => (is => 'ro', isa => Str, required => 1); | ||||||
| 154 | |||||||
| 155 | has ttdir => (is => 'ro', isa => Maybe[Str]); | ||||||
| 156 | has templates => (is => 'lazy', isa => Object); | ||||||
| 157 | |||||||
| 158 | sub _build_templates { | ||||||
| 159 | 291 | 291 | 4430 | my $self = shift; | |||
| 160 | return Text::Amuse::Compile::Templates->new(ttdir => $self->ttdir, | ||||||
| 161 | 291 | 9018 | format_id => $self->options->{format_id}); | ||||
| 162 | } | ||||||
| 163 | |||||||
| 164 | has virtual => (is => 'ro', isa => Bool, default => sub { 0 }); | ||||||
| 165 | has standalone => (is => 'ro', isa => Bool, default => sub { 0 }); | ||||||
| 166 | has tt => (is => 'ro', isa => Object, default => sub { Template::Tiny->new }); | ||||||
| 167 | has logger => (is => 'ro', isa => Maybe[CodeRef]); | ||||||
| 168 | has fileobj => (is => 'ro', isa => Maybe[Object]); | ||||||
| 169 | has document => (is => 'lazy', isa => Object); | ||||||
| 170 | has options => (is => 'ro', isa => HashRef, default => sub { +{} }); | ||||||
| 171 | has full_options => (is => 'lazy', isa => HashRef); | ||||||
| 172 | has tex_options => (is => 'lazy', isa => HashRef); | ||||||
| 173 | has html_options => (is => 'lazy', isa => HashRef); | ||||||
| 174 | has wants_slides => (is => 'lazy', isa => Bool); | ||||||
| 175 | has is_deleted => (is => 'lazy', isa => Bool); | ||||||
| 176 | has file_header => (is => 'lazy', isa => Object); | ||||||
| 177 | has coverpage_only_if_toc => (is => 'ro', isa => Bool, default => sub { 0 }); | ||||||
| 178 | has fonts => (is => 'ro', required => 1, isa => InstanceOf['Text::Amuse::Compile::Fonts::Selected']); | ||||||
| 179 | has epub_embed_fonts => (is => 'ro', isa => Bool, default => sub { 1 }); | ||||||
| 180 | has indexes => (is => 'rwp', isa => Maybe[ArrayRef]); | ||||||
| 181 | has include_paths => (is => 'ro', isa => ArrayRef, default => sub { [] }); | ||||||
| 182 | has volumes => (is => 'lazy', isa => ArrayRef); | ||||||
| 183 | has run_timeout => (is => 'ro', isa => Int, default => sub { 600 }); | ||||||
| 184 | |||||||
| 185 | sub _build_file_header { | ||||||
| 186 | 307 | 307 | 4245 | my $self = shift; | |||
| 187 | 307 | 684 | my $header; | ||||
| 188 | 307 | 100 | 1786 | if ($self->virtual) { | |||
| 189 | 19 | 421 | $header = { $self->document->headers }; | ||||
| 190 | } | ||||||
| 191 | else { | ||||||
| 192 | 288 | 1378 | $header = muse_fast_scan_header($self->muse_file); | ||||
| 193 | 288 | 50 | 33 | 181890 | $self->log_fatal("Not a muse file!") unless $header && %$header; | ||
| 194 | } | ||||||
| 195 | 307 | 14997 | return Text::Amuse::Compile::MuseHeader->new($header); | ||||
| 196 | } | ||||||
| 197 | |||||||
| 198 | sub _build_is_deleted { | ||||||
| 199 | 301 | 301 | 17486 | return shift->file_header->is_deleted; | |||
| 200 | } | ||||||
| 201 | |||||||
| 202 | sub _build_wants_slides { | ||||||
| 203 | 16 | 16 | 439 | return shift->file_header->wants_slides; | |||
| 204 | } | ||||||
| 205 | |||||||
| 206 | sub _build_document { | ||||||
| 207 | 280 | 280 | 4751 | my $self = shift; | |||
| 208 | 280 | 578 | my %args; | ||||
| 209 | 280 | 50 | 1306 | die "virtual files need an already built document" if $self->virtual; | |||
| 210 | 280 | 100 | 3997 | if (my $fileobj = $self->fileobj) { | |||
| 211 | 275 | 1719 | %args = $fileobj->text_amuse_constructor; | ||||
| 212 | } | ||||||
| 213 | else { | ||||||
| 214 | 5 | 33 | %args = (file => $self->muse_file); | ||||
| 215 | } | ||||||
| 216 | 280 | 4537 | return Text::Amuse->new(%args, | ||||
| 217 | include_paths => $self->include_paths, | ||||||
| 218 | ); | ||||||
| 219 | } | ||||||
| 220 | |||||||
| 221 | sub _build_tex_options { | ||||||
| 222 | 250 | 250 | 4804 | my $self = shift; | |||
| 223 | 250 | 5784 | return $self->_escape_options_hashref(ltx => $self->full_options); | ||||
| 224 | } | ||||||
| 225 | |||||||
| 226 | sub _build_html_options { | ||||||
| 227 | 113 | 113 | 3252 | my $self = shift; | |||
| 228 | 113 | 5690 | return $self->_escape_options_hashref(html => $self->full_options); | ||||
| 229 | } | ||||||
| 230 | |||||||
| 231 | sub _build_full_options { | ||||||
| 232 | 292 | 292 | 3621 | my $self = shift; | |||
| 233 | # merge the options with the ones found in the header. | ||||||
| 234 | # print "Building full options\n" if DEBUG; | ||||||
| 235 | 292 | 589 | my %options = %{ $self->options }; | ||||
| 292 | 2120 | ||||||
| 236 | # these values are picked from the file, if not provided by the compiler | ||||||
| 237 | 292 | 1233 | foreach my $override (qw/cover coverwidth nocoverpage notoc | ||||
| 238 | impressum | ||||||
| 239 | continuefootnotes | ||||||
| 240 | centerchapter | ||||||
| 241 | centersection | ||||||
| 242 | nofinalpage/) { | ||||||
| 243 | 2628 | 85337 | $options{$override} = $self->$override; | ||||
| 244 | } | ||||||
| 245 | 292 | 18773 | return \%options; | ||||
| 246 | } | ||||||
| 247 | |||||||
| 248 | sub _build_volumes { | ||||||
| 249 | 242 | 242 | 3742 | my $self = shift; | |||
| 250 | 242 | 596 | my @volumes; | ||||
| 251 | 242 | 100 | 66 | 2417 | if (!$self->virtual and -f $self->muse_file) { | ||
| 252 | 226 | 1033 | my @lines = Path::Tiny::path($self->muse_file)->lines_utf8; | ||||
| 253 | |||||||
| 254 | 226 | 100 | 199403 | if (grep { /^; +;;;#\w+/ } @lines) { | |||
| 18687 | 40092 | ||||||
| 255 | 2 | 10 | my @current; | ||||
| 256 | my @current_meta; | ||||||
| 257 | 2 | 0 | my @original_meta; | ||||
| 258 | |||||||
| 259 | # muse starts with the directives | ||||||
| 260 | 2 | 6 | my $in_meta = 1; | ||||
| 261 | 2 | 5 | my $in_volume_meta = 0; | ||||
| 262 | LINE: | ||||||
| 263 | 2 | 9 | while (@lines) { | ||||
| 264 | 70 | 113 | my $line = shift @lines; | ||||
| 265 | # accumulate in the current pile until there's a blank line | ||||||
| 266 | 70 | 197 | my $blank = $line =~ m/\A\s*\z/; | ||||
| 267 | |||||||
| 268 | 70 | 100 | 207 | if ($line =~ m/\A; +;;;(#[A-Za-z0-9_-]+\w+.*)\z/s) { | |||
| 100 | |||||||
| 269 | 6 | 18 | my $directive = $1; | ||||
| 270 | 6 | 12 | $in_meta = 0; | ||||
| 271 | 6 | 100 | 18 | if (!$in_volume_meta) { | |||
| 272 | # entered a new volume | ||||||
| 273 | 5 | 9 | $in_volume_meta = 1; | ||||
| 274 | 5 | 100 | 16 | if (@current) { | |||
| 275 | 3 | 18 | push @volumes, [ @current_meta, @original_meta, @current ]; | ||||
| 276 | } | ||||||
| 277 | 5 | 16 | @current = @current_meta = (); | ||||
| 278 | } | ||||||
| 279 | 6 | 13 | push @current_meta, $directive; | ||||
| 280 | 6 | 20 | next LINE; | ||||
| 281 | } | ||||||
| 282 | elsif (!$blank) { | ||||||
| 283 | 31 | 67 | $in_volume_meta = 0; | ||||
| 284 | } | ||||||
| 285 | |||||||
| 286 | 64 | 100 | 150 | if ($in_meta) { | |||
| 287 | 12 | 35 | push @original_meta, $line; | ||||
| 288 | } | ||||||
| 289 | else { | ||||||
| 290 | 52 | 142 | push @current, $line; | ||||
| 291 | } | ||||||
| 292 | } | ||||||
| 293 | # end of loop, flush the stack | ||||||
| 294 | 2 | 50 | 10 | if (@current) { | |||
| 295 | 2 | 16 | push @volumes, [ @current_meta, @original_meta, @current ]; | ||||
| 296 | } | ||||||
| 297 | # print Dumper(\@original_meta, \@volumes); | ||||||
| 298 | } | ||||||
| 299 | } | ||||||
| 300 | 242 | 8909 | return \@volumes; | ||||
| 301 | } | ||||||
| 302 | |||||||
| 303 | sub cover { | ||||||
| 304 | 386 | 386 | 1 | 996 | my $self = shift; | ||
| 305 | # options passed take precendence | ||||||
| 306 | 386 | 100 | 2255 | if (exists $self->options->{cover}) { | |||
| 307 | 48 | 100 | 520 | if ($self->_looks_like_a_sane_name($self->options->{cover})) { | |||
| 308 | 26 | 179 | return $self->options->{cover}; | ||||
| 309 | } | ||||||
| 310 | else { | ||||||
| 311 | 22 | 149 | return ''; | ||||
| 312 | } | ||||||
| 313 | } | ||||||
| 314 | 338 | 100 | 8626 | if (my $cover = $self->file_header->cover) { | |||
| 315 | # already validated by the MuseHeader class | ||||||
| 316 | 37 | 1464 | return $cover; | ||||
| 317 | } | ||||||
| 318 | } | ||||||
| 319 | |||||||
| 320 | sub coverwidth { | ||||||
| 321 | 292 | 292 | 1 | 696 | my $self = shift; | ||
| 322 | # print "Building coverwidth\n"; | ||||||
| 323 | # validation here is not crucial, as the TeX routine will pass it | ||||||
| 324 | # through the class. | ||||||
| 325 | 292 | 100 | 1528 | if (exists $self->options->{coverwidth}) { | |||
| 326 | # print "Picking coverwidth from options\n"; | ||||||
| 327 | 8 | 33 | return $self->options->{coverwidth}; | ||||
| 328 | } | ||||||
| 329 | # obey this thing only if the file set the cover | ||||||
| 330 | 284 | 100 | 6671 | if ($self->file_header->cover) { | |||
| 331 | # print "Picking coverwidth from file\n"; | ||||||
| 332 | 43 | 50 | 2552 | return $self->file_header->coverwidth || 1; | |||
| 333 | } | ||||||
| 334 | 241 | 8139 | return 1; | ||||
| 335 | } | ||||||
| 336 | |||||||
| 337 | sub nocoverpage { | ||||||
| 338 | 545 | 545 | 1 | 2868 | shift->_look_at_header('nocoverpage'); | ||
| 339 | } | ||||||
| 340 | |||||||
| 341 | sub notoc { | ||||||
| 342 | 292 | 292 | 1 | 955 | shift->_look_at_header('notoc'); | ||
| 343 | } | ||||||
| 344 | |||||||
| 345 | sub nofinalpage { | ||||||
| 346 | 292 | 292 | 1 | 981 | shift->_look_at_header('nofinalpage'); | ||
| 347 | } | ||||||
| 348 | |||||||
| 349 | sub impressum { | ||||||
| 350 | 292 | 292 | 1 | 1067 | shift->_look_at_header('impressum'); | ||
| 351 | } | ||||||
| 352 | |||||||
| 353 | 292 | 292 | 1 | 1335 | sub continuefootnotes { shift->_look_at_header('continuefootnotes') } | ||
| 354 | 473 | 473 | 1 | 10534 | sub centerchapter { shift->_look_at_header('centerchapter') } | ||
| 355 | 473 | 473 | 1 | 1723 | sub centersection { shift->_look_at_header('centersection') } | ||
| 356 | |||||||
| 357 | sub _look_at_header { | ||||||
| 358 | 2659 | 2659 | 8204 | my ($self, $method) = @_; | |||
| 359 | # these are booleans, so we enforce them | ||||||
| 360 | 2659 | 100 | 100 | 57283 | !!$self->file_header->$method || !!$self->options->{$method} || 0; | ||
| 361 | } | ||||||
| 362 | |||||||
| 363 | =head2 Options which are looked up in the file headers first | ||||||
| 364 | |||||||
| 365 | See L |
||||||
| 366 | |||||||
| 367 | =over 4 | ||||||
| 368 | |||||||
| 369 | =item cover | ||||||
| 370 | |||||||
| 371 | =item coverwidth | ||||||
| 372 | |||||||
| 373 | =item nocoverpage | ||||||
| 374 | |||||||
| 375 | =item notoc | ||||||
| 376 | |||||||
| 377 | =item nofinalpage | ||||||
| 378 | |||||||
| 379 | =item impressum | ||||||
| 380 | |||||||
| 381 | =item continuefootnotes | ||||||
| 382 | |||||||
| 383 | =item centerchapter | ||||||
| 384 | |||||||
| 385 | =item centersection | ||||||
| 386 | |||||||
| 387 | =back | ||||||
| 388 | |||||||
| 389 | =cut | ||||||
| 390 | |||||||
| 391 | sub _escape_options_hashref { | ||||||
| 392 | 869 | 869 | 12135 | my ($self, $format, $ref) = @_; | |||
| 393 | 869 | 50 | 33 | 5378 | die "Wrong usage of internal method" unless $format && $ref; | ||
| 394 | 869 | 1897 | my %out; | ||||
| 395 | 869 | 5941 | foreach my $k (keys %$ref) { | ||||
| 396 | 14514 | 100 | 5588660 | if (defined $ref->{$k}) { | |||
| 397 | 13089 | 100 | 100 | 75101 | if ($k eq 'logo' or $k eq 'cover') { | ||
| 100 | |||||||
| 398 | 878 | 100 | 5395 | if (my $checked = $self->_looks_like_a_sane_name($ref->{$k})) { | |||
| 399 | 116 | 457 | $out{$k} = $checked; | ||||
| 400 | } | ||||||
| 401 | } | ||||||
| 402 | elsif (ref($ref->{$k})) { | ||||||
| 403 | # pass it verbatim | ||||||
| 404 | 287 | 1000 | $out{$k} = $ref->{$k}; | ||||
| 405 | } | ||||||
| 406 | else { | ||||||
| 407 | 11924 | 41780 | $out{$k} = muse_format_line($format, $ref->{$k}); | ||||
| 408 | } | ||||||
| 409 | } | ||||||
| 410 | else { | ||||||
| 411 | 1425 | 4452 | $out{$k} = undef; | ||||
| 412 | } | ||||||
| 413 | } | ||||||
| 414 | 869 | 282516 | return \%out; | ||||
| 415 | } | ||||||
| 416 | |||||||
| 417 | |||||||
| 418 | sub muse_file { | ||||||
| 419 | 745 | 745 | 1 | 1873 | my $self = shift; | ||
| 420 | 745 | 25604 | return $self->name . $self->suffix; | ||||
| 421 | } | ||||||
| 422 | |||||||
| 423 | sub status_file { | ||||||
| 424 | 305 | 305 | 1 | 2961 | return shift->name . '.status'; | ||
| 425 | } | ||||||
| 426 | |||||||
| 427 | =head2 purge_all | ||||||
| 428 | |||||||
| 429 | Remove all the output files related to basename | ||||||
| 430 | |||||||
| 431 | =head2 purge_slides | ||||||
| 432 | |||||||
| 433 | Remove all the files produces by the C |
||||||
| 434 | and file.sl.log and all the leftovers (.sl.toc, .sl.aux, etc.). | ||||||
| 435 | |||||||
| 436 | =head2 purge_latex | ||||||
| 437 | |||||||
| 438 | Remove files left by previous latex compilation, i.e. file.pdf and | ||||||
| 439 | file.log and all the leftovers (toc, aux, etc.). | ||||||
| 440 | |||||||
| 441 | =head2 purge_latex_leftovers | ||||||
| 442 | |||||||
| 443 | Remove the latex leftover files (toc, aux, etc.). | ||||||
| 444 | |||||||
| 445 | =head2 purge_slides_leftovers | ||||||
| 446 | |||||||
| 447 | Remove the latex leftover files (.sl.toc, .sl.aux, etc.). | ||||||
| 448 | |||||||
| 449 | =head2 purge('.epub', ...) | ||||||
| 450 | |||||||
| 451 | Remove the files associated with this file, by extension. | ||||||
| 452 | |||||||
| 453 | =cut | ||||||
| 454 | |||||||
| 455 | sub _compiled_extensions { | ||||||
| 456 | 320 | 320 | 2319 | return qw/.sl.tex .tex .a4.pdf .lt.pdf .ok .html .bare.html .epub .zip/; | |||
| 457 | } | ||||||
| 458 | |||||||
| 459 | sub _latex_extensions { | ||||||
| 460 | 640 | 640 | 2553 | return qw/.pdf .log/; | |||
| 461 | } | ||||||
| 462 | |||||||
| 463 | sub _slides_extensions { | ||||||
| 464 | 320 | 320 | 1149 | my $self = shift; | |||
| 465 | 320 | 5622 | return map { '.sl' . $_ } $self->_latex_extensions; | ||||
| 640 | 4656 | ||||||
| 466 | } | ||||||
| 467 | |||||||
| 468 | sub _latex_leftover_extensions { | ||||||
| 469 | 640 | 640 | 3105 | return qw/.aux .nav .out .snm .toc .tuc .vrb/; | |||
| 470 | } | ||||||
| 471 | |||||||
| 472 | sub _slides_leftover_extensions { | ||||||
| 473 | 320 | 320 | 736 | my $self = shift; | |||
| 474 | 320 | 5572 | return map { '.sl' . $_ } $self->_latex_leftover_extensions; | ||||
| 2240 | 5766 | ||||||
| 475 | } | ||||||
| 476 | |||||||
| 477 | sub purged_extensions { | ||||||
| 478 | 320 | 320 | 1 | 3411 | my $self = shift; | ||
| 479 | 320 | 1346 | my @exts = ( | ||||
| 480 | $self->_compiled_extensions, | ||||||
| 481 | $self->_latex_extensions, | ||||||
| 482 | $self->_latex_leftover_extensions, | ||||||
| 483 | $self->_slides_extensions, | ||||||
| 484 | $self->_slides_leftover_extensions, | ||||||
| 485 | ); | ||||||
| 486 | 320 | 4251 | return @exts; | ||||
| 487 | } | ||||||
| 488 | |||||||
| 489 | sub purge { | ||||||
| 490 | 769 | 769 | 1 | 4237 | my ($self, @exts) = @_; | ||
| 491 | 769 | 1451 | $self->log_info("Started purging\n") if DEBUG; | ||||
| 492 | 769 | 2992 | my $basename = $self->name; | ||||
| 493 | 769 | 3121 | foreach my $ext (@exts) { | ||||
| 494 | 8621 | 50 | 23227 | $self->log_fatal("wtf? Refusing to purge " . $basename . $ext) | |||
| 495 | if ($ext eq '.muse'); | ||||||
| 496 | 8621 | 14192 | my $target = $basename . $ext; | ||||
| 497 | 8621 | 100 | 230703 | if (-f $target) { | |||
| 498 | 139 | 301 | $self->log_info("Removing target $target\n") if DEBUG; | ||||
| 499 | 139 | 50 | 20493 | unlink $target or $self->log_fatal("Couldn't unlink $target $!"); | |||
| 500 | } | ||||||
| 501 | } | ||||||
| 502 | 769 | 5378 | $self->log_info("Ended purging\n") if DEBUG; | ||||
| 503 | } | ||||||
| 504 | |||||||
| 505 | sub purge_all { | ||||||
| 506 | 302 | 302 | 1 | 18143 | my $self = shift; | ||
| 507 | 302 | 1833 | $self->purge($self->purged_extensions); | ||||
| 508 | } | ||||||
| 509 | |||||||
| 510 | sub purge_latex { | ||||||
| 511 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 512 | 0 | 0 | $self->purge($self->_latex_extensions, $self->_latex_leftover_extensions); | ||||
| 513 | } | ||||||
| 514 | |||||||
| 515 | sub purge_slides { | ||||||
| 516 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 517 | 0 | 0 | $self->purge($self->_slides_extensions, $self->_slides_leftover_extensions); | ||||
| 518 | } | ||||||
| 519 | |||||||
| 520 | sub purge_latex_leftovers { | ||||||
| 521 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 522 | 0 | 0 | $self->purge($self->_latex_leftover_extensions); | ||||
| 523 | } | ||||||
| 524 | |||||||
| 525 | sub purge_slides_leftovers { | ||||||
| 526 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 527 | 0 | 0 | $self->purge($self->_slides_leftover_extensions); | ||||
| 528 | } | ||||||
| 529 | |||||||
| 530 | sub _write_file { | ||||||
| 531 | 0 | 0 | 0 | my ($self, $target, @strings) = @_; | |||
| 532 | 0 | 0 | 0 | open (my $fh, ">:encoding(UTF-8)", $target) | |||
| 533 | or $self->log_fatal("Couldn't open $target $!"); | ||||||
| 534 | |||||||
| 535 | 0 | 0 | print $fh @strings; | ||||
| 536 | |||||||
| 537 | 0 | 0 | 0 | close $fh or $self->log_fatal("Couldn't close $target"); | |||
| 538 | 0 | 0 | return; | ||||
| 539 | } | ||||||
| 540 | |||||||
| 541 | |||||||
| 542 | =head1 METHODS | ||||||
| 543 | |||||||
| 544 | =head2 Formats | ||||||
| 545 | |||||||
| 546 | Emit the respective format, saving it in a file. Return value is | ||||||
| 547 | meaningless, but exceptions could be raised. | ||||||
| 548 | |||||||
| 549 | =over 4 | ||||||
| 550 | |||||||
| 551 | =item html | ||||||
| 552 | |||||||
| 553 | =item bare_html | ||||||
| 554 | |||||||
| 555 | =item pdf | ||||||
| 556 | |||||||
| 557 | =item epub | ||||||
| 558 | |||||||
| 559 | =item lt_pdf | ||||||
| 560 | |||||||
| 561 | =item a4_pdf | ||||||
| 562 | |||||||
| 563 | =item zip | ||||||
| 564 | |||||||
| 565 | The zipped sources. Beware that if you don't call html or tex before | ||||||
| 566 | this, the attachments (if any) are ignored if both html and tex files | ||||||
| 567 | exist. Hence, the muse-compile.pl scripts forces the --tex and --html | ||||||
| 568 | switches. | ||||||
| 569 | |||||||
| 570 | =cut | ||||||
| 571 | |||||||
| 572 | sub _render_css { | ||||||
| 573 | 181 | 181 | 1013 | my ($self, %tokens) = @_; | |||
| 574 | 181 | 636 | my $out = ''; | ||||
| 575 | 181 | 5400 | $self->tt->process($self->templates->css, { | ||||
| 576 | fonts => $self->fonts, | ||||||
| 577 | centersection => $self->centersection, | ||||||
| 578 | centerchapter => $self->centerchapter, | ||||||
| 579 | %tokens | ||||||
| 580 | }, \$out); | ||||||
| 581 | 181 | 4198996 | return $out; | ||||
| 582 | } | ||||||
| 583 | |||||||
| 584 | |||||||
| 585 | sub html { | ||||||
| 586 | 112 | 112 | 1 | 25344 | my $self = shift; | ||
| 587 | 112 | 466 | $self->purge('.html'); | ||||
| 588 | 112 | 433 | my $outfile = $self->name . '.html'; | ||||
| 589 | 112 | 4041 | my $doc = $self->document; | ||||
| 590 | 112 | 50 | 13015 | my $title = $doc->header_as_html->{title} || 'Untitled'; | |||
| 591 | $self->_process_template($self->templates->html, | ||||||
| 592 | { | ||||||
| 593 | doc => $doc, | ||||||
| 594 | title => $self->_remove_tags($title), | ||||||
| 595 | css => $self->_render_css(html => 1), | ||||||
| 596 | 112 | 1309037 | options => { %{$self->html_options} }, | ||||
| 112 | 6129 | ||||||
| 597 | }, | ||||||
| 598 | $outfile); | ||||||
| 599 | } | ||||||
| 600 | |||||||
| 601 | sub bare_html { | ||||||
| 602 | 8 | 8 | 1 | 2688 | my $self = shift; | ||
| 603 | 8 | 57 | $self->purge('.bare.html'); | ||||
| 604 | 8 | 36 | my $outfile = $self->name . '.bare.html'; | ||||
| 605 | $self->_process_template($self->templates->bare_html, | ||||||
| 606 | { | ||||||
| 607 | doc => $self->document, | ||||||
| 608 | 8 | 312 | options => { %{$self->html_options} }, | ||||
| 8 | 1416 | ||||||
| 609 | }, | ||||||
| 610 | $outfile); | ||||||
| 611 | } | ||||||
| 612 | |||||||
| 613 | sub a4_pdf { | ||||||
| 614 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 615 | 0 | 0 | $self->_compile_imposed('a4'); | ||||
| 616 | } | ||||||
| 617 | |||||||
| 618 | sub lt_pdf { | ||||||
| 619 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 620 | 0 | 0 | $self->_compile_imposed('lt'); | ||||
| 621 | } | ||||||
| 622 | |||||||
| 623 | sub _compile_imposed { | ||||||
| 624 | 0 | 0 | 0 | my ($self, $size) = @_; | |||
| 625 | 0 | 0 | 0 | $self->log_fatal("Missing size") unless $size; | |||
| 626 | # the trick: first call tex with an argument, then pdf, then | ||||||
| 627 | # impose, then rename. | ||||||
| 628 | 0 | 0 | $self->tex(papersize => "half-$size"); | ||||
| 629 | 0 | 0 | my $pdf = $self->pdf; | ||||
| 630 | 0 | 0 | my $outfile = $self->name . ".$size.pdf"; | ||||
| 631 | 0 | 0 | 0 | if ($pdf) { | |||
| 632 | 0 | 0 | my $imposer = PDF::Imposition->new( | ||||
| 633 | file => $pdf, | ||||||
| 634 | schema => '2up', | ||||||
| 635 | signature => '40-80', | ||||||
| 636 | cover => 1, | ||||||
| 637 | outfile => $outfile | ||||||
| 638 | ); | ||||||
| 639 | 0 | 0 | $imposer->impose; | ||||
| 640 | } | ||||||
| 641 | else { | ||||||
| 642 | 0 | 0 | $self->log_fatal("PDF was not produced!"); | ||||
| 643 | } | ||||||
| 644 | 0 | 0 | return $outfile; | ||||
| 645 | } | ||||||
| 646 | |||||||
| 647 | |||||||
| 648 | =item tex | ||||||
| 649 | |||||||
| 650 | This method is a bit tricky, because it's called with arguments | ||||||
| 651 | internally by C |
||||||
| 652 | C |
||||||
| 653 | |||||||
| 654 | With no arguments, this method enforces the options C |
||||||
| 655 | and C |
||||||
| 656 | the imposed output, unless C |
||||||
| 657 | |||||||
| 658 | This means that the twoside and binding correction options follow this | ||||||
| 659 | logic: if you have some imposed format, they are ignored for the | ||||||
| 660 | standalone PDF but applied for the imposed ones. If you have only | ||||||
| 661 | the standalone PDF, they are applied to it. | ||||||
| 662 | |||||||
| 663 | =cut | ||||||
| 664 | |||||||
| 665 | sub tex { | ||||||
| 666 | 244 | 244 | 1 | 37520 | my ($self, @args) = @_; | ||
| 667 | 244 | 1311 | my $texfile = $self->name . '.tex'; | ||||
| 668 | 244 | 50 | 1116 | $self->log_fatal("Wrong usage") if @args % 2; | |||
| 669 | 244 | 760 | my %arguments = @args; | ||||
| 670 | 244 | 100 | 100 | 2484 | unless (@args || $self->standalone) { | ||
| 671 | 7 | 45 | %arguments = ( | ||||
| 672 | twoside => 0, | ||||||
| 673 | oneside => 1, | ||||||
| 674 | bcor => '0mm', | ||||||
| 675 | ); | ||||||
| 676 | } | ||||||
| 677 | 244 | 1181 | $self->purge('.tex'); | ||||
| 678 | 244 | 8492 | my $template_body = $self->templates->latex; | ||||
| 679 | 244 | 1992 | my $tokens = $self->_prepare_tex_tokens(%arguments, template_body => $template_body); | ||||
| 680 | |||||||
| 681 | # - if there's the ; ;;;#title magic cookie, split the volume. X | ||||||
| 682 | # - process the template normally. X | ||||||
| 683 | # - split the body between PREAMBLE \begin{document} BODY \end{document} END X | ||||||
| 684 | # - determine if the indexes and the toc go at the end or at the beginning, looking at the template X | ||||||
| 685 | # - split the muse body and create temporary files, adding the headers in the magic comments. X | ||||||
| 686 | # - process them, but override the wants_toc / wants_indexes depending on previous steps X | ||||||
| 687 | # - discard everything outside the \begin{document} and \end{document} X | ||||||
| 688 | # - concatenate the initial preamble, these bodies, and the end, and return it. X | ||||||
| 689 | |||||||
| 690 | 244 | 21223 | my $volumes = $self->volumes; | ||||
| 691 | 244 | 100 | 66 | 10479 | if ($volumes and @$volumes > 1) { | ||
| 692 | 2 | 14 | my $tex_parse = qr{\A(.*\\begin\{document\})(.*)(\\end\{document\}.*)}s; | ||||
| 693 | 2 | 5 | my $full; | ||||
| 694 | 2 | 23 | $self->tt->process($template_body, $tokens, \$full); | ||||
| 695 | # print $full; | ||||||
| 696 | 2 | 50 | 1245900 | if ($full =~ m/$tex_parse/s) { | |||
| 697 | 2 | 19 | my ($preamble, $body, $end) = ($1, $2, $3); | ||||
| 698 | # print Dumper([$preamble, $body, $end ]); | ||||||
| 699 | 2 | 9 | my @pieces = ($preamble); | ||||
| 700 | 2 | 7 | my $last = scalar $#$volumes; | ||||
| 701 | |||||||
| 702 | # check if the template is custom | ||||||
| 703 | 2 | 50 | 48 | my $toc_i = $$template_body =~ m/latex_body.*tableofcontents/s ? $last : 0; | |||
| 704 | 2 | 50 | 26 | my $idx_i = $$template_body =~ m/printindex.*latex_body/s ? 0 : $last; | |||
| 705 | |||||||
| 706 | 2 | 10 | for (my $i = 0; $i <= $last; $i++) { | ||||
| 707 | 5 | 1395 | my $vol = $volumes->[$i]; | ||||
| 708 | 5 | 5899 | my $doc = muse_to_object(join('', @$vol)); | ||||
| 709 | 5 | 5843 | my $latex = $self->_interpolate_magic_comments($tokens->{format_id}, $doc); | ||||
| 710 | |||||||
| 711 | 5 | 100 | 31 | if (my @raw_indexes = $self->document_indexes) { | |||
| 712 | my $indexer = Text::Amuse::Compile::Indexer->new(latex_body => $latex, | ||||||
| 713 | language_code => $doc->language_code, | ||||||
| 714 | 2 | logger => sub {}, # silence here | |||||
| 715 | 2 | 14 | index_specs => \@raw_indexes); | ||||
| 716 | 2 | 400 | $latex = $indexer->indexed_tex_body; | ||||
| 717 | } | ||||||
| 718 | |||||||
| 719 | my %partial_tokens = ( | ||||||
| 720 | 5 | 76 | options => { %{ $tokens->{options} } }, | ||||
| 721 | 5 | 272 | safe_options => { %{ $tokens->{safe_options} } }, | ||||
| 722 | tex_setup_langs => 'DUMMY', # irrelevant | ||||||
| 723 | doc => $doc, | ||||||
| 724 | latex_body => $latex, | ||||||
| 725 | 5 | 14 | tex_indexes => [ @{ $tokens->{tex_indexes} } ], | ||||
| 5 | 44 | ||||||
| 726 | ); | ||||||
| 727 | 5 | 100 | 58 | if ($i != $toc_i) { | |||
| 728 | 3 | 11 | $partial_tokens{safe_options}{wants_toc} = 0; | ||||
| 729 | } | ||||||
| 730 | 5 | 100 | 21 | if ($i != $idx_i) { | |||
| 731 | 3 | 9 | $partial_tokens{tex_indexes} = []; | ||||
| 732 | } | ||||||
| 733 | |||||||
| 734 | # print Dumper(\%partial_tokens); | ||||||
| 735 | |||||||
| 736 | |||||||
| 737 | # here clear wants_toc / indexes | ||||||
| 738 | |||||||
| 739 | 5 | 15 | my $out; | ||||
| 740 | 5 | 53 | $self->tt->process($template_body, \%partial_tokens, \$out); | ||||
| 741 | 5 | 50 | 3070078 | if ($out =~ m/$tex_parse/s) { | |||
| 742 | 5 | 224 | push @pieces, $2; | ||||
| 743 | } | ||||||
| 744 | } | ||||||
| 745 | 2 | 904 | push @pieces, $end; | ||||
| 746 | 2 | 17 | Path::Tiny::path($texfile)->spew_utf8(@pieces); | ||||
| 747 | 2 | 3753 | return $texfile; | ||||
| 748 | } | ||||||
| 749 | } | ||||||
| 750 | 242 | 1583 | $self->_process_template($template_body, $tokens, $texfile); | ||||
| 751 | } | ||||||
| 752 | |||||||
| 753 | =item sl_tex | ||||||
| 754 | |||||||
| 755 | Produce a file with extension C<.sl.tex>, a LaTeX Beamer source file. | ||||||
| 756 | If the source muse file doesn't require slides, do nothing. | ||||||
| 757 | |||||||
| 758 | =item sl_pdf | ||||||
| 759 | |||||||
| 760 | Compiles the file produced by C |
||||||
| 761 | slides with extension C<.sl.pdf> | ||||||
| 762 | |||||||
| 763 | =back | ||||||
| 764 | |||||||
| 765 | =cut | ||||||
| 766 | |||||||
| 767 | sub sl_tex { | ||||||
| 768 | 9 | 9 | 1 | 30 | my ($self) = @_; | ||
| 769 | # no slides for virtual files | ||||||
| 770 | 9 | 50 | 48 | return if $self->virtual; | |||
| 771 | 9 | 46 | $self->purge('.sl.tex'); | ||||
| 772 | 9 | 34 | my $texfile = $self->name . '.sl.tex'; | ||||
| 773 | 9 | 50 | 216 | return unless $self->wants_slides; | |||
| 774 | 9 | 284 | my $template_body = $self->templates->slides; | ||||
| 775 | 9 | 52 | return $self->_process_template($template_body, | ||||
| 776 | $self->_prepare_tex_tokens(is_slide => 1, | ||||||
| 777 | template_body => $template_body, | ||||||
| 778 | ), | ||||||
| 779 | $texfile); | ||||||
| 780 | } | ||||||
| 781 | |||||||
| 782 | sub sl_pdf { | ||||||
| 783 | 0 | 0 | 1 | 0 | my $self = shift; | ||
| 784 | 0 | 0 | $self->purge_slides; # remove .sl.pdf and .sl.log | ||||
| 785 | 0 | 0 | my $source = $self->name . '.sl.tex'; | ||||
| 786 | 0 | 0 | 0 | unless (-f $source) { | |||
| 787 | 0 | 0 | $source = $self->sl_tex; | ||||
| 788 | } | ||||||
| 789 | 0 | 0 | 0 | if ($source) { | |||
| 790 | 0 | 0 | 0 | $self->log_fatal("Missing source file $source!") unless -f $source; | |||
| 791 | 0 | 0 | 0 | if (my $out = $self->_compile_pdf($source)) { | |||
| 792 | 0 | 0 | $self->purge_slides_leftovers; | ||||
| 793 | 0 | 0 | return $out; | ||||
| 794 | } | ||||||
| 795 | } | ||||||
| 796 | 0 | 0 | return; | ||||
| 797 | } | ||||||
| 798 | |||||||
| 799 | sub pdf { | ||||||
| 800 | 0 | 0 | 1 | 0 | my ($self, %opts) = @_; | ||
| 801 | 0 | 0 | my $source = $self->name . '.tex'; | ||||
| 802 | 0 | 0 | 0 | unless (-f $source) { | |||
| 803 | 0 | 0 | $self->tex; | ||||
| 804 | } | ||||||
| 805 | 0 | 0 | 0 | $self->log_fatal("Missing source file $source!") unless -f $source; | |||
| 806 | 0 | 0 | $self->purge_latex; | ||||
| 807 | 0 | 0 | 0 | if (my $out = $self->_compile_pdf($source)) { | |||
| 808 | 0 | 0 | $self->purge_latex_leftovers; | ||||
| 809 | 0 | 0 | return $out; | ||||
| 810 | } | ||||||
| 811 | 0 | 0 | return; | ||||
| 812 | } | ||||||
| 813 | |||||||
| 814 | sub _compile_pdf { | ||||||
| 815 | 0 | 0 | 0 | my ($self, $source) = @_; | |||
| 816 | 0 | 0 | my ($output, $logfile); | ||||
| 817 | 0 | 0 | 0 | die "Missing $source!" unless $source; | |||
| 818 | 0 | 0 | 0 | if ($source =~ m/(.+)\.tex$/) { | |||
| 819 | 0 | 0 | my $name = $1; | ||||
| 820 | 0 | 0 | $output = $name . '.pdf'; | ||||
| 821 | 0 | 0 | $logfile = $name . '.log'; | ||||
| 822 | } | ||||||
| 823 | else { | ||||||
| 824 | 0 | 0 | die "Source must be a tex source file\n"; | ||||
| 825 | } | ||||||
| 826 | 0 | 0 | $self->log_info("Compiling $source to $output\n") if DEBUG; | ||||
| 827 | 0 | 0 | my $max = 3; | ||||
| 828 | 0 | 0 | my @run_xindy; | ||||
| 829 | # maybe a check on the toc if more runs are needed? | ||||||
| 830 | # 1. create the toc | ||||||
| 831 | # 2. insert the toc | ||||||
| 832 | # 3. adjust the toc. Should be ok, right? | ||||||
| 833 | 0 | 0 | 0 | foreach my $idx (@{ $self->indexes || [] }) { | |||
| 0 | 0 | ||||||
| 834 | push @run_xindy, [ | ||||||
| 835 | texindy => '--quiet', | ||||||
| 836 | -L => $idx->{language}, | ||||||
| 837 | -I => 'xelatex', | ||||||
| 838 | -C => 'utf8', | ||||||
| 839 | 0 | 0 | $idx->{name} . '.idx', | ||||
| 840 | ]; | ||||||
| 841 | } | ||||||
| 842 | 0 | 0 | 0 | if (@run_xindy) { | |||
| 843 | 0 | 0 | $max++; | ||||
| 844 | } | ||||||
| 845 | 0 | 0 | foreach my $i (1..$max) { | ||||
| 846 | 0 | 0 | 0 | 0 | if ($i > 2 and @run_xindy) { | ||
| 847 | 0 | 0 | foreach my $exec (@run_xindy) { | ||||
| 848 | 0 | 0 | $self->log_info("Executing " . join(" ", @$exec) . "\n"); | ||||
| 849 | 0 | 0 | my ($xin, $xout, $xerr); | ||||
| 850 | 0 | 0 | my $xindy_ok = run $exec, \$xin, \$xout, \$xerr, timeout($self->run_timeout); | ||||
| 851 | 0 | 0 | 0 | unless ($xindy_ok) { | |||
| 852 | 0 | 0 | $self->log_fatal("Errors running " . join(" ", @$exec) ."\n"); | ||||
| 853 | } | ||||||
| 854 | } | ||||||
| 855 | } | ||||||
| 856 | 0 | 0 | 0 | my $latexname = $self->luatex ? 'LuaLaTeX' : 'XeLaTeX'; | |||
| 857 | 0 | 0 | 0 | my $latex = $self->luatex ? 'lualatex' : 'xelatex'; | |||
| 858 | 0 | 0 | my @run = ($latex, '-interaction=nonstopmode', $source); | ||||
| 859 | 0 | 0 | my ($in, $out, $err); | ||||
| 860 | 0 | 0 | my $ok = run \@run, \$in, \$out, \$err, timeout($self->run_timeout); | ||||
| 861 | 0 | 0 | my $shitout; | ||||
| 862 | 0 | 0 | foreach my $line (split(/\n/, $out)) { | ||||
| 863 | 0 | 0 | 0 | if ($line =~ m/^[!#]/) { | |||
| 864 | 0 | 0 | 0 | if ($line =~ m/^! Paragraph ended before/) { | |||
| 865 | 0 | 0 | $self->log_info("***** WARNING *****\n" | ||||
| 866 | . "It is possible that you have a multiparagraph footnote\n" | ||||||
| 867 | . "inside an header or inside a em or strong tag.\n" | ||||||
| 868 | . "Unfortunately this is not supported in the PDF output.\n" | ||||||
| 869 | . "Please correct it.\n"); | ||||||
| 870 | } | ||||||
| 871 | 0 | 0 | 0 | if ($line =~ m/^! LaTeX Error: Unknown option.*fragile.*for package.*bigfoot/) { | |||
| 872 | 0 | 0 | my $help =< | ||||
| 873 | It appears that your TeX installation has an obsolete version of the | ||||||
| 874 | bigfoot package. You can upgrade this package following this | ||||||
| 875 | procedure (per user, not global). | ||||||
| 876 | |||||||
| 877 | cd /tmp/ | ||||||
| 878 | mkdir -p `kpsewhich -var-value TEXMFHOME`/tex/latex/bigfoot | ||||||
| 879 | wget http://mirrors.ctan.org/macros/latex/contrib/bigfoot.zip | ||||||
| 880 | unzip bigfoot.zip | ||||||
| 881 | cd bigfoot | ||||||
| 882 | make | ||||||
| 883 | mv *.sty `kpsewhich -var-value TEXMFHOME`/tex/latex/bigfoot | ||||||
| 884 | texhash `kpsewhich -var-value TEXMFHOME` | ||||||
| 885 | |||||||
| 886 | Please contact the sys-admin if the commands above mean nothing to you. | ||||||
| 887 | HELP | ||||||
| 888 | 0 | 0 | $self->log_info("***** WARNING *****\n" . $help); | ||||
| 889 | } | ||||||
| 890 | 0 | 0 | $shitout++; | ||||
| 891 | } | ||||||
| 892 | 0 | 0 | 0 | if ($shitout) { | |||
| 893 | # List of CHECK values | ||||||
| 894 | # FB_DEFAULT | ||||||
| 895 | # I |
||||||
| 896 | # If CHECK is 0, encoding and decoding replace any | ||||||
| 897 | # malformed character with a substitution character. | ||||||
| 898 | # When you encode, SUBCHAR is used. When you decode, | ||||||
| 899 | # the Unicode REPLACEMENT CHARACTER, code point | ||||||
| 900 | # U+FFFD, is used. If the data is supposed to be | ||||||
| 901 | # UTF-8, an optional lexical warning of warning | ||||||
| 902 | # category "utf8" is given. | ||||||
| 903 | 0 | 0 | $self->log_info(decode_utf8($line)); | ||||
| 904 | } | ||||||
| 905 | } | ||||||
| 906 | 0 | 0 | 0 | unless ($ok) { | |||
| 907 | 0 | 0 | $self->log_info("$latexname compilation failed\n"); | ||||
| 908 | 0 | 0 | 0 | if (-f $logfile) { | |||
| 909 | # if we have a .pdf file, this means something was | ||||||
| 910 | # produced. Hence, remove the .pdf | ||||||
| 911 | 0 | 0 | unlink $output; | ||||
| 912 | 0 | 0 | $self->log_fatal("Bailing out\n"); | ||||
| 913 | } | ||||||
| 914 | else { | ||||||
| 915 | 0 | 0 | $self->log_info("Skipping PDF generation\n"); | ||||
| 916 | 0 | 0 | return; | ||||
| 917 | } | ||||||
| 918 | } | ||||||
| 919 | } | ||||||
| 920 | 0 | 0 | $self->parse_tex_log_file($logfile); | ||||
| 921 | 0 | 0 | $self->log_info("Compilation over\n") if DEBUG; | ||||
| 922 | 0 | 0 | return $output; | ||||
| 923 | } | ||||||
| 924 | |||||||
| 925 | |||||||
| 926 | |||||||
| 927 | sub zip { | ||||||
| 928 | 25 | 25 | 1 | 3688 | my $self = shift; | ||
| 929 | 25 | 152 | $self->purge('.zip'); | ||||
| 930 | 25 | 119 | my $zipname = $self->name . '.zip'; | ||||
| 931 | 25 | 399 | my $tempdir = File::Temp->newdir; | ||||
| 932 | 25 | 19995 | my $tempdirname = $tempdir->dirname; | ||||
| 933 | 25 | 421 | foreach my $todo (qw/tex html/) { | ||||
| 934 | 50 | 17361 | my $target = $self->name . '.' . $todo; | ||||
| 935 | 50 | 100 | 830 | unless (-f $target) { | |||
| 936 | 24 | 174 | $self->$todo; | ||||
| 937 | } | ||||||
| 938 | 50 | 50 | 1053 | $self->log_fatal("Couldn't produce $target") unless -f $target; | |||
| 939 | 50 | 50 | 493 | copy($target, $tempdirname) | |||
| 940 | or $self->log_fatal("Couldn't copy $target in $tempdirname $!"); | ||||||
| 941 | } | ||||||
| 942 | 25 | 14981 | copy ($self->name . '.muse', $tempdirname); | ||||
| 943 | |||||||
| 944 | 25 | 13715 | my $text = $self->document; | ||||
| 945 | 25 | 437 | foreach my $attach ($text->attachments) { | ||||
| 946 | 0 | 0 | 0 | copy($attach, $tempdirname) | |||
| 947 | or $self->log_fatal("Couldn't copy $attach to $tempdirname $!"); | ||||||
| 948 | } | ||||||
| 949 | 25 | 100 | 1034 | if (my $cover = $self->cover) { | |||
| 950 | 8 | 100 | 152 | if (-f $cover) { | |||
| 951 | 6 | 50 | 32 | copy($cover, $tempdirname) | |||
| 952 | or $self->log_info("Cannot find the cover to attach"); | ||||||
| 953 | } | ||||||
| 954 | } | ||||||
| 955 | 25 | 4281 | my $zip = Archive::Zip->new; | ||||
| 956 | 25 | 50 | 1394 | $zip->addTree($tempdirname, $self->name) == AZ_OK | |||
| 957 | or $self->log_fatal("Failure zipping $tempdirname"); | ||||||
| 958 | 25 | 50 | 549272 | $zip->writeToFileNamed($zipname) == AZ_OK | |||
| 959 | or $self->log_fatal("Failure writing $zipname"); | ||||||
| 960 | 25 | 283806 | return $zipname; | ||||
| 961 | } | ||||||
| 962 | |||||||
| 963 | |||||||
| 964 | sub epub { | ||||||
| 965 | 69 | 69 | 1 | 1797 | my $self = shift; | ||
| 966 | 69 | 426 | $self->purge('.epub'); | ||||
| 967 | 69 | 293 | my $epubname = $self->name . '.epub'; | ||||
| 968 | |||||||
| 969 | 69 | 2650 | my $text = $self->document; | ||||
| 970 | |||||||
| 971 | 69 | 9166 | my @pieces; | ||||
| 972 | 69 | 100 | 755 | if ($text->can('as_splat_html_with_attrs')) { | |||
| 973 | 10 | 50 | @pieces = $text->as_splat_html_with_attrs; | ||||
| 974 | } | ||||||
| 975 | else { | ||||||
| 976 | @pieces = map { | ||||||
| 977 | 59 | 663 | +{ | ||||
| 978 | 176 | 635064 | text => $_, | ||||
| 979 | language_code => $text->language_code, | ||||||
| 980 | html_direction => $text->html_direction, | ||||||
| 981 | } | ||||||
| 982 | } $text->as_splat_html; | ||||||
| 983 | } | ||||||
| 984 | 69 | 3237 | my @toc = $text->raw_html_toc; | ||||
| 985 | # fixed in 0.51 | ||||||
| 986 | 69 | 50 | 533438 | if (my $missing = scalar(@pieces) - scalar(@toc)) { | |||
| 987 | 0 | 0 | $self->log_fatal("This shouldn't happen: missing pieces: $missing"); | ||||
| 988 | } | ||||||
| 989 | 69 | 3295 | my $epub = EBook::EPUB::Lite->new; | ||||
| 990 | |||||||
| 991 | # embedded CSS | ||||||
| 992 | 69 | 100 | 677042 | if ($self->epub_embed_fonts) { | |||
| 993 | # pass all | ||||||
| 994 | 67 | 50 | 530 | if (my $fonts = $self->fonts) { | |||
| 995 | 67 | 254 | my %done; | ||||
| 996 | 67 | 198 | foreach my $family (@{ $fonts->families }) { | ||||
| 67 | 529 | ||||||
| 997 | 201 | 100 | 8822 | if ($family->has_files) { | |||
| 998 | 12 | 278 | foreach my $ff (@{ $family->font_files }) { | ||||
| 12 | 71 | ||||||
| 999 | # do not produce duplicate entries when using | ||||||
| 1000 | # the same file | ||||||
| 1001 | 48 | 50 | 565 | unless ($done{$ff->basename}) { | |||
| 1002 | 48 | 1554 | $epub->copy_file($ff->file, | ||||
| 1003 | $ff->basename, | ||||||
| 1004 | $ff->mimetype); | ||||||
| 1005 | 48 | 47108 | $done{$ff->basename}++; | ||||
| 1006 | } | ||||||
| 1007 | } | ||||||
| 1008 | } | ||||||
| 1009 | } | ||||||
| 1010 | } | ||||||
| 1011 | } | ||||||
| 1012 | 69 | 1825 | my $css = $self->_render_css( | ||||
| 1013 | epub => 1, | ||||||
| 1014 | epub_embed_fonts => $self->epub_embed_fonts, | ||||||
| 1015 | ); | ||||||
| 1016 | 69 | 955 | $epub->add_stylesheet("stylesheet.css" => $css); | ||||
| 1017 | |||||||
| 1018 | # build the title page and some metadata | ||||||
| 1019 | 69 | 63252 | my $header = $text->header_as_html; | ||||
| 1020 | |||||||
| 1021 | 69 | 117574 | my @navpoints; | ||||
| 1022 | 69 | 223 | my $order = 0; | ||||
| 1023 | |||||||
| 1024 | 69 | 100 | 538 | if (my $cover = $self->cover) { | |||
| 1025 | 9 | 100 | 331 | if (-f $cover) { | |||
| 1026 | 7 | 50 | 566 | if (my $basename = File::Basename::basename($cover)) { | |||
| 1027 | 7 | 37 | my $coverpage = <<'HTML'; | ||||
| 1028 | |||||||
| 1029 | |||||||
| 1030 | |||||||
| 1031 | |||||||
| 1032 | |
||||||
| 1033 | |||||||
| 1037 | |||||||
| 1038 | |||||||
| 1039 | |||||||
| 1040 | width="100%" height="100%" viewBox="0 0 573 800" preserveAspectRatio="xMidYMid meet"> | ||||||
| 1041 | |
||||||
| 1042 | |||||||
| 1043 | |||||||
| 1044 | |||||||
| 1045 | HTML | ||||||
| 1046 | 7 | 95 | $coverpage =~ s/__IMAGE__/$basename/; | ||||
| 1047 | 7 | 56 | my $cover_id = $epub->copy_file($cover, $basename, | ||||
| 1048 | $self->_mime_for_attachment($basename)); | ||||||
| 1049 | 7 | 6874 | $epub->add_meta_item(cover => $cover_id); | ||||
| 1050 | 7 | 7187 | my $cpid = $epub->add_xhtml("coverpage.xhtml", $coverpage); | ||||
| 1051 | 7 | 10814 | $epub->guide->add_reference(type => 'cover', href => "coverpage.xhtml"); | ||||
| 1052 | 7 | 7241 | push @navpoints, { | ||||
| 1053 | label => 'Cover', | ||||||
| 1054 | id => $cpid, | ||||||
| 1055 | content => "coverpage.xhtml", | ||||||
| 1056 | play_order => ++$order, | ||||||
| 1057 | level => 1, | ||||||
| 1058 | }; | ||||||
| 1059 | } | ||||||
| 1060 | } | ||||||
| 1061 | } | ||||||
| 1062 | |||||||
| 1063 | 69 | 2766 | my $titlepage = qq{ \n}; |
||||
| 1064 | |||||||
| 1065 | 69 | 100 | 543 | if ($text->header_defined->{author}) { | |||
| 1066 | 16 | 490 | my $author = $header->{author}; | ||||
| 1067 | 16 | 90 | $epub->add_author($self->_clean_html($author)); | ||||
| 1068 | 16 | 100 | 3768 | $titlepage .= "$author\n" if $text->wants_preamble; |
|||
| 1069 | } | ||||||
| 1070 | 69 | 4742 | my $muse_header = $self->file_header; | ||||
| 1071 | 69 | 1118 | foreach my $aut ($muse_header->authors_as_html_list) { | ||||
| 1072 | 5 | 712 | $epub->add_author($self->_clean_html($aut)); | ||||
| 1073 | } | ||||||
| 1074 | 69 | 467 | foreach my $topic ($muse_header->topics_as_html_list) { | ||||
| 1075 | 11 | 1805 | $epub->add_subject($self->_clean_html($topic)); | ||||
| 1076 | } | ||||||
| 1077 | 69 | 50 | 430 | if ($text->header_defined->{title}) { | |||
| 1078 | 69 | 1036 | my $t = $header->{title}; | ||||
| 1079 | 69 | 480 | $epub->add_title($self->_clean_html($t)); | ||||
| 1080 | 69 | 100 | 18913 | $titlepage .= "$t\n" if $text->wants_preamble; |
|||
| 1081 | } | ||||||
| 1082 | else { | ||||||
| 1083 | 0 | 0 | $epub->add_title('Untitled'); | ||||
| 1084 | } | ||||||
| 1085 | |||||||
| 1086 | 69 | 100 | 1412 | if ($text->header_defined->{subtitle}) { | |||
| 1087 | 2 | 21 | my $st = $header->{subtitle}; | ||||
| 1088 | 2 | 50 | 8 | $titlepage .= "$st\n" if $text->wants_preamble; |
|||
| 1089 | } | ||||||
| 1090 | 69 | 100 | 943 | if ($text->header_defined->{date}) { | |||
| 1091 | 1 | 50 | 5 | if ($header->{date} =~ m/([0-9]{4})/) { | |||
| 1092 | 0 | 0 | $epub->add_date($1); | ||||
| 1093 | } | ||||||
| 1094 | 1 | 50 | 3 | $titlepage .= "$header->{date}" if $text->wants_preamble; |
|||
| 1095 | } | ||||||
| 1096 | |||||||
| 1097 | 69 | 864 | $epub->add_language($text->language_code); | ||||
| 1098 | |||||||
| 1099 | 69 | 18352 | $titlepage .= qq{ \n}; |
||||
| 1100 | |||||||
| 1101 | 69 | 50 | 66 | 319 | if ($text->header_defined->{seriesname} && $text->header_defined->{seriesnumber}) { | ||
| 1102 | $titlepage .= qq{ } |
||||||
| 1103 | . $header->{seriesname} . ' ' . $header->{seriesnumber} | ||||||
| 1104 | 2 | 114 | . qq{}; | ||||
| 1105 | } | ||||||
| 1106 | |||||||
| 1107 | 69 | 2017 | my @impressum_map = ( | ||||
| 1108 | [ source => [qw/add_source/], ], | ||||||
| 1109 | [ notes => [qw/add_description/], ], | ||||||
| 1110 | [ rights => [qw/add_rights/], ], | ||||||
| 1111 | [ isbn => [qw/add_identifier ISBN/], ], | ||||||
| 1112 | [ publisher => [qw/add_publisher/], ], | ||||||
| 1113 | [ colophon => [] ], | ||||||
| 1114 | ); | ||||||
| 1115 | |||||||
| 1116 | 69 | 284 | foreach my $imp (@impressum_map) { | ||||
| 1117 | 414 | 3422 | my $k = $imp->[0]; | ||||
| 1118 | 414 | 100 | 926 | if ($text->header_defined->{$k}) { | |||
| 1119 | 24 | 355 | my $str = $header->{$k}; | ||||
| 1120 | 24 | 48 | my ($method, @additional_args) = @{$imp->[1]}; | ||||
| 24 | 74 | ||||||
| 1121 | 24 | 100 | 74 | if ($method) { | |||
| 1122 | 22 | 87 | $epub->$method($self->_clean_html($str), @additional_args); | ||||
| 1123 | } | ||||||
| 1124 | 24 | 100 | 3805 | if ($k eq 'isbn') { | |||
| 1125 | 2 | 8 | $str = 'ISBN ' . $str; | ||||
| 1126 | } | ||||||
| 1127 | 24 | 100 | 122 | $titlepage .= qq{ $str \n} |
|||
| 1128 | if $text->wants_postamble; | ||||||
| 1129 | } | ||||||
| 1130 | } | ||||||
| 1131 | 69 | 657 | $titlepage .= "\n\n"; | ||||
| 1132 | # create the front page | ||||||
| 1133 | 69 | 346 | my $firstpage = ''; | ||||
| 1134 | $self->tt->process($self->templates->minimal_html, | ||||||
| 1135 | { | ||||||
| 1136 | 69 | 50 | 50 | 2385 | title => $self->_remove_tags($header->{title} || 'Untitled'), | ||
| 1137 | text => $titlepage, | ||||||
| 1138 | html_direction => $text->html_direction, | ||||||
| 1139 | language_code => $text->language_code, | ||||||
| 1140 | }, | ||||||
| 1141 | \$firstpage) | ||||||
| 1142 | or $self->log_fatal($self->tt->error); | ||||||
| 1143 | |||||||
| 1144 | 69 | 30100 | my $tpid = $epub->add_xhtml("titlepage.xhtml", $firstpage); | ||||
| 1145 | |||||||
| 1146 | # main loop | ||||||
| 1147 | push @navpoints, { | ||||||
| 1148 | 69 | 50 | 87271 | label => $self->_clean_html($header->{title} || 'Untitled'), | |||
| 1149 | id => $tpid, | ||||||
| 1150 | content => "titlepage.xhtml", | ||||||
| 1151 | play_order => ++$order, | ||||||
| 1152 | level => 1, | ||||||
| 1153 | }; | ||||||
| 1154 | |||||||
| 1155 | 69 | 189 | my %internal_links; | ||||
| 1156 | { | ||||||
| 1157 | 69 | 143 | my $piecenumber = 0; | ||||
| 69 | 189 | ||||||
| 1158 | 69 | 244 | foreach my $piece (@pieces) { | ||||
| 1159 | # we insert these in Text::Amuse, so it's not a wild regexp. | ||||||
| 1160 | 313 | 1583 | while ($piece->{text} =~ m/<\/a>/g) { | ||||
| 1161 | 86 | 210 | my $label = $1; | ||||
| 1162 | $internal_links{$label} = | ||||||
| 1163 | 86 | 312 | $self->_format_epub_fragment($toc[$piecenumber]{index}); | ||||
| 1164 | } | ||||||
| 1165 | 313 | 550 | $piecenumber++; | ||||
| 1166 | } | ||||||
| 1167 | } | ||||||
| 1168 | my $fix_link = sub { | ||||||
| 1169 | 123 | 123 | 298 | my ($target) = @_; | |||
| 1170 | 123 | 50 | 299 | die unless $target; | |||
| 1171 | 123 | 100 | 333 | if (my $file = $internal_links{$target}) { | |||
| 1172 | 109 | 803 | return $file . '#' . $target; | ||||
| 1173 | } | ||||||
| 1174 | else { | ||||||
| 1175 | # broken link | ||||||
| 1176 | 14 | 102 | return '#' . $target; | ||||
| 1177 | } | ||||||
| 1178 | 69 | 862 | }; | ||||
| 1179 | 69 | 296 | while (@pieces) { | ||||
| 1180 | 313 | 691 | my $piece = shift @pieces; | ||||
| 1181 | 313 | 727 | my $index = shift @toc; | ||||
| 1182 | 313 | 694 | my $xhtml = ""; | ||||
| 1183 | # print Dumper($index); | ||||||
| 1184 | 313 | 1265 | my $filename = $self->_format_epub_fragment($index->{index}); | ||||
| 1185 | 313 | 1041 | my $prefix = '*' x $index->{level}; | ||||
| 1186 | 313 | 801 | my $title = $prefix . " " . $index->{string}; | ||||
| 1187 | 313 | 1213 | $piece->{text} =~ s/(($2) . '"'/ge; | ||||
| 123 | 273 | ||||||
| 1188 | |||||||
| 1189 | 313 | 50 | 9117 | $self->tt->process($self->templates->minimal_html, | |||
| 1190 | { | ||||||
| 1191 | title => $self->_remove_tags($title), | ||||||
| 1192 | %$piece, | ||||||
| 1193 | }, | ||||||
| 1194 | \$xhtml) | ||||||
| 1195 | or $self->log_fatal($self->tt->error); | ||||||
| 1196 | |||||||
| 1197 | 313 | 119273 | my $id = $epub->add_xhtml($filename, $xhtml); | ||||
| 1198 | push @navpoints, { | ||||||
| 1199 | label => $self->_clean_html($index->{string}), | ||||||
| 1200 | content => $filename, | ||||||
| 1201 | id => $id, | ||||||
| 1202 | play_order => ++$order, | ||||||
| 1203 | level => $index->{level}, | ||||||
| 1204 | 313 | 216514 | }; | ||||
| 1205 | } | ||||||
| 1206 | 69 | 439 | $self->_epub_create_toc($epub, \@navpoints); | ||||
| 1207 | |||||||
| 1208 | # attachments | ||||||
| 1209 | 69 | 484 | foreach my $att ($text->attachments) { | ||||
| 1210 | 6 | 100 | 40099 | $self->log_fatal("Referenced file $att does not exist!") unless -f $att; | |||
| 1211 | 5 | 40 | $epub->copy_file($att, $att, $self->_mime_for_attachment($att)); | ||||
| 1212 | } | ||||||
| 1213 | # finish | ||||||
| 1214 | 68 | 473981 | $epub->pack_zip($epubname); | ||||
| 1215 | 68 | 3604039 | return $epubname; | ||||
| 1216 | } | ||||||
| 1217 | |||||||
| 1218 | sub _epub_create_toc { | ||||||
| 1219 | 69 | 69 | 224 | my ($self, $epub, $navpoints) = @_; | |||
| 1220 | 69 | 181 | my %levelnavs; | ||||
| 1221 | # print Dumper($navpoints); | ||||||
| 1222 | NAVPOINT: | ||||||
| 1223 | 69 | 449 | foreach my $navpoint (@$navpoints) { | ||||
| 1224 | 389 | 2357 | my %nav = %$navpoint; | ||||
| 1225 | 389 | 1031 | my $level = delete $nav{level}; | ||||
| 1226 | 389 | 50 | 1086 | die "Shouldn't happen: false level: $level" unless $level; | |||
| 1227 | 389 | 50 | 1657 | die "Shouldn't happen either: $level not 1-4" unless $level =~ m/\A[1-4]\z/; | |||
| 1228 | 389 | 764 | my $checklevel = $level - 1; | ||||
| 1229 | |||||||
| 1230 | 389 | 677 | my $current; | ||||
| 1231 | 389 | 1064 | while ($checklevel > 0) { | ||||
| 1232 | 264 | 100 | 780 | if (my $parent = $levelnavs{$checklevel}) { | |||
| 1233 | 234 | 1024 | $current = $parent->add_navpoint(%nav); | ||||
| 1234 | 234 | 42020 | last; | ||||
| 1235 | } | ||||||
| 1236 | 30 | 83 | $checklevel--; | ||||
| 1237 | } | ||||||
| 1238 | 389 | 100 | 1177 | unless ($current) { | |||
| 1239 | 155 | 4290 | $current = $epub->add_navpoint(%nav); | ||||
| 1240 | } | ||||||
| 1241 | 389 | 109738 | for my $clear ($level..4) { | ||||
| 1242 | 1190 | 2479 | delete $levelnavs{$clear}; | ||||
| 1243 | } | ||||||
| 1244 | 389 | 2042 | $levelnavs{$level} = $current; | ||||
| 1245 | } | ||||||
| 1246 | # probably not needed, but let's be sure we don't leave circular | ||||||
| 1247 | # refs. | ||||||
| 1248 | 69 | 259 | foreach my $k (keys %levelnavs) { | ||||
| 1249 | 149 | 386 | delete $levelnavs{$k}; | ||||
| 1250 | } | ||||||
| 1251 | } | ||||||
| 1252 | |||||||
| 1253 | sub _remove_tags { | ||||||
| 1254 | 494 | 494 | 1275 | my ($self, $string) = @_; | |||
| 1255 | 494 | 50 | 1306 | return "" unless defined $string; | |||
| 1256 | 494 | 2090 | $string =~ s/<.+?>//g; | ||||
| 1257 | 494 | 4107 | return $string; | ||||
| 1258 | } | ||||||
| 1259 | |||||||
| 1260 | sub _clean_html { | ||||||
| 1261 | 505 | 505 | 1517 | my ($self, $string) = @_; | |||
| 1262 | 505 | 50 | 1558 | return "" unless defined $string; | |||
| 1263 | 505 | 1693 | $string =~ s/<.+?>//g; | ||||
| 1264 | 505 | 1102 | $string =~ s/</ | ||||
| 1265 | 505 | 1014 | $string =~ s/>/>/g; | ||||
| 1266 | 505 | 931 | $string =~ s/"/"/g; | ||||
| 1267 | 505 | 910 | $string =~ s/'/'/g; | ||||
| 1268 | 505 | 863 | $string =~ s/ / /g; | ||||
| 1269 | 505 | 846 | $string =~ s/ / /g; | ||||
| 1270 | 505 | 887 | $string =~ s/&/&/g; | ||||
| 1271 | 505 | 6294 | return $string; | ||||
| 1272 | } | ||||||
| 1273 | |||||||
| 1274 | =head2 Logging | ||||||
| 1275 | |||||||
| 1276 | While the C |
||||||
| 1277 | very well be empty, the object uses these two methods: | ||||||
| 1278 | |||||||
| 1279 | =over 4 | ||||||
| 1280 | |||||||
| 1281 | =item log_info(@strings) | ||||||
| 1282 | |||||||
| 1283 | If C |
||||||
| 1284 | Otherwise print to the standard output. | ||||||
| 1285 | |||||||
| 1286 | =item log_fatal(@strings) | ||||||
| 1287 | |||||||
| 1288 | Calls C |
||||||
| 1289 | |||||||
| 1290 | =item parse_tex_log_file($logfile) | ||||||
| 1291 | |||||||
| 1292 | (Internal) Parse the produced logfile for missing characters. | ||||||
| 1293 | |||||||
| 1294 | =back | ||||||
| 1295 | |||||||
| 1296 | =head1 INTERNAL CONSTANTS | ||||||
| 1297 | |||||||
| 1298 | =head2 DEBUG | ||||||
| 1299 | |||||||
| 1300 | Set from AMW_DEBUG environment. | ||||||
| 1301 | |||||||
| 1302 | =cut | ||||||
| 1303 | |||||||
| 1304 | |||||||
| 1305 | |||||||
| 1306 | sub log_info { | ||||||
| 1307 | 27 | 27 | 1 | 2711 | my ($self, @info) = @_; | ||
| 1308 | 27 | 135 | my $logger = $self->logger; | ||||
| 1309 | 27 | 100 | 132 | if ($logger) { | |||
| 1310 | 26 | 158 | $logger->(@info); | ||||
| 1311 | } | ||||||
| 1312 | else { | ||||||
| 1313 | 1 | 68 | print @info; | ||||
| 1314 | } | ||||||
| 1315 | } | ||||||
| 1316 | |||||||
| 1317 | sub log_fatal { | ||||||
| 1318 | 1 | 1 | 1 | 3 | my ($self, @info) = @_; | ||
| 1319 | 1 | 23 | $self->log_info(@info); | ||||
| 1320 | 1 | 50 | 4 | my $failure = join("\n", @info) || "Fatal exception"; | |||
| 1321 | 1 | 43 | die "$failure\n"; | ||||
| 1322 | } | ||||||
| 1323 | |||||||
| 1324 | sub parse_tex_log_file { | ||||||
| 1325 | 1 | 1 | 1 | 77 | my ($self, $logfile) = @_; | ||
| 1326 | 1 | 50 | 5 | die "Missing file argument!" unless $logfile; | |||
| 1327 | 1 | 50 | 38 | if (-f $logfile) { | |||
| 1328 | # if you're wandering why we open this in raw mode: The log | ||||||
| 1329 | # file produced by XeLaTeX is utf8, but it splits the output | ||||||
| 1330 | # at 80 bytes or so. This of course sometimes, expecially | ||||||
| 1331 | # working with cyrillic scripts, cut the multibyte character | ||||||
| 1332 | # in half, producing invalid utf8 octects. | ||||||
| 1333 | 1 | 50 | 76 | open (my $fh, '<:raw', $logfile) | |||
| 1334 | or $self->log_fatal("Couldn't open $logfile $!"); | ||||||
| 1335 | |||||||
| 1336 | 1 | 4 | my %errors; | ||||
| 1337 | 1 | 3 | my $continue = 0; | ||||
| 1338 | |||||||
| 1339 | 1 | 72 | while (my $line = <$fh>) { | ||||
| 1340 | 1257 | 2042 | chomp $line; | ||||
| 1341 | 1257 | 100 | 5044 | if ($line =~ m/^missing character/i) { | |||
| 100 | |||||||
| 100 | |||||||
| 1342 | # if we get the warning, nothing we can do about it, | ||||||
| 1343 | # but shouldn't happen. | ||||||
| 1344 | 4 | 23 | $errors{$line} = 1; | ||||
| 1345 | } | ||||||
| 1346 | elsif ($line =~ m/^Overfull/) { | ||||||
| 1347 | 2 | 34 | $self->log_info(decode_utf8($line) . "\n"); | ||||
| 1348 | 2 | 22 | $continue++; | ||||
| 1349 | } | ||||||
| 1350 | elsif ($continue) { | ||||||
| 1351 | 2 | 19 | $self->log_info(decode_utf8($line) . "\n\n"); | ||||
| 1352 | 2 | 13 | $continue = 0; | ||||
| 1353 | } | ||||||
| 1354 | } | ||||||
| 1355 | 1 | 16 | close $fh; | ||||
| 1356 | 1 | 9 | foreach my $error (sort keys %errors) { | ||||
| 1357 | 4 | 35 | $self->log_info(decode_utf8($error) . "...\n"); | ||||
| 1358 | } | ||||||
| 1359 | } | ||||||
| 1360 | } | ||||||
| 1361 | |||||||
| 1362 | sub cleanup { | ||||||
| 1363 | 5 | 5 | 1 | 20012 | my $self = shift; | ||
| 1364 | 5 | 50 | 37 | if (my $f = $self->status_file) { | |||
| 1365 | 5 | 100 | 130 | if (-f $f) { | |||
| 1366 | 4 | 50 | 721 | unlink $f or $self->log_fatal("Couldn't unlink $f $!"); | |||
| 1367 | } | ||||||
| 1368 | else { | ||||||
| 1369 | 1 | 94 | $self->log_info("Couldn't find " . File::Spec->rel2abs($f)); | ||||
| 1370 | } | ||||||
| 1371 | } | ||||||
| 1372 | } | ||||||
| 1373 | |||||||
| 1374 | sub _process_template { | ||||||
| 1375 | 371 | 371 | 7978 | my ($self, $template_ref, $tokens, $outfile) = @_; | |||
| 1376 | 371 | 915 | eval { | ||||
| 1377 | 371 | 1076 | my $out = ''; | ||||
| 1378 | 371 | 50 | 33 | 3368 | die "Wrong usage" unless ($template_ref && $tokens && $outfile); | ||
| 33 | |||||||
| 1379 | 371 | 4354 | $self->tt->process($template_ref, $tokens, \$out); | ||||
| 1380 | 371 | 50 | 144814032 | open (my $fh, '>:encoding(UTF-8)', $outfile) or die "Couldn't open $outfile $!"; | |||
| 1381 | 371 | 123400 | print $fh $out, "\n"; | ||||
| 1382 | 371 | 18243 | close $fh; | ||||
| 1383 | }; | ||||||
| 1384 | 371 | 50 | 2314 | if ($@) { | |||
| 1385 | 0 | 0 | $self->log_fatal("Error processing template for $outfile: $@"); | ||||
| 1386 | }; | ||||||
| 1387 | 371 | 24052 | return $outfile; | ||||
| 1388 | } | ||||||
| 1389 | |||||||
| 1390 | |||||||
| 1391 | # method for options to pass to the tex template | ||||||
| 1392 | sub _prepare_tex_tokens { | ||||||
| 1393 | 253 | 253 | 1882 | my ($self, %args) = @_; | |||
| 1394 | 253 | 7971 | my $doc = $self->document; | ||||
| 1395 | 253 | 29144 | my $is_slide = delete $args{is_slide}; | ||||
| 1396 | 253 | 770 | my $template_body = delete $args{template_body}; | ||||
| 1397 | 253 | 50 | 1020 | die "Missing required argument template_body " unless $template_body; | |||
| 1398 | 253 | 548 | my %tokens = %{ $self->tex_options }; | ||||
| 253 | 6215 | ||||||
| 1399 | 253 | 12754 | my $escaped_args = $self->_escape_options_hashref(ltx => \%args); | ||||
| 1400 | 253 | 982 | foreach my $k (keys %$escaped_args) { | ||||
| 1401 | 46 | 96 | $tokens{$k} = $escaped_args->{$k}; | ||||
| 1402 | } | ||||||
| 1403 | # now tokens have the unparsed options | ||||||
| 1404 | # now validate the options against the new shiny module | ||||||
| 1405 | 253 | 606 | my %options = (%{ $self->full_options }, %args); | ||||
| 253 | 7106 | ||||||
| 1406 | # print Dumper($self->full_options); | ||||||
| 1407 | 253 | 9680 | my $template_options = eval { Text::Amuse::Compile::TemplateOptions->new(%options) }; | ||||
| 253 | 8807 | ||||||
| 1408 | 253 | 100 | 11863 | unless ($template_options) { | |||
| 1409 | 12 | 296 | $template_options = Text::Amuse::Compile::TemplateOptions->new; | ||||
| 1410 | 12 | 429 | $self->log_info("# Validation failed: $@, setting one by one\n"); | ||||
| 1411 | 12 | 125 | foreach my $method ($template_options->config_setters) { | ||||
| 1412 | 504 | 100 | 6361 | if (exists $options{$method}) { | |||
| 1413 | 160 | 255 | eval { $template_options->$method($options{$method}) }; | ||||
| 160 | 3979 | ||||||
| 1414 | } | ||||||
| 1415 | } | ||||||
| 1416 | } | ||||||
| 1417 | 253 | 1843 | my $safe_options = | ||||
| 1418 | $self->_escape_options_hashref(ltx => $template_options->config_output); | ||||||
| 1419 | |||||||
| 1420 | # defaults | ||||||
| 1421 | 253 | 21179 | my %parsed = (%$safe_options, | ||||
| 1422 | class => 'scrbook', | ||||||
| 1423 | lang => 'english', | ||||||
| 1424 | mainlanguage_script => '', | ||||||
| 1425 | wants_toc => 0, | ||||||
| 1426 | ); | ||||||
| 1427 | |||||||
| 1428 | |||||||
| 1429 | 253 | 2515 | my $fonts = $self->fonts; | ||||
| 1430 | |||||||
| 1431 | # not used but for legacy templates | ||||||
| 1432 | 253 | 2733 | $parsed{mainfont} = $fonts->main->name; | ||||
| 1433 | 253 | 1869 | $parsed{sansfont} = $fonts->sans->name; | ||||
| 1434 | 253 | 1581 | $parsed{monofont} = $fonts->mono->name; | ||||
| 1435 | 253 | 1319 | $parsed{fontsize} = $fonts->size; | ||||
| 1436 | |||||||
| 1437 | 253 | 17299 | my $latex_body = $self->_interpolate_magic_comments($template_options->format_id, $doc); | ||||
| 1438 | |||||||
| 1439 | 253 | 1287 | my $enable_secondary_footnotes = $latex_body =~ m/\\footnoteB\{/; | ||||
| 1440 | |||||||
| 1441 | # check if the template body support this conditional, which is new. If not, | ||||||
| 1442 | # always setup bigfoot | ||||||
| 1443 | # print "SECONDARY FOOTNOTES ENABLED? $enable_secondary_footnotes\n"; | ||||||
| 1444 | 253 | 100 | 3219 | if (index($$template_body, '[% IF enable_secondary_footnotes %]', 0) < 0) { | |||
| 1445 | 1 | 4 | $enable_secondary_footnotes = 1; | ||||
| 1446 | } | ||||||
| 1447 | # print "SECONDARY FOOTNOTES ENABLED? $enable_secondary_footnotes\n"; | ||||||
| 1448 | |||||||
| 1449 | 253 | 1743 | my $main_is_rtl = Text::Amuse::Utils::lang_code_is_rtl($doc->language_code); | ||||
| 1450 | |||||||
| 1451 | 253 | 100 | 9636 | my $tex_setup_langs = $fonts | |||
| 1452 | ->compose_polyglossia_fontspec_stanza(lang => $doc->language, | ||||||
| 1453 | others => $doc->other_languages || [], | ||||||
| 1454 | enable_secondary_footnotes => $enable_secondary_footnotes, | ||||||
| 1455 | bidi => $doc->is_bidi, | ||||||
| 1456 | main_is_rtl => $main_is_rtl, | ||||||
| 1457 | has_ruby => $doc->has_ruby, | ||||||
| 1458 | is_slide => $is_slide, | ||||||
| 1459 | captions => Text::Amuse::Utils::language_code_locale_captions($doc->language_code), | ||||||
| 1460 | ); | ||||||
| 1461 | |||||||
| 1462 | 253 | 1190 | my @indexes; | ||||
| 1463 | 253 | 100 | 1829 | if (my @raw_indexes = $self->document_indexes) { | |||
| 1464 | my $indexer = Text::Amuse::Compile::Indexer->new(latex_body => $latex_body, | ||||||
| 1465 | language_code => $doc->language_code, | ||||||
| 1466 | 0 | 0 | 0 | logger => $self->logger || sub { print @_ }, | |||
| 1467 | 5 | 33 | 27 | index_specs => \@raw_indexes); | |||
| 1468 | 5 | 10450 | $latex_body = $indexer->indexed_tex_body; | ||||
| 1469 | 5 | 122 | my %xindy_langs = ( | ||||
| 1470 | bg => 'bulgarian', | ||||||
| 1471 | cs => 'czech', | ||||||
| 1472 | da => 'danish', | ||||||
| 1473 | de => 'german-din', # ae is sorted like ae. alternative -duden | ||||||
| 1474 | el => 'greek', | ||||||
| 1475 | en => 'english', | ||||||
| 1476 | es => 'spanish-modern', | ||||||
| 1477 | et => 'estonian', | ||||||
| 1478 | fi => 'finnish', | ||||||
| 1479 | fr => 'french', | ||||||
| 1480 | hr => 'croatian', | ||||||
| 1481 | hu => 'hungarian', | ||||||
| 1482 | is => 'icelandic', | ||||||
| 1483 | it => 'italian', | ||||||
| 1484 | lv => 'latvian', | ||||||
| 1485 | lt => 'lithuanian', | ||||||
| 1486 | mk => 'macedonian', | ||||||
| 1487 | # nl => 'dutch', # unclear why missing | ||||||
| 1488 | no => 'norwegian', | ||||||
| 1489 | sr => 'croatian', # serbian is cyrillic | ||||||
| 1490 | ro => 'romanian', | ||||||
| 1491 | ru => 'russian', | ||||||
| 1492 | sk => 'slovak-small', # exists also slovak-large | ||||||
| 1493 | sl => 'slovenian', | ||||||
| 1494 | pl => 'polish', | ||||||
| 1495 | pt => 'portuguese', | ||||||
| 1496 | sq => 'albanian', | ||||||
| 1497 | sv => 'swedish', | ||||||
| 1498 | tr => 'turkish', | ||||||
| 1499 | uk => 'ukrainian', | ||||||
| 1500 | vi => 'vietnamese', | ||||||
| 1501 | ); | ||||||
| 1502 | @indexes = map { +{ | ||||||
| 1503 | name => $_->index_name, | ||||||
| 1504 | title => $_->index_label, | ||||||
| 1505 | 7 | 50 | 132 | language => $xindy_langs{$doc->language_code} || 'general', | |||
| 1506 | } } | ||||||
| 1507 | 5 | 12 | @{ $indexer->specifications }; | ||||
| 5 | 169 | ||||||
| 1508 | } | ||||||
| 1509 | 253 | 100 | 8445 | $self->_set_indexes(@indexes ? \@indexes : undef); | |||
| 1510 | # no cover page if header or compiler says so, or | ||||||
| 1511 | # if coverpage_only_if_toc is set and doc doesn't have a toc. | ||||||
| 1512 | 253 | 100 | 100 | 11968 | if ($self->nocoverpage or | ||
| 100 | |||||||
| 1513 | ($self->coverpage_only_if_toc && !$doc->wants_toc)) { | ||||||
| 1514 | 22 | 1137 | $parsed{nocoverpage} = 1; | ||||
| 1515 | 22 | 79 | $parsed{class} = 'scrartcl'; | ||||
| 1516 | 22 | 81 | delete $parsed{opening}; # not needed for article. | ||||
| 1517 | } | ||||||
| 1518 | |||||||
| 1519 | |||||||
| 1520 | 253 | 100 | 14510 | unless ($parsed{notoc}) { | |||
| 1521 | 239 | 100 | 1350 | if ($doc->wants_toc) { | |||
| 1522 | 159 | 10641 | $parsed{wants_toc} = 1; | ||||
| 1523 | } | ||||||
| 1524 | } | ||||||
| 1525 | |||||||
| 1526 | return { | ||||||
| 1527 | 253 | 9474 | options => \%tokens, | ||||
| 1528 | safe_options => \%parsed, | ||||||
| 1529 | doc => $doc, | ||||||
| 1530 | tex_setup_langs => $tex_setup_langs, | ||||||
| 1531 | latex_body => $latex_body, | ||||||
| 1532 | enable_secondary_footnotes => $enable_secondary_footnotes, | ||||||
| 1533 | tex_metadata => $self->file_header->tex_metadata, | ||||||
| 1534 | tex_indexes => \@indexes, | ||||||
| 1535 | # in case we need it for volumes | ||||||
| 1536 | format_id => $template_options->format_id, | ||||||
| 1537 | }; | ||||||
| 1538 | } | ||||||
| 1539 | |||||||
| 1540 | sub _interpolate_magic_comments { | ||||||
| 1541 | 258 | 258 | 3700 | my ($self, $format, $doc) = @_; | |||
| 1542 | 258 | 100 | 1192 | $format ||= 'DEFAULT'; | |||
| 1543 | 258 | 2199 | my $latex = $doc->as_latex; | ||||
| 1544 | # format is validated. | ||||||
| 1545 | # switch is gmx, no "s", we are line-based here | ||||||
| 1546 | 258 | 9697374 | my $prefix = qr{ | ||||
| 1547 | \% | ||||||
| 1548 | \x{20}+ | ||||||
| 1549 | \: | ||||||
| 1550 | (?: | ||||||
| 1551 | \Q$format\E | \* | ALL | ||||||
| 1552 | ) | ||||||
| 1553 | \: | ||||||
| 1554 | \x{20}+ | ||||||
| 1555 | \\textbackslash\{\} | ||||||
| 1556 | }x; | ||||||
| 1557 | 258 | 1355 | my $size = qr{-?[1-9][0-9]*(?:mm|cm|pt|em)}x; | ||||
| 1558 | |||||||
| 1559 | 258 | 8767 | $latex =~ s/^ | ||||
| 1560 | $prefix | ||||||
| 1561 | ( # permitted commands | ||||||
| 1562 | sloppy | | ||||||
| 1563 | fussy | | ||||||
| 1564 | newpage | | ||||||
| 1565 | strut | | ||||||
| 1566 | flushbottom | | ||||||
| 1567 | raggedbottom | | ||||||
| 1568 | vfill | | ||||||
| 1569 | amusewiki[a-zA-Z]+ | | ||||||
| 1570 | clearpage | | ||||||
| 1571 | cleardoublepage | | ||||||
| 1572 | vskip \x{20}+ $size | ||||||
| 1573 | ) | ||||||
| 1574 | \x{20}* | ||||||
| 1575 | $ | ||||||
| 1576 | /\\$1/gmx; | ||||||
| 1577 | 258 | 4563 | $latex =~ s/^ | ||||
| 1578 | $prefix | ||||||
| 1579 | ( (this)? pagestyle ) | ||||||
| 1580 | \\ \{ | ||||||
| 1581 | ( plain | empty | headings | myheadings | scrheadings ) | ||||||
| 1582 | \\ \} | ||||||
| 1583 | \x{20}* | ||||||
| 1584 | $ | ||||||
| 1585 | /\\$1\{$3\}/gmx; | ||||||
| 1586 | |||||||
| 1587 | 258 | 4467 | $latex =~ s/^ | ||||
| 1588 | $prefix | ||||||
| 1589 | ( enlargethispage ) | ||||||
| 1590 | \\ \{ | ||||||
| 1591 | ( $size ) | ||||||
| 1592 | \\ \} | ||||||
| 1593 | \x{20}* | ||||||
| 1594 | $ | ||||||
| 1595 | /\\$1\{$2\}/gmx; | ||||||
| 1596 | |||||||
| 1597 | 258 | 1139 | my $regular = qr{[^\#\$\%\&\~\^\\\{\}_]+}; | ||||
| 1598 | 258 | 5378 | $latex =~ s/^ | ||||
| 1599 | $prefix | ||||||
| 1600 | markboth | ||||||
| 1601 | \\ \{ | ||||||
| 1602 | ($regular) | ||||||
| 1603 | \\\} | ||||||
| 1604 | \\\{ | ||||||
| 1605 | ($regular) | ||||||
| 1606 | \\\} | ||||||
| 1607 | \x{20}* | ||||||
| 1608 | $ | ||||||
| 1609 | /\\markboth\{$1}\{$2\}/gmx; | ||||||
| 1610 | 258 | 4003 | $latex =~ s/^ | ||||
| 1611 | $prefix | ||||||
| 1612 | markright | ||||||
| 1613 | \\ \{ | ||||||
| 1614 | ($regular) | ||||||
| 1615 | \\\} | ||||||
| 1616 | \x{20}* | ||||||
| 1617 | $ | ||||||
| 1618 | /\\markright\{$1}/gmx; | ||||||
| 1619 | |||||||
| 1620 | # with looseness, we need to attach it to the next paragraph, so | ||||||
| 1621 | # eat all the space and replace with a single \n | ||||||
| 1622 | |||||||
| 1623 | 258 | 3656 | $latex =~ s/^ | ||||
| 1624 | $prefix | ||||||
| 1625 | looseness\=(-?[0-9]) | ||||||
| 1626 | $ | ||||||
| 1627 | \s* | ||||||
| 1628 | /\\looseness=$1\n/gmx; | ||||||
| 1629 | |||||||
| 1630 | # add to toc | ||||||
| 1631 | 258 | 5486 | $latex =~ s/^ | ||||
| 1632 | $prefix | ||||||
| 1633 | addcontentsline | ||||||
| 1634 | \\\{ | ||||||
| 1635 | (toc|lof|lot) | ||||||
| 1636 | \\\} | ||||||
| 1637 | \\\{ | ||||||
| 1638 | (part|chapter|section|subsection) | ||||||
| 1639 | \\\} | ||||||
| 1640 | \\\{ | ||||||
| 1641 | ($regular) | ||||||
| 1642 | \\\} | ||||||
| 1643 | \x{20}* | ||||||
| 1644 | $ | ||||||
| 1645 | /\\addcontentsline{$1}{$2}{$3}/gmx; | ||||||
| 1646 | |||||||
| 1647 | 258 | 1669 | return $latex; | ||||
| 1648 | } | ||||||
| 1649 | |||||||
| 1650 | sub _looks_like_a_sane_name { | ||||||
| 1651 | 926 | 926 | 2951 | my ($self, $name) = @_; | |||
| 1652 | 926 | 50 | 3337 | return unless defined $name; | |||
| 1653 | 926 | 2043 | my $out; | ||||
| 1654 | 926 | 2099 | eval { | ||||
| 1655 | 926 | 6138 | $out = Text::Amuse::Compile::TemplateOptions::check_filename($name); | ||||
| 1656 | }; | ||||||
| 1657 | 926 | 100 | 66 | 4301 | if (!$out || $@) { | ||
| 1658 | 784 | 1620 | $self->log_info("$name is not good: $@") if DEBUG; | ||||
| 1659 | 784 | 3624 | return; | ||||
| 1660 | } | ||||||
| 1661 | else { | ||||||
| 1662 | 142 | 272 | $self->log_info("$name is good") if DEBUG; | ||||
| 1663 | 142 | 627 | return $out; | ||||
| 1664 | } | ||||||
| 1665 | } | ||||||
| 1666 | |||||||
| 1667 | sub _mime_for_attachment { | ||||||
| 1668 | 12 | 12 | 73 | my ($self, $att) = @_; | |||
| 1669 | 12 | 50 | 53 | die "Missing argument" unless $att; | |||
| 1670 | 12 | 26 | my $mime; | ||||
| 1671 | 12 | 100 | 187 | if ($att =~ m/\.jpe?g$/) { | |||
| 50 | |||||||
| 1672 | 4 | 18 | $mime = "image/jpeg"; | ||||
| 1673 | } | ||||||
| 1674 | elsif ($att =~ m/\.png$/) { | ||||||
| 1675 | 8 | 19 | $mime = "image/png"; | ||||
| 1676 | } | ||||||
| 1677 | else { | ||||||
| 1678 | 0 | 0 | $self->log_fatal("Unrecognized attachment $att!"); | ||||
| 1679 | } | ||||||
| 1680 | 12 | 92 | return $mime; | ||||
| 1681 | } | ||||||
| 1682 | |||||||
| 1683 | sub _format_epub_fragment { | ||||||
| 1684 | 399 | 399 | 886 | my ($self, $index) = @_; | |||
| 1685 | 399 | 100 | 2851 | return sprintf('piece%06d.xhtml', $index || 0); | |||
| 1686 | } | ||||||
| 1687 | |||||||
| 1688 | sub document_indexes { | ||||||
| 1689 | 260 | 260 | 1 | 402180 | my ($self) = @_; | ||
| 1690 | 260 | 100 | 8082 | my @docs = ($self->virtual ? ($self->document->docs) : ( $self->document )); | |||
| 1691 | 14 | 164 | my @comments = grep { /\AINDEX +([a-z]+): (.+)/ } | ||||
| 1692 | 14 | 123 | map { $_->string } | ||||
| 1693 | 9098 | 1038368 | grep { $_->type eq 'comment' } | ||||
| 1694 | 260 | 3094 | map { $_->document->elements } @docs; | ||||
| 291 | 2006 | ||||||
| 1695 | 260 | 3423 | return @comments; | ||||
| 1696 | } | ||||||
| 1697 | |||||||
| 1698 | |||||||
| 1699 | 1; |