source: main/trunk/greenstone2/perllib/cpan/Mojo/Template.pm

Last change on this file was 32205, checked in by ak19, 6 years ago

First set of commits to do with implementing the new 'paged_html' output option of PDFPlugin that uses using xpdftools' new pdftohtml. So far tested only on Linux (64 bit), but things work there so I'm optimistically committing the changes since they work. 2. Committing the pre-built Linux binaries of XPDFtools for both 32 and 64 bit built by the XPDF group. 2. To use the correct bitness variant of xpdftools, setup.bash now exports the BITNESS env var, consulted by gsConvert.pl. 3. All the perl code changes to do with using xpdf tools' pdftohtml to generate paged_html and feed it in the desired form into GS(3): gsConvert.pl, PDFPlugin.pm and its parent ConvertBinaryPFile.pm have been modified to make it all work. xpdftools' pdftohtml generates a folder containing an html file and a screenshot for each page in a PDF (as well as an index.html linking to each page's html). However, we want a single html file that contains each individual 'page' html's content in a div, and need to do some further HTML style, attribute and structure modifications to massage the xpdftool output to what we want for GS. In order to parse and manipulate the HTML 'DOM' to do this, we're using the Mojo::DOM package that Dr Bainbridge found and which he's compiled up. Mojo::DOM is therefore also committed in this revision. Some further changes and some display fixes are required, but need to check with the others about that.

File size: 17.2 KB
Line 
1package Mojo::Template;
2use Mojo::Base -base;
3
4use Carp 'croak';
5use Mojo::ByteStream;
6use Mojo::Exception;
7use Mojo::File 'path';
8use Mojo::Util qw(decode encode monkey_patch);
9
10use constant DEBUG => $ENV{MOJO_TEMPLATE_DEBUG} || 0;
11
12has [qw(append code prepend unparsed)] => '';
13has [qw(auto_escape compiled vars)];
14has capture_end => 'end';
15has capture_start => 'begin';
16has comment_mark => '#';
17has encoding => 'UTF-8';
18has escape => sub { \&Mojo::Util::xml_escape };
19has [qw(escape_mark expression_mark trim_mark)] => '=';
20has [qw(line_start replace_mark)] => '%';
21has name => 'template';
22has namespace => 'Mojo::Template::SandBox';
23has tag_start => '<%';
24has tag_end => '%>';
25has tree => sub { [] };
26
27sub parse {
28 my ($self, $template) = @_;
29
30 # Clean start
31 $self->unparsed($template)->tree(\my @tree)->compiled(undef);
32
33 my $tag = $self->tag_start;
34 my $replace = $self->replace_mark;
35 my $expr = $self->expression_mark;
36 my $escp = $self->escape_mark;
37 my $cpen = $self->capture_end;
38 my $cmnt = $self->comment_mark;
39 my $cpst = $self->capture_start;
40 my $trim = $self->trim_mark;
41 my $end = $self->tag_end;
42 my $start = $self->line_start;
43
44 my $line_re
45 = qr/^(\s*)\Q$start\E(?:(\Q$replace\E)|(\Q$cmnt\E)|(\Q$expr\E))?(.*)$/;
46 my $token_re = qr/
47 (
48 \Q$tag\E(?:\Q$replace\E|\Q$cmnt\E) # Replace
49 |
50 \Q$tag$expr\E(?:\Q$escp\E)?(?:\s*\Q$cpen\E(?!\w))? # Expression
51 |
52 \Q$tag\E(?:\s*\Q$cpen\E(?!\w))? # Code
53 |
54 (?:(?<!\w)\Q$cpst\E\s*)?(?:\Q$trim\E)?\Q$end\E # End
55 )
56 /x;
57 my $cpen_re = qr/^\Q$tag\E(?:\Q$expr\E)?(?:\Q$escp\E)?\s*\Q$cpen\E(.*)$/;
58 my $end_re = qr/^(?:(\Q$cpst\E)\s*)?(\Q$trim\E)?\Q$end\E$/;
59
60 # Split lines
61 my $op = 'text';
62 my ($trimming, $capture);
63 for my $line (split "\n", $template) {
64
65 # Turn Perl line into mixed line
66 if ($op eq 'text' && $line =~ $line_re) {
67
68 # Escaped start
69 if ($2) { $line = "$1$start$5" }
70
71 # Comment
72 elsif ($3) { $line = "$tag$3 $trim$end" }
73
74 # Expression or code
75 else { $line = $4 ? "$1$tag$4$5 $end" : "$tag$5 $trim$end" }
76 }
77
78 # Escaped line ending
79 $line .= "\n" if $line !~ s/\\\\$/\\\n/ && $line !~ s/\\$//;
80
81 # Mixed line
82 for my $token (split $token_re, $line) {
83
84 # Capture end
85 ($token, $capture) = ("$tag$1", 1) if $token =~ $cpen_re;
86
87 # End
88 if ($op ne 'text' && $token =~ $end_re) {
89
90 # Capture start
91 splice @tree, -1, 0, ['cpst'] if $1;
92
93 # Trim left side
94 _trim(\@tree) if ($trimming = $2) && @tree > 1;
95
96 # Hint at end
97 push @tree, [$op = 'text', ''];
98 }
99
100 # Code
101 elsif ($token eq $tag) { $op = 'code' }
102
103 # Expression
104 elsif ($token eq "$tag$expr") { $op = 'expr' }
105
106 # Expression that needs to be escaped
107 elsif ($token eq "$tag$expr$escp") { $op = 'escp' }
108
109 # Comment
110 elsif ($token eq "$tag$cmnt") { $op = 'cmnt' }
111
112 # Text (comments are just ignored)
113 elsif ($op ne 'cmnt') {
114
115 # Replace
116 $token = $tag if $token eq "$tag$replace";
117
118 # Trim right side (convert whitespace to line noise)
119 if ($trimming && $token =~ s/^(\s+)//) {
120 push @tree, ['code', $1];
121 $trimming = 0;
122 }
123
124 # Token (with optional capture end)
125 push @tree, $capture ? ['cpen'] : (), [$op, $token];
126 $capture = 0;
127 }
128 }
129
130 # Optimize successive text lines separated by a newline
131 push @tree, ['line'] and next
132 if $tree[-4] && $tree[-4][0] ne 'line'
133 || (!$tree[-3] || $tree[-3][0] ne 'text' || $tree[-3][1] !~ /\n$/)
134 || ($tree[-2][0] ne 'line' || $tree[-1][0] ne 'text');
135 $tree[-3][1] .= pop(@tree)->[1];
136 }
137
138 return $self;
139}
140
141sub process {
142 my $self = shift;
143
144 # Use a local stack trace for compile exceptions
145 my $compiled = $self->compiled;
146 unless ($compiled) {
147 my $code = $self->_compile->code;
148 monkey_patch $self->namespace, '_escape', $self->escape;
149 return Mojo::Exception->new($@)->inspect($self->unparsed, $code)
150 ->trace->verbose(1)
151 unless $compiled = eval $self->_wrap($code, @_);
152 $self->compiled($compiled);
153 }
154
155 # Use a real stack trace for normal exceptions
156 local $SIG{__DIE__} = sub {
157 CORE::die $_[0] if ref $_[0];
158 CORE::die Mojo::Exception->new(shift)
159 ->trace->inspect($self->unparsed, $self->code)->verbose(1);
160 };
161
162 my $output;
163 return eval { $output = $compiled->(@_); 1 } ? $output : $@;
164}
165
166sub render { shift->parse(shift)->process(@_) }
167
168sub render_file {
169 my ($self, $path) = (shift, shift);
170
171 $self->name($path) unless defined $self->{name};
172 my $template = path($path)->slurp;
173 my $encoding = $self->encoding;
174 croak qq{Template "$path" has invalid encoding}
175 if $encoding && !defined($template = decode $encoding, $template);
176
177 return $self->render($template, @_);
178}
179
180sub _compile {
181 my $self = shift;
182
183 my $tree = $self->tree;
184 my $escape = $self->auto_escape;
185
186 my @blocks = ('');
187 my ($i, $capture, $multi);
188 while (++$i <= @$tree && (my $next = $tree->[$i])) {
189 my ($op, $value) = @{$tree->[$i - 1]};
190 push @blocks, '' and next if $op eq 'line';
191 my $newline = chomp($value //= '');
192
193 # Text (quote and fix line ending)
194 if ($op eq 'text') {
195 $value = join "\n", map { quotemeta $_ } split("\n", $value, -1);
196 $value .= '\n' if $newline;
197 $blocks[-1] .= "\$_O .= \"" . $value . "\";" if length $value;
198 }
199
200 # Code or multi-line expression
201 elsif ($op eq 'code' || $multi) { $blocks[-1] .= $value }
202
203 # Capture end
204 elsif ($op eq 'cpen') {
205 $blocks[-1] .= 'return Mojo::ByteStream->new($_O) }';
206
207 # No following code
208 $blocks[-1] .= ';' if ($next->[1] // '') =~ /^\s*$/;
209 }
210
211 # Expression
212 if ($op eq 'expr' || $op eq 'escp') {
213
214 # Escaped
215 if (!$multi && ($op eq 'escp' && !$escape || $op eq 'expr' && $escape)) {
216 $blocks[-1] .= "\$_O .= _escape scalar + $value";
217 }
218
219 # Raw
220 elsif (!$multi) { $blocks[-1] .= "\$_O .= scalar + $value" }
221
222 # Multi-line
223 $multi = !$next || $next->[0] ne 'text';
224
225 # Append semicolon
226 $blocks[-1] .= ';' unless $multi || $capture;
227 }
228
229 # Capture start
230 if ($op eq 'cpst') { $capture = 1 }
231 elsif ($capture) {
232 $blocks[-1] .= "sub { my \$_O = ''; ";
233 $capture = 0;
234 }
235 }
236
237 return $self->code(join "\n", @blocks)->tree([]);
238}
239
240sub _line {
241 my $name = shift->name;
242 $name =~ y/"//d;
243 return qq{#line @{[shift]} "$name"};
244}
245
246sub _trim {
247 my $tree = shift;
248
249 # Skip captures
250 my $i = $tree->[-2][0] eq 'cpst' || $tree->[-2][0] eq 'cpen' ? -3 : -2;
251
252 # Only trim text
253 return unless $tree->[$i][0] eq 'text';
254
255 # Convert whitespace text to line noise
256 splice @$tree, $i, 0, ['code', $1] if $tree->[$i][1] =~ s/(\s+)$//;
257}
258
259sub _wrap {
260 my ($self, $body, $vars) = @_;
261
262 # Variables
263 my $args = '';
264 if ($self->vars && (my @vars = grep {/^\w+$/} keys %$vars)) {
265 $args = 'my (' . join(',', map {"\$$_"} @vars) . ')';
266 $args .= '= @{shift()}{qw(' . join(' ', @vars) . ')};';
267 }
268
269 # Wrap lines
270 my $num = () = $body =~ /\n/g;
271 my $code = $self->_line(1) . "\npackage @{[$self->namespace]};";
272 $code .= "use Mojo::Base -strict; no warnings 'ambiguous';";
273 $code .= "sub { my \$_O = ''; @{[$self->prepend]};{ $args { $body\n";
274 $code .= $self->_line($num + 1) . "\n;}@{[$self->append]}; } \$_O };";
275
276 warn "-- Code for @{[$self->name]}\n@{[encode 'UTF-8', $code]}\n\n" if DEBUG;
277 return $code;
278}
279
2801;
281
282=encoding utf8
283
284=head1 NAME
285
286Mojo::Template - Perl-ish templates
287
288=head1 SYNOPSIS
289
290 use Mojo::Template;
291
292 # Use Perl modules
293 my $mt = Mojo::Template->new;
294 say $mt->render(<<'EOF');
295 % use Time::Piece;
296 <div>
297 % my $now = localtime;
298 Time: <%= $now->hms %>
299 </div>
300 EOF
301
302 # Render with arguments
303 say $mt->render(<<'EOF', [1 .. 13], 'Hello World!');
304 % my ($numbers, $title) = @_;
305 <div>
306 <h1><%= $title %></h1>
307 % for my $i (@$numbers) {
308 Test <%= $i %>
309 % }
310 </div>
311 EOF
312
313 # Render with named variables
314 say $mt->vars(1)->render(<<'EOF', {title => 'Hello World!'});
315 <div>
316 <h1><%= $title %></h1>
317 %= 5 + 5
318 </div>
319 EOF
320
321=head1 DESCRIPTION
322
323L<Mojo::Template> is a minimalistic, fast, and very Perl-ish template engine,
324designed specifically for all those small tasks that come up during big
325projects. Like preprocessing a configuration file, generating text from heredocs
326and stuff like that.
327
328See L<Mojolicious::Guides::Rendering> for information on how to generate
329content with the L<Mojolicious> renderer.
330
331=head1 SYNTAX
332
333For all templates L<strict>, L<warnings>, L<utf8> and Perl 5.10
334L<features|feature> are automatically enabled.
335
336 <% Perl code %>
337 <%= Perl expression, replaced with result %>
338 <%== Perl expression, replaced with XML escaped result %>
339 <%# Comment, useful for debugging %>
340 <%% Replaced with "<%", useful for generating templates %>
341 % Perl code line, treated as "<% line =%>" (explained later)
342 %= Perl expression line, treated as "<%= line %>"
343 %== Perl expression line, treated as "<%== line %>"
344 %# Comment line, useful for debugging
345 %% Replaced with "%", useful for generating templates
346
347Escaping behavior can be reversed with the L</"auto_escape"> attribute, this is
348the default in L<Mojolicious> C<.ep> templates, for example.
349
350 <%= Perl expression, replaced with XML escaped result %>
351 <%== Perl expression, replaced with result %>
352
353L<Mojo::ByteStream> objects are always excluded from automatic escaping.
354
355 % use Mojo::ByteStream 'b';
356 <%= b('<div>excluded!</div>') %>
357
358Whitespace characters around tags can be trimmed by adding an additional equal
359sign to the end of a tag.
360
361 <% for (1 .. 3) { %>
362 <%= 'Trim all whitespace characters around this expression' =%>
363 <% } %>
364
365Newline characters can be escaped with a backslash.
366
367 This is <%= 1 + 1 %> a\
368 single line
369
370And a backslash in front of a newline character can be escaped with another
371backslash.
372
373 This will <%= 1 + 1 %> result\\
374 in multiple\\
375 lines
376
377A newline character gets appended automatically to every template, unless the
378last character is a backslash. And empty lines at the end of a template are
379ignored.
380
381 There is <%= 1 + 1 %> no newline at the end here\
382
383You can capture whole template blocks for reuse later with the C<begin> and
384C<end> keywords. Just be aware that both keywords are part of the surrounding
385tag and not actual Perl code, so there can only be whitespace after C<begin>
386and before C<end>.
387
388 <% my $block = begin %>
389 <% my $name = shift; =%>
390 Hello <%= $name %>.
391 <% end %>
392 <%= $block->('Baerbel') %>
393 <%= $block->('Wolfgang') %>
394
395Perl lines can also be indented freely.
396
397 % my $block = begin
398 % my $name = shift;
399 Hello <%= $name %>.
400 % end
401 %= $block->('Baerbel')
402 %= $block->('Wolfgang')
403
404L<Mojo::Template> templates get compiled to a Perl subroutine, that means you
405can access arguments simply via C<@_>.
406
407 % my ($foo, $bar) = @_;
408 % my $x = shift;
409 test 123 <%= $foo %>
410
411The compilation of templates to Perl code can make debugging a bit tricky, but
412L<Mojo::Template> will return L<Mojo::Exception> objects that stringify to
413error messages with context.
414
415 Bareword "xx" not allowed while "strict subs" in use at template line 4.
416 2: </head>
417 3: <body>
418 4: % my $i = 2; xx
419 5: %= $i * 2
420 6: </body>
421
422=head1 ATTRIBUTES
423
424L<Mojo::Template> implements the following attributes.
425
426=head2 auto_escape
427
428 my $bool = $mt->auto_escape;
429 $mt = $mt->auto_escape($bool);
430
431Activate automatic escaping.
432
433 # "&lt;html&gt;"
434 Mojo::Template->new(auto_escape => 1)->render("<%= '<html>' %>");
435
436=head2 append
437
438 my $code = $mt->append;
439 $mt = $mt->append('warn "Processed template"');
440
441Append Perl code to compiled template. Note that this code should not contain
442newline characters, or line numbers in error messages might end up being wrong.
443
444=head2 capture_end
445
446 my $end = $mt->capture_end;
447 $mt = $mt->capture_end('end');
448
449Keyword indicating the end of a capture block, defaults to C<end>.
450
451 <% my $block = begin %>
452 Some data!
453 <% end %>
454
455=head2 capture_start
456
457 my $start = $mt->capture_start;
458 $mt = $mt->capture_start('begin');
459
460Keyword indicating the start of a capture block, defaults to C<begin>.
461
462 <% my $block = begin %>
463 Some data!
464 <% end %>
465
466=head2 code
467
468 my $code = $mt->code;
469 $mt = $mt->code($code);
470
471Perl code for template if available.
472
473=head2 comment_mark
474
475 my $mark = $mt->comment_mark;
476 $mt = $mt->comment_mark('#');
477
478Character indicating the start of a comment, defaults to C<#>.
479
480 <%# This is a comment %>
481
482=head2 compiled
483
484 my $compiled = $mt->compiled;
485 $mt = $mt->compiled($compiled);
486
487Compiled template code if available.
488
489=head2 encoding
490
491 my $encoding = $mt->encoding;
492 $mt = $mt->encoding('UTF-8');
493
494Encoding used for template files, defaults to C<UTF-8>.
495
496=head2 escape
497
498 my $cb = $mt->escape;
499 $mt = $mt->escape(sub {...});
500
501A callback used to escape the results of escaped expressions, defaults to
502L<Mojo::Util/"xml_escape">.
503
504 $mt->escape(sub {
505 my $str = shift;
506 return reverse $str;
507 });
508
509=head2 escape_mark
510
511 my $mark = $mt->escape_mark;
512 $mt = $mt->escape_mark('=');
513
514Character indicating the start of an escaped expression, defaults to C<=>.
515
516 <%== $foo %>
517
518=head2 expression_mark
519
520 my $mark = $mt->expression_mark;
521 $mt = $mt->expression_mark('=');
522
523Character indicating the start of an expression, defaults to C<=>.
524
525 <%= $foo %>
526
527=head2 line_start
528
529 my $start = $mt->line_start;
530 $mt = $mt->line_start('%');
531
532Character indicating the start of a code line, defaults to C<%>.
533
534 % $foo = 23;
535
536=head2 name
537
538 my $name = $mt->name;
539 $mt = $mt->name('foo.mt');
540
541Name of template currently being processed, defaults to C<template>. Note that
542this value should not contain quotes or newline characters, or error messages
543might end up being wrong.
544
545=head2 namespace
546
547 my $namespace = $mt->namespace;
548 $mt = $mt->namespace('main');
549
550Namespace used to compile templates, defaults to C<Mojo::Template::SandBox>.
551Note that namespaces should only be shared very carefully between templates,
552since functions and global variables will not be cleared automatically.
553
554=head2 prepend
555
556 my $code = $mt->prepend;
557 $mt = $mt->prepend('my $self = shift;');
558
559Prepend Perl code to compiled template. Note that this code should not contain
560newline characters, or line numbers in error messages might end up being wrong.
561
562=head2 replace_mark
563
564 my $mark = $mt->replace_mark;
565 $mt = $mt->replace_mark('%');
566
567Character used for escaping the start of a tag or line, defaults to C<%>.
568
569 <%% my $foo = 23; %>
570
571=head2 tag_start
572
573 my $start = $mt->tag_start;
574 $mt = $mt->tag_start('<%');
575
576Characters indicating the start of a tag, defaults to C<E<lt>%>.
577
578 <% $foo = 23; %>
579
580=head2 tag_end
581
582 my $end = $mt->tag_end;
583 $mt = $mt->tag_end('%>');
584
585Characters indicating the end of a tag, defaults to C<%E<gt>>.
586
587 <%= $foo %>
588
589=head2 tree
590
591 my $tree = $mt->tree;
592 $mt = $mt->tree([['text', 'foo'], ['line']]);
593
594Template in parsed form if available. Note that this structure should only be
595used very carefully since it is very dynamic.
596
597=head2 trim_mark
598
599 my $mark = $mt->trim_mark;
600 $mt = $mt->trim_mark('-');
601
602Character activating automatic whitespace trimming, defaults to C<=>.
603
604 <%= $foo =%>
605
606=head2 unparsed
607
608 my $unparsed = $mt->unparsed;
609 $mt = $mt->unparsed('<%= 1 + 1 %>');
610
611Raw unparsed template if available.
612
613=head2 vars
614
615 my $bool = $mt->vars;
616 $mt = $mt->vars($bool);
617
618Instead of a list of values, use a hash reference with named variables to pass
619data to templates.
620
621 # "works!"
622 Mojo::Template->new(vars => 1)->render('<%= $test %>!', {test => 'works'});
623
624=head1 METHODS
625
626L<Mojo::Template> inherits all methods from L<Mojo::Base> and implements the
627following new ones.
628
629=head2 parse
630
631 $mt = $mt->parse('<%= 1 + 1 %>');
632
633Parse template into L</"tree">.
634
635=head2 process
636
637 my $output = $mt->process;
638 my $output = $mt->process(@args);
639 my $output = $mt->process({foo => 'bar'});
640
641Process previously parsed template and return the result, or a
642L<Mojo::Exception> object if rendering failed.
643
644 # Parse and process
645 say Mojo::Template->new->parse('Hello <%= $_[0] %>')->process('Bender');
646
647 # Reuse template (for much better performance)
648 my $mt = Mojo::Template->new;
649 say $mt->render('Hello <%= $_[0] %>!', 'Bender');
650 say $mt->process('Fry');
651 say $mt->process('Leela');
652
653=head2 render
654
655 my $output = $mt->render('<%= 1 + 1 %>');
656 my $output = $mt->render('<%= shift() + shift() %>', @args);
657 my $output = $mt->render('<%= $foo %>', {foo => 'bar'});
658
659Render template and return the result, or a L<Mojo::Exception> object if
660rendering failed.
661
662 # Longer version
663 my $output = $mt->parse('<%= 1 + 1 %>')->process;
664
665 # Render with arguments
666 say Mojo::Template->new->render('<%= $_[0] %>', 'bar');
667
668 # Render with named variables
669 say Mojo::Template->new(vars => 1)->render('<%= $foo %>', {foo => 'bar'});
670
671=head2 render_file
672
673 my $output = $mt->render_file('/tmp/foo.mt');
674 my $output = $mt->render_file('/tmp/foo.mt', @args);
675 my $output = $mt->render_file('/tmp/bar.mt', {foo => 'bar'});
676
677Same as L</"render">, but renders a template file.
678
679=head1 DEBUGGING
680
681You can set the C<MOJO_TEMPLATE_DEBUG> environment variable to get some
682advanced diagnostics information printed to C<STDERR>.
683
684 MOJO_TEMPLATE_DEBUG=1
685
686=head1 SEE ALSO
687
688L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
689
690=cut
Note: See TracBrowser for help on using the repository browser.