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