| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package FrameMaker::MifTree; |
|
2
|
|
|
|
|
|
|
# $Id: MifTree.pm 2 2006-05-02 11:15:26Z roel $ |
|
3
|
3
|
|
|
3
|
|
97861
|
use 5.008_001; # minimum version for Unicode support |
|
|
3
|
|
|
|
|
11
|
|
|
|
3
|
|
|
|
|
140
|
|
|
4
|
3
|
|
|
3
|
|
18
|
use strict; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
113
|
|
|
5
|
3
|
|
|
3
|
|
17
|
use warnings; |
|
|
3
|
|
|
|
|
11
|
|
|
|
3
|
|
|
|
|
135
|
|
|
6
|
3
|
|
|
3
|
|
25
|
use warnings::register; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
608
|
|
|
7
|
3
|
|
|
3
|
|
112
|
use Carp; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
311
|
|
|
8
|
3
|
|
|
3
|
|
4392
|
use File::Temp; |
|
|
3
|
|
|
|
|
85169
|
|
|
|
3
|
|
|
|
|
255
|
|
|
9
|
3
|
|
|
3
|
|
2693
|
use IO::Tokenized ':parse'; # These are... |
|
|
3
|
|
|
|
|
6782
|
|
|
|
3
|
|
|
|
|
817
|
|
|
10
|
3
|
|
|
3
|
|
3000
|
use IO::Tokenized::File; # ... from CPAN. |
|
|
3
|
|
|
|
|
7728
|
|
|
|
3
|
|
|
|
|
155
|
|
|
11
|
3
|
|
|
3
|
|
9987
|
use IO::Tokenized::Scalar; # Subclass in IO::Tokenized::File style, |
|
|
3
|
|
|
|
|
23
|
|
|
|
3
|
|
|
|
|
148
|
|
|
12
|
|
|
|
|
|
|
# uses IO::Scalar from the IO::Stringy bundle. |
|
13
|
3
|
|
|
3
|
|
4485
|
use Tree::DAG_Node 1.04; # Get this module from CPAN. |
|
|
3
|
|
|
|
|
76737
|
|
|
|
3
|
|
|
|
|
187
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
FrameMaker::MifTree - A MIF Parser |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 VERSION |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
This document describes version 0.075, released 2 May 2006. |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
use FrameMaker::MifTree; |
|
26
|
|
|
|
|
|
|
my $mif = FrameMaker::MifTree->new; |
|
27
|
|
|
|
|
|
|
$mif->parse_miffile('filename.mif'); |
|
28
|
|
|
|
|
|
|
@strings = $mif->daughters_by_name('String', recurse => 1); |
|
29
|
|
|
|
|
|
|
print $strings[0]->string; |
|
30
|
|
|
|
|
|
|
$strings[3]->string('Just another new string.'); |
|
31
|
|
|
|
|
|
|
$mif->dump_miffile('newmif.mif'); |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
The FrameMaker::MifTree class is implemented as a Tree::DAG_Node subclass, and |
|
36
|
|
|
|
|
|
|
thus inherits all the methods of that class. Two methods are overridden. Please |
|
37
|
|
|
|
|
|
|
read L to see what other methods are available. |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
MIF (Maker Interchange Format) is an Adobe FrameMaker file format in ASCII, |
|
40
|
|
|
|
|
|
|
consisting of statements that create an easily parsed, readable text file of |
|
41
|
|
|
|
|
|
|
all the text, graphics, formatting, and layout constructs that FrameMaker |
|
42
|
|
|
|
|
|
|
understands. Because MIF is an alternative representation of a FrameMaker |
|
43
|
|
|
|
|
|
|
document, it allows FrameMaker and other applications to exchange information |
|
44
|
|
|
|
|
|
|
while preserving graphics, document content, and format. |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
This document does not tell you what the syntax of a MIF file is, nor does it |
|
47
|
|
|
|
|
|
|
document the meaning of the MIF statements. For this, please read (and re-read) |
|
48
|
|
|
|
|
|
|
the MIF_Reference.pdf, provided by Adobe. |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
MifTree not only knows the MIF syntax, but it also has some understanding of |
|
51
|
|
|
|
|
|
|
the allowed structures (within their contexts) and attribute types. The file |
|
52
|
|
|
|
|
|
|
FrameMaker/MifTree/MifTreeTags holds all the valid MIF statements and the |
|
53
|
|
|
|
|
|
|
attribute type for every statement. This file may need some improvement, as it |
|
54
|
|
|
|
|
|
|
is created by analyzing a large collection of MIF files written by FrameMaker |
|
55
|
|
|
|
|
|
|
(and an automatic analysis of the I, which showed several typos |
|
56
|
|
|
|
|
|
|
and inconsistencies in that manual). The current file is for MIF version 7.00. |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head2 Dependencies |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
This class implementation depends on the following modules, all available from |
|
61
|
|
|
|
|
|
|
CPAN: |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=over 4 |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=item * |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Tree::DAG_Node |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=item * |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
IO::Tokenized and IO::Tokenized::File and the custom-made IO::Tokenized::Scalar |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item * |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
IO::Stringy (only IO::Scalar is needed) |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=back |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
BEGIN { |
|
82
|
3
|
|
|
3
|
|
43
|
use Exporter (); |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
427
|
|
|
83
|
3
|
|
|
3
|
|
8
|
our $VERSION = 0.075; |
|
84
|
3
|
|
|
|
|
96
|
our @ISA = qw(Tree::DAG_Node Exporter); |
|
85
|
3
|
|
|
|
|
10
|
our @EXPORT = qw("e &unquote &encode_path &decode_path &convert); |
|
86
|
3
|
|
|
|
|
8
|
our @EXPORT_OK = qw(%fmcharset %fmnamedchars); |
|
87
|
3
|
|
|
|
|
22235
|
our %EXPORT_TAGS = (); |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
our @EXPORT_OK; |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
our (%mifnodes, %mifleaves, %attribute_types, %fmcharset, %fmnamedchars); |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
our $use_unicode; |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
for my $do (qw(FrameMaker/MifTree/MifTreeTags FrameMaker/MifTree/FmCharset)) { |
|
96
|
|
|
|
|
|
|
do $do or croak $! || $@; |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
our $fm_to_unicode = '$s =~ tr/' . |
|
99
|
|
|
|
|
|
|
join('', map { sprintf '\x%02x', ord } keys %fmcharset) . '/' . |
|
100
|
|
|
|
|
|
|
join('', map { sprintf '\x{%04x}', ord } values %fmcharset) . '/'; |
|
101
|
|
|
|
|
|
|
our $unicode_to_fm = '$s =~ tr/' . |
|
102
|
|
|
|
|
|
|
join('', map { sprintf '\x{%04x}', ord } values %fmcharset) . '/' . |
|
103
|
|
|
|
|
|
|
join('', map { sprintf '\x%02x', ord } keys %fmcharset) . '/'; |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
our $default_unit = ''; |
|
106
|
|
|
|
|
|
|
our @parserdefinition = ( |
|
107
|
|
|
|
|
|
|
[ COMMENT => qr/#.*/ ], |
|
108
|
|
|
|
|
|
|
[ RANGLE => qr/>/, sub{''} ], |
|
109
|
|
|
|
|
|
|
[ MIFTAG => qr/<\s*[a-z][a-z0-9]*/i, sub {(my $m = shift) =~ s/^/; $m;} ], |
|
110
|
|
|
|
|
|
|
[ ATTRIBS => qr/`.*?'|[^=&>#]+/ ], |
|
111
|
|
|
|
|
|
|
[ FACET => qr/[=&].+/ ], |
|
112
|
|
|
|
|
|
|
[ MACRO => qr/define\s*\(.*?\)/ ] |
|
113
|
|
|
|
|
|
|
); |
|
114
|
|
|
|
|
|
|
our %unit_to_factor = ( |
|
115
|
|
|
|
|
|
|
'' => 1 / 72, |
|
116
|
|
|
|
|
|
|
pt => 1 / 72, |
|
117
|
|
|
|
|
|
|
point => 1 / 72, |
|
118
|
|
|
|
|
|
|
q(") => 1, |
|
119
|
|
|
|
|
|
|
in => 1, |
|
120
|
|
|
|
|
|
|
mm => 1 / 25.4, |
|
121
|
|
|
|
|
|
|
millimeter => 1 / 25.4, |
|
122
|
|
|
|
|
|
|
cm => 1 / 2.54, |
|
123
|
|
|
|
|
|
|
centimeter => 1 / 2.54, |
|
124
|
|
|
|
|
|
|
pc => 1 / 6, |
|
125
|
|
|
|
|
|
|
pica => 1 / 6, |
|
126
|
|
|
|
|
|
|
dd => 0.01483, |
|
127
|
|
|
|
|
|
|
didot => 0.01483, |
|
128
|
|
|
|
|
|
|
cc => 12 * 0.01483, |
|
129
|
|
|
|
|
|
|
cicero => 12 * 0.01483 |
|
130
|
|
|
|
|
|
|
); |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=head2 Overridden Methods |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=over 4 |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item C |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Adds a list of daughter object to a node. The difference with the DAG_Node |
|
139
|
|
|
|
|
|
|
method is that it checks for a valid MIF construct. Only the mother/daughter |
|
140
|
|
|
|
|
|
|
relationship is checked. |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=cut |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub add_daughters { |
|
145
|
|
|
|
|
|
|
# extends functionality of Tree::DAG_Node's sub |
|
146
|
14
|
|
|
14
|
1
|
28
|
my($mother, @daughters) = @_; |
|
147
|
|
|
|
|
|
|
|
|
148
|
14
|
100
|
66
|
|
|
53
|
if (ref $mother && $mother->name) { # only when called on object and if |
|
149
|
|
|
|
|
|
|
# we know the name of the mother |
|
150
|
|
|
|
|
|
|
# check for allowed daughters |
|
151
|
12
|
50
|
33
|
|
|
1483
|
if (warnings::enabled || $^W) { |
|
152
|
12
|
|
|
|
|
20
|
for my $daughter (@daughters) { |
|
153
|
12
|
50
|
0
|
|
|
26
|
warnings::warn 'Node "' . ($mother->name || '') . |
|
|
|
|
0
|
|
|
|
|
|
154
|
|
|
|
|
|
|
'" does not allow daughter "' . ($daughter->name || '') . '"' |
|
155
|
|
|
|
|
|
|
unless $mother->allows_daughter($daughter); |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
14
|
|
|
|
|
212
|
$mother->SUPER::add_daughters(@daughters); |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=item C |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
The attributes method of the FrameMaker::MifTree class does not require a |
|
166
|
|
|
|
|
|
|
reference as an attribute, as does the DAG_Node equivalent. As an extra, the |
|
167
|
|
|
|
|
|
|
method checks if the method is called on a leaf, since the MIF structure does |
|
168
|
|
|
|
|
|
|
not allow attributes on non-ending nodes. The method reads/sets the raw |
|
169
|
|
|
|
|
|
|
attribute, no string conversion, path encoding/decoding or value extraction is |
|
170
|
|
|
|
|
|
|
done. To obtain or set one of those values, use the specific L
|
|
171
|
|
|
|
|
|
|
Methods> mentioned below. |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=cut |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub attributes { # read/write attribute-method |
|
176
|
|
|
|
|
|
|
# overrides Tree::DAG_Node's sub -- doesn't carp that 'attributes' needs |
|
177
|
|
|
|
|
|
|
# to be a ref |
|
178
|
57
|
|
|
57
|
1
|
85
|
my $this = shift; |
|
179
|
57
|
50
|
|
|
|
98
|
croak 'Must be called on object' unless ref $this; |
|
180
|
57
|
100
|
|
|
|
124
|
$this->{attributes} = $_[0] if (@_); |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# check if the attribute is valid |
|
183
|
57
|
50
|
33
|
|
|
5867
|
if ((warnings::enabled || $^W) && ! $this->check_attribute) { |
|
|
|
|
33
|
|
|
|
|
|
184
|
0
|
|
|
|
|
0
|
warnings::warn $this->get_attribute_error; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
57
|
|
|
|
|
173
|
return $this->{attributes}; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=back |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=head2 Quick Creators |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
The following methods can be used instead of the DAG_Node standard methods to |
|
195
|
|
|
|
|
|
|
build your MIF structure. It's just a lazy way of adding daughters, but it |
|
196
|
|
|
|
|
|
|
improves readability of your code if you create something like: |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
my $mif = FrameMaker::MifTree->new->add_node( |
|
199
|
|
|
|
|
|
|
AFrames => FrameMaker::MifTree->add_node( |
|
200
|
|
|
|
|
|
|
Frame => FrameMaker::MifTree->add_node( |
|
201
|
|
|
|
|
|
|
ImportObject => FrameMaker::MifTree->add_leaf( |
|
202
|
|
|
|
|
|
|
ImportObFileDI => encode_path('c:\bar\foo.eps')) |
|
203
|
|
|
|
|
|
|
), |
|
204
|
|
|
|
|
|
|
FrameMaker::MifTree->add_node( |
|
205
|
|
|
|
|
|
|
ImportObject => FrameMaker::MifTree->add_leaf( |
|
206
|
|
|
|
|
|
|
ImportObFileDI => encode_path('../../foo/boo.eps')) |
|
207
|
|
|
|
|
|
|
) |
|
208
|
|
|
|
|
|
|
) |
|
209
|
|
|
|
|
|
|
); |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=over 4 |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=item C |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Adds a new daughter to the object. The first argument specifies the name, all |
|
216
|
|
|
|
|
|
|
the following arguments are taken either as the attribute for the leaf, or as a |
|
217
|
|
|
|
|
|
|
list of granddaughter objects to add to the newly created daughter. (In MIFTree |
|
218
|
|
|
|
|
|
|
world, newly born daughters mature in split seconds.) |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=cut |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub add_leaf { |
|
223
|
|
|
|
|
|
|
# same sub to add either a leaf (2nd argument is a scalar) or a node (2nd |
|
224
|
|
|
|
|
|
|
# argument is a FrameMaker::MifTree object) |
|
225
|
13
|
|
|
13
|
1
|
27
|
my ($this, $name, @that) = @_; |
|
226
|
|
|
|
|
|
|
|
|
227
|
13
|
|
33
|
|
|
30
|
my $class = ref($this) || $this; |
|
228
|
13
|
|
|
|
|
36
|
my $daughter = $class->new(); |
|
229
|
13
|
|
|
|
|
363
|
$daughter->name($name); |
|
230
|
13
|
50
|
33
|
|
|
86
|
if ( ref $that[0] && $that[0]->isa('FrameMaker::MifTree') ) { |
|
231
|
|
|
|
|
|
|
# assume list of nodes |
|
232
|
0
|
|
|
|
|
0
|
$daughter->add_daughters(@that); |
|
233
|
|
|
|
|
|
|
} else { # probably dealing with an attribute for the leaf |
|
234
|
13
|
|
|
|
|
37
|
$daughter->attributes($that[0]); |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
|
|
237
|
13
|
50
|
|
|
|
50
|
$this->add_daughters($daughter) if (ref $this) ; # called on object |
|
238
|
|
|
|
|
|
|
|
|
239
|
13
|
|
|
|
|
816
|
return $daughter; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=item C |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
An exact synonym for the C method. |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=cut |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub add_node { # alias |
|
249
|
13
|
|
|
13
|
1
|
26
|
my ($it, @them) = @_; |
|
250
|
13
|
|
|
|
|
24
|
$it->add_leaf(@them); |
|
251
|
|
|
|
|
|
|
} |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=item C |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Adds a facet to the object. In DAG_Node tree terms, this is implemented as a |
|
256
|
|
|
|
|
|
|
leaf with the name "_facet" and a filehandle to a temp file as its attribute. |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=cut |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub add_facet { |
|
261
|
0
|
|
|
0
|
1
|
0
|
my $this = $_[0]; |
|
262
|
|
|
|
|
|
|
|
|
263
|
0
|
|
0
|
|
|
0
|
my $class = ref($this) || $this; |
|
264
|
0
|
|
|
|
|
0
|
my $daughter = $class->new(); |
|
265
|
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
0
|
$daughter->name('_facet'); |
|
267
|
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
0
|
my $fh = File::Temp::tempfile(); |
|
269
|
0
|
|
|
|
|
0
|
$daughter->attributes($fh); |
|
270
|
|
|
|
|
|
|
|
|
271
|
0
|
0
|
|
|
|
0
|
$this->add_daughters($daughter) if (ref $this) ; # called on object |
|
272
|
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
0
|
return $daughter; |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
=back |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=head2 Search in Tree |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=over 4 |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=item C<$OBJ-Edaughters_by_name(NAMESTRING, recurse =E BOOLEAN)> |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
Find all daughters that listen to the name NAMESTRING, either walking the tree |
|
285
|
|
|
|
|
|
|
("recurse" is true), or only on the mother's daughters ("recurse" false or |
|
286
|
|
|
|
|
|
|
omitted; the latter throws a warning that it will not recurse -- I've spent too |
|
287
|
|
|
|
|
|
|
much time debugging code where I forgot to add the "recurse" parameter). Returns |
|
288
|
|
|
|
|
|
|
the first object in scalar context, or a list of all found objects in list |
|
289
|
|
|
|
|
|
|
context. |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Maybe one day I'll add magic to this function so you get the next item if you |
|
292
|
|
|
|
|
|
|
call the method on the same object without arguments. |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Note that "daughter_by_name" is an exact alias for this method. |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
=cut |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
sub daughters_by_name { |
|
299
|
7
|
|
|
7
|
1
|
47
|
my ($obj, $name, $recurse, $rec_val) = @_[0 .. 3]; |
|
300
|
7
|
|
|
|
|
9
|
my $wantsarray = wantarray; |
|
301
|
7
|
50
|
|
|
|
20
|
$rec_val = $recurse, $recurse = 'recurse' if @_ == 3; # backward compatible |
|
302
|
7
|
50
|
33
|
|
|
663
|
if ((warnings::enabled || $^W) && ! defined $recurse) { |
|
|
|
|
33
|
|
|
|
|
|
303
|
0
|
|
|
|
|
0
|
warnings::warn 'daughters_by_name will NOT recurse'; |
|
304
|
|
|
|
|
|
|
} |
|
305
|
7
|
|
100
|
|
|
21
|
$rec_val ||= 0; |
|
306
|
7
|
|
|
|
|
10
|
my @found = (); |
|
307
|
7
|
|
|
|
|
26
|
for my $daughter ($obj->daughters) { |
|
308
|
|
|
|
|
|
|
$daughter->walk_down({ |
|
309
|
|
|
|
|
|
|
callback => sub { |
|
310
|
41
|
100
|
66
|
41
|
|
724
|
push @found, $_[0] if (defined $_[0]->name && $_[0]->name eq $name); |
|
311
|
41
|
50
|
100
|
|
|
577
|
$rec_val = 0 if ($rec_val && @found && ! $wantsarray); # stop searching |
|
|
|
|
66
|
|
|
|
|
|
312
|
41
|
|
|
|
|
73
|
return $rec_val; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
17
|
|
|
|
|
234
|
}); |
|
315
|
|
|
|
|
|
|
} |
|
316
|
7
|
50
|
|
|
|
106
|
return $wantsarray ? @found : $found[0]; |
|
317
|
|
|
|
|
|
|
} |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item C<$OBJ-Edaughter_by_name(NAMESTRING, recurse =E BOOLEAN)> |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
Alias for "daughters_by_name". |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=cut |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub daughter_by_name { # alias |
|
326
|
0
|
|
|
0
|
1
|
0
|
my ($it, @them) = @_; |
|
327
|
0
|
|
|
|
|
0
|
$it->daughters_by_name(@them); |
|
328
|
|
|
|
|
|
|
} |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=item C<$OBJ-Edaughters_by_name_and_attr(NAMESTRING, ATTRIBUTE, recurse |
|
331
|
|
|
|
|
|
|
=E BOOLEAN)> |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Find all daughters that listen to the name NAMESTRING and have the raw |
|
334
|
|
|
|
|
|
|
attribute ATTRIBUTE, either walking the tree ("recurse" is true), or only on |
|
335
|
|
|
|
|
|
|
the mother's daughters ("recurse" false or omitted). Returns the first object |
|
336
|
|
|
|
|
|
|
in scalar context, or a list of all found objects in list context. ATTRIBUTE |
|
337
|
|
|
|
|
|
|
must be raw data, so use C, C, C and |
|
338
|
|
|
|
|
|
|
C as appropriate. |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
If you specify an empty string or undef as the NAMESTRING, this method will |
|
341
|
|
|
|
|
|
|
just look for ATTRIBUTE. |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
Note that "daughters_by_name_and_attr" is an exact alias for this method. |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=cut |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub daughters_by_name_and_attr { |
|
348
|
0
|
|
|
0
|
1
|
0
|
my ($obj, $name, $attr, $recurse, $rec_val) = @_[0 .. 4]; |
|
349
|
0
|
|
|
|
|
0
|
my $wantsarray = wantarray; |
|
350
|
0
|
0
|
|
|
|
0
|
$rec_val = $recurse, $recurse = 'recurse' if @_ == 4; # backward compatible |
|
351
|
0
|
0
|
0
|
|
|
0
|
if ((warnings::enabled || $^W) && ! defined $recurse) { |
|
|
|
|
0
|
|
|
|
|
|
352
|
0
|
|
|
|
|
0
|
warnings::warn 'daughters_by_name will NOT recurse'; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
0
|
|
0
|
|
|
0
|
$rec_val ||= 0; |
|
355
|
0
|
|
|
|
|
0
|
my @found = (); |
|
356
|
0
|
|
|
|
|
0
|
for my $daughter ($obj->daughters) { |
|
357
|
|
|
|
|
|
|
$daughter->walk_down({ |
|
358
|
|
|
|
|
|
|
callback => sub { |
|
359
|
0
|
0
|
|
0
|
|
0
|
if ( $_[0]->is_leaf ) { |
|
360
|
0
|
0
|
0
|
|
|
0
|
if ( (!$name || (defined $_[0]->name && $_[0]->name eq $name)) |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
361
|
|
|
|
|
|
|
&& (defined $_[0]->attributes && $_[0]->attributes eq $attr) ) { |
|
362
|
0
|
|
|
|
|
0
|
push @found, $_[0]; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
} |
|
365
|
0
|
0
|
0
|
|
|
0
|
$rec_val = 0 if ($rec_val && @found && ! $wantsarray); # stop searching |
|
|
|
|
0
|
|
|
|
|
|
366
|
0
|
|
|
|
|
0
|
return $rec_val; |
|
367
|
|
|
|
|
|
|
} |
|
368
|
0
|
|
|
|
|
0
|
}); |
|
369
|
|
|
|
|
|
|
} |
|
370
|
0
|
0
|
|
|
|
0
|
return $wantsarray ? @found : $found[0]; |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=item C<$OBJ-Edaughter_by_name_and_attr(NAMESTRING, ATTRIBUTE, recurse |
|
374
|
|
|
|
|
|
|
=E BOOLEAN)> |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
Alias for "daughters_by_name_and_attr". |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=cut |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
sub daughter_by_name_and_attr { # alias |
|
381
|
0
|
|
|
0
|
1
|
0
|
my ($it, @them) = @_; |
|
382
|
0
|
|
|
|
|
0
|
$it->daughters_by_name_and_attr(@them); |
|
383
|
|
|
|
|
|
|
} |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=item C<$OBJ-Efind_string(QUOTED_REGEX)> |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Returns a list of all strings that match QUOTED_REGEX under $OBJ. When called |
|
388
|
|
|
|
|
|
|
in scalar context, only the first match is returned. The string is in Unicode |
|
389
|
|
|
|
|
|
|
if the global modifier Cuse_unicode> is set (off by |
|
390
|
|
|
|
|
|
|
default.) |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=cut |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
sub find_string { |
|
395
|
0
|
|
|
0
|
1
|
0
|
my ($obj, $re, $use_unicode_deprecated) = @_[0 .. 2]; |
|
396
|
0
|
|
|
|
|
0
|
my $wantsarray = wantarray; |
|
397
|
0
|
|
|
|
|
0
|
my @found = (); |
|
398
|
0
|
|
|
|
|
0
|
for my $str_obj ($obj->daughters_by_name('String', recurse => 1)) { |
|
399
|
0
|
|
|
|
|
0
|
my $string = $str_obj->string(undef, $use_unicode_deprecated); |
|
400
|
0
|
0
|
|
|
|
0
|
push @found, $string if $string =~ /$re/; |
|
401
|
0
|
0
|
0
|
|
|
0
|
last if @found && ! $wantsarray; |
|
402
|
|
|
|
|
|
|
} |
|
403
|
0
|
0
|
|
|
|
0
|
return $wantsarray ? @found : $found[0]; |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=item C<$OBJ-Echarleaves_to_strings()> |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
Changes all the leaves with the name "Char" below $OBJ to their equivalent |
|
409
|
|
|
|
|
|
|
String leaves. This has no effect on the content of the MIF file; it just makes |
|
410
|
|
|
|
|
|
|
the file less ambiguous. Returns undef. |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=cut |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
#TODO I intend to move these two methods to a separate class later |
|
415
|
|
|
|
|
|
|
sub charleaves_to_strings { |
|
416
|
1
|
|
|
1
|
1
|
2
|
my $obj = $_[0]; |
|
417
|
1
|
|
|
|
|
58
|
local $use_unicode = 1; |
|
418
|
1
|
|
|
|
|
4
|
for ($obj->daughters_by_name('Char', recurse => 1)) { |
|
419
|
2
|
|
|
|
|
14
|
my $new_att_string = $fmnamedchars{$_->attribute}; |
|
420
|
2
|
|
|
|
|
7
|
$_->name('String'); |
|
421
|
2
|
|
|
|
|
13
|
$_->string($new_att_string); |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=item C<$OBJ-Efold_strings()> |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
This method folds all subsequent paragraph lines in a paragraph into one |
|
428
|
|
|
|
|
|
|
paragraph line. If you want to do operations on text, you should first use this |
|
429
|
|
|
|
|
|
|
method on (part of) the tree. In MIF, the flow of text over the lines is |
|
430
|
|
|
|
|
|
|
maintained, but since this information is not used while FrameMaker parses the |
|
431
|
|
|
|
|
|
|
MIF file, it is safe to remove this information. Returns undef. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
All "Char" leaves except a "HardReturn" are transformed to their string |
|
434
|
|
|
|
|
|
|
equivalents. A "HardReturn" character forces a new paragraph line. |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=cut |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub fold_strings { |
|
439
|
1
|
|
|
1
|
1
|
103
|
my $obj = $_[0]; |
|
440
|
1
|
|
|
|
|
3
|
local $use_unicode = 0; |
|
441
|
1
|
|
|
|
|
5
|
for my $para ($obj->daughters_by_name('Para', recurse => 1)) { |
|
442
|
|
|
|
|
|
|
|
|
443
|
1
|
|
|
|
|
5
|
$para->charleaves_to_strings; |
|
444
|
|
|
|
|
|
|
|
|
445
|
1
|
|
|
|
|
2
|
my $first_paraline; |
|
446
|
1
|
|
|
|
|
6
|
for my $daughter ($para->daughters) { |
|
447
|
4
|
50
|
|
|
|
19
|
if ($daughter->name ne 'ParaLine') { |
|
|
|
100
|
|
|
|
|
|
|
448
|
0
|
|
|
|
|
0
|
$first_paraline = undef; |
|
449
|
|
|
|
|
|
|
} elsif ( ! defined $first_paraline ) { |
|
450
|
1
|
|
|
|
|
10
|
$first_paraline = $daughter; |
|
451
|
|
|
|
|
|
|
} else { |
|
452
|
3
|
|
|
|
|
21
|
my @strobj = $first_paraline->daughters_by_name('String', recurse => 0); |
|
453
|
3
|
100
|
66
|
|
|
12
|
if (@strobj && $strobj[-1]->string =~ /\x09$/) { # character HardReturn |
|
454
|
2
|
|
|
|
|
4
|
$first_paraline = $daughter; # forces new ParaLine |
|
455
|
|
|
|
|
|
|
} else { |
|
456
|
1
|
|
|
|
|
9
|
$first_paraline->add_daughters( |
|
457
|
1
|
|
|
|
|
5
|
grep {$_->name ne 'TextRectID'} $daughter->daughters |
|
458
|
|
|
|
|
|
|
); |
|
459
|
1
|
|
|
|
|
96
|
$para->remove_daughter($daughter); |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
} |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
|
|
464
|
1
|
|
|
|
|
48
|
for my $paraline ($para->daughters_by_name('ParaLine', recurse => 0)) { |
|
465
|
3
|
|
|
|
|
54
|
my $first_str; |
|
466
|
3
|
|
|
|
|
10
|
for my $daughter ($paraline->daughters) { |
|
467
|
7
|
50
|
|
|
|
52
|
if ($daughter->name ne 'String') { |
|
|
|
100
|
|
|
|
|
|
|
468
|
0
|
|
|
|
|
0
|
$first_str = undef; |
|
469
|
|
|
|
|
|
|
} elsif ( ! defined $first_str ) { |
|
470
|
3
|
|
|
|
|
21
|
$first_str = $daughter; |
|
471
|
|
|
|
|
|
|
} else { |
|
472
|
4
|
|
|
|
|
22
|
(my $str = $daughter->string) =~ tr/\x06//d; |
|
473
|
4
|
|
|
|
|
11
|
$first_str->string($first_str->string . $str); |
|
474
|
4
|
|
|
|
|
13
|
$paraline->remove_daughter($daughter); |
|
475
|
|
|
|
|
|
|
} |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
} |
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=back |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=head2 Attribute Methods |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=over 4 |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=item C<$OBJ-Estring(STRING)> |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
Reads or sets the object's attribute as a MIF string. The method just calls |
|
491
|
|
|
|
|
|
|
C and C as appropriate. |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
If the global modifier Cuse_unicode> is set to true, |
|
494
|
|
|
|
|
|
|
the string will be converted from Unicode to the FrameMaker character set |
|
495
|
|
|
|
|
|
|
first. (The method now throws a warning when you specify USE_UNICODE as the |
|
496
|
|
|
|
|
|
|
second argument.) |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=cut |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
sub string { # read/write attribute-method |
|
501
|
17
|
|
|
17
|
1
|
33
|
my ($this, $new_val, $unicode_deprecated) = @_[0 .. 2]; |
|
502
|
17
|
100
|
|
|
|
34
|
$this->attributes(quote($new_val, $unicode_deprecated)) if defined $new_val; |
|
503
|
17
|
|
|
|
|
33
|
return unquote($this->attributes, $unicode_deprecated); |
|
504
|
|
|
|
|
|
|
} |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=item C<$OBJ-Epathname(PATHSTRING)> |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
Returns the object's attribute as local pathname, or sets it to the device |
|
509
|
|
|
|
|
|
|
independent pathname. The method just calls C and C |
|
510
|
|
|
|
|
|
|
as appropriate. PATHSTRING must also be a local pathname. |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=cut |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
sub pathname { # read/write attribute-method |
|
515
|
0
|
|
|
0
|
1
|
0
|
my $this = shift; |
|
516
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on object' unless ref $this; |
|
517
|
0
|
0
|
|
|
|
0
|
$this->attributes(encode_path($_[0])) if (@_); |
|
518
|
0
|
|
|
|
|
0
|
return decode_path($this->attributes); |
|
519
|
|
|
|
|
|
|
} |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=item C<$OBJ-Eabs_pathname(FROMROOT)> |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Returns the object's attribute as a local pathname. The method just calls |
|
524
|
|
|
|
|
|
|
C, passing on the FROMROOT argument. Use this method if you want |
|
525
|
|
|
|
|
|
|
to make sure that you always receive absolute pathnames, independently from |
|
526
|
|
|
|
|
|
|
what is stored in the attribute. |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=cut |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
sub abs_pathname { # read/write attribute-method |
|
531
|
0
|
|
|
0
|
1
|
0
|
my ($this, $root) = @_[0, 1]; |
|
532
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on object' unless ref $this; |
|
533
|
0
|
|
|
|
|
0
|
return decode_path($this->attributes, $root); |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=item C<$OBJ-Eboolean(BOOLEAN)> |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
Returns or sets the object's TRUE or FALSE value. |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=cut |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
sub boolean { # read/write attribute-method |
|
543
|
0
|
|
|
0
|
1
|
0
|
my $this = shift; |
|
544
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on object' unless ref $this; |
|
545
|
0
|
0
|
|
|
|
0
|
$this->attributes($_[0] ? 'Yes' : 'No') if (@_); |
|
|
|
0
|
|
|
|
|
|
|
546
|
0
|
0
|
|
|
|
0
|
return $this->attributes eq 'Yes' ? 1 : $this->attributes eq 'No' ? 0 : undef; |
|
|
|
0
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=item C<$OBJ-Emeasurements(LIST)> |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Returns or sets a list of measurements. When called in scalar context, only the |
|
552
|
|
|
|
|
|
|
first measurement is returned. Everything is in the default unit of |
|
553
|
|
|
|
|
|
|
measurement. (Can be set using Cdefault_unit>. If this |
|
554
|
|
|
|
|
|
|
variable is set to the empty string (which also happens to be the default), |
|
555
|
|
|
|
|
|
|
points are output.) You always get the values without the unit specifier, so |
|
556
|
|
|
|
|
|
|
calculations can be made directly on this. To get a value from the list, do |
|
557
|
|
|
|
|
|
|
something like: |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
my $q; |
|
560
|
|
|
|
|
|
|
$q = FrameMaker::MifTree->new->add_leaf( |
|
561
|
|
|
|
|
|
|
PgfCellMargins => "0.0 pt 1.0 pt 2.0 pt 3.0 pt" |
|
562
|
|
|
|
|
|
|
); |
|
563
|
|
|
|
|
|
|
my $k = ($q->measurements)[1]; |
|
564
|
|
|
|
|
|
|
print "k is now: $k\n" # prints "k is now: 1" |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
In MIF, a maximum of four values can be supplied, but this is never checked by |
|
567
|
|
|
|
|
|
|
this method. |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=cut |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
sub measurements { # read/write attribute-method |
|
572
|
0
|
|
|
0
|
1
|
0
|
my $this = shift; |
|
573
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on object' unless ref $this; |
|
574
|
0
|
0
|
|
|
|
0
|
$this->attributes( join(' ', map { convert($_, 'pt') } @_) ) if @_; |
|
|
0
|
|
|
|
|
0
|
|
|
575
|
0
|
|
|
|
|
0
|
my @mlist = (); |
|
576
|
0
|
|
|
|
|
0
|
my $attribute = $this->attributes; |
|
577
|
0
|
|
|
|
|
0
|
while ( $attribute =~ /\G(\d*\.?\d+\D*)/gi ) { |
|
578
|
0
|
|
|
|
|
0
|
push @mlist, $1; |
|
579
|
|
|
|
|
|
|
} |
|
580
|
0
|
|
|
|
|
0
|
@mlist = map { convert($_, undef, 1) } @mlist; |
|
|
0
|
|
|
|
|
0
|
|
|
581
|
0
|
0
|
|
|
|
0
|
return wantarray ? @mlist : $mlist[0]; |
|
582
|
|
|
|
|
|
|
} |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=item C<$OBJ-Epercentage(FRACTION)> |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
Returns or sets the object's percentage value as a fraction (1 = 100%). |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=cut |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
sub percentage { # read/write attribute-method |
|
591
|
0
|
|
|
0
|
1
|
0
|
my $this = shift; |
|
592
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on object' unless ref $this; |
|
593
|
0
|
0
|
|
|
|
0
|
$this->attributes($_[0] * 100 . '%') if @_; |
|
594
|
0
|
|
|
|
|
0
|
my ($value) = $this->attributes =~ /\d*\.?\d+/; |
|
595
|
0
|
|
|
|
|
0
|
return $value / 100; |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=item C<$OBJ-Efacet_data()> |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
Returns the object's facet data as a list of lines. (Use a C to |
|
601
|
|
|
|
|
|
|
C to set the objects data. Not a very elegant implementation, but |
|
602
|
|
|
|
|
|
|
I consider a facet to be rather esoteric, and we have to be efficient on memory |
|
603
|
|
|
|
|
|
|
usage as well...) |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=cut |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
sub facet_data { # read-only attribute-method |
|
608
|
0
|
|
|
0
|
1
|
0
|
my $this = shift; |
|
609
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on object' unless ref $this; |
|
610
|
0
|
|
|
|
|
0
|
my $fh = $this->facet_handle; |
|
611
|
0
|
0
|
|
|
|
0
|
if ($fh) { |
|
612
|
0
|
|
|
|
|
0
|
sysseek $fh, 0, Fcntl::SEEK_SET; |
|
613
|
0
|
|
|
|
|
0
|
my @list = <$fh>; |
|
614
|
0
|
|
|
|
|
0
|
return @list; |
|
615
|
|
|
|
|
|
|
} else { |
|
616
|
0
|
0
|
0
|
|
|
0
|
warnings::warn 'No facet data available' if warnings::enabled || $^W; |
|
617
|
|
|
|
|
|
|
} |
|
618
|
|
|
|
|
|
|
} |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=item C<$OBJ-Efacet_handle()> |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
Returns the filehandle to the object's facet data. Since the temporary file is |
|
623
|
|
|
|
|
|
|
sysopened, you should use C instead of C to respect the |
|
624
|
|
|
|
|
|
|
buffering considerations. |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=cut |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
sub facet_handle { # read-only attribute-method |
|
629
|
0
|
|
|
0
|
1
|
0
|
my $this = shift; |
|
630
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on object' unless ref $this; |
|
631
|
0
|
0
|
|
|
|
0
|
$this = $this->daughters_by_name( |
|
632
|
|
|
|
|
|
|
'_facet', |
|
633
|
|
|
|
|
|
|
recurse => 0, |
|
634
|
|
|
|
|
|
|
) unless $this->name eq '_facet'; |
|
635
|
0
|
0
|
|
|
|
0
|
croak 'Must be called on facet' unless $this->name eq '_facet'; |
|
636
|
0
|
0
|
|
|
|
0
|
return fileno($this->attributes) ? $this->attributes : undef; |
|
637
|
|
|
|
|
|
|
} |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=item Cdefault_unit(UNIT)> |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
This class method returns or sets the global default units of measurement. See |
|
642
|
|
|
|
|
|
|
C for a list of valid assignments. |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
FrameMaker::MifTree's default units of measurement can (and probably will) |
|
645
|
|
|
|
|
|
|
differ from the default that are specified in the MIF file. |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
The default for C is an empty string, which means that no unit |
|
648
|
|
|
|
|
|
|
specifier will be output, and all values are in "points". |
|
649
|
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=cut |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
sub default_unit { # read/write attribute-method |
|
653
|
0
|
|
|
0
|
1
|
0
|
my ($this, $value) = @_[0, 1]; |
|
654
|
0
|
0
|
0
|
|
|
0
|
croak 'This does not seem to be a valid unit of measurement' |
|
655
|
|
|
|
|
|
|
if (defined $value && ! defined $unit_to_factor{$value}); |
|
656
|
0
|
|
|
|
|
0
|
$default_unit = $value; |
|
657
|
0
|
|
|
|
|
0
|
return $default_unit; |
|
658
|
|
|
|
|
|
|
} |
|
659
|
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=item Cuse_unicode(BOOLEAN)> |
|
661
|
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
This class global method returns or sets if strings are in Unicode or not. |
|
663
|
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
B Most FrameMaker characters map easily to a Unicode |
|
665
|
|
|
|
|
|
|
equivalent. This is not true however, for the discretionary hyphen (hexadecimal |
|
666
|
|
|
|
|
|
|
04, EChar DiscHyphenE), the FrameMaker "soft hyphen" (hexadecimal 06 |
|
667
|
|
|
|
|
|
|
EChar SoftHyphenE), and the "do not hyphenate" character (hexadecimal |
|
668
|
|
|
|
|
|
|
05, EChar NoHyphenE). |
|
669
|
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
The discretionary hyphen has a null default appearance in the middle of a line. |
|
671
|
|
|
|
|
|
|
At any intraword break that is used for a line break a hyphen glyph will be |
|
672
|
|
|
|
|
|
|
shown. Oddly enough this is defined in Unicode as a I, and so it |
|
673
|
|
|
|
|
|
|
maps to the soft hyphen (U+00AD) character. |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
The I in FrameMaker is used for automatically inserted hyphens by |
|
676
|
|
|
|
|
|
|
the FrameMaker hyphenation algorithm. It has no meaning in the MIF, since |
|
677
|
|
|
|
|
|
|
FrameMaker will reflow a document upon import. But to preserve it in the |
|
678
|
|
|
|
|
|
|
Unicode string, it is mapped to the Unicode hyphen character (U+2010). You |
|
679
|
|
|
|
|
|
|
should remove it with C |
if you don't want it.
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
The NoHyphen is a real control character that just prevents a word from being |
|
682
|
|
|
|
|
|
|
hyphenated automatically by FrameMaker. To preserve this character when doing |
|
683
|
|
|
|
|
|
|
a to and fro conversion, I decided to map it to the Unicode zero-width joiner |
|
684
|
|
|
|
|
|
|
(U+200D). |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
Everything is controlled from the C file, so make changes |
|
687
|
|
|
|
|
|
|
there if you don't like my choices. Or better, override the %fmcharset hash. |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=cut |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
sub use_unicode { |
|
692
|
0
|
0
|
|
0
|
1
|
0
|
$use_unicode = $_[1] if exists $_[1]; |
|
693
|
0
|
|
|
|
|
0
|
return $use_unicode; |
|
694
|
|
|
|
|
|
|
} |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=back |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=head2 Tests on Tree Object |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=over 4 |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=item C<$OBJ-Eis_node()> |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Tests if the object is a valid MIF node statement. That is, if its name occurs |
|
705
|
|
|
|
|
|
|
in the %mifnodes hash. Returns a list of valid daughters when a match is found. |
|
706
|
|
|
|
|
|
|
(In my terminology, "nodes" can have daughters, whereas leaves don't.) |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
=cut |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
sub is_node { |
|
711
|
12
|
|
|
12
|
1
|
23
|
my $this = shift; |
|
712
|
12
|
50
|
|
|
|
62
|
$this = $this->name if ref $this; |
|
713
|
12
|
50
|
|
|
|
80
|
return @{$mifnodes{$this}} if defined $mifnodes{$this}; |
|
|
12
|
|
|
|
|
72
|
|
|
714
|
|
|
|
|
|
|
} |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
=item C<$OBJ-Eis_leaf()> |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
Tests if the object is a valid MIF leaf statement and thus can have an |
|
719
|
|
|
|
|
|
|
attribute value. The name is just looked up in the %mifleaves hash. |
|
720
|
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=cut |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
sub is_leaf { |
|
724
|
36
|
|
|
36
|
1
|
36
|
my $this = shift; |
|
725
|
36
|
50
|
|
|
|
122
|
$this = $this->name if ref $this; |
|
726
|
36
|
50
|
|
|
|
253
|
return $this if exists $mifleaves{$this}; |
|
727
|
|
|
|
|
|
|
} |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=item C<$OBJ-Eallows_daughter(DAUGHTEROBJECT)> |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
Checks if a mother object can have a specific daughter object. I just thought |
|
732
|
|
|
|
|
|
|
this could come in handy when you want to bind one object tree to another. |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
=cut |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
sub allows_daughter { |
|
737
|
12
|
|
|
12
|
1
|
18
|
my ($mother, $daughter) = @_[0, 1]; |
|
738
|
12
|
50
|
|
|
|
24
|
croak 'Must be called on object' unless ref $mother; |
|
739
|
12
|
50
|
|
|
|
44
|
croak 'Mother "' . $mother->name . '" must be called with daughter object' |
|
740
|
|
|
|
|
|
|
unless $daughter->isa('FrameMaker::MifTree'); |
|
741
|
12
|
50
|
|
|
|
32
|
if (defined $daughter->name) { |
|
742
|
12
|
|
|
|
|
86
|
return grep { $_ eq $daughter->name } $mother->is_node; |
|
|
276
|
|
|
|
|
1426
|
|
|
743
|
|
|
|
|
|
|
} |
|
744
|
|
|
|
|
|
|
} |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=item C<$OBJ-Echeck_attribute> |
|
747
|
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
Checks if the attribute conforms to the type. Currently the following types are |
|
749
|
|
|
|
|
|
|
defined: |
|
750
|
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
0xnnn |
|
752
|
|
|
|
|
|
|
ID |
|
753
|
|
|
|
|
|
|
L_T_R_B |
|
754
|
|
|
|
|
|
|
L_T_W_H |
|
755
|
|
|
|
|
|
|
W_H |
|
756
|
|
|
|
|
|
|
W_W |
|
757
|
|
|
|
|
|
|
X_Y |
|
758
|
|
|
|
|
|
|
X_Y_W_H |
|
759
|
|
|
|
|
|
|
boolean |
|
760
|
|
|
|
|
|
|
data |
|
761
|
|
|
|
|
|
|
degrees |
|
762
|
|
|
|
|
|
|
dimension |
|
763
|
|
|
|
|
|
|
empty *) |
|
764
|
|
|
|
|
|
|
integer |
|
765
|
|
|
|
|
|
|
keyword |
|
766
|
|
|
|
|
|
|
number |
|
767
|
|
|
|
|
|
|
pathname |
|
768
|
|
|
|
|
|
|
percentage |
|
769
|
|
|
|
|
|
|
seconds_microseconds |
|
770
|
|
|
|
|
|
|
string |
|
771
|
|
|
|
|
|
|
tagstring |
|
772
|
|
|
|
|
|
|
*) no attribute allowed; some leaves and all non-ending nodes have this |
|
773
|
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
The function returns TRUE if the attribute seems valid, and FALSE if there is |
|
775
|
|
|
|
|
|
|
an error. Use L to see the error. |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
=cut |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
sub check_attribute { |
|
780
|
57
|
|
|
57
|
1
|
72
|
my $it = shift; |
|
781
|
57
|
50
|
|
|
|
86
|
return $it->get_attribute_error ? undef : 1; |
|
782
|
|
|
|
|
|
|
} |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=item C<$OBJ-Eget_attribute_error> |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
Returns a meaningful text string if the attribute appears to be invalid. |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
=cut |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
sub get_attribute_error { |
|
791
|
57
|
|
|
57
|
1
|
52
|
my $it = shift; |
|
792
|
57
|
|
|
|
|
58
|
my $errVal; |
|
793
|
57
|
100
|
|
|
|
119
|
if ( defined $it->{attributes} ) { |
|
794
|
36
|
50
|
|
|
|
60
|
unless ( $it->is_leaf ) { |
|
795
|
0
|
|
|
|
|
0
|
$errVal = 'Node "' . $it->name . '" is not a leaf. ' . |
|
796
|
|
|
|
|
|
|
'Only leaves can have meaningful attributes'; |
|
797
|
|
|
|
|
|
|
} else { |
|
798
|
36
|
|
|
|
|
90
|
my $attrType = $mifleaves{$it->name}; |
|
799
|
|
|
|
|
|
|
# must access 'attributes' key directly; sorry |
|
800
|
36
|
50
|
|
|
|
398
|
unless ( $it->{attributes} =~ $attribute_types{$attrType} ) { |
|
801
|
0
|
|
|
|
|
0
|
$errVal = 'Attribute on leaf "' . $it->name . '" seems invalid. ' . |
|
802
|
|
|
|
|
|
|
qq(Expected "$attrType" for ") . $it->{attributes} . '"'; |
|
803
|
|
|
|
|
|
|
} |
|
804
|
|
|
|
|
|
|
} |
|
805
|
|
|
|
|
|
|
} |
|
806
|
57
|
|
|
|
|
239
|
return $errVal; |
|
807
|
|
|
|
|
|
|
} |
|
808
|
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
=item C<$OBJ-Evalidate(FROMROOT)> |
|
810
|
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
Not yet implemented. |
|
812
|
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
Validates a MIF tree object. If you set FROMROOT to true, the validation starts |
|
814
|
|
|
|
|
|
|
from $OBJ->root, and special checking is done on the root object. This special |
|
815
|
|
|
|
|
|
|
behaviour is needed because the method cannot know if a FrameMaker::MifTree |
|
816
|
|
|
|
|
|
|
object is to represent a complete MIF file, and not just a fragment. So please |
|
817
|
|
|
|
|
|
|
remember always to set FROMROOT if you want to validate a complete MIF tree, |
|
818
|
|
|
|
|
|
|
even if $OBJ already points to the root object. |
|
819
|
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=cut |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
sub validate { |
|
823
|
0
|
|
|
0
|
1
|
0
|
my ($it, $from_root) = @_[0, 1]; |
|
824
|
0
|
0
|
|
|
|
0
|
$it = $it->root if $from_root; |
|
825
|
0
|
|
|
|
|
0
|
croak 'Method not yet implemented.' |
|
826
|
|
|
|
|
|
|
# 1. hard-coded checking on root object |
|
827
|
|
|
|
|
|
|
# 2. walk_down, checking allows_daughter and is_leaf for every node |
|
828
|
|
|
|
|
|
|
# 3. if is_leaf: check_attribute |
|
829
|
|
|
|
|
|
|
} |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
=back |
|
832
|
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=head2 From/to MIF Syntax |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
=over 4 |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
=item Cdump_mif()> |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
Dumps out the current tree as a list of MIF statements in valid MIF file |
|
840
|
|
|
|
|
|
|
syntax. You can write the resulting list to a file. The method tries to mimic |
|
841
|
|
|
|
|
|
|
the Adobe MIF parser file layout as closely as possible. Please note that this |
|
842
|
|
|
|
|
|
|
method can be memory intensive, since it creates a whole new copy of your MIF |
|
843
|
|
|
|
|
|
|
tree in memory. If you just want to write the MIF tree to a file, you may want |
|
844
|
|
|
|
|
|
|
to use L instead. |
|
845
|
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=cut |
|
847
|
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
sub dump_mif { |
|
849
|
0
|
|
|
0
|
1
|
0
|
my $obj = $_[0]; |
|
850
|
0
|
|
|
|
|
0
|
my @list = (); |
|
851
|
|
|
|
|
|
|
$obj->walk_down({ |
|
852
|
|
|
|
|
|
|
callback => sub { |
|
853
|
0
|
|
|
0
|
|
0
|
my $this = $_[0]; |
|
854
|
0
|
0
|
|
|
|
0
|
if (defined $this->mother) { # don't print root element |
|
855
|
0
|
0
|
0
|
|
|
0
|
if ((warnings::enabled || $^W) && ! $this->name) { |
|
|
|
|
0
|
|
|
|
|
|
856
|
0
|
|
|
|
|
0
|
warnings::warn 'Missing name on node ' . $this->address; |
|
857
|
|
|
|
|
|
|
} |
|
858
|
0
|
0
|
0
|
|
|
0
|
if ( ! $this->is_node && ! defined $this->attributes ) { |
|
859
|
0
|
0
|
0
|
|
|
0
|
if (warnings::enabled || $^W) { |
|
860
|
0
|
|
|
|
|
0
|
warnings::warn 'Undefined attribute on leaf "'. $this->name . '"'; |
|
861
|
|
|
|
|
|
|
} |
|
862
|
0
|
|
|
|
|
0
|
$this->attributes(''); |
|
863
|
|
|
|
|
|
|
} |
|
864
|
0
|
0
|
|
|
|
0
|
if ($this->name eq '_facet') { |
|
865
|
0
|
|
|
|
|
0
|
push @list, $this->facet_data; |
|
866
|
|
|
|
|
|
|
} else { |
|
867
|
0
|
0
|
|
|
|
0
|
push @list, |
|
|
|
0
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
' ' x (scalar $this->ancestors - 1) . |
|
869
|
|
|
|
|
|
|
'<' . $this->name . |
|
870
|
|
|
|
|
|
|
($this->name eq 'DocFileInfo' ? "\n" |
|
871
|
|
|
|
|
|
|
: ' ' ) . # not very elegant huh? |
|
872
|
|
|
|
|
|
|
($this->is_node ? "\n" |
|
873
|
|
|
|
|
|
|
: $this->attributes . ">\n"); |
|
874
|
|
|
|
|
|
|
} |
|
875
|
|
|
|
|
|
|
} |
|
876
|
0
|
|
|
|
|
0
|
1; # continue recursion |
|
877
|
|
|
|
|
|
|
}, |
|
878
|
|
|
|
|
|
|
callbackback => sub { |
|
879
|
0
|
|
|
0
|
|
0
|
my $this = $_[0]; |
|
880
|
0
|
0
|
|
|
|
0
|
if (defined $this->mother) { # don't print anything for root... |
|
881
|
0
|
0
|
|
|
|
0
|
if ($this->is_node) { # ... or for leaves |
|
882
|
0
|
|
|
|
|
0
|
push @list, ' ' x (scalar $this->ancestors - 1) . |
|
883
|
|
|
|
|
|
|
'> # End of ' . $this->name . "\n"; |
|
884
|
|
|
|
|
|
|
} |
|
885
|
|
|
|
|
|
|
} else { |
|
886
|
0
|
|
|
|
|
0
|
push @list, "# End of MIFFile\n"; |
|
887
|
|
|
|
|
|
|
} |
|
888
|
|
|
|
|
|
|
} |
|
889
|
0
|
|
|
|
|
0
|
}); |
|
890
|
0
|
|
|
|
|
0
|
return @list; |
|
891
|
|
|
|
|
|
|
} |
|
892
|
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
=item Cdump_miffile(FILENAME)> |
|
894
|
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
Dumps out the current tree of MIF statements into a valid MIF file syntax. The |
|
896
|
|
|
|
|
|
|
method returns with a FALSE result if the file cannot be written. |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
=cut |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
sub dump_miffile { |
|
901
|
0
|
|
|
0
|
1
|
0
|
my ($obj, $filename) = @_[0, 1]; |
|
902
|
0
|
0
|
|
|
|
0
|
open(my $MIF, ">$filename") || return undef; |
|
903
|
|
|
|
|
|
|
$obj->walk_down({ |
|
904
|
|
|
|
|
|
|
callback => sub { |
|
905
|
0
|
|
|
0
|
|
0
|
my $this = $_[0]; |
|
906
|
0
|
0
|
|
|
|
0
|
if (defined $this->mother) { # don't print root element |
|
907
|
0
|
0
|
0
|
|
|
0
|
if ((warnings::enabled || $^W) && ! $this->name) { |
|
|
|
|
0
|
|
|
|
|
|
908
|
0
|
|
|
|
|
0
|
warnings::warn 'Missing name on node ' . $this->address; |
|
909
|
|
|
|
|
|
|
} |
|
910
|
0
|
0
|
0
|
|
|
0
|
if ( ! $this->is_node && ! defined $this->attributes ) { |
|
911
|
0
|
0
|
0
|
|
|
0
|
if (warnings::enabled || $^W) { |
|
912
|
0
|
|
|
|
|
0
|
warnings::warn 'Undefined attribute on leaf "' . $this->name . '"'; |
|
913
|
|
|
|
|
|
|
} |
|
914
|
0
|
|
|
|
|
0
|
$this->attributes(''); |
|
915
|
|
|
|
|
|
|
} |
|
916
|
0
|
0
|
|
|
|
0
|
if ($this->name eq '_facet') { |
|
917
|
0
|
|
|
|
|
0
|
print $MIF $this->facet_data; |
|
918
|
|
|
|
|
|
|
} else { |
|
919
|
0
|
0
|
|
|
|
0
|
print $MIF |
|
|
|
0
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
' ' x (scalar $this->ancestors - 1) . |
|
921
|
|
|
|
|
|
|
'<' . $this->name . |
|
922
|
|
|
|
|
|
|
($this->name eq 'DocFileInfo' ? "\n" |
|
923
|
|
|
|
|
|
|
: ' ' ) . # not very elegant huh? |
|
924
|
|
|
|
|
|
|
($this->is_node ? "\n" |
|
925
|
|
|
|
|
|
|
: $this->attributes . ">\n"); |
|
926
|
|
|
|
|
|
|
} |
|
927
|
|
|
|
|
|
|
} |
|
928
|
0
|
|
|
|
|
0
|
1; # continue recursion |
|
929
|
|
|
|
|
|
|
}, |
|
930
|
|
|
|
|
|
|
callbackback => sub { |
|
931
|
0
|
|
|
0
|
|
0
|
my $this = $_[0]; |
|
932
|
0
|
0
|
|
|
|
0
|
if (defined $this->mother) { # don't print anything for root... |
|
933
|
0
|
0
|
|
|
|
0
|
if ($this->is_node) { # ... or for leaves |
|
934
|
0
|
|
|
|
|
0
|
print $MIF ' ' x (scalar $this->ancestors - 1) . |
|
935
|
|
|
|
|
|
|
'> # End of ' . $this->name . "\n"; |
|
936
|
|
|
|
|
|
|
} |
|
937
|
|
|
|
|
|
|
} else { |
|
938
|
0
|
|
|
|
|
0
|
print $MIF "# End of MIFFile\n"; |
|
939
|
|
|
|
|
|
|
} |
|
940
|
|
|
|
|
|
|
} |
|
941
|
0
|
|
|
|
|
0
|
}); |
|
942
|
0
|
|
|
|
|
0
|
return 1; |
|
943
|
|
|
|
|
|
|
} |
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
=item C<$OBJ-Eparse_mif(STRING)> |
|
946
|
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
Parses a string of MIF statements into the object. This is also a very quick |
|
948
|
|
|
|
|
|
|
way to set up an object tree: |
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
my $new_obj = FrameMaker::MifTree->new(); |
|
951
|
|
|
|
|
|
|
$new_obj->parse_mif(<
|
|
952
|
|
|
|
|
|
|
# The only required statement |
|
953
|
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
# The actual text of this document |
|
956
|
|
|
|
|
|
|
> # end of Paraline #End of ParaLine statement |
|
957
|
|
|
|
|
|
|
> # end of Para #End of Para statement |
|
958
|
|
|
|
|
|
|
ENDMIF |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
Implemented by tying the scalar to a filehandle and calling IO::Tokenizer on |
|
961
|
|
|
|
|
|
|
the resulting handle. |
|
962
|
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
The parser currently has the following limitations: |
|
964
|
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=over 8 |
|
966
|
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
=item * |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
All comments are lost. |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=item * |
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
Macro statements are not (yet) implemented. |
|
974
|
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
=item * |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
Include statements are not (yet) implemented. |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
=back |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
Maybe I'll do something about it. Someday. |
|
982
|
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
=cut |
|
984
|
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
sub parse_mif { |
|
986
|
1
|
|
|
1
|
1
|
91
|
my ($obj, $string) = @_[0, 1]; |
|
987
|
1
|
|
33
|
|
|
4
|
my $class = ref($obj) || croak 'Must be called on object'; |
|
988
|
1
|
|
|
|
|
2
|
my $facet_handle = 0; |
|
989
|
|
|
|
|
|
|
|
|
990
|
1
|
|
|
|
|
11
|
my $fh = IO::Tokenized::Scalar->new(); |
|
991
|
1
|
|
|
|
|
11
|
$fh->setparser(@parserdefinition); |
|
992
|
1
|
|
|
|
|
443
|
$fh->open(\$string); |
|
993
|
|
|
|
|
|
|
|
|
994
|
1
|
|
|
|
|
10
|
my $cur_obj = $obj; |
|
995
|
1
|
|
|
|
|
11
|
while ( my ($tok, $val) = $fh->gettoken ) { |
|
996
|
40
|
50
|
|
|
|
1508
|
if ( $tok eq 'FACET' ) { |
|
997
|
0
|
0
|
|
|
|
0
|
unless ($facet_handle) { |
|
998
|
0
|
|
|
|
|
0
|
$cur_obj->add_facet; |
|
999
|
0
|
|
|
|
|
0
|
$facet_handle = $cur_obj->facet_handle; |
|
1000
|
|
|
|
|
|
|
} |
|
1001
|
0
|
|
|
|
|
0
|
syswrite $facet_handle, "$val\n"; |
|
1002
|
|
|
|
|
|
|
} else { |
|
1003
|
40
|
|
|
|
|
47
|
$facet_handle = 0; |
|
1004
|
40
|
100
|
|
|
|
100
|
if ( $tok eq 'MIFTAG' ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1005
|
13
|
|
|
|
|
29
|
$cur_obj = $cur_obj->add_node($val); |
|
1006
|
|
|
|
|
|
|
} elsif ( $tok eq 'RANGLE' ) { |
|
1007
|
13
|
|
|
|
|
36
|
$cur_obj = $cur_obj->mother; |
|
1008
|
|
|
|
|
|
|
} elsif ( $tok eq 'ATTRIBS' ) { |
|
1009
|
8
|
50
|
|
|
|
17
|
if (defined $cur_obj->attributes) { |
|
1010
|
0
|
|
|
|
|
0
|
$cur_obj->attributes($cur_obj->attributes . $val) |
|
1011
|
|
|
|
|
|
|
} else { |
|
1012
|
8
|
|
|
|
|
15
|
$cur_obj->attributes($val) |
|
1013
|
|
|
|
|
|
|
} |
|
1014
|
|
|
|
|
|
|
} |
|
1015
|
|
|
|
|
|
|
} |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
1
|
|
|
|
|
48
|
$fh->close; |
|
1018
|
|
|
|
|
|
|
} |
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
=item C<$OBJ-Eparse_miffile(FILENAME)> |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
Parses a file from disk into a DAG_Node tree structure. See L for |
|
1023
|
|
|
|
|
|
|
details. |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
=cut |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
sub parse_miffile { |
|
1028
|
0
|
|
|
0
|
1
|
0
|
my ($obj, $filename) = @_[0, 1]; |
|
1029
|
0
|
0
|
|
|
|
0
|
croak qq(File "$filename" not found) unless -f $filename; |
|
1030
|
0
|
|
0
|
|
|
0
|
my $class = ref($obj) || croak 'Must be called on object'; |
|
1031
|
0
|
|
|
|
|
0
|
my $facet_handle = 0; |
|
1032
|
|
|
|
|
|
|
|
|
1033
|
0
|
|
|
|
|
0
|
my $fh = IO::Tokenized::File->new(); |
|
1034
|
0
|
|
|
|
|
0
|
$fh->setparser(@parserdefinition); |
|
1035
|
0
|
|
|
|
|
0
|
$fh->buffer_space(524_288); |
|
1036
|
0
|
|
|
|
|
0
|
$fh->open($filename); |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
0
|
|
|
|
|
0
|
my $cur_obj = $obj; |
|
1039
|
0
|
|
|
|
|
0
|
while ( my ($tok, $val) = $fh->gettoken ) { |
|
1040
|
0
|
0
|
|
|
|
0
|
if ( $tok eq 'FACET' ) { |
|
1041
|
0
|
0
|
|
|
|
0
|
unless ($facet_handle) { |
|
1042
|
0
|
|
|
|
|
0
|
$cur_obj->add_facet; |
|
1043
|
0
|
|
|
|
|
0
|
$facet_handle = $cur_obj->facet_handle; |
|
1044
|
|
|
|
|
|
|
} |
|
1045
|
0
|
|
|
|
|
0
|
syswrite $facet_handle, "$val\n"; |
|
1046
|
|
|
|
|
|
|
} else { |
|
1047
|
0
|
|
|
|
|
0
|
$facet_handle = 0; |
|
1048
|
0
|
0
|
|
|
|
0
|
if ( $tok eq 'MIFTAG' ) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1049
|
0
|
|
|
|
|
0
|
$cur_obj = $cur_obj->add_node($val); |
|
1050
|
|
|
|
|
|
|
} elsif ( $tok eq 'RANGLE' ) { |
|
1051
|
0
|
|
|
|
|
0
|
$cur_obj = $cur_obj->mother; |
|
1052
|
|
|
|
|
|
|
} elsif ( $tok eq 'ATTRIBS' ) { |
|
1053
|
0
|
0
|
|
|
|
0
|
if (defined $cur_obj->attributes) { |
|
1054
|
0
|
|
|
|
|
0
|
$cur_obj->attributes($cur_obj->attributes . $val) |
|
1055
|
|
|
|
|
|
|
} else { |
|
1056
|
0
|
|
|
|
|
0
|
$cur_obj->attributes($val) |
|
1057
|
|
|
|
|
|
|
} |
|
1058
|
|
|
|
|
|
|
} |
|
1059
|
|
|
|
|
|
|
} |
|
1060
|
|
|
|
|
|
|
} |
|
1061
|
0
|
|
|
|
|
0
|
$fh->close; |
|
1062
|
|
|
|
|
|
|
} |
|
1063
|
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
=back |
|
1065
|
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=head2 Old-style Functions |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
All these functions are exported by default. |
|
1069
|
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
=over 4 |
|
1071
|
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
=item C |
|
1073
|
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
Quotes a string with MIF style quotes, and escapes forbidden characters. |
|
1075
|
|
|
|
|
|
|
Backslashes, backticks, single quotes, greater-than and tabs are escaped, |
|
1076
|
|
|
|
|
|
|
non-ASCII values are written in their hexadecimal representation. So: |
|
1077
|
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
Some `symbols': E \E<216>E<191>!> |
|
1079
|
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
is written as |
|
1081
|
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
`Some \Qsymbols\q: \> \\\xaf \xc0 !' |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
As a special case, escaped hexadecimals are preserved in the input string. If |
|
1085
|
|
|
|
|
|
|
you want a literal \x00 string, precede it with an extra backslash. |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
print quote("\x09 "); # prints `\x09 ', a forced return in FrameMaker |
|
1088
|
|
|
|
|
|
|
print quote("\\x09 "); # prints `\\x09 '; this will show up literally |
|
1089
|
|
|
|
|
|
|
# as \x09 in FrameMaker |
|
1090
|
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
(Note that after emitting a forced return, you I start a new ParaLine.) |
|
1092
|
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
If the global modifier $FrameMaker::MifTree::use_unicode is true, the string |
|
1094
|
|
|
|
|
|
|
will be converted from Unicode to the FrameMaker character set. |
|
1095
|
|
|
|
|
|
|
|
|
1096
|
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
=cut |
|
1098
|
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
sub quote { |
|
1100
|
6
|
|
|
6
|
1
|
8
|
my ($s, $use_unicode_deprecated) = @_; |
|
1101
|
6
|
50
|
|
|
|
16
|
return unless defined $s; |
|
1102
|
6
|
50
|
33
|
|
|
547
|
if ((warnings::enabled || $^W) && defined $use_unicode_deprecated) { |
|
|
|
|
33
|
|
|
|
|
|
1103
|
0
|
|
|
|
|
0
|
warnings::warn 'USE_UNICODE as 2nd argument is now deprecated'; |
|
1104
|
|
|
|
|
|
|
} |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
6
|
100
|
33
|
|
|
25
|
if ($use_unicode_deprecated || $use_unicode) { |
|
1107
|
2
|
|
|
|
|
2
|
my $s_orig = $s; |
|
1108
|
2
|
|
|
|
|
291
|
eval($unicode_to_fm); |
|
1109
|
2
|
50
|
|
|
|
14
|
warnings::warn qq(Error in "quote" while converting $s_orig\n$@) if $@; |
|
1110
|
|
|
|
|
|
|
} |
|
1111
|
|
|
|
|
|
|
|
|
1112
|
6
|
|
|
|
|
22
|
$s =~ s/\\(?!x[a-f0-9]{2})/\\\\/g; # single backslash to escaped backslash |
|
1113
|
|
|
|
|
|
|
# except when followed by hex sequence |
|
1114
|
6
|
|
|
|
|
11
|
$s =~ s/\\\\\\(?=x[a-f0-9]{2})/\\/g; # correct double backslash case |
|
1115
|
6
|
|
|
|
|
9
|
$s =~ s/`/\\Q/g; # backtick |
|
1116
|
6
|
|
|
|
|
11
|
$s =~ s/'/\\q/g; # single straight quote |
|
1117
|
6
|
|
|
|
|
10
|
$s =~ s/>/\\>/g; # escape 'greater than' |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
# control and high chars |
|
1120
|
6
|
|
|
|
|
18
|
$s =~ s/([\x00-\x1a\x80-\xff])/'\x' . sprintf('%02x ', ord $1)/ge; |
|
|
2
|
|
|
|
|
9
|
|
|
1121
|
|
|
|
|
|
|
|
|
1122
|
6
|
|
|
|
|
29
|
return "`$s'"; |
|
1123
|
|
|
|
|
|
|
} |
|
1124
|
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
=item C |
|
1126
|
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
The opposite action. Surrounding quotes are removed and all escaped sequences |
|
1128
|
|
|
|
|
|
|
are transliterated into their original character. |
|
1129
|
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
If the global modifier $FrameMaker::MifTree::use_unicode is true, the string |
|
1131
|
|
|
|
|
|
|
will be converted from the FrameMaker character set to Unicode. |
|
1132
|
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
$FrameMaker::MifTree::use_unicode can be exported on request. |
|
1134
|
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
=cut |
|
1136
|
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
sub unquote { |
|
1138
|
17
|
|
|
17
|
1
|
22
|
my ($s, $use_unicode_deprecated) = @_; |
|
1139
|
17
|
50
|
|
|
|
32
|
return unless defined $s; |
|
1140
|
17
|
50
|
33
|
|
|
1530
|
if ((warnings::enabled || $^W) && defined $use_unicode_deprecated) { |
|
|
|
|
33
|
|
|
|
|
|
1141
|
0
|
|
|
|
|
0
|
warnings::warn 'USE_UNICODE as 2nd argument is now deprecated'; |
|
1142
|
|
|
|
|
|
|
} |
|
1143
|
|
|
|
|
|
|
|
|
1144
|
17
|
50
|
|
|
|
111
|
$s =~ s/^`// && $s =~ s/'$//; # unquote |
|
1145
|
17
|
|
|
|
|
52
|
$s =~ s/\\x([a-f0-9]{1,2}) ?/chr hex $1/ge; # escaped non-ASCII chars |
|
|
8
|
|
|
|
|
34
|
|
|
1146
|
17
|
|
|
|
|
21
|
$s =~ s/\\>/>/g; # greater than |
|
1147
|
17
|
|
|
|
|
18
|
$s =~ s/\\q/'/g; # single quote |
|
1148
|
17
|
|
|
|
|
20
|
$s =~ s/\\Q/`/g; # backtick |
|
1149
|
17
|
|
|
|
|
16
|
$s =~ s/\\\\/\\/g; # backslash |
|
1150
|
|
|
|
|
|
|
|
|
1151
|
17
|
100
|
33
|
|
|
52
|
if ($use_unicode_deprecated || $use_unicode) { |
|
1152
|
2
|
|
|
|
|
4
|
my $s_orig = $s; |
|
1153
|
2
|
|
|
|
|
231
|
eval($fm_to_unicode); |
|
1154
|
2
|
50
|
|
|
|
10
|
warnings::warn qq(Error in "unquote" while converting $s_orig\n$@) if $@; |
|
1155
|
|
|
|
|
|
|
} |
|
1156
|
|
|
|
|
|
|
|
|
1157
|
17
|
|
|
|
|
59
|
return $s; |
|
1158
|
|
|
|
|
|
|
} |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
=item C |
|
1161
|
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
Encodes path names to the MIF path syntax. Usage: |
|
1163
|
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
$mifPathString = encode_path('D:\Dos\Path\With\Backslashes\Filename'); |
|
1165
|
|
|
|
|
|
|
$mifPathString = encode_path('..\..\Also\Relative\Path\Is\Allowed\Filename'); |
|
1166
|
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
The path name must not be in a MIF quoted style. It returns the device |
|
1168
|
|
|
|
|
|
|
independent path name I the quotes. |
|
1169
|
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
=cut |
|
1171
|
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
sub encode_path { |
|
1173
|
0
|
|
|
0
|
1
|
|
my $s = shift; |
|
1174
|
0
|
0
|
|
|
|
|
return unless defined $s; |
|
1175
|
|
|
|
|
|
|
|
|
1176
|
0
|
0
|
|
|
|
|
$s =~ s{^`}{} && $s =~ s{'$}{}; # Remove quotes, just in case... |
|
1177
|
0
|
|
|
|
|
|
$s =~ s{\\}{/}g; # All backslashes to forward slashes |
|
1178
|
|
|
|
|
|
|
|
|
1179
|
0
|
|
|
|
|
|
$s =~ s{^([a-z]:)}{$1}i; # drive letter |
|
1180
|
0
|
|
|
|
|
|
$s =~ s{^//}{}; # unc path |
|
1181
|
0
|
|
|
|
|
|
$s =~ s{\.\./}{}g; # .. 'up' in hierarchy to |
|
1182
|
0
|
|
|
|
|
|
$s =~ s{([^<])}{$1}g; # correct last to |
|
1183
|
0
|
|
|
|
|
|
$s =~ s{/}{}g; # 'component' separators |
|
1184
|
0
|
|
|
|
|
|
$s =~ s{^([^<])}{$1}; # start relative path with |
|
1185
|
0
|
|
|
|
|
|
$s =~ s{`}{\\Q}g; # backtick |
|
1186
|
0
|
|
|
|
|
|
$s =~ s{'}{\\q}g; # single straight quote |
|
1187
|
0
|
|
|
|
|
|
$s =~ s{([\x81-\xff])}{'\x' . sprintf('%lx', ord $1) . ' '}ge; # high chars |
|
|
0
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
|
|
1189
|
0
|
|
|
|
|
|
return "`$s'"; |
|
1190
|
|
|
|
|
|
|
} |
|
1191
|
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
=item C |
|
1193
|
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
Usage: |
|
1195
|
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
print decode_path ('C:MydirSubdirFilename'); |
|
1197
|
|
|
|
|
|
|
# prints C:/Mydir/Subdir/Filename |
|
1198
|
|
|
|
|
|
|
print decode_path ('SubdirFilename'); |
|
1199
|
|
|
|
|
|
|
# prints ../../Subdir/Filename |
|
1200
|
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
Currently only Windows path names are supported (meaning that Unix and MacOS |
|
1202
|
|
|
|
|
|
|
style paths remain untested). MIF string quotes are removed. ROOTPATH, if |
|
1203
|
|
|
|
|
|
|
specified, is the path that is prepended if STRING happens to be a relative |
|
1204
|
|
|
|
|
|
|
path. |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
=cut |
|
1207
|
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
sub decode_path { |
|
1209
|
0
|
|
|
0
|
1
|
|
my ($s, $root) = @_[0, 1]; |
|
1210
|
0
|
0
|
|
|
|
|
return unless defined $s; |
|
1211
|
0
|
|
0
|
|
|
|
($root ||= '') =~ s{([^\\/])$}{$1/}; # add slash if necessary |
|
1212
|
|
|
|
|
|
|
|
|
1213
|
0
|
0
|
|
|
|
|
$s =~ s{^`}{} && $s =~ s{'$}{}; |
|
1214
|
|
|
|
|
|
|
|
|
1215
|
0
|
0
|
|
|
|
|
$root = '' unless $s =~ m{^<[cu]\\>}; # only use $root when |
|
1216
|
|
|
|
|
|
|
# relative path is found |
|
1217
|
0
|
|
|
|
|
|
$s =~ s{}{}; |
|
1218
|
0
|
|
|
|
|
|
$s =~ s{}{//}; |
|
1219
|
0
|
|
|
|
|
|
$s =~ s{()?}{../}g; |
|
1220
|
0
|
|
|
|
|
|
$s =~ s{^}{}g; # path starting with indicates relative path name |
|
1221
|
0
|
|
|
|
|
|
$s =~ s{}{/}g; |
|
1222
|
0
|
|
|
|
|
|
$s =~ s{\\q}{'}g; # single quote |
|
1223
|
0
|
|
|
|
|
|
$s =~ s{\\Q}{`}g; # backtick |
|
1224
|
0
|
|
|
|
|
|
$s =~ s{\\x([a-f0-9]{2}) ?}{chr hex $1}ge; # escaped non-ASCII chars |
|
|
0
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
|
|
1226
|
0
|
|
|
|
|
|
return "$root$s"; |
|
1227
|
|
|
|
|
|
|
} |
|
1228
|
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
=item C |
|
1230
|
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
Converts a value in one unit of measurement into another. If you leave out the |
|
1232
|
|
|
|
|
|
|
unit of measurement it defaults to FrameMaker::MifTree->default_unit (not to the |
|
1233
|
|
|
|
|
|
|
MIF document's default unit of measurement!). Other measurements are: |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
{ |
|
1236
|
|
|
|
|
|
|
pt => 1 / 72, |
|
1237
|
|
|
|
|
|
|
point => 1 / 72, |
|
1238
|
|
|
|
|
|
|
" => 1, |
|
1239
|
|
|
|
|
|
|
in => 1, |
|
1240
|
|
|
|
|
|
|
mm => 1 / 25.4, |
|
1241
|
|
|
|
|
|
|
millimeter => 1 / 25.4, |
|
1242
|
|
|
|
|
|
|
cm => 1 / 2.54, |
|
1243
|
|
|
|
|
|
|
centimeter => 1 / 2.54, |
|
1244
|
|
|
|
|
|
|
pc => 1 / 6, |
|
1245
|
|
|
|
|
|
|
pica => 1 / 6, |
|
1246
|
|
|
|
|
|
|
dd => 0.01483, |
|
1247
|
|
|
|
|
|
|
didot => 0.01483, |
|
1248
|
|
|
|
|
|
|
cc => 12 * 0.01483, |
|
1249
|
|
|
|
|
|
|
cicero => 12 * 0.01483 |
|
1250
|
|
|
|
|
|
|
} |
|
1251
|
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
The optional argument SUPPRESSUNIT determines if the unit of measurement needs |
|
1253
|
|
|
|
|
|
|
to be written in the result. Note that you won't get a unit of measurement |
|
1254
|
|
|
|
|
|
|
included in your result when you leave out NEWUNIT and specify |
|
1255
|
|
|
|
|
|
|
Cdefault_unit> to be the empty string, even if you set |
|
1256
|
|
|
|
|
|
|
SUPPRESSUNIT to be false. In that case the returned value is in points. So |
|
1257
|
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
FrameMaker::MifTree->default_unit(''); |
|
1259
|
|
|
|
|
|
|
print convert('12.0 didot'); # prints the value in points: 12.8131 |
|
1260
|
|
|
|
|
|
|
FrameMaker::MifTree->default_unit('mm'); |
|
1261
|
|
|
|
|
|
|
print convert('12.0 didot', 'pt', 1); # also prints 12.8131 |
|
1262
|
|
|
|
|
|
|
FrameMaker::MifTree->default_unit('pt'); |
|
1263
|
|
|
|
|
|
|
print convert('12.0 didot', '', 1); # also prints 12.8131 |
|
1264
|
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
All values are rounded to 4 decimals. |
|
1266
|
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
=cut |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
sub convert { |
|
1270
|
0
|
|
|
0
|
1
|
|
my ($num_val, $old_unit) = shift =~ /(-?\d*\.?\d+)\s*(\D*)/; |
|
1271
|
0
|
|
0
|
|
|
|
my $new_unit = shift || $default_unit; |
|
1272
|
0
|
|
|
|
|
|
my $suppress_unit = shift; |
|
1273
|
0
|
|
0
|
|
|
|
$old_unit ||= $default_unit; |
|
1274
|
0
|
|
|
|
|
|
$old_unit =~ s/\s//g; |
|
1275
|
0
|
|
|
|
|
|
$new_unit =~ s/\s//g; |
|
1276
|
0
|
|
|
|
|
|
my $new_value = sprintf '%.4f', |
|
1277
|
|
|
|
|
|
|
$num_val * $unit_to_factor{$old_unit} / |
|
1278
|
|
|
|
|
|
|
$unit_to_factor{$new_unit}; |
|
1279
|
0
|
0
|
0
|
|
|
|
$new_unit = " $new_unit" unless $new_unit eq q(") || $new_unit eq ''; |
|
1280
|
0
|
0
|
|
|
|
|
return $new_value . ($suppress_unit ? '' : $new_unit); |
|
1281
|
|
|
|
|
|
|
} |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
3
|
|
|
3
|
|
9925
|
END {} # Global destructor |
|
1284
|
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
1; |
|
1286
|
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
__END__ |