File Coverage

blib/lib/RTF/Control.pm
Criterion Covered Total %
statement 298 444 67.1
branch 50 128 39.0
condition 10 29 34.4
subroutine 69 83 83.1
pod 16 30 53.3
total 443 714 62.0


line stmt bran cond sub pod time code
1             package RTF::Control;
2             $RTF::Control::VERSION = '1.12';
3 8     8   2340 use strict;
  8         15  
  8         248  
4 8     8   39 use warnings;
  8         15  
  8         387  
5              
6             # Sonovision-Itep, Philippe Verdret 1998-1999
7             # TPF - Pete Sergeant 2003 - 2004
8              
9             =head1 NAME
10              
11             RTF::Control - Application of RTF::Parser for document conversion
12              
13             =head1 VERSION
14              
15             version 1.12
16              
17             =head1 DESCRIPTION
18              
19             Application of RTF::Parser for document conversion
20              
21             =head1 OVERVIEW
22              
23             L is a sublass of L. L can be seen as
24             a helper module for people wanting to write their own document convertors -
25             L and L both subclass it.
26              
27             I am the new maintainer of this module. My aim is to keep the interface
28             identical to the old interface while cleaning up, documenting, and testing
29             the internals. There are things in the interface I'm unhappy with, and things
30             I like - however, I'm maintaining rather than developing the module, so, the
31             interface is mostly frozen.
32              
33             =head1 HOW IT ALL WORKS
34              
35             For starters, go and look at the source of M
36              
37              
38             Except for B, the following is a list of variables
39             exported by RTF::Control that you're expected to tinker with in your
40             own subclass.
41              
42             =head2 RTF::Parser subs
43              
44             If you read the docs of RTF::Parser you'll see that you can redefine some
45             subs there - RTF::Control has its own definitions for all of these, but you
46             might want to over-ride C, C, and C. We'll look
47             at what the defaults of each of these do, and what you need to do if you
48             want to override any of them a little further down.
49              
50             =head2 %symbol
51              
52             This hash is actually merged into %do_on_control, with the value wrapped in
53             a subroutine that effectively says C. You can put any control
54             words that should map directly to a certain output in here - C<\tab>, for
55             example could be C<$symbol{'tab'} = "\t">.
56              
57             =head2 %info
58              
59             This hash gets filled with document meta-data, as per the RTF specification.
60              
61             =head2 %par_props
62              
63             Not really sure, but paragraph properties
64              
65             =head2 %do_on_event %do_on_control
66              
67             %do_on_control tells us what to do when we meet a specific control word.
68             The values are coderefs. %do_on_event also holds coderefs, but these are
69             more abstract things to do, say when the stylesheet changes. %do_on_event
70             thingies tend to be called by %do_on_control thingies, as far as I can tell.
71              
72             =head2 $style $newstyle
73              
74             Style is the current style, $newstyle is the one we're about to
75             change to if we're about to change...
76              
77             =head2 $event
78              
79             Current event
80              
81             =head2 $text
82              
83             Pending text
84              
85             =cut
86              
87             # Define all our dependencies and other fluff
88              
89 8     8   4879 use RTF::Parser;
  8         21  
  8         330  
90 8     8   47 use RTF::Config;
  8         15  
  8         657  
91 8     8   4600 use RTF::Charsets; # define names of chars
  8         21  
  8         1006  
92              
93 8     8   48 use File::Basename;
  8         14  
  8         916  
94 8     8   41 use Exporter;
  8         13  
  8         509  
95              
96             # I'm an RTF::Parser! I'm an Exporter! I'm a class!
97             # "When I grow up, I'm going to bovine university!"
98              
99             @RTF::Control::ISA = qw(Exporter RTF::Parser);
100              
101             # Define the symbols we'll be exporting - these are
102             # documented in the API part of the POD and a little
103             # further down
104              
105 8         927 use vars qw(
106              
107             %symbol %info %par_props %do_on_event %do_on_control
108             $style $newstyle $event $text
109              
110 8     8   41 );
  8         10  
111              
112             # There are used to that we have named arguments for callbacks
113             # I don't like this, but hey, I didn't design it, and I'm meant
114             # to be maintaining the interface :-)
115              
116 8     8   66 use constant SELF => 0; # rtf processor instance
  8         14  
  8         510  
117 8     8   34 use constant CONTROL => 1; # control word
  8         16  
  8         3371  
118 8     8   39 use constant ARG => 2; # associated argument
  8         13  
  8         344  
119 8     8   98 use constant EVENT => 3; # start/end event
  8         14  
  8         563  
120 8     8   39 use constant TOP => -1; # access to the TOP element of a stack
  8         13  
  8         2956  
121              
122             # Actually export stuff...
123              
124             @RTF::Control::EXPORT = qw(
125              
126             output
127              
128             %symbol
129             %info
130             %do_on_event
131             %do_on_control
132             %par_props
133              
134             $style
135             $newstyle
136             $event
137             $text
138              
139             SELF
140             CONTROL
141             ARG
142             EVENT
143             TOP
144              
145             );
146              
147             # Flags to specify where we are... Because this is all undocumented
148             # I feel justified putting these into a hash at some point in the
149             # near future... They also shouldn't be package variables, they
150             # should be class variables, but, to be honest, trying to rid this
151             # module of package variables seems an exercise in futility if I'm
152             # actually trying to maintain the interface... I could internalise
153             # everything that isn't part of the API though
154              
155             my $IN_STYLESHEET = 0; # inside or outside style table
156             my $IN_FONTTBL = 0; # inside or outside font table
157             my $IN_TABLE = 0;
158              
159             # Declare where we're going to be holding meta-data etc
160             my %fonttbl;
161             my %stylesheet;
162             my %colortbl;
163              
164             # Property stacks
165             my @par_props_stack = (); # stack of paragraph properties
166             my @char_props_stack = (); # stack of character properties
167             my @control = (); # stack of control instructions, rename control_stack
168              
169             # Some other stuff
170             my $stylename = '';
171             my $cstylename = ''; # previous encountered style
172             my $cli = 0; # current line indent value
173             my $styledef = '';
174              
175             =head2 new
176              
177             Returns an RTF::Control object. RTF::Control is a subclass of RTF::Parser.
178             Internally, we call RTF::Parser's new() method, and then we call an internal
179             method called _configure(), which takes care of options we were passed.
180              
181             ADD STUFF ON -output AND -confdir
182              
183             =cut
184              
185             sub new {
186              
187 9     9 1 196 my $proto = shift;
188 9   33     86 my $class = ref($proto) || $proto;
189              
190 9         110 my $self = $class->SUPER::new(@_);
191              
192 9         76 $self->_configure(@_);
193              
194 9         36 return $self;
195              
196             }
197              
198             # This is a private method. It accepts a hash (well, a list)
199             # of values, and stores them. If one of them is 'output',
200             # it calls a function I'm yet to examine. This was done
201             # in a horrendous way - it's now a lot tidier. :-)
202              
203             sub _configure {
204              
205 14     14   3462 my $self = shift;
206              
207 14         52 my %options = @_;
208              
209             # Sanitize the options
210 14         25 my %clean_options;
211 14         46 for my $key ( keys %options ) {
212              
213 12         25 my $oldkey = $key;
214              
215 12         42 $key =~ s/^-//;
216 12         35 $key = lc($key);
217              
218 12         48 $clean_options{$key} = $options{$oldkey}
219              
220             }
221              
222 14         108 $self->{'_RTF_Control_Options'} = \%clean_options;
223              
224 14 100       111 $self->set_top_output_to( $clean_options{'output'} )
225             if $clean_options{'output'};
226              
227 14         55 return $self;
228              
229             }
230              
231 8     8   43 use constant APPLICATION_DIR => 0;
  8         14  
  8         3367  
