home *** CD-ROM | disk | FTP | other *** search
/ CLIX - Fazer Clix Custa Nix / CLIX-CD.cdr / mac / lib / HTML / Formatter.pm < prev    next >
Text File  |  1997-08-18  |  8KB  |  533 lines

  1. package HTML::Formatter;
  2.  
  3. # $Id: Formatter.pm,v 1.15 1997/07/03 06:50:11 aas Exp $
  4.  
  5. =head1 NAME
  6.  
  7. HTML::Formatter - Base class for HTML formatters
  8.  
  9. =head1 SYNOPSIS
  10.  
  11.  package HTML::FormatXX;
  12.  require HTML::Formatter;
  13.  @ISA=qw(HTML::Formatter);
  14.  
  15. =head1 DESCRIPTION
  16.  
  17. HTML formatters are able to format a HTML syntax tree into various
  18. printable formats.  Different formatters produce output for different
  19. output media.  Common for all formatters are that they will return the
  20. formatted output when the format() method is called.  Format() takes a
  21. HTML::Element as parameter.
  22.  
  23. =head1 SEE ALSO
  24.  
  25. L<HTML::FormatText>, L<HTML::FormatPS>, L<HTML::Element>
  26.  
  27. =head1 COPYRIGHT
  28.  
  29. Copyright (c) 1995 Gisle Aas. All rights reserved.
  30.  
  31. This library is free software; you can redistribute it and/or
  32. modify it under the same terms as Perl itself.
  33.  
  34. =head1 AUTHOR
  35.  
  36. Gisle Aas <aas@oslonett.no>
  37.  
  38. =cut
  39.  
  40.  
  41. require HTML::Element;
  42.  
  43. use strict;
  44. use Carp;
  45.  
  46. sub new
  47. {
  48.     my $class = shift;
  49.     bless { }, $class;
  50. }
  51.  
  52. sub format
  53. {
  54.     my($self, $html) = @_;
  55.     $self->begin();
  56.     $html->traverse(
  57.     sub {
  58.         my($node, $start, $depth) = @_;
  59.         if (ref $node) {
  60.         my $tag = $node->tag;
  61.         my $func = $tag . '_' . ($start ? "start" : "end");
  62.         # We protect the call by eval, so we can recover if
  63.         # a handler is not defined for the tag.
  64.         my $retval = eval { $self->$func($node); };
  65.         return $@ ? 1 : $retval;
  66.         } else {
  67.         $self->textflow($node);
  68.         }
  69.         1;
  70.     }
  71.      );
  72.     $self->end();
  73.     join('', @{$self->{output}});
  74. }
  75.  
  76. sub begin
  77. {
  78.     my $self = shift;
  79.  
  80.     # Flags
  81.     $self->{anchor}    = 0;
  82.     $self->{underline} = 0;
  83.     $self->{bold}      = 0;
  84.     $self->{italic}    = 0;
  85.     $self->{center}    = 0;
  86.     $self->{nobr}      = 0;
  87.  
  88.     $self->{font_size}     = [3];   # last element is current size
  89.     $self->{basefont_size} = [3];
  90.  
  91.     $self->{makers} = [];           # last element is current marker
  92.     $self->{vspace} = undef;        # vertical space
  93.     $self->{eat_leading_space} = 0;
  94.  
  95.     $self->{output} = [];
  96. }
  97.  
  98. sub end
  99. {
  100. }
  101.  
  102. sub html_start { 1; }  sub html_end {}
  103. sub head_start { 0; }
  104. sub body_start { 1; }  sub body_end {}
  105.  
  106. sub header_start
  107. {
  108.     my($self, $level, $node) = @_;
  109.     my $align = $node->attr('align');
  110.     if (defined($align) && lc($align) eq 'center') {
  111.     $self->{center}++;
  112.     }
  113.     1,
  114. }
  115.  
  116. sub header_end
  117. {
  118.     my($self, $level, $node) = @_;
  119.     my $align = $node->attr('align');
  120.     if (defined($align) && lc($align) eq 'center') {
  121.     $self->{center}--;
  122.     }
  123. }
  124.  
  125. sub h1_start { shift->header_start(1, @_) }
  126. sub h2_start { shift->header_start(2, @_) }
  127. sub h3_start { shift->header_start(3, @_) }
  128. sub h4_start { shift->header_start(4, @_) }
  129. sub h5_start { shift->header_start(5, @_) }
  130. sub h6_start { shift->header_start(6, @_) }
  131.  
  132. sub h1_end   { shift->header_end(1, @_) }
  133. sub h2_end   { shift->header_end(2, @_) }
  134. sub h3_end   { shift->header_end(3, @_) }
  135. sub h4_end   { shift->header_end(4, @_) }
  136. sub h5_end   { shift->header_end(5, @_) }
  137. sub h6_end   { shift->header_end(6, @_) }
  138.  
  139. sub br_start
  140. {
  141.     my $self = shift;
  142.     $self->vspace(0);
  143.     $self->eat_leading_space;
  144.  
  145. }
  146.  
  147. sub hr_start
  148. {
  149.     my $self = shift;
  150.     $self->vspace(1);
  151.     $self->eat_leading_space;
  152. }
  153.  
  154. sub img_start
  155. {
  156.     shift->out(shift->attr('alt') || "[IMAGE]");
  157. }
  158.  
  159. sub a_start
  160. {
  161.     shift->{anchor}++;
  162.     1;
  163. }
  164.  
  165. sub a_end
  166. {
  167.     shift->{anchor}--;
  168. }
  169.  
  170. sub u_start
  171. {
  172.     shift->{underline}++;
  173.     1;
  174. }
  175.  
  176. sub u_end
  177. {
  178.     shift->{underline}--;
  179. }
  180.  
  181. sub b_start
  182. {
  183.     shift->{bold}++;
  184.     1;
  185. }
  186.  
  187. sub b_end
  188. {
  189.     shift->{bold}--;
  190. }
  191.  
  192. sub tt_start
  193. {
  194.     shift->{teletype}++;
  195.     1;
  196. }
  197.  
  198. sub tt_end
  199. {
  200.     shift->{teletype}--;
  201. }
  202.  
  203. sub i_start
  204. {
  205.     shift->{italic}++;
  206.     1;
  207. }
  208.  
  209. sub i_end
  210. {
  211.     shift->{italic}--;
  212. }
  213.  
  214. sub center_start
  215. {
  216.     shift->{center}++;
  217.     1;
  218. }
  219.  
  220. sub center_end
  221. {
  222.     shift->{center}--;
  223. }
  224.  
  225. sub nobr_start
  226. {
  227.     shift->{nobr}++;
  228.     1;
  229. }
  230.  
  231. sub nobr_end
  232. {
  233.     shift->{nobr}--;
  234. }
  235.  
  236. sub wbr_start
  237. {
  238.     1;
  239. }
  240.  
  241. sub font_start
  242. {
  243.     my($self, $elem) = @_;
  244.     my $size = $elem->attr('size');
  245.     return 1 unless defined $size;
  246.     if ($size =~ /^\s*[+\-]/) {
  247.     my $base = $self->{basefont_size}[-1];
  248.     $size = $base + $size;
  249.     }
  250.     push(@{$self->{font_size}}, $size);
  251.     1;
  252. }
  253.  
  254. sub font_end
  255. {
  256.     my($self, $elem) = @_;
  257.     my $size = $elem->attr('size');
  258.     return unless defined $size;
  259.     pop(@{$self->{font_size}});
  260. }
  261.  
  262. sub basefont_start
  263. {
  264.     my($self, $elem) = @_;
  265.     my $size = $elem->attr('size');
  266.     return unless defined $size;
  267.     push(@{$self->{basefont_size}}, $size);
  268.     1;
  269. }
  270.  
  271. sub basefont_end
  272. {
  273.     my($self, $elem) = @_;
  274.     my $size = $elem->attr('size');
  275.     return unless defined $size;
  276.     pop(@{$self->{basefont_size}});
  277. }
  278.  
  279. # Aliases for logical markup
  280. BEGIN {
  281.     *cite_start   = \&i_start;
  282.     *cite_end     = \&i_end;
  283.     *code_start   = \&tt_start;
  284.     *code_end     = \&tt_end;
  285.     *em_start     = \&i_start;
  286.     *em_end       = \&i_end;
  287.     *kbd_start    = \&tt_start;
  288.     *kbd_end      = \&tt_end;
  289.     *samp_start   = \&tt_start;
  290.     *samp_end     = \&tt_end;
  291.     *strong_start = \&b_start;
  292.     *strong_end   = \&b_end;
  293.     *var_start    = \&tt_start;
  294.     *var_end      = \&tt_end;
  295. }
  296.  
  297. sub p_start
  298. {
  299.     my $self = shift;
  300.     $self->vspace(1);
  301.     $self->eat_leading_space;
  302.     1;
  303. }
  304.  
  305. sub p_end
  306. {
  307.     shift->vspace(1);
  308. }
  309.  
  310. sub pre_start
  311. {
  312.     my $self = shift;
  313.     $self->{pre}++;
  314.     $self->vspace(1);
  315.     1;
  316. }
  317.  
  318. sub pre_end
  319. {
  320.     my $self = shift;
  321.     $self->{pre}--;
  322.     $self->vspace(1);
  323. }
  324.  
  325. BEGIN {
  326.     *listing_start = \&pre_start;
  327.     *listing_end   = \&pre_end;
  328.     *xmp_start     = \&pre_start;
  329.     *xmp_end       = \&pre_end;
  330. }
  331.  
  332. sub blockquote_start
  333. {
  334.     my $self = shift;
  335.     $self->vspace(1);
  336.     $self->eat_leading_space;
  337.     $self->adjust_lm( +2 );
  338.     $self->adjust_rm( -2 );
  339.     1;
  340. }
  341.  
  342. sub blockquote_end
  343. {
  344.     my $self = shift;
  345.     $self->vspace(1);
  346.     $self->adjust_lm( -2 );
  347.     $self->adjust_rm( +2 );
  348. }
  349.  
  350. sub address_start
  351. {
  352.     my $self = shift;
  353.     $self->vspace(1);
  354.     $self->eat_leading_space;
  355.     $self->i_start(@_);
  356.     1;
  357. }
  358.  
  359. sub address_end
  360. {
  361.     my $self = shift;
  362.     $self->i_end(@_);
  363.     $self->vspace(1);
  364. }
  365.  
  366. # Handling of list elements
  367.  
  368. sub ul_start
  369. {
  370.     my $self = shift;
  371.     $self->vspace(1);
  372.     push(@{$self->{markers}}, "*");
  373.     $self->adjust_lm( +2 );
  374.     1;
  375. }
  376.  
  377. sub ul_end
  378. {
  379.     my $self = shift;
  380.     pop(@{$self->{markers}});
  381.     $self->adjust_lm( -2 );
  382.     $self->vspace(1);
  383. }
  384.  
  385. sub li_start
  386. {
  387.     my $self = shift;
  388.     $self->bullet($self->{markers}[-1]);
  389.     $self->adjust_lm(+2);
  390.     $self->eat_leading_space;
  391.     1;
  392. }
  393.  
  394. sub bullet
  395. {
  396.     shift->out(@_);
  397. }
  398.  
  399. sub li_end
  400. {
  401.     my $self = shift;
  402.     $self->vspace(1);
  403.     $self->adjust_lm( -2);
  404.     my $markers = $self->{markers};
  405.     if ($markers->[-1] =~ /^\d+/) {
  406.     # increment ordered markers
  407.     $markers->[-1]++;
  408.     }
  409. }
  410.  
  411. BEGIN {
  412.     *menu_start = \&ul_start;
  413.     *menu_end   = \&ul_end;
  414.     *dir_start  = \&ul_start;
  415.     *dir_end    = \&ul_end;
  416. }
  417.  
  418. sub ol_start
  419. {
  420.     my $self = shift;
  421.  
  422.     $self->vspace(1);
  423.     push(@{$self->{markers}}, 1);
  424.     $self->adjust_lm(+2);
  425.     1;
  426. }
  427.  
  428. sub ol_end
  429. {
  430.     my $self = shift;
  431.     $self->adjust_lm(-2);
  432.     pop(@{$self->{markers}});
  433.     $self->vspace(1);
  434. }
  435.  
  436.  
  437. sub dl_start
  438. {
  439.     my $self = shift;
  440.     $self->adjust_lm(+2);
  441.     $self->vspace(1);
  442.     1;
  443. }
  444.  
  445. sub dl_end
  446. {
  447.     my $self = shift;
  448.     $self->adjust_lm(-2);
  449.     $self->vspace(1);
  450. }
  451.  
  452. sub dt_start
  453. {
  454.     my $self = shift;
  455.     $self->vspace(1);
  456.     $self->eat_leading_space;
  457.     1;
  458. }
  459.  
  460. sub dt_end
  461. {
  462. }
  463.  
  464. sub dd_start
  465. {
  466.     my $self = shift;
  467.     $self->adjust_lm(+6);
  468.     $self->vspace(0);
  469.     $self->eat_leading_space;
  470.     1;
  471. }
  472.  
  473. sub dd_end
  474. {
  475.     shift->adjust_lm(-6);
  476. }
  477.  
  478.  
  479. # Things not formated at all
  480. sub table_start { shift->out('[TABLE NOT SHOWN]'); 0; }
  481. sub form_start  { shift->out('[FORM NOT SHOWN]');  0; }
  482.  
  483.  
  484.  
  485. sub textflow
  486. {
  487.     my $self = shift;
  488.     if ($self->{pre}) {
  489.     $self->pre_out($_[0]);
  490.     } else {
  491.     for (split(/(\s+)/, $_[0])) {
  492.         next unless length $_;
  493.         if ($self->{eat_leading_space}) {
  494.         $self->{eat_leading_space} = 0;
  495.         next if /^\s/;
  496.         }
  497.         $self->out($_);
  498.     }
  499.     }
  500. }
  501.  
  502.  
  503.  
  504. sub eat_leading_space
  505. {
  506.     shift->{eat_leading_space} = 1;
  507. }
  508.  
  509.  
  510. sub vspace
  511. {
  512.     my($self, $new) = @_;
  513.     return if defined $self->{vspace} and $self->{vspace} > $new;
  514.     $self->{vspace} = $new;
  515. }
  516.  
  517. sub collect
  518. {
  519.     push(@{shift->{output}}, @_);
  520. }
  521.  
  522. sub out
  523. {
  524.     confess "Must be overridden my subclass";
  525. }
  526.  
  527. sub pre_out
  528. {
  529.     confess "Must be overridden my subclass";
  530. }
  531.  
  532. 1;
  533.