line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package XML::SAX::RTF; |
2
|
|
|
|
|
|
|
require 5.005_62; |
3
|
1
|
|
|
1
|
|
986
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
42
|
|
4
|
1
|
|
|
1
|
|
2064
|
use XML::SAX::Base; |
|
1
|
|
|
|
|
41145
|
|
|
1
|
|
|
|
|
36
|
|
5
|
1
|
|
|
1
|
|
12
|
use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK ); |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
2975
|
|
6
|
|
|
|
|
|
|
require Exporter; |
7
|
|
|
|
|
|
|
@ISA = qw( Exporter XML::SAX::Base ); |
8
|
|
|
|
|
|
|
@EXPORT = qw( Version ); |
9
|
|
|
|
|
|
|
$VERSION = '0.2'; |
10
|
0
|
|
|
0
|
1
|
|
sub Version { $VERSION; } |
11
|
|
|
|
|
|
|
our %features = |
12
|
|
|
|
|
|
|
( |
13
|
|
|
|
|
|
|
DEBUG => 0, |
14
|
|
|
|
|
|
|
); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# |
18
|
|
|
|
|
|
|
# internal globals |
19
|
|
|
|
|
|
|
# |
20
|
|
|
|
|
|
|
my $file = ''; # name of file being parsed |
21
|
|
|
|
|
|
|
my $inbuf; # input buffer with RTF to be processed |
22
|
|
|
|
|
|
|
my $level; # element nesting level in result doc |
23
|
|
|
|
|
|
|
my @elements; # open element stack for result doc |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub new { |
27
|
|
|
|
|
|
|
# |
28
|
|
|
|
|
|
|
# constructor |
29
|
|
|
|
|
|
|
# |
30
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
31
|
0
|
|
|
|
|
|
my $obj = {@_}; |
32
|
0
|
|
|
|
|
|
my $self = bless( $obj, $class ); |
33
|
0
|
|
|
|
|
|
return $self; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub parse_file { |
38
|
|
|
|
|
|
|
# |
39
|
|
|
|
|
|
|
# parse a document, one line at a time |
40
|
|
|
|
|
|
|
# |
41
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
42
|
0
|
|
|
|
|
|
$file = shift; |
43
|
0
|
|
|
|
|
|
my $buf = ''; |
44
|
0
|
0
|
|
|
|
|
if( open( F, $file )) { |
45
|
0
|
|
|
|
|
|
while( ) { |
46
|
0
|
|
|
|
|
|
$buf .= $_; |
47
|
|
|
|
|
|
|
} |
48
|
0
|
|
|
|
|
|
close F; |
49
|
|
|
|
|
|
|
} |
50
|
0
|
|
|
|
|
|
$self->parse_string( $buf ); |
51
|
0
|
|
|
|
|
|
$file = ''; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub parse_string { |
56
|
|
|
|
|
|
|
# |
57
|
|
|
|
|
|
|
# parse a string containing RTF |
58
|
|
|
|
|
|
|
# |
59
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
60
|
0
|
|
|
|
|
|
$inbuf = shift; |
61
|
0
|
|
|
|
|
|
$level = 0; |
62
|
0
|
|
|
|
|
|
@elements = (); |
63
|
0
|
|
|
|
|
|
$self->_parse(); |
64
|
0
|
|
|
|
|
|
$self->_close_everything; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub set_feature { |
69
|
|
|
|
|
|
|
# |
70
|
|
|
|
|
|
|
# set a parser feature |
71
|
|
|
|
|
|
|
# |
72
|
0
|
|
|
0
|
1
|
|
my( $self, $feature, $value ) = @_; |
73
|
0
|
0
|
|
|
|
|
if( exists( $features{ $feature })) { |
74
|
0
|
|
|
|
|
|
$features{ $feature } = $value; |
75
|
|
|
|
|
|
|
} else { |
76
|
0
|
|
|
|
|
|
$self->SUPER::set_feature( $feature, $value ); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub get_feature { |
82
|
|
|
|
|
|
|
# |
83
|
|
|
|
|
|
|
# query a parser feature |
84
|
|
|
|
|
|
|
# |
85
|
0
|
|
|
0
|
1
|
|
my( $self, $feature ) = @_; |
86
|
0
|
0
|
|
|
|
|
if( exists( $features{ $feature })) { |
87
|
0
|
|
|
|
|
|
return $features{ $feature }; |
88
|
|
|
|
|
|
|
} else { |
89
|
0
|
|
|
|
|
|
return $self->SUPER::get_feature( $feature ); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
my %paramcmds = |
95
|
|
|
|
|
|
|
# |
96
|
|
|
|
|
|
|
# commands with parameters to wrap |
97
|
|
|
|
|
|
|
# |
98
|
|
|
|
|
|
|
( |
99
|
|
|
|
|
|
|
b => 'bold', |
100
|
|
|
|
|
|
|
deff => 'default-font', |
101
|
|
|
|
|
|
|
deflang => 'language', |
102
|
|
|
|
|
|
|
dy => 'day', |
103
|
|
|
|
|
|
|
edmins => 'minutes-edited', |
104
|
|
|
|
|
|
|
f => 'font', |
105
|
|
|
|
|
|
|
fcharset => 'charset', |
106
|
|
|
|
|
|
|
footery => 'footery', |
107
|
|
|
|
|
|
|
fprq => 'pitch', |
108
|
|
|
|
|
|
|
fs => 'font-size', |
109
|
|
|
|
|
|
|
headery => 'headery', |
110
|
|
|
|
|
|
|
hr => 'hour', |
111
|
|
|
|
|
|
|
id => 'id', |
112
|
|
|
|
|
|
|
keepn => 'keep-next', |
113
|
|
|
|
|
|
|
li => 'indent-left', |
114
|
|
|
|
|
|
|
margl => 'margin-left', |
115
|
|
|
|
|
|
|
margr => 'margin-right', |
116
|
|
|
|
|
|
|
min => 'min', |
117
|
|
|
|
|
|
|
mo => 'month', |
118
|
|
|
|
|
|
|
nofchars => 'number-chars', |
119
|
|
|
|
|
|
|
nofcharsws => 'number-nonspace-chars', |
120
|
|
|
|
|
|
|
nofpages => 'number-pages', |
121
|
|
|
|
|
|
|
nofwords => 'numver-words', |
122
|
|
|
|
|
|
|
nowidctlpar => 'nowidctlpar', |
123
|
|
|
|
|
|
|
pard => 'style-default', |
124
|
|
|
|
|
|
|
qc => 'align-center', |
125
|
|
|
|
|
|
|
qj => 'align-justify', |
126
|
|
|
|
|
|
|
ql => 'align-left', |
127
|
|
|
|
|
|
|
qr => 'align-right', |
128
|
|
|
|
|
|
|
ri => 'indent-right', |
129
|
|
|
|
|
|
|
rtf => 'rtf-version', |
130
|
|
|
|
|
|
|
sa => 'space-after', |
131
|
|
|
|
|
|
|
sb => 'space-before', |
132
|
|
|
|
|
|
|
sbasedon => 'style-base', |
133
|
|
|
|
|
|
|
sec => 'sec', |
134
|
|
|
|
|
|
|
sl => 'space-line', |
135
|
|
|
|
|
|
|
snext => 'style-next', |
136
|
|
|
|
|
|
|
vern => 'version', |
137
|
|
|
|
|
|
|
yr => 'year', |
138
|
|
|
|
|
|
|
); |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
my %params = |
142
|
|
|
|
|
|
|
# |
143
|
|
|
|
|
|
|
# commands that are parameters, to be wrapped |
144
|
|
|
|
|
|
|
# |
145
|
|
|
|
|
|
|
( |
146
|
|
|
|
|
|
|
ascii => 'character-set', |
147
|
|
|
|
|
|
|
mac => 'character-set', |
148
|
|
|
|
|
|
|
pc => 'character-set', |
149
|
|
|
|
|
|
|
pca => 'character-set', |
150
|
|
|
|
|
|
|
fnil => 'family', |
151
|
|
|
|
|
|
|
froman => 'family', |
152
|
|
|
|
|
|
|
fswiss => 'family', |
153
|
|
|
|
|
|
|
fmodern => 'family', |
154
|
|
|
|
|
|
|
fscript => 'family', |
155
|
|
|
|
|
|
|
fdecor => 'family', |
156
|
|
|
|
|
|
|
ftech => 'family', |
157
|
|
|
|
|
|
|
fbidi => 'family', |
158
|
|
|
|
|
|
|
ftnil => 'type', |
159
|
|
|
|
|
|
|
fttruetype => 'type', |
160
|
|
|
|
|
|
|
); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
my %groupnames = |
164
|
|
|
|
|
|
|
# |
165
|
|
|
|
|
|
|
# commands labelling groups |
166
|
|
|
|
|
|
|
# |
167
|
|
|
|
|
|
|
( |
168
|
|
|
|
|
|
|
author => 'author', |
169
|
|
|
|
|
|
|
b => 'bold', |
170
|
|
|
|
|
|
|
buptim => 'time-backedup', |
171
|
|
|
|
|
|
|
category => 'category', |
172
|
|
|
|
|
|
|
colortbl => 'color-table', |
173
|
|
|
|
|
|
|
comment => 'comment', |
174
|
|
|
|
|
|
|
company => 'company', |
175
|
|
|
|
|
|
|
creatim => 'time-created', |
176
|
|
|
|
|
|
|
cs => 'char-style', |
177
|
|
|
|
|
|
|
edmins => 'minutes-edited', |
178
|
|
|
|
|
|
|
f => 'font', |
179
|
|
|
|
|
|
|
field => 'field', |
180
|
|
|
|
|
|
|
filetbl => 'file-table', |
181
|
|
|
|
|
|
|
fldinst => 'field-inst', |
182
|
|
|
|
|
|
|
fldrslt => 'field-result', |
183
|
|
|
|
|
|
|
footer => 'footer', |
184
|
|
|
|
|
|
|
footerf => 'footer-first', |
185
|
|
|
|
|
|
|
footerl => 'footer-left', |
186
|
|
|
|
|
|
|
footerr => 'footer-right', |
187
|
|
|
|
|
|
|
footnote => 'footnote', |
188
|
|
|
|
|
|
|
fonttbl => 'font-table', |
189
|
|
|
|
|
|
|
header => 'header', |
190
|
|
|
|
|
|
|
headerf => 'header-first', |
191
|
|
|
|
|
|
|
headerl => 'header-left', |
192
|
|
|
|
|
|
|
headerr => 'header-right', |
193
|
|
|
|
|
|
|
i => 'italic', |
194
|
|
|
|
|
|
|
info => 'info', |
195
|
|
|
|
|
|
|
keywords => 'keywords', |
196
|
|
|
|
|
|
|
listtables => 'list-tables', |
197
|
|
|
|
|
|
|
manager => 'manager', |
198
|
|
|
|
|
|
|
nofchars => 'number-chars', |
199
|
|
|
|
|
|
|
nofcharsws => 'number-nonspace-chars', |
200
|
|
|
|
|
|
|
nofpages => 'number-pages', |
201
|
|
|
|
|
|
|
nofwords => 'numver-words', |
202
|
|
|
|
|
|
|
operator => 'operator', |
203
|
|
|
|
|
|
|
pn => 'para-number', |
204
|
|
|
|
|
|
|
pnseclvl => 'pn-sec-level', |
205
|
|
|
|
|
|
|
pntext => 'pn-text', |
206
|
|
|
|
|
|
|
pntxta => 'pn-txta', |
207
|
|
|
|
|
|
|
pntxtb => 'pn-txtb', |
208
|
|
|
|
|
|
|
printim => 'time-printed', |
209
|
|
|
|
|
|
|
revtbl => 'rev-table', |
210
|
|
|
|
|
|
|
revtim => 'time-revised', |
211
|
|
|
|
|
|
|
s => 'para-style', |
212
|
|
|
|
|
|
|
title => 'title', |
213
|
|
|
|
|
|
|
subject => 'subject', |
214
|
|
|
|
|
|
|
stylesheet => 'stylesheet', |
215
|
|
|
|
|
|
|
ul => 'ul', |
216
|
|
|
|
|
|
|
vern => 'version', |
217
|
|
|
|
|
|
|
version => 'version', |
218
|
|
|
|
|
|
|
); |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
my %wraptext = |
222
|
|
|
|
|
|
|
# |
223
|
|
|
|
|
|
|
# situations where we want to wrap text in an element |
224
|
|
|
|
|
|
|
# |
225
|
|
|
|
|
|
|
( |
226
|
|
|
|
|
|
|
font => 'name', |
227
|
|
|
|
|
|
|
'para-style' => 'name', |
228
|
|
|
|
|
|
|
); |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub _parse { |
232
|
|
|
|
|
|
|
# |
233
|
|
|
|
|
|
|
# parse contents of the input buffer |
234
|
|
|
|
|
|
|
# |
235
|
0
|
|
|
0
|
|
|
my $self = shift; |
236
|
0
|
|
|
|
|
|
while( $inbuf ) { |
237
|
0
|
0
|
|
|
|
|
if( $inbuf =~ /^\{/ ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
238
|
0
|
|
|
|
|
|
$self->_handle_group(); |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
} elsif( $inbuf =~ /^\}/ ) { |
241
|
0
|
0
|
|
|
|
|
$self->_parse_error() unless( $level > 0 ); |
242
|
0
|
|
|
|
|
|
return; |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
} elsif( $inbuf =~ /^\\/ ) { |
245
|
0
|
|
|
|
|
|
$self->_handle_ctlword(); |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
} else { |
248
|
0
|
|
|
|
|
|
$self->_handle_content(); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub _handle_content { |
255
|
|
|
|
|
|
|
# |
256
|
|
|
|
|
|
|
# process character data |
257
|
|
|
|
|
|
|
# |
258
|
0
|
|
|
0
|
|
|
my $self = shift; |
259
|
0
|
|
|
|
|
|
my $curr = $self->_current_element; |
260
|
0
|
0
|
|
|
|
|
if( $inbuf =~ /([^\\\{\}]+)/ ) { |
261
|
0
|
|
|
|
|
|
my $data = $1; |
262
|
0
|
|
|
|
|
|
$inbuf = $'; |
263
|
0
|
0
|
|
|
|
|
if( exists( $wraptext{ $curr })) { |
264
|
0
|
|
|
|
|
|
$data =~ s/;//; |
265
|
0
|
|
|
|
|
|
$self->_indent_start_element( $wraptext{ $curr }); |
266
|
0
|
|
|
|
|
|
$self->_characters( $data ); |
267
|
0
|
|
|
|
|
|
$self->_end_element; |
268
|
|
|
|
|
|
|
} else { |
269
|
0
|
|
|
|
|
|
$self->_characters( $data ); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} else { |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
sub _handle_ctlword { |
277
|
|
|
|
|
|
|
# |
278
|
|
|
|
|
|
|
# process a control word |
279
|
|
|
|
|
|
|
# |
280
|
0
|
|
|
0
|
|
|
my $self = shift; |
281
|
0
|
|
|
|
|
|
$inbuf =~ s/^\\//; |
282
|
0
|
0
|
|
|
|
|
if( $inbuf =~ /^([a-z]+)/ ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
283
|
0
|
|
|
|
|
|
my $command = $1; |
284
|
0
|
|
|
|
|
|
my $parameter; |
285
|
0
|
|
|
|
|
|
$inbuf = $'; |
286
|
0
|
0
|
|
|
|
|
if( $inbuf =~ /^(-?[0-9]+)/ ) { |
287
|
0
|
|
|
|
|
|
$parameter = $1; |
288
|
0
|
|
|
|
|
|
$inbuf = $'; |
289
|
|
|
|
|
|
|
} |
290
|
0
|
0
|
|
|
|
|
if( $inbuf =~ /^ / ) { |
291
|
0
|
|
|
|
|
|
$inbuf = $'; |
292
|
|
|
|
|
|
|
} |
293
|
0
|
|
|
|
|
|
$self->_command( $command, $parameter ); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
} elsif( $inbuf =~/([\\\{\}])/ ) { |
296
|
0
|
|
|
|
|
|
$self->_characters( $1 ); |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
} elsif( $inbuf =~/([^a-z])/ ) { |
299
|
0
|
|
|
|
|
|
my $command = $1; |
300
|
0
|
|
|
|
|
|
$inbuf = $'; |
301
|
0
|
|
|
|
|
|
$self->_start_element( 'command', {'param' => $command} ); |
302
|
0
|
|
|
|
|
|
$self->_end_element; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
} else { |
305
|
0
|
|
|
|
|
|
parse_error(); |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
sub _command { |
311
|
|
|
|
|
|
|
# |
312
|
|
|
|
|
|
|
# process a command |
313
|
|
|
|
|
|
|
# |
314
|
0
|
|
|
0
|
|
|
my( $self, $command, $param ) = @_; |
315
|
|
|
|
|
|
|
|
316
|
0
|
0
|
|
|
|
|
if( $command eq 'par' ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
317
|
0
|
0
|
|
|
|
|
$self->_end_element |
318
|
|
|
|
|
|
|
if( $self->_current_element eq 'para' ); |
319
|
0
|
|
|
|
|
|
$self->_indent_start_element( 'para' ); |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
} elsif( exists( $paramcmds{$command} )) { |
322
|
0
|
|
|
|
|
|
$self->_indent_start_element( $paramcmds{ $command }); |
323
|
0
|
|
|
|
|
|
$self->_characters( $param ); |
324
|
0
|
|
|
|
|
|
$self->_end_element; |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
} elsif( exists( $params{ $command })) { |
327
|
0
|
|
|
|
|
|
$self->_indent_start_element( $params{ $command }); |
328
|
0
|
|
|
|
|
|
$self->_characters( $command ); |
329
|
0
|
|
|
|
|
|
$self->_end_element; |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
} elsif( defined( $param )) { |
332
|
0
|
|
|
|
|
|
$self->_start_element( $command, { param => $param }); |
333
|
0
|
|
|
|
|
|
$self->_end_element; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
} else { |
336
|
0
|
|
|
|
|
|
$self->_start_element( $command ); |
337
|
0
|
|
|
|
|
|
$self->_end_element; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub _handle_group { |
343
|
|
|
|
|
|
|
# |
344
|
|
|
|
|
|
|
# process a group |
345
|
|
|
|
|
|
|
# |
346
|
0
|
|
|
0
|
|
|
my $self = shift; |
347
|
0
|
|
|
|
|
|
$inbuf =~ s/^\{//; |
348
|
0
|
0
|
0
|
|
|
|
if( $level == 0 ) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
349
|
0
|
|
|
|
|
|
$self->_start_element( 'rtfdoc' ); |
350
|
0
|
|
|
|
|
|
$self->_indent_start_element( 'header' ); |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
} elsif(( $inbuf =~ /^\s*\\([a-z]+)/ and exists( $groupnames{$1} )) |
353
|
|
|
|
|
|
|
or( $inbuf =~ /^\s*\\\*\\([a-z]+)/ and exists( $groupnames{$1} ))) { |
354
|
0
|
|
|
|
|
|
$inbuf = $'; |
355
|
0
|
|
|
|
|
|
my $name = $groupnames{$1}; |
356
|
0
|
0
|
0
|
|
|
|
if( $name eq 'info' and $self->_current_element eq 'header' ) { |
|
|
0
|
|
|
|
|
|
357
|
0
|
|
|
|
|
|
$self->_indent_end_element; |
358
|
0
|
|
|
|
|
|
$self->_indent_start_element( 'document' ); |
359
|
0
|
|
|
|
|
|
$self->_indent_start_element( $name ); |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
} elsif( $inbuf =~ /^(-?[0-9]+)/ ) { |
362
|
0
|
|
|
|
|
|
my $param = $1; |
363
|
0
|
|
|
|
|
|
$inbuf = $'; |
364
|
0
|
|
|
|
|
|
$self->_indent_start_element( $name, { number => $param }); |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
} else { |
367
|
0
|
|
|
|
|
|
$self->_indent_start_element( $name ); |
368
|
|
|
|
|
|
|
} |
369
|
0
|
0
|
|
|
|
|
$inbuf = $' if( $inbuf =~ /^ / ); |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
} elsif( $self->_current_element eq 'stylesheet' ) { |
372
|
0
|
|
|
|
|
|
$self->_indent_start_element( 'para-style' ); |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
} else { |
375
|
0
|
|
|
|
|
|
$self->_indent_start_element( 'group', { level => $level }); |
376
|
|
|
|
|
|
|
} |
377
|
0
|
|
|
|
|
|
$self->_parse(); |
378
|
0
|
|
|
|
|
|
$inbuf =~ s/^\}//; |
379
|
0
|
|
|
|
|
|
$self->_indent_end_element; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub _characters { |
384
|
|
|
|
|
|
|
# |
385
|
|
|
|
|
|
|
# clean up characters, call handler |
386
|
|
|
|
|
|
|
# |
387
|
0
|
|
|
0
|
|
|
my( $self, $data ) = @_; |
388
|
0
|
0
|
|
|
|
|
return unless( defined( $data )); |
389
|
0
|
|
|
|
|
|
$self->_debug( "CHARACTERS: [$data]", 3 ); |
390
|
0
|
|
|
|
|
|
$data = $self->_unprotect_chars( $data ); |
391
|
0
|
|
|
|
|
|
$data =~ s/&/&/g; |
392
|
0
|
|
|
|
|
|
$data =~ s/</g; |
393
|
0
|
|
|
|
|
|
$data =~ s/>/>/g; |
394
|
0
|
|
|
|
|
|
$data =~ s/\n//g; |
395
|
0
|
|
|
|
|
|
$self->SUPER::characters({ Data => $data }); |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub _newline { |
400
|
|
|
|
|
|
|
# |
401
|
|
|
|
|
|
|
# output a newline character |
402
|
|
|
|
|
|
|
# |
403
|
0
|
|
|
0
|
|
|
my $self = shift; |
404
|
0
|
|
|
|
|
|
$self->SUPER::characters({ Data => "\n" }); |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub _indent_start_element { |
409
|
|
|
|
|
|
|
# |
410
|
|
|
|
|
|
|
# start new element with indentation |
411
|
|
|
|
|
|
|
# |
412
|
0
|
|
|
0
|
|
|
my( $self, $name, $params ) = @_; |
413
|
0
|
|
|
|
|
|
$self->_newline; |
414
|
0
|
|
|
|
|
|
$self->_characters( ' ' x $level ); |
415
|
0
|
|
|
|
|
|
$self->_start_element( $name, $params ); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub _indent_end_element { |
420
|
|
|
|
|
|
|
# |
421
|
|
|
|
|
|
|
# end an indented element |
422
|
|
|
|
|
|
|
# |
423
|
0
|
|
|
0
|
|
|
my $self = shift; |
424
|
0
|
|
|
|
|
|
$self->_newline; |
425
|
0
|
|
|
|
|
|
$self->_characters( ' ' x ( $level-1 )); |
426
|
0
|
|
|
|
|
|
$self->_end_element; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
sub _start_element { |
431
|
|
|
|
|
|
|
# |
432
|
|
|
|
|
|
|
# generate element start event, push name onto stack |
433
|
|
|
|
|
|
|
# |
434
|
0
|
|
|
0
|
|
|
my( $self, $name, $atts ) = @_; |
435
|
0
|
|
|
|
|
|
$self->_debug( "START ELEMENT: $name", 3 ); |
436
|
0
|
|
|
|
|
|
$level ++; |
437
|
0
|
|
|
|
|
|
push( @elements, $name ); |
438
|
0
|
0
|
|
|
|
|
if( $atts ) { |
439
|
0
|
|
|
|
|
|
$self->SUPER::start_element({ Name => $name, Attributes => $atts }); |
440
|
|
|
|
|
|
|
} else { |
441
|
0
|
|
|
|
|
|
$self->SUPER::start_element({ Name => $name }); |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub _end_element { |
447
|
|
|
|
|
|
|
# |
448
|
|
|
|
|
|
|
# generate element finished event, pop name from stack |
449
|
|
|
|
|
|
|
# |
450
|
0
|
|
|
0
|
|
|
my $self = shift; |
451
|
0
|
|
|
|
|
|
my $name = pop( @elements ); |
452
|
0
|
|
|
|
|
|
$self->_debug( "END ELEMENT: $name", 3 ); |
453
|
0
|
|
|
|
|
|
$level --; |
454
|
0
|
|
|
|
|
|
$self->SUPER::end_element({ Name => $name }); |
455
|
0
|
|
|
|
|
|
return $name; |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
sub _current_element { |
460
|
|
|
|
|
|
|
# |
461
|
|
|
|
|
|
|
# return name of current element on stack |
462
|
|
|
|
|
|
|
# |
463
|
0
|
|
|
0
|
|
|
my $self = shift; |
464
|
0
|
|
|
|
|
|
return $elements[ $#elements ]; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
sub _inside { |
469
|
|
|
|
|
|
|
# |
470
|
|
|
|
|
|
|
# return true if current element or ancestor has given name |
471
|
|
|
|
|
|
|
# |
472
|
0
|
|
|
0
|
|
|
my $self = shift; |
473
|
0
|
|
|
|
|
|
my $name = shift; |
474
|
0
|
|
|
|
|
|
foreach( @elements ) { |
475
|
0
|
0
|
|
|
|
|
return 1 if( $name eq $_ ); |
476
|
|
|
|
|
|
|
} |
477
|
0
|
|
|
|
|
|
return 0; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub _close_everything { |
482
|
|
|
|
|
|
|
# |
483
|
|
|
|
|
|
|
# close all open elements |
484
|
|
|
|
|
|
|
# |
485
|
0
|
|
|
0
|
|
|
my $self = shift; |
486
|
0
|
|
|
|
|
|
$self->_debug( "ENTER _close_everything", 2 ); |
487
|
0
|
|
|
|
|
|
while( $level ) { |
488
|
0
|
|
|
|
|
|
$self->_indent_end_element; |
489
|
|
|
|
|
|
|
} |
490
|
0
|
|
|
|
|
|
$self->_debug( "EXIT _close_everything", 2 ); |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
sub _protect_chars { |
495
|
|
|
|
|
|
|
# |
496
|
|
|
|
|
|
|
# escape special characters from parsing |
497
|
|
|
|
|
|
|
# |
498
|
0
|
|
|
0
|
|
|
my $self = shift; |
499
|
0
|
|
|
|
|
|
my $data = shift; |
500
|
0
|
|
|
|
|
|
$data =~ s/&/\001RTF-AMPERSAND\001/g; |
501
|
|
|
|
|
|
|
#$data =~ s/\\>/\001RTF-GREATER-THAN\001/g; |
502
|
|
|
|
|
|
|
#$data =~ s/\\\001RTF-LESS-THAN\001/g; |
503
|
0
|
|
|
|
|
|
return $data; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
sub _unprotect_chars { |
508
|
|
|
|
|
|
|
# |
509
|
|
|
|
|
|
|
# resolve escaped characters |
510
|
|
|
|
|
|
|
# |
511
|
0
|
|
|
0
|
|
|
my $self = shift; |
512
|
0
|
|
|
|
|
|
my $data = shift; |
513
|
0
|
|
|
|
|
|
$data =~ s/\001RTF-AMPERSAND\001/&/g; |
514
|
0
|
|
|
|
|
|
$data =~ s/\001RTF-COLON\001/:/g; |
515
|
0
|
|
|
|
|
|
$data =~ s/\001RTF-EQUALS\001/=/g; |
516
|
0
|
|
|
|
|
|
return $data; |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
sub _parse_error { |
521
|
|
|
|
|
|
|
# |
522
|
|
|
|
|
|
|
# handle parse exception |
523
|
|
|
|
|
|
|
# |
524
|
0
|
|
|
0
|
|
|
my $self = shift; |
525
|
0
|
|
|
|
|
|
print STDERR "PARSE ERROR!\n"; |
526
|
0
|
0
|
|
|
|
|
print STDERR "HERE: $1\n" if( $inbuf =~ /(...........................)/ ); |
527
|
0
|
|
|
|
|
|
exit; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub _debug { |
532
|
|
|
|
|
|
|
# |
533
|
|
|
|
|
|
|
# print a debug message |
534
|
|
|
|
|
|
|
# |
535
|
0
|
|
|
0
|
|
|
my( $self, $message, $level ) = @_; |
536
|
0
|
0
|
|
|
|
|
if( $features{DEBUG} >= $level ) { |
537
|
0
|
|
|
|
|
|
print STDERR "XML::SAX::RTF DEBUG-$level> $message\n"; |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
1; |
543
|
|
|
|
|
|
|
__END__ |