232              
233             =head2 application_dir
234              
235             I'm leaving this method in because removing it will cause a backward-compatability
236             nightmare. This method returns the ( wait for it ) path that the .pm file corresponding
237             to the class that the object is contained, without a trailing semi-colon. Obviously
238             this is nasty in several ways. If you've set C<-confdir> in C that will be
239             returned instead. You should definitely take that route if you're on an OS on which
240             Perl can't use / as a directory seperator.
241              
242             =cut
243              
244             sub application_dir {
245              
246             # Grab our object
247 1     1 1 5 my $self = shift;
248              
249             # Return -confdir if set
250 1 50       11 return $self->{'_RTF_Control_Options'}->{'confdir'}
251             if $self->{'_RTF_Control_Options'}->{'confdir'};
252              
253             # Grab the class name
254 0         0 my $class = ref $self;
255              
256             # Clean it up and look it up in %INC
257 0         0 $class =~ s|::|/|g;
258 0         0 $class = $INC{"$class.pm"};
259              
260 0         0 return dirname $class;
261              
262             }
263              
264             =head2 charmap_reader
265              
266             This nicely abstracts away using application_dir and so on. It's a method
267             call. It'll take the name of the class, and an argument for the module/file
268             it's looking for. This is likely to be 'ansi' or 'charmap'. This argument,
269             for historical reasons (ho ho ho) will have any _'s removed in the check for
270             a module name ... C< $self->charmap_reader('char_map') > will thus look for, for
271             example, C< RTF::TEXT::charmap > to load. It'll return the data in the file as
272             an array of lines. This description sucks.
273              
274             =cut
275              
276             sub charmap_reader {
277              
278 7     7 1 3483 my $self = shift;
279 7         10 my $file = shift;
280              
281 7         11 my @char_map_data;
282              
283             # Try and work out what our character set module would be called...
284 7         9 my $module_file = $file;
285 7         20 $module_file =~ s/_//g;
286 7         95 my $module_name = ref($self) . '::' . $module_file;
287              
288             # Can we load it?
289 7     31   548 eval "use $module_name";
  31     7   110  
  7     10   14  
  7     5   86  
  4         13  
  4         10  
  4         32  
  7         25  
  4         8  
  4         24  
  4         25  
  4         8  
  4         21  
290              
291             # That would be a no...
292 7 50       24 if ($@) {
293              
294             # Create a path for the charset file using the old method...
295 0         0 my $charset_file = $_[SELF]->application_dir(__FILE__) . "/$file";
296              
297             # Try and open it...
298 0 0       0 open( CHAR_MAP, "< $charset_file" ) or
299             die "Unable to open the charset file '$charset_file': $!";
300              
301             # Read in the data...
302 0         0 @char_map_data = ();
303              
304             # Why yes, yes we can...
305             } else {
306              
307 7         18 my $sub_name = $module_name . '::' . 'data';
308 7         136 @char_map_data = main->$sub_name();
309              
310             }
311              
312 7         171 return @char_map_data;
313              
314             }
315              
316             ###########################################################################
317              
318             # This stuff is all to do with the stack, and I'm not really sure how it
319             # works. I'm hoping it'll become more obvious as I go. The routines themselves
320             # are now all documented, but who knows what the stack is or why? hrm?
321              
322             # This hurts my little brane.
323              
324             # Holds the output stack
325             my @output_stack;
326              
327             # Defines how large the output stack can be
328 8     8   41 use constant MAX_OUTPUT_STACK_SIZE => 0; # PV: 8 seems a good value
  8         17  
  8         2202  
329             # PS: Then why not default it to that?
330              
331             =head1 Stack manipulation
332              
333             =head2 dump_stack
334              
335             Serializes and prints the stack to STDERR
336              
337             =cut
338              
339             # Serializes the stack, and prints it to STDERR.
340             sub dump_stack {
341              
342 0     0 1 0 my $stack_size = @output_stack;
343              
344 0         0 print STDERR "Stack size: $stack_size\n";
345              
346 0         0 print STDERR $stack_size-- . " |$_|\n" for reverse @output_stack;
347              
348             }
349              
350             =head2 output
351              
352             Holder routine for the current thing to do with output text we're given.
353             It starts off as the same as C<$string_output_sub>, which adds the string
354             to the element at the C of the output stack. However, the idea, I
355             believe, is to allow that to be changed at will, using C.
356              
357             =cut
358              
359             sub output {
360              
361 52 50   52 1 229 $output_stack[TOP] .= $_[0] if defined $_[0];
362              
363             }
364              
365             # I'm guessing (because I'm generous ;-) that this is done because
366             # subclasses might want to modifiy the values of these. These are
367             # obviously the two different ways to spit out ... something. We
368             # start with the string_output_sub being what &output does tho.
369              
370             my $nul_output_sub = sub {
371             #print STDERR "** $_[0] **\n";
372             };
373              
374             my $string_output_sub = sub {
375              
376 9 100   3   30 $output_stack[TOP] .= $_[0] if $_[0];
377              
378             };
379              
380             =head2 push_output
381              
382             Adds a blank element to the end of the stack. It will change (or
383             maintain) the function of C to be C<$string_output_sub>,
384             unless you pass it the argument C< 'nul' >, in which case it will
385             set C to be C<$nul_output_sub>.
386              
387             =cut
388              
389             sub push_output {
390              
391             # If we've set a maximum output stack and then exceeded it, complain.
392 55     55 1 61 if (MAX_OUTPUT_STACK_SIZE) {
393             die "max size of output stack exceeded"
394             if @output_stack == MAX_OUTPUT_STACK_SIZE;
395             }
396              
397             # If we didn't get an argument, output becomes string...
398 55 100       182 unless ( defined( $_[0] ) ) {
    50          
399              
400 8     8   42 no warnings 'redefine';
  8         12  
  8         521  
401 49         98 *output = $string_output_sub;
402              
403             # If we were given 'nul', set output to $nul_output_sub
404             } elsif ( $_[0] eq 'nul' ) {
405              
406 8     8   37 no warnings 'redefine';
  8         12  
  8         898  
407 6         28 *output = $nul_output_sub;
408              
409             }
410              
411             # Add an empty element to the end of the ouput stack
412 55         116 push @output_stack, '';
413              
414             }
415              
416             =head2 pop_output
417              
418             Removes and returns the last element of the ouput stack
419              
420             =cut
421              
422             # Remove and return an element of the output stack, which
423             # should basically be the in-scope text... See how &do_on_info
424             # uses this
425              
426             sub pop_output {
427              
428 34     34 1 105 pop @output_stack;
429              
430             }
431              
432 8     8   41 use constant SET_TOP_OUTPUT_TO_TRACE => 0;
  8         15  
  8         1144  
433              
434             =head2 set_top_output_to
435              
436             Only called at init time, is a method call not a function.
437             Sets the action of C, depending on whether
438             you pass it a filehandle or string reference.
439              
440             =cut
441              
442             # Sets flush_top_output to print to the appropriate thingy
443             sub set_top_output_to {
444              
445 6     6 1 13 my $self = shift;
446              
447             # Are we being passed a filehandle?
448              
449 6         20 local *X = $_[0];
450              
451 6 100       36 if ( fileno X ) {
    50          
452              
453 2         4 my $stream = *X;
454              
455             # Debugging info if asked for
456 2         3 print STDERR "stream: ", fileno X, "\n" if SET_TOP_OUTPUT_TO_TRACE;
457              
458             # Turn off warnings
459 8     8   38 no warnings 'redefine';
  8         14  
  8         1120  
460              
461             # Overwrite &flush_top_output
462             *flush_top_output = sub {
463 0     0   0 print $stream $output_stack[TOP];
464 0         0 $output_stack[TOP] = '';
465 2         27 };
466              
467             # We've been passed a reference to a scalar...
468              
469             } elsif ( ref $_[0] eq 'SCALAR' ) {
470              
471 4         8 print STDERR "output to string\n" if SET_TOP_OUTPUT_TO_TRACE;
472              
473 4         7 my $content_ref = $_[0];
474              
475 8     8   40 no warnings 'redefine';
  8         21  
  8         1332  
476              
477             *flush_top_output = sub {
478 25     25   44 $$content_ref .= $output_stack[TOP];
479 25         47 $output_stack[TOP] = '';
480 4         56 };
481              
482             # Someone's done something weird
483              
484             } else {
485              
486 0         0 warn "unknown output specification: $_[0]\n";
487              
488             }
489              
490             }
491              
492             # the default prints on the selected output filehandle
493              
494             =head2 flush_top_output
495              
496             Output the top element of the stack in the way specified by the call
497             to C
498              
499             =cut
500              
501             sub flush_top_output {
502              
503 0     0 1 0 print $output_stack[TOP];
504 0         0 $output_stack[TOP] = '';
505              
506             }
507              
508             ###########################################################################
509             # Trace management
510 8     8   46 use constant RTF_DEBUG => 0;
  8         20  
  8         367  
511 8     8   36 use constant TRACE => 0;
  8         16  
  8         356  
512 8     8   38 use constant STACK_TRACE => 0; #
  8         12  
  8         308  
513 8         343 use constant STYLESHEET_TRACE =>
514 8     8   33 0; # If you want to see the stylesheet of the document
  8         19  
515 8     8   37 use constant STYLE_TRACE => 0; #
  8         12  
  8         322  
516 8     8   38 use constant LIST_TRACE => 0;
  8         12  
  8         1523  
517              
518             $| = 1 if TRACE or STACK_TRACE or RTF_DEBUG;
519              
520             # Debugging function - prints the number of _'s matching
521             # the number of controls in our current control stack,
522             # and anything else we were passed, and the $. - input
523             # line number.
524              
525             sub trace {
526             #my(@caller) = (caller(1));
527             #my $sub = (@caller)[3];
528             #$sub =~ s/.*:://;
529             #$sub = sprintf "%-12s", $sub;
530 0 0   0 0 0 shift if ref $_[0];
531 0         0 print STDERR "[$.]", ( '_' x $#control . "@_\n" );
532             }
533             $SIG{__DIE__} = sub {
534             require Carp;
535             Carp::confess;
536             }
537             if RTF_DEBUG;
538              
539             ###########################################################################
540             # Some generic routines
541 8     8   40 use constant DISCARD_CONTENT => 0;
  8         12  
  8         7527  
542              
543             # This seems to be what we do when we hit a control word
544             # we're not going to parse. He seems to be manually
545             # implementing this some times - I wonder why?
546              
547             sub discard_content {
548              
549             # Read in information about the control word we hit
550 0     0 0 0 my ( $control, $arg, $cevent ) = ( $_[CONTROL], $_[ARG], $_[EVENT] );
551              
552 0         0 trace "($_[CONTROL], $_[ARG], $_[EVENT])" if DISCARD_CONTENT;
553              
554             # This I don't understand. Presumably if we've hit 0, then it's
555             # the close of a part of the document being dictated by a char
556             # property, like, say, \b1I'm bold\b0 I'm not.
557              
558 0 0 0     0 if ( defined $arg && $_[ARG] eq "0" ) {
    0 0        
    0          
    0          
559              
560             # Remove the last element on the output stack
561 0         0 pop_output();
562              
563             # Set the property as on(?) on the control stack
564             # This should probably be a 0. Something to test
565             # later.
566 0         0 $control[TOP]->{"$_[CONTROL]1"} = 1;
567              
568             # Add a blank element to the end of the output stack
569             } elsif ( $_[EVENT] eq 'start' ) {
570              
571 0         0 push_output();
572 0         0 $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
573              
574             } elsif ( defined $arg && $_[ARG] eq "1" ) {
575 0         0 $cevent = 'start';
576 0         0 push_output();
577              
578             } elsif ( $_[EVENT] eq 'end' ) { # End of discard
579              
580 0         0 my $string = pop_output();
581              
582 0 0       0 if ( length $string > 30 ) {
583 0         0 $string =~ s/(.{1,10}).*(.{1,10})/$1 ... $2/;
584             }
585              
586 0         0 trace "discard content of \\$control: $string" if DISCARD_CONTENT;
587              
588             } else {
589              
590 0         0 die "($_[CONTROL], $_[ARG], $_[EVENT])" if DISCARD_CONTENT;
591              
592             }
593              
594             }
595              
596             # Document meta-data collator. Whenever we hit an info group,
597             # this sub is called. All it does is put all the text 'in-scope'
598             # into the %info hash...
599              
600             sub do_on_info {
601              
602 0     0 0 0 my $string;
603 0   0     0 my $arg = $_[ARG] || '';
604              
605 0 0       0 if ( $_[EVENT] eq 'start' ) {
606              
607 0         0 push_output();
608 0         0 $control[TOP]->{"$_[CONTROL]$arg"} = 1;
609              
610             } else {
611              
612 0         0 $string = pop_output();
613 0         0 $info{"$_[CONTROL]$arg"} = $string;
614              
615             }
616             }
617              
618             # SYMBOLS
619             # default mapping for symbols
620             # char processed by the parser symbol() callback: - _ ~ : | { } * ' \\
621             %symbol = qw(
622             | |
623             _ _
624             : :
625             bullet *
626             endash -
627             emdash --
628             ldblquote ``
629             rdblquote ''
630             );
631             $symbol{rquote} = "\'";
632             $symbol{lquote} = "\`";
633             $symbol{'column'} = "\t";
634             $symbol{'tab'} = "\t";
635             $symbol{'line'} = "\n";
636             $symbol{'page'} = "\f";
637              
638             # Handler for symbols - prints the symbol corresponding
639             # to our first argument...
640              
641             sub do_on_symbol {
642              
643 0     0 0 0 output $symbol{ $_[CONTROL] };
644              
645             }
646              
647             my %symbol_ctrl = map { # install the do_on_symbol() routine
648             if (/^[a-z]+$/) {
649             $_ => \&do_on_symbol;
650             } else {
651             'undef' => undef;
652             }
653             } keys %symbol;
654              
655             ###########################################################################################
656             my %char_props; # control hash must be declarated before install_callback()
657             # purpose: associate callbacks to controls
658             # 1. an hash name that contains the controls
659             # 2. a callback name
660              
661             # Sets the call back given as the second argument
662             # as the %do_on_control for all controls currently
663             # in %char_props. DON'T UNDERSTAND.
664              
665             sub install_callback { # not a method!!!
666              
667 8     8 0 23 my ( $control, $callback ) = ( $_[1], $_[2] );
668 8     8   57 no strict 'refs';
  8         10  
  8         1469  
669 8 50       81 unless (%char_props) { # why I can't write %{$control}
670 0         0 die "'%$control' not defined";
671             }
672 8         39 for ( keys %char_props ) {
673 48         50 $do_on_control{$_} = \&{$callback};
  48         119  
674             }
675             }
676             # TOGGLES
677             # {\ ...}
678             # {\0 ...}
679             ###########################################################################
680             # How to give a general definition?
681             #my %control_definition = ( # control => [default_value nassociated_callback]
682             # 'char_props' => qw(0 do_on_control),
683             # );
684              
685             # Remove character formatting properties ... there are actually more
686             # character formatting properties defined in the RTF spec, but
687             # these seem to be the ones supported by this module...
688              
689             sub reset_char_props {
690              
691 54         167 %char_props = map {
692              
693 9     9 0 23 $_ => 0
694              
695             } qw(b i ul sub super strike);
696             }
697              
698             my $char_prop_change = 0;
699             my %current_char_props = %char_props;
700 8     8   41 use constant OUTPUT_CHAR_PROPS => 0;
  8         13  
  8         1897  
701              
702             # Force a START or END event on our current character
703             # properties... This is a method call.
704              
705             sub force_char_props { # force a START/END event
706              
707             # Obviously you're not allowed to do this in the fonttable
708             # or style sheet...
709              
710 5 100 66 5 0 29 return if $IN_STYLESHEET or $IN_FONTTBL;
711              
712 4         7 trace "@_" if OUTPUT_CHAR_PROPS;
713              
714             # [0] is our object
715 4         7 $event = $_[1]; # END or START
716             # close or open all activated char prorperties
717              
718 4         8 push_output();
719              
720 4         15 while ( my ( $char_prop, $value ) = each %char_props ) {
721              
722 24 50       84 next unless $value;
723              
724 0         0 trace "$event active char props: $char_prop" if OUTPUT_CHAR_PROPS;
725              
726 0 0       0 if ( defined( my $action = $do_on_event{$char_prop} ) ) {
727              
728 0         0 ( $style, $event ) = ( $char_prop, $event );
729              
730 0         0 &$action;
731              
732             }
733              
734 0         0 $current_char_props{$char_prop} = $value;
735              
736             }
737              
738 4         9 $char_prop_change = 0;
739              
740 4         10 pop_output();
741              
742             }
743              
744 8     8   44 use constant PROCESS_CHAR_PROPS => 0;
  8         16  
  8         2231  
745              
746             # Only run outside of stylesheets and fonttables,
747             # and only when the $char_prop_change flag is
748             # set.
749              
750             sub process_char_props {
751              
752 32 100 66 32 0 201 return if $IN_STYLESHEET or $IN_FONTTBL;
753              
754 26 100       72 return unless $char_prop_change;
755              
756             # Add a new output block
757              
758 20         37 push_output();
759              
760             # Go through char_props (is the what we were, or what we're going to?!)
761 20         65 while ( my ( $char_prop, $value ) = each %char_props ) {
762              
763             # Get the current character property
764 120         154 my $prop = $current_char_props{$char_prop};
765              
766             # Set it to an explicit 0 if not set
767 120 100       188 $prop = defined $prop ? $prop : 0;
768              
769 120         103 trace "$char_prop $value" if PROCESS_CHAR_PROPS;
770              
771             # If the values in %char_props and $current_char_props don't match..
772 120 100       408 if ( $prop != $value ) {
773              
774             # See if we have an event...
775 14 50       38 if ( defined( my $action = $do_on_event{$char_prop} ) ) {
776              
777             # Set event to start or end depending on if
778             # the $value is a literal 1.
779 14 100       32 $event = $value == 1 ? 'start' : 'end';
780              
781 14         27 ( $style, $event ) = ( $char_prop, $event );
782              
783             # Fire the event
784 14         36 &$action;
785              
786             }
787              
788             # Set the $current_char_props to equal what was in %char_props
789 14         156 $current_char_props{$char_prop} = $value;
790              
791             }
792              
793 120         345 trace "$char_prop - $prop - $value" if PROCESS_CHAR_PROPS;
794              
795             }
796              
797             # Reset the flag
798 20         23 $char_prop_change = 0;
799              
800             # Return whatever was on the stack
801 20         46 pop_output();
802              
803             }
804              
805 8     8   42 use constant DO_ON_CHAR_PROP => 0;
  8         16  
  8         2688  
806              
807             # Again, not called in a font or stylesheet, for obvious reasons.
808             # Set the char_prop_change flag. If the argument is '0', we set
809             # that character property to that - if the event is start, we set
810             # it to one, otherwise we throw a warning.
811              
812             sub do_on_char_prop { # associated callback
813              
814 11 50 33 11 0 57 return if $IN_STYLESHEET or $IN_FONTTBL;
815              
816 11         35 my ( $control, $arg, $cevent ) = ( $_[CONTROL], $_[ARG], $_[EVENT] );
817              
818 11         81 trace "my(\$control, \$arg, \$cevent) = ($_[CONTROL], $_[ARG], $_[EVENT]);"
819             if DO_ON_CHAR_PROP;
820              
821 11         70 $char_prop_change = 1;
822              
823 11 100 66     121 if ( defined( $_[ARG] ) and $_[ARG] eq "0" ) { # \b0
    50          
824              
825 2         13 $char_props{ $_[CONTROL] } = 0;
826              
827             } elsif ( $_[EVENT] eq 'start' ) { # eg. \b or \b1
828              
829 9         40 $char_props{ $_[CONTROL] } = 1;
830              
831             } else { # 'end'
832              
833 0         0 warn "statement not reachable";
834 0         0 $char_props{ $_[CONTROL] } = 0;
835              
836             }
837              
838             }
839              
840             # LOOK MA! THIS BE IMPORTANT
841             __PACKAGE__->reset_char_props();
842             __PACKAGE__->install_callback( 'char_props', 'do_on_char_prop' );
843              
844             ###########################################################################
845             # not more used!!!
846             #use constant DO_ON_TOGGLE => 0;
847             #sub do_on_toggle { # associated callback
848             ##
849             #
850             # return if $IN_STYLESHEET or $IN_FONTTBL;
851             # my($control, $arg, $cevent) = ($_[CONTROL], $_[ARG], $_[EVENT]);
852             # trace "my(\$control, \$arg, \$cevent) = ($_[CONTROL], $_[ARG], $_[EVENT]);" if DO_ON_TOGGLE;
853             #
854             # if ($_[ARG] eq "0") { # \b0, register an START event for this control
855             # $control[TOP]->{"$_[CONTROL]1"} = 1; # register a start event for this properties
856             # $cevent = 'end';
857             # } elsif ($_[EVENT] eq 'start') { # \b or \b1
858             # $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
859             # } else { # $_[EVENT] eq 'end'
860             # if ($_[ARG] eq "1") {
861             # $cevent = 'start';
862             # } else {
863             # }
864             # }
865             # trace "(\$style, \$event, \$text) = ($control, $cevent, '')" if DO_ON_TOGGLE;
866             # if (defined (my $action = $do_on_event{$control})) {
867             # ($style, $event, $text) = ($control, $cevent, '');
868             # &$action;
869             # }
870             #}
871              
872             ###########################################################################
873             # FLAGS
874 8     8   42 use constant DO_ON_FLAG => 0;
  8         13  
  8         1074  
875              
876             # Simply sets that pargraph properties of said flag to 1
877              
878             sub do_on_flag {
879              
880             #my($control, $arg, $cevent) = ($_[CONTROL], $_[ARG], $_[EVENT]);
881 0 0   0 0 0 die if $_[ARG]; # no argument by definition
882 0         0 trace "$_[CONTROL]" if DO_ON_FLAG;
883 0         0 $par_props{ $_[CONTROL] } = 1;
884              
885             }
886              
887 8     8   41 use vars qw/%charset/;
  8         14  
  8         697  
888             my $bullet_item = 'b7'; # will be redefined in a next release!!!
889              
890             # Try to find a "RTF//char_map" file
891             # possible values for the control word are: ansi, mac, pc, pca
892             sub define_charset {
893              
894 0     0 0 0 my $charset = $_[CONTROL];
895 0         0 eval {
896 8     8   39 no strict 'refs';
  8         14  
  8         3522  
897 0         0 *charset = \%{"$charset"};
  0         0  
898             };
899              
900 0 0       0 warn $@ if $@;
901              
902 0         0 my @charset_data = $_[SELF]->charmap_reader('char_map');
903              
904 0         0 my ( $name, $char, $hexa );
905 0         0 my %char = map {
906              
907 0         0 s/^\s+//;
908 0 0       0 next unless /\S/;
909 0         0 ( $name, $char ) = split /\s+/;
910              
911 0 0       0 if ( !defined( $hexa = $charset{$name} ) ) {
912              
913 0         0 'undef' => undef;
914              
915             } else {
916              
917 0         0 $hexa => $char;
918              
919             }
920              
921             } (@charset_data);
922              
923 0         0 %charset = %char; # for a direct translation of hexadecimal values
924 0 0       0 warn $@ if $@;
925              
926             }
927              
928             my %flag_ctrl = (
929             'ql' => \&do_on_flag,
930             'qr' => \&do_on_flag,
931             'qc' => \&do_on_flag,
932             'qj' => \&do_on_flag,
933              
934             #
935             'ansi' => \&define_charset, # The default
936             'mac' => \&define_charset, # Apple Macintosh
937             'pc' => \&define_charset, # IBM PC code page 437
938             'pca' => \&define_charset, # IBM PC code page 850
939             #
940              
941             'pict' => \&discard_content, #
942             'xe' => \&discard_content, # index entry
943             #'v' => \&discard_content, # hidden text
944             );
945              
946             sub do_on_destination {
947 0     0 0 0 trace "currently do nothing";
948             }
949             my %destination_ctrl = ();
950              
951             sub do_on_value {
952 0     0 0 0 trace "currently do nothing";
953             }
954             my %value_ctrl = ();
955              
956             my %pn = (); # paragraph numbering
957             my $field_ref = ''; # identifier associated to a field
958             #trace "define callback for $_[CONTROL]";
959              
960             # BEGIN API REDEFINITION
961              
962             # Ok, so this is actually the place to start as far as concerns
963             # working out how the hell^Wfuck this thing works. I'm moving
964             # all the constants to the top, and adding API documentation
965             # here so future readers will have less trouble.
966              
967 8     8   47 use constant GROUP_START_TRACE => 0;
  8         19  
  8         405  
968 8     8   38 use constant GROUP_END_TRACE => 0;
  8         13  
  8         335  
969 8     8   35 use constant TEXT_TRACE => 0;
  8         12  
  8         329  
970 8     8   43 use constant PARSE_START_END => 0;
  8         12  
  8         3385  
971              
972             # Called when we first start actually parsing the document
973             sub parse_start {
974              
975 9     9 1 14 my $self = shift;
976              
977             # Place holders for non-printed data
978              
979 9         20 %info = ();
980 9         22 %fonttbl = ();
981 9         14 %colortbl = ();
982 9         15 %stylesheet = ();
983              
984             # Add an initial element to our output stack
985              
986 9         26 push_output();
987              
988             # If there's an event defined for the start of a document,
989             # execute it now...
990              
991 9 50       37 if ( defined( my $action = $do_on_event{'document'} ) ) {
992              
993             # $event tells our action handler what's happening...
994              
995 9         17 $event = 'start';
996              
997             # Actually execute said action
998              
999 9         28 &$action;
1000              
1001             }
1002              
1003             # Prints and clears the top element on the output stack
1004              
1005 9         21 flush_top_output();
1006              
1007             # Add another element to the output stack
1008              
1009 9         18 push_output();
1010              
1011             }
1012              
1013             # Called at the end of parsing
1014             sub parse_end {
1015              
1016 9     9 1 21 my $self = shift;
1017              
1018             # @output_stack+0 forces scalar context?
1019 9         12 trace "parseEnd \@output_stack: ", @output_stack + 0 if STACK_TRACE;
1020              
1021             # Call the end of document even if it exists
1022 9 50       29 if ( defined( my $action = $do_on_event{'document'} ) ) {
1023              
1024 9         26 ( $style, $event, $text ) = ( $cstylename, 'end', '' );
1025 9         26 &$action;
1026              
1027             }
1028              
1029             # Print and clear the top element on the output stack
1030              
1031 9         17 flush_top_output(); # @output_stack == 2;
1032              
1033             }
1034              
1035             sub group_start { # on {
1036              
1037 16     16 1 23 my $self = shift;
1038              
1039 16         20 trace "" if GROUP_START_TRACE;
1040              
1041             # Take a copy of the parent block's paragraph properties
1042 16         63 push @par_props_stack, {%par_props};
1043              
1044             # Take a copy of the parent block's character properties
1045 16         97 push @char_props_stack, {%char_props};
1046              
1047             # Aha! More accurately, controls we've opened, so we can close them in group_end()
1048 16         58 push @control, {}; # hash of controls
1049              
1050             }
1051              
1052             sub group_end { # on }
1053             # par properties
1054              
1055             # Retrieve parent block's paragraph properties
1056 16     16 1 30 %par_props = %{ pop @par_props_stack };
  16         52  
1057              
1058             # And use it to set the current stylename
1059 16         235 $cstylename = $par_props{'stylename'}; # the current style
1060              
1061             # Char properties
1062             # process control like \b0
1063              
1064             # Grab the character properties of our parent
1065 16         21 %char_props = %{ pop @char_props_stack };
  16         118  
1066              
1067             # Fire off the 'char props have changed' event
1068 16         47 $char_prop_change = 1;
1069 16         48 output process_char_props();
1070              
1071             # Always a /really/ /really/ bad sign :-(
1072 8     8   54 no strict qw/refs/;
  8         14  
  8         9853  
1073              
1074             # Send an end thingy to each control we're closing
1075 16         30 foreach my $control ( keys %{ pop @control } ) { # End Events!
  16         68  
1076 9         28 $control =~ /([^\d]+)(\d+)?/; # eg: b0, b1
1077 9         10 trace "($#control): $1-$2" if GROUP_END_TRACE;
1078             # sub associated to $1 is already defined in the "Action" package
1079 9         13 &{"RTF::Action::$1"}( $_[SELF], $1, $2, 'end' );
  9         36  
1080             }
1081             }
1082              
1083             # Just dump text
1084             sub text {
1085              
1086 5     5 1 4 trace "$_[1]" if TEXT_TRACE;
1087 5         15 output( $_[1] );
1088              
1089             }
1090              
1091             # If we have an equiv, print it, otherwise, print the original
1092              
1093             sub char {
1094              
1095 0 0   0 1 0 if ( defined( my $char = $charset{ $_[1] } ) ) {
1096             #print STDERR "$_[1] => $char\n";
1097 0         0 output "$char";
1098             } else {
1099 0         0 output "$_[1]";
1100             }
1101             }
1102              
1103             sub symbol { # symbols: \ - _ ~ : | { } * \'
1104              
1105 0 0   0 1 0 if ( defined( my $sym = $symbol{ $_[1] } ) ) {
1106 0         0 output "$sym";
1107             } else {
1108 0         0 output "$_[1]"; # as it
1109             }
1110             }
1111              
1112             sub debug {
1113              
1114 0     0 0 0 my $function = shift;
1115              
1116 0         0 print STDERR "[RTF::Control::$function]" . ( join '|', @_ ), "\n";
1117              
1118             }
1119              
1120             %do_on_control = (
1121             %do_on_control,
1122             %flag_ctrl,
1123             %value_ctrl,
1124             %symbol_ctrl,
1125             %destination_ctrl,
1126              
1127             # Resets character formatting in scope... Note how we don't
1128             # check for start and end events? My guess is this is because
1129             # the original author is a BAD BAD MAN, and because running
1130             # reset_char_props() when \plain goes out of scope doesn't
1131             # cause any side effects. Something to experiment with when
1132             # I have a regression test suite...
1133             ###########################################################
1134              
1135             'plain' => sub {
1136              
1137             reset_char_props();
1138              
1139             },
1140              
1141             ###########################################################
1142              
1143             # The only thing puzzling me here is why we're doing a null
1144             # call to push_output. This (and other subroutines below)
1145             # are ripe for a bit of refactoring - they all do the same
1146             # thing!
1147             ###########################################################
1148              
1149             'rtf' => sub { # rtfN, N is version number
1150              
1151 6 100   2   19 if ( $_[EVENT] eq 'start' ) {
1152              
1153 3         7 push_output('nul');
1154 3         12 $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1155              
1156             } else {
1157              
1158             # There may actually be content at this point!
1159 3         10 flush_top_output();
1160              
1161             # The buffer should be empty at this point.
1162             # Make it so :-) This should use an RTF::Tokenizer
1163             # method before I release this as production.
1164             # TODO...
1165              
1166 3         9 $_[SELF]->{_TOKENIZER}->{_BUFFER} = '';
1167 3         21 $_[SELF]->{_TOKENIZER}->{_FILEHANDLE} = '';
1168              
1169             }
1170              
1171             },
1172              
1173             ###########################################################
1174              
1175             # Info group. The &do_on_info sub is trivial, and merely puts
1176             # the rest of the text in a destination into %info, with the
1177             # key being the field (like 'title'). creatim is kinda clever
1178             # then in that it turns the rest of those fields into one
1179             # long text string.
1180             #
1181             # Other information we could grab:
1182             # {\printim\yr1997\mo11\dy3\hr11\min5}
1183             # {\version3}{\edmins1}{\nofpages3}{\nofwords1278}{\nofchars7287}
1184             # {\*\company SONOVISION-ITEP}{\vern57443}
1185             ###########################################################
1186              
1187             'info' => sub { # {\info {...}}
1188              
1189             if ( $_[EVENT] eq 'start' ) {
1190              
1191             # Stops us collecting any text we don't want
1192              
1193             push_output('nul');
1194             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1195              
1196             } else {
1197              
1198             pop_output();
1199              
1200             }
1201              
1202             },
1203              
1204             'title' => \&do_on_info, # destination
1205             'author' => \&do_on_info, # destination
1206             'revtim' => \&do_on_info, # destination
1207             'creatim' =>
1208             \&do_on_info, # destination, {\creatim\yr1996\mo9\dy18\hr9\min17}
1209             'yr' => sub { output "$_[ARG]-" }, # value
1210             'mo' => sub { output "$_[ARG]-" }, # value
1211             'dy' => sub { output "$_[ARG]-" }, # value
1212             'hr' => sub { output "$_[ARG]-" }, # value
1213             'min' => sub { output "$_[ARG]" }, # value
1214              
1215             ###########################################################
1216              
1217             # Read binary data - only, this function has been removed
1218             # from RTF::Parser.pm. Ooops. Add it back in and PUT IN
1219             # A TEST.
1220             ###########################################################
1221              
1222             'bin' => sub { $_[SELF]->read_bin( $_[ARG] ) }, # value
1223              
1224             # \ulnone should be treated as if it were \ul0...
1225             ###########################################################
1226              
1227             'ulnone' => sub {
1228              
1229             $_[SELF]->do_on_char_prop( 'ul', '0', 'start' );
1230              
1231             },
1232              
1233             # Clearly we're not interested in the colour table....
1234             ###########################################################
1235              
1236             'colortbl' => \&discard_content,
1237              
1238             # The start of the font-table. There's a global(ish) flag
1239             # $IN_FONTTBL that influences how other parts of the module
1240             # work. The main thing we do is turn this flag on when we
1241             # get to this point. The 'push_output('nul')' also turns
1242             # off any output while we're in the font table.
1243             ###########################################################
1244              
1245             'fonttbl' => sub {
1246              
1247 3 50       5 if ( $_[EVENT] eq 'start' ) {
1248              
1249             # Set the global flag
1250 3         10 $IN_FONTTBL = 1;
1251              
1252             # Turn off output
1253 0         0 push_output('nul');
1254              
1255             # Remember that this event has fired, and close it
1256             # when we go out of scope.
1257 6         25 $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1258              
1259             } else {
1260              
1261 0         0 $IN_FONTTBL = 0;
1262 0         0 pop_output();
1263              
1264             }
1265              
1266             },
1267              
1268             ###########################################################
1269              
1270             # We seem to not want anything to do with the filetable
1271             # either - I guess the reason we define a control for it
1272             # (because otherwise it'd get skipped as an unknow destination
1273             # I think) is so that subclassers can handle it if they
1274             # want.
1275             ###########################################################
1276              
1277             'filetbl' => sub {
1278              
1279             #trace "$#control $_[CONTROL] $_[ARG] $_[EVENT]";
1280             if ( $_[EVENT] eq 'start' ) {
1281              
1282             push_output('nul');
1283             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1284              
1285             } else {
1286              
1287             pop_output();
1288              
1289             }
1290              
1291             },
1292              
1293             ###########################################################
1294              
1295             # A font control - highly context-dependant control word ... Can be used
1296             # to introduce a font definition when we're in the font-table, to specify
1297             # which font a style uses in the style-table, or to change the font we're
1298             # currently using when used as a paragraph/character property.
1299             ###########################################################
1300              
1301             'f' => sub {
1302              
1303 8         10459 use constant FONTTBL_TRACE =>
1304 8     8   42 0; # if you want to see the fonttbl of the document
  8         16  
1305              
1306             # We're in the middle of the font-table, so this is a font definition.
1307             # We're only really interested in what happens when we pass *out* of
1308             # scope, because at that point we'll have grabbed the font-name. I'd
1309             # like to add panose support at some point.
1310              
1311 0 50       0 if ($IN_FONTTBL) {
    50          
1312              
1313 0 0       0 if ( $_[EVENT] eq 'start' ) {
1314              
1315             # Add a new element to the output stack that we can
1316             # snarf back in a minute when we hit the group close
1317              
1318 0         0 push_output();
1319              
1320             # Say we're open
1321              
1322 0         0 $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1323              
1324             } else {
1325              
1326             # Grab the element from the output stack, which'll be
1327             # our fontname
1328              
1329 0         0 my $fontname = pop_output;
1330              
1331             # This will be something like 'f1'
1332              
1333 0         0 my $fontdef = "$_[CONTROL]$_[ARG]";
1334              
1335             # Remove the trailing semi-colon and any space
1336              
1337 0 100       0 if ( $fontname =~ s/\s*;$// ) {
1338              
1339 0         0 trace "$fontdef => $fontname" if FONTTBL_TRACE;
1340              
1341             # Set the fontdef and the fontname in the font-table hash
1342              
1343 0         0 $fonttbl{$fontdef} = $fontname;
1344              
1345             } else {
1346              
1347 0         0 warn "can't analyze $fontname";
1348              
1349             }
1350              
1351             }
1352              
1353 3         10 return;
1354              
1355             # We're in the style sheet. This part doesn't make much sense
1356             # just yet, will come back to it. Looks like \f is being used
1357             # to recognise when a style definition is finished?! Bizarre.
1358              
1359             } elsif ($IN_STYLESHEET) { # eg. \f1 => Normal;
1360              
1361 3 0       6 return if $styledef; # if you have already encountered an \sn
1362 3         6 $styledef = "$_[CONTROL]$_[ARG]";
1363              
1364 3 100       61 if ( $_[EVENT] eq 'start' ) {
1365              
1366             #trace "start $_[CONTROL]$_[ARG]" if STYLESHEET;
1367 3         16 push_output();
1368 0         0 $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1369              
1370             } else {
1371              
1372 0         0 my $stylename = pop_output;
1373             #trace "end\n $_[CONTROL]" if STYLESHEET;
1374              
1375 0 0       0 if ( $stylename =~ s/\s*;$// ) {
1376              
1377 0         0 trace "$styledef => $stylename" if STYLESHEET_TRACE;
1378 0         0 $stylesheet{$styledef} = $stylename;
1379              
1380             } else {
1381              
1382 0         0 warn
1383             "can't analyze '$stylename' ($styledef; event: $_[EVENT])";
1384              
1385             }
1386              
1387             }
1388              
1389 4         6 $styledef = '';
1390 4         31 return;
1391              
1392             }
1393              
1394 0 50       0 return if $styledef; # if you have already encountered an \sn
1395              
1396             # This doesn't make a great deal of sense
1397 0         0 $styledef = "$_[CONTROL]$_[ARG]";
1398 0         0 $stylename = $stylesheet{"$styledef"};
1399 0         0 trace "$styledef => $stylename" if STYLESHEET_TRACE;
1400              
1401 0 0       0 return unless $stylename;
1402              
1403 0 0       0 if ( $cstylename ne $stylename ) { # notify a style changing
1404              
1405 0 0       0 if ( defined( my $action = $do_on_event{'style_change'} ) ) {
1406              
1407 0         0 ( $style, $newstyle ) = ( $cstylename, $stylename );
1408 0         0 &$action;
1409              
1410             }
1411              
1412             }
1413              
1414 0         0 $cstylename = $stylename;
1415 0         0 $par_props{'stylename'} = $cstylename; # the current style
1416              
1417             },
1418              
1419             ###########################################################
1420              
1421             # Stylesheet - like font-table above, we set the flag, and
1422             # make sure we don't grab any unwanted text...
1423              
1424             'stylesheet' => sub {
1425              
1426             trace "stylesheet $#control $_[CONTROL] $_[ARG] $_[EVENT]"
1427             if STYLESHEET_TRACE;
1428              
1429             if ( $_[EVENT] eq 'start' ) {
1430              
1431             $IN_STYLESHEET = 1;
1432             push_output('nul');
1433             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1434              
1435             } else {
1436              
1437             $IN_STYLESHEET = 0;
1438             pop_output;
1439              
1440             }
1441             },
1442              
1443             ###########################################################
1444              
1445             # Stylesheet definition
1446             ###########################################################
1447              
1448             's' => sub {
1449              
1450             my ( $control, $arg, $cevent ) = ( $_[CONTROL], $_[ARG], $_[EVENT] );
1451              
1452             $styledef = "$_[CONTROL]$_[ARG]";
1453              
1454             # This looks pretty much identical to \f - only, looking at it,
1455             # it probably doesn't work. My head hurts.
1456              
1457             if ($IN_STYLESHEET) {
1458              
1459             if ( $_[EVENT] eq 'start' ) {
1460              
1461             push_output();
1462             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1463              
1464             } else {
1465              
1466             my $stylename = pop_output;
1467             warn "empty stylename" and return if $stylename eq '';
1468              
1469             if ( $stylename =~ s/\s*;$// ) {
1470              
1471             trace "$styledef => $stylename|" if STYLESHEET_TRACE;
1472             $stylesheet{$styledef} = $stylename;
1473             $styledef = '';
1474              
1475             } else {
1476              
1477             warn "can't analyze style name: '$stylename'";
1478              
1479             }
1480              
1481             }
1482              
1483             return;
1484              
1485             }
1486              
1487             $stylename = $stylesheet{"$styledef"};
1488              
1489             if ( $cstylename ne $stylename ) {
1490              
1491             if ( defined( my $action = $do_on_event{'style_change'} ) ) {
1492              
1493             ( $style, $newstyle ) = ( $cstylename, $stylename );
1494              
1495             &$action;
1496              
1497             }
1498              
1499             }
1500              
1501             $cstylename = $stylename;
1502             $par_props{'stylename'} = $cstylename; # the current style
1503             trace "$styledef => $stylename" if STYLESHEET_TRACE;
1504              
1505             },
1506              
1507             ###########################################################
1508              
1509             # Tells us we're starting a row...
1510             ###########################################################
1511              
1512             'trowd' => sub {
1513              
1514 8     8   48 use constant TABLE_TRACE => 0;
  8         18  
  8         18804  
1515              
1516             #print STDERR "=>Beginning of ROW\n";
1517              
1518             # If we're not in a table...
1519              
1520             unless ($IN_TABLE) {
1521              
1522             # Set the flag to say we now are
1523              
1524             $IN_TABLE = 1;
1525              
1526             # Fire off a table even if we have one
1527              
1528             if ( defined( my $action = $do_on_event{'table'} ) ) {
1529              
1530             $event = 'start';
1531             trace "table $event $text\n" if TABLE_TRACE;
1532             &$action;
1533              
1534             }
1535              
1536             # Add lots of output holders for various things...
1537              
1538             push_output(); # table content
1539             push_output(); # row sequence
1540             push_output(); # cell sequence
1541             push_output(); # cell content
1542              
1543             }
1544              
1545             },
1546              
1547             # Perhaps the control that opens a table? Never the less,
1548             # an exact clone of the function above!
1549             ###########################################################
1550              
1551             'intbl' => sub {
1552              
1553             $par_props{'intbl'} = 1;
1554              
1555             unless ($IN_TABLE) {
1556              
1557             warn "ouverture en catastrophe" if TABLE_TRACE;
1558             $IN_TABLE = 1;
1559              
1560             if ( defined( my $action = $do_on_event{'table'} ) ) {
1561              
1562             $event = 'start';
1563             trace "table $event $text\n" if TABLE_TRACE;
1564             &$action;
1565              
1566             }
1567              
1568             push_output();
1569             push_output();
1570             push_output();
1571             push_output();
1572              
1573             }
1574              
1575             },
1576              
1577             # The end of a row
1578             ###########################################################
1579              
1580             'row' => sub { # row end
1581              
1582             # Grab the cell and the 'cell sequence'
1583              
1584             $text = pop_output;
1585             $text = pop_output . $text;
1586              
1587             # Fire off the 'end cell' handler if we have one
1588              
1589             if ( defined( my $action = $do_on_event{'cell'} ) ) {
1590              
1591             $event = 'end';
1592             trace "row $event $text\n" if TABLE_TRACE;
1593             &$action;
1594              
1595             }
1596              
1597             # Grab any row text
1598              
1599             $text = pop_output;
1600              
1601             # Fire off the end-row event
1602              
1603             if ( defined( my $action = $do_on_event{'row'} ) ) {
1604              
1605             $event = 'end';
1606             trace "row $event $text\n" if TABLE_TRACE;
1607             &$action;
1608             }
1609              
1610             # Prep the next row
1611              
1612             push_output();
1613             push_output();
1614             push_output();
1615              
1616             },
1617              
1618             ###########################################################
1619              
1620             # End of a cell
1621             ###########################################################
1622              
1623             'cell' => sub { # end of cell
1624              
1625             trace "process cell content: $text\n" if TABLE_TRACE;
1626             $text = pop_output;
1627              
1628             # Fire the paragraph handler
1629              
1630             if ( defined( my $action = $do_on_event{'par'} ) ) {
1631              
1632             ( $style, $event, ) = ( 'par', 'end', );
1633             &$action;
1634              
1635             } else {
1636              
1637             warn "$text";
1638              
1639             }
1640              
1641             $text = pop_output;
1642              
1643             # Fire the end-cell handler
1644              
1645             if ( defined( my $action = $do_on_event{'cell'} ) ) {
1646              
1647             $event = 'end';
1648             trace "cell $event $text\n" if TABLE_TRACE;
1649             &$action;
1650              
1651             }
1652             # prepare next cell
1653             push_output();
1654             push_output();
1655             trace "\@output_stack in table: ", @output_stack + 0 if STACK_TRACE;
1656              
1657             },
1658              
1659             ###########################################################
1660              
1661             # And thus the paragraph ends
1662             ###########################################################
1663              
1664             'par' => sub { # END OF PARAGRAPH
1665              
1666 0         0 trace "($_[CONTROL], $_[ARG], $_[EVENT])" if STYLE_TRACE;
1667              
1668             # Close a table. Add to $text, and call even handlers
1669             # for cell, row, and table, in order.
1670              
1671 0 50 33     0 if ( $IN_TABLE and not $par_props{'intbl'} ) { # End of Table
1672              
1673 0         0 $IN_TABLE = 0;
1674 0         0 my $next_text = pop_output; # next paragraph content
1675              
1676 0         0 $text = pop_output;
1677 0         0 $text = pop_output . "$text";
1678              
1679 0 0       0 if ( defined( my $action = $do_on_event{'cell'} ) ) { # end of cell
1680              
1681 0         0 $event = 'end';
1682 0         0 trace "cell $event $text\n" if TABLE_TRACE;
1683 0         0 &$action;
1684              
1685             }
1686              
1687 4         26 $text = pop_output;
1688              
1689 0 0       0 if ( defined( my $action = $do_on_event{'row'} ) ) { # end of row
1690              
1691 4         11 $event = 'end';
1692 4         12 trace "row $event $text\n" if TABLE_TRACE;
1693 4         20 &$action;
1694              
1695             }
1696              
1697 0         0 $text = pop_output;
1698              
1699 0 50       0 if ( defined( my $action = $do_on_event{'table'} ) )
1700             { # end of table
1701              
1702 0         0 $event = 'end';
1703 0         0 trace "table $event $text\n" if TABLE_TRACE;
1704 0         0 &$action;
1705              
1706             }
1707              
1708 0         0 push_output();
1709 0         0 trace "end of table ($next_text)\n" if TABLE_TRACE;
1710 0         0 output($next_text);
1711              
1712             } else {
1713              
1714             #push_output();
1715              
1716             }
1717              
1718             # paragraph style
1719 4 0 33     16 if ( defined($cstylename) and $cstylename ne '' )
1720             { # end of previous style
1721              
1722 4         24 $style = $cstylename;
1723              
1724             } else {
1725              
1726 4         9 $cstylename = $style = 'par'; # no better solution
1727              
1728             }
1729              
1730 4         9 $par_props{'stylename'} = $cstylename; # the current style
1731              
1732 0 50       0 if ( $par_props{intbl} ) { # paragraph in tbl
    0          
1733              
1734 0         0 trace "process cell content: $text\n" if TABLE_TRACE;
1735              
1736 0 0       0 if ( defined( my $action = $do_on_event{$style} ) ) {
    50          
1737              
1738 0         0 ( $style, $event, $text ) = ( $style, 'end', pop_output );
1739 0         0 &$action;
1740              
1741             } elsif ( defined( $action = $do_on_event{'par'} ) ) {
1742              
1743             #($style, $event, $text) = ('par', 'end', pop_output);
1744 0         0 ( $style, $event, $text ) = ( $style, 'end', pop_output );
1745 0         0 &$action;
1746              
1747             } else {
1748              
1749 4         9 warn;
1750              
1751             }
1752              
1753 4         6 push_output();
1754              
1755             #} elsif (defined (my $action = $do_on_event{'par_styles'})) {
1756             } elsif ( defined( my $action = $do_on_event{$style} ) ) {
1757              
1758 4         25 ( $style, $event, $text ) = ( $style, 'end', pop_output );
1759 3         18 &$action;
1760 18         38 flush_top_output();
1761 3         7 push_output();
1762              
1763             } elsif ( defined( $action = $do_on_event{'par'} ) ) {
1764              
1765             #($style, $event, $text) = ('par', 'end', pop_output);
1766 3         17 ( $style, $event, $text ) = ( $style, 'end', pop_output );
1767 2         15 &$action;
1768             flush_top_output();
1769             push_output();
1770              
1771             } else {
1772              
1773             trace "no definition for '$style' in %do_on_event\n" if STYLE_TRACE;
1774             flush_top_output();
1775             push_output();
1776              
1777             }
1778             # redefine this!!!
1779             $cli = $par_props{'li'};
1780             $styledef = '';
1781             $par_props{'bullet'} = $par_props{'number'} = $par_props{'tab'} = 0; #
1782              
1783             },
1784             # Resets to default paragraph properties
1785             # Stop inheritence of paragraph properties
1786              
1787             'pard' => sub {
1788              
1789             # !!!-> reset_par_props()
1790             foreach (qw(qj qc ql qr intbl li)) {
1791              
1792             $par_props{$_} = 0;
1793              
1794             }
1795              
1796             foreach (qw(list_item)) {
1797              
1798             $par_props{$_} = '';
1799              
1800             }
1801              
1802             },
1803              
1804             # ###########################
1805              
1806             'pn' => sub { # Turn on PARAGRAPH NUMBERING
1807              
1808             #trace "($_[CONTROL], $_[ARG], $_[EVENT])" if TRACE;
1809              
1810             if ( $_[EVENT] eq 'start' ) {
1811              
1812             %pn = ();
1813             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1814              
1815             } else {
1816              
1817             # I don't like this!!! redesign the parser???
1818             trace("Level: $pn{level} - Type: $pn{type} - Bullet: $pn{bullet}")
1819             if LIST_TRACE;
1820             $par_props{list_item} = \%pn;
1821              
1822             }
1823              
1824             },
1825              
1826             'pnlvl' => sub { # Paragraph level $_[ARG] is a level from 1 to 9
1827              
1828             $pn{level} = $_[ARG];
1829              
1830             },
1831              
1832             'pnlvlbody' => sub { # Paragraph level 10
1833              
1834             $pn{level} = 10;
1835              
1836             },
1837              
1838             'pnlvlblt' => sub { # Paragraph level 11, processs the 'pntxtb' group
1839              
1840             $pn{level} = 11; # bullet
1841              
1842             },
1843              
1844             'pntxtb' => sub {
1845              
1846             if ( $_[EVENT] eq 'start' ) {
1847              
1848             push_output();
1849             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1850              
1851             } else {
1852              
1853             $pn{'bullet'} = pop_output();
1854              
1855             }
1856              
1857             },
1858              
1859             'pntxta' => sub {
1860              
1861             if ( $_[EVENT] eq 'start' ) {
1862              
1863             push_output();
1864             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1865              
1866             } else {
1867              
1868             pop_output();
1869              
1870             }
1871              
1872             },
1873             # Numbering Types
1874             'pncard' => sub { # Cardinal numbering: One, Two, Three
1875             $pn{type} = $_[CONTROL];
1876             },
1877              
1878             'pndec' => sub { # Decimal numbering: 1, 2, 3
1879             $pn{type} = $_[CONTROL];
1880             },
1881              
1882             'pnucltr' => sub { # Uppercase alphabetic numbering
1883             $pn{type} = $_[CONTROL];
1884             },
1885              
1886             'pnlcltr' => sub { # Lowercase alphabetic numbering
1887             $pn{type} = $_[CONTROL];
1888             },
1889              
1890             'pnucrm' => sub { # Uppercase roman numbering
1891             $pn{type} = $_[CONTROL];
1892             },
1893              
1894             'pnlcrm' => sub { # Lowercase roman numbering
1895             $pn{type} = $_[CONTROL];
1896             },
1897              
1898             'pntext' => sub { # ignore text content
1899              
1900             if ( $_[EVENT] eq 'start' ) {
1901              
1902             push_output();
1903             $control[TOP]->{"$_[CONTROL]$_[ARG]"} = 1;
1904              
1905             } else {
1906              
1907             pop_output();
1908              
1909             }
1910              
1911             },
1912             #'tab' => sub { $par_props{'tab'} = 1 }, # special char
1913              
1914             'li' => sub { # line indent - value
1915              
1916 8     8   55 use constant LI_TRACE => 0;
  8         28  
  8         1362  
1917             my $indent = $_[ARG];
1918             $indent =~ s/^-//;
1919             trace "line indent: $_[ARG] -> $indent" if LI_TRACE;
1920             $par_props{'li'} = $indent;
1921              
1922             }, );
1923             ###########################################################################
1924              
1925 8     8   49 use vars qw(%not_processed);
  8         21  
  8         3127  
1926              
1927             END {
1928 8 50   8   6852 if (@control) {
1929 0         0 trace "END{} - Control stack not empty [size: ", @control + 0, "]: ";
1930 0         0 foreach my $hash (@control) {
1931 0         0 while ( my ( $key, $value ) = each %$hash ) {
1932 0         0 trace "$key => $value";
1933             }
1934             }
1935             }
1936 8 50       8 if ($LOG_FILE) {
1937 0         0 select STDERR;
1938 0 0       0 unless ( open LOG, "> $LOG_FILE" ) {
1939 0         0 print qq^$::BASENAME: unable to output data to "$LOG_FILE"$::EOM^;
1940 0         0 return 0;
1941             }
1942 0         0 select LOG;
1943 0         0 my ( $key, $value ) = ( '', '' );
1944 0         0 while ( my ( $key, $value ) = each %not_processed ) {
1945 0         0 printf LOG "%-20s\t%3d\n", "$key", "$value";
1946             }
1947 0         0 close LOG;
1948 0         0 print STDERR qq^See Informations in the "$LOG_FILE" file\n^;
1949             }
1950             }
1951             1;
1952             __END__