our
$VERSION
=
'3.026'
;
our
$LAST_UPDATE
=
'3.026'
;
use
vars
qw($cr $irreg_char $reg_char $ws_char $delim_char %types)
;
$ws_char
=
'[ \t\r\n\f\0]'
;
$delim_char
=
'[][<>{}()/%]'
;
$reg_char
=
'[^][<>{}()/% \t\r\n\f\0]'
;
$irreg_char
=
'[][<>{}()/% \t\r\n\f\0]'
;
$cr
=
'\s*(?:\015|\012|(?:\015\012))'
;
my
$re_comment
=
qr/(?:\%[^\r\n]*)/
;
my
$re_whitespace
=
qr/(?:[ \t\r\n\f\0]|$re_comment)/
;
%types
= (
'Page'
=>
'PDF::Builder::Basic::PDF::Page'
,
'Pages'
=>
'PDF::Builder::Basic::PDF::Pages'
,
);
my
$readDebug
= 0;
use
POSIX
qw(ceil floor)
;
sub
new {
my
(
$class
,
$root
) =
@_
;
my
$self
=
$class
->_new();
unless
(
$root
) {
$root
= PDFDict();
$root
->{
'Type'
} = PDFName(
'Catalog'
);
}
$self
->new_obj(
$root
);
$self
->{
'Root'
} =
$root
;
return
$self
;
}
sub
open
{
my
(
$class
,
$filename
,
$update
,
%options
) =
@_
;
if
(
defined
$options
{
'-diags'
} && !
defined
$options
{
'diags'
}) {
$options
{
'diags'
} =
delete
(
$options
{
'-diags'
}); }
my
(
$fh
,
$buffer
);
$options
{
'diags'
} = 0
if
not
defined
$options
{
'diags'
};
my
$comment
=
''
;
my
$self
=
$class
->_new();
if
(
ref
$filename
) {
$self
->{
' INFILE'
} =
$filename
;
if
(
$update
) {
$self
->{
' update'
} = 1;
$self
->{
' OUTFILE'
} =
$filename
;
}
$fh
=
$filename
;
}
else
{
die
"File '$filename' does not exist!"
unless
-f
$filename
;
$fh
= IO::File->new((
$update
?
'+'
:
''
) .
"<$filename"
) ||
return
;
$self
->{
' INFILE'
} =
$fh
;
if
(
$update
) {
$self
->{
' update'
} = 1;
$self
->{
' OUTFILE'
} =
$fh
;
$self
->{
' fname'
} =
$filename
;
}
}
binmode
$fh
,
':raw'
;
$fh
->
seek
(0, 0);
$fh
->
read
(
$buffer
, 255);
unless
(
$buffer
=~ m/^\
%PDF
\-(\d+\.\d+)(.*?)
$cr
/mo) {
die
"$filename does not contain a valid PDF version number"
;
}
$self
->{
' version'
} = $1;
if
(
defined
$2 &&
length
($2) > 0) {
$comment
= $2;
}
$fh
->
seek
(0, 2);
my
$end
=
$fh
->
tell
();
$self
->{
' epos'
} =
$end
;
foreach
my
$offset
(1 .. 64) {
$fh
->
seek
(
$end
- 16 *
$offset
, 0);
$fh
->
read
(
$buffer
, 16 *
$offset
);
last
if
$buffer
=~ m/startxref(
$cr
|\s*)\d+(
$cr
|\s*)\%\
%eof
.*?/i;
}
unless
(
$buffer
=~ m/startxref[^\d]+([0-9]+)(
$cr
|\s*)\%\
%eof
.*?/i) {
if
(
$options
{
'diags'
} == 1) {
warn
"Malformed PDF file $filename"
;
}
}
my
$xpos
= $1;
$self
->{
' xref_position'
} =
$xpos
;
my
$tdict
=
$self
->readxrtr(
$xpos
,
%options
);
foreach
my
$key
(
keys
%$tdict
) {
$self
->{
$key
} =
$tdict
->{
$key
};
}
return
$self
;
}
sub
version {
my
$self
=
shift
();
my
$header_version
=
$self
->header_version();
my
$trailer_version
=
$self
->trailer_version();
my
$old_version
= (
defined
$trailer_version
&&
$trailer_version
>
$header_version
)?
$trailer_version
:
$header_version
;
if
(
@_
) {
my
$version
=
shift
();
my
%opts
=
@_
;
if
(
defined
$opts
{
'-silent'
} && !
defined
$opts
{
'silent'
}) {
$opts
{
'silent'
} =
delete
(
$opts
{
'-silent'
}); }
if
(
$version
=~ m/^\d+$/) {
$version
=
"1.$version"
; }
if
(
$version
!~ /^[12]\.[0-9]+$/) {
croak
"Invalid version '$version' ignored"
unless
defined
$opts
{
'silent'
};
return
$old_version
;
}
if
(
$old_version
>
$version
) {
croak
"Warning: call to header_version() to LOWER the output PDF version number!"
unless
defined
$opts
{
'silent'
};
}
$self
->header_version(
$version
,
'silent'
=>1);
$self
->trailer_version(
$version
,
'silent'
=>1);
return
$version
;
}
return
$old_version
;
}
sub
header_version {
my
$self
=
shift
();
my
$old_version
=
$self
->{
' version'
};
if
(
@_
) {
my
$version
=
shift
();
my
%opts
=
@_
;
if
(
defined
$opts
{
'-silent'
} && !
defined
$opts
{
'silent'
}) {
$opts
{
'silent'
} =
delete
(
$opts
{
'-silent'
}); }
if
(
$version
=~ m/^\d+$/) {
$version
=
"1.$version"
; }
if
(
$version
!~ /^[12]\.[0-9]+$/) {
croak
"Invalid header_version '$version' ignored"
unless
defined
$opts
{
'silent'
};
return
$old_version
;
}
if
(
$old_version
>
$version
) {
croak
"Warning: call to header_version() to LOWER the output PDF version number!"
unless
defined
$opts
{
'silent'
};
}
$self
->{
' version'
} =
$version
;
return
$version
;
}
return
$old_version
;
}
sub
trailer_version {
my
$self
=
shift
();
my
$old_version
=
undef
;
if
(
$self
->{
'Root'
}->{
'Version'
}) {
$self
->{
'Root'
}->{
'Version'
}->realise();
$old_version
=
$self
->{
'Root'
}->{
'Version'
}->val();
}
if
(
@_
) {
my
$version
=
shift
();
my
%opts
=
@_
;
if
(
defined
$opts
{
'-silent'
} && !
defined
$opts
{
'silent'
}) {
$opts
{
'silent'
} =
delete
(
$opts
{
'-silent'
}); }
if
(
$version
=~ m/^\d+$/) {
$version
=
"1.$version"
; }
if
(
$version
!~ /^[12]\.[0-9]+$/) {
croak
"Invalid trailer_version '$version' ignored"
unless
defined
$opts
{
'silent'
};
return
$old_version
;
}
if
(
defined
$old_version
&&
$old_version
>
$version
) {
croak
"Warning: call to trailer_version() to LOWER the output PDF version number!"
unless
defined
$opts
{
'silent'
};
}
$self
->{
'Root'
}->{
'Version'
} = PDFName(
$version
);
$self
->out_obj(
$self
->{
'Root'
});
return
$version
;
}
return
$old_version
;
}
sub
require_version {
my
(
$self
,
$min_version
) =
@_
;
my
$current_version
=
$self
->version();
$self
->version(
$min_version
)
if
$current_version
<
$min_version
;
return
$current_version
;
}
sub
release {
my
$self
=
shift
();
return
$self
unless
ref
(
$self
);
my
@tofree
=
values
%$self
;
foreach
my
$key
(
keys
%$self
) {
$self
->{
$key
} =
undef
;
delete
$self
->{
$key
};
}
no
warnings
'recursion'
;
while
(
my
$item
=
shift
@tofree
) {
if
(blessed(
$item
) and
$item
->can(
'release'
)) {
$item
->release(1);
}
elsif
(
ref
(
$item
) eq
'ARRAY'
) {
push
@tofree
,
@$item
;
}
elsif
(
ref
(
$item
) eq
'HASH'
) {
push
@tofree
,
values
%$item
;
foreach
my
$key
(
keys
%$item
) {
$item
->{
$key
} =
undef
;
delete
$item
->{
$key
};
}
}
else
{
$item
=
undef
;
}
}
return
;
}
sub
append_file {
my
$self
=
shift
();
return
unless
$self
->{
' update'
};
my
$fh
=
$self
->{
' INFILE'
};
my
$version
=
$self
->{
' version'
} || 1.4;
$fh
->
seek
(0, 0);
$fh
->
print
(
"%PDF-$version"
);
my
$tdict
= PDFDict();
$tdict
->{
'Prev'
} = PDFNum(
$self
->{
' loc'
});
$tdict
->{
'Info'
} =
$self
->{
'Info'
};
if
(
defined
$self
->{
' newroot'
}) {
$tdict
->{
'Root'
} =
$self
->{
' newroot'
};
}
else
{
$tdict
->{
'Root'
} =
$self
->{
'Root'
};
}
$tdict
->{
'Size'
} =
$self
->{
'Size'
};
foreach
my
$key
(
grep
{
$_
!~ m/^\s/ }
keys
%$self
) {
$tdict
->{
$key
} =
$self
->{
$key
}
unless
defined
$tdict
->{
$key
};
}
$fh
->
seek
(
$self
->{
' epos'
}, 0);
$self
->out_trailer(
$tdict
,
$self
->{
' update'
});
close
$self
->{
' OUTFILE'
};
return
;
}
sub
out_file {
my
(
$self
,
$fname
) =
@_
;
$self
=
$self
->create_file(
$fname
);
$self
=
$self
->close_file();
return
$self
;
}
sub
create_file {
my
(
$self
,
$filename
) =
@_
;
my
$fh
;
$self
->{
' fname'
} =
$filename
;
if
(
ref
$filename
) {
$fh
=
$filename
;
}
else
{
$fh
= IO::File->new(
">$filename"
) ||
die
"Unable to open $filename for writing"
;
binmode
(
$fh
,
':raw'
);
}
$self
->{
' OUTFILE'
} =
$fh
;
$fh
->
print
(
'%PDF-'
. (
$self
->{
' version'
} //
'1.4'
) .
"\n"
);
$fh
->
print
(
"%\xC6\xCD\xCD\xB5\n"
);
return
$self
;
}
sub
close_file {
my
$self
=
shift
();
my
$tdict
= PDFDict();
$tdict
->{
'Info'
} =
$self
->{
'Info'
}
if
defined
$self
->{
'Info'
};
$tdict
->{
'Root'
} = (
defined
$self
->{
' newroot'
} and
$self
->{
' newroot'
} ne
''
) ?
$self
->{
' newroot'
} :
$self
->{
'Root'
};
$tdict
->{
'Size'
} =
$self
->{
'Size'
} || PDFNum(1);
$tdict
->{
'Prev'
} = PDFNum(
$self
->{
' loc'
})
if
$self
->{
' loc'
};
if
(
$self
->{
' update'
}) {
foreach
my
$key
(
grep
{
$_
!~ m/^[\s\-]/ }
keys
%$self
) {
$tdict
->{
$key
} =
$self
->{
$key
}
unless
defined
$tdict
->{
$key
};
}
my
$fh
=
$self
->{
' INFILE'
};
$fh
->
seek
(
$self
->{
' epos'
}, 0);
}
$self
->out_trailer(
$tdict
,
$self
->{
' update'
});
close
(
$self
->{
' OUTFILE'
});
if
($^O eq
'MacOS'
and not
ref
(
$self
->{
' fname'
})) {
MacPerl::SetFileInfo(
'CARO'
,
'TEXT'
,
$self
->{
' fname'
});
}
return
$self
;
}
sub
readval {
my
(
$self
,
$str
,
%opts
) =
@_
;
my
$fh
=
$self
->{
' INFILE'
};
my
(
$result
,
$value
);
my
$update
=
defined
(
$opts
{
'update'
}) ?
$opts
{
'update'
} : 1;
$str
= update(
$fh
,
$str
)
if
$update
;
$str
=~ s/^
$ws_char
+//;
$str
=~ s/^\%[^\015\012]
*$ws_char
+//;
if
(
$str
=~ m/^<</s) {
$str
=
substr
(
$str
, 2);
$str
= update(
$fh
,
$str
)
if
$update
;
$result
= PDFDict();
while
(
$str
!~ m/^>>/) {
$str
=~ s/^
$ws_char
+//;
$str
=~ s/^\%[^\015\012]
*$ws_char
+//;
if
(
$str
=~ s|^/(
$reg_char
+)||) {
my
$key
= PDF::Builder::Basic::PDF::Name::name_to_string($1,
$self
);
(
$value
,
$str
) =
$self
->readval(
$str
,
%opts
);
$result
->{
$key
} =
$value
unless
ref
(
$value
) eq
'PDF::Builder::Basic::PDF::Null'
;
}
elsif
(
$str
=~ s|^/
$ws_char
+||) {
(
$value
,
$str
) =
$self
->readval(
$str
,
%opts
);
$result
->{
'null'
} =
$value
;
}
elsif
(
$str
=~ s|^//|/|) {
(
$value
,
$str
) =
$self
->readval(
$str
,
%opts
);
$result
->{
'null'
} =
$value
;
}
else
{
die
"Invalid dictionary key"
;
}
$str
= update(
$fh
,
$str
)
if
$update
;
}
$str
=~ s/^>>//;
$str
= update(
$fh
,
$str
)
if
$update
;
if
((
$str
=~ s/^stream(?:(?:\015\012)|\012|\015)//) and (
$result
->{
'Length'
}->val() != 0)) {
my
$length
=
$result
->{
'Length'
}->val();
$result
->{
' streamsrc'
} =
$fh
;
$result
->{
' streamloc'
} =
$fh
->
tell
() -
length
(
$str
);
unless
(
$opts
{
'nostreams'
}) {
if
(
$length
>
length
(
$str
)) {
$value
=
$str
;
$length
-=
length
(
$str
);
read
$fh
,
$str
,
$length
+ 11;
}
else
{
$value
=
''
;
}
$value
.=
substr
(
$str
, 0,
$length
);
$result
->{
' stream'
} =
$value
;
$result
->{
' nofilt'
} = 1;
$str
= update(
$fh
,
$str
, 1)
if
$update
;
$str
=
substr
(
$str
,
index
(
$str
,
'endstream'
) + 9);
}
}
if
(
defined
$result
->{
'Type'
} and
defined
$types
{
$result
->{
'Type'
}->val()}) {
bless
$result
,
$types
{
$result
->{
'Type'
}->val()};
}
}
elsif
(
$str
=~ m/^([0-9]+)(?:
$ws_char
|
$re_comment
)+([0-9]+)(?:
$ws_char
|
$re_comment
)+R/s) {
my
$num
= $1;
$value
= $2;
$str
=~ s/^([0-9]+)(?:
$ws_char
|
$re_comment
)+([0-9]+)(?:
$ws_char
|
$re_comment
)+R//s;
unless
(
$result
=
$self
->test_obj(
$num
,
$value
)) {
$result
= PDF::Builder::Basic::PDF::Objind->new();
$result
->{
' objnum'
} =
$num
;
$result
->{
' objgen'
} =
$value
;
$self
->add_obj(
$result
,
$num
,
$value
);
}
$result
->{
' parent'
} =
$self
;
weaken
$result
->{
' parent'
};
}
elsif
(
$str
=~ m/^([0-9]+)(?:
$ws_char
|
$re_comment
)+([0-9]+)(?:
$ws_char
|
$re_comment
)+obj/s) {
my
$obj
;
my
$num
= $1;
$value
= $2;
$str
=~ s/^([0-9]+)(?:
$ws_char
|
$re_comment
)+([0-9]+)(?:
$ws_char
|
$re_comment
)+obj//s;
(
$obj
,
$str
) =
$self
->readval(
$str
,
%opts
);
if
(
$result
=
$self
->test_obj(
$num
,
$value
)) {
$result
->merge(
$obj
);
}
else
{
$result
=
$obj
;
$self
->add_obj(
$result
,
$num
,
$value
);
$result
->{
' realised'
} = 1;
}
$str
= update(
$fh
,
$str
)
if
$update
;
$str
=~ s/^endobj//;
}
elsif
(
$str
=~ m|^/(
$reg_char
*)|s) {
$value
= $1;
$str
=~ s|^/(
$reg_char
*)||s;
$result
= PDF::Builder::Basic::PDF::Name->from_pdf(
$value
,
$self
);
}
elsif
(
$str
=~ m/^\(/) {
my
$value
=
'('
;
$str
=
substr
(
$str
, 1);
my
$nested_level
= 1;
while
(1) {
if
(
$str
=~ /^([^\\()]+)(.*)/s) {
$value
.= $1;
$str
= $2;
}
if
(
$str
=~ /^(\\[()])/) {
$value
.= $1;
$str
=
substr
(
$str
, 2);
}
elsif
(
$str
=~ /^\(/) {
$value
.=
'('
;
$str
=
substr
(
$str
, 1);
$nested_level
++;
}
elsif
(
$str
=~ /^\)/) {
$value
.=
')'
;
$str
=
substr
(
$str
, 1);
$nested_level
--;
last
unless
$nested_level
;
}
elsif
(
$str
=~ /^(\\[^()])/) {
$value
.= $1;
$str
=
substr
(
$str
, 2);
}
else
{
$fh
->
read
(
$str
, 255,
length
(
$str
)) or
die
'Unterminated string.'
;
}
}
$result
= PDF::Builder::Basic::PDF::String->from_pdf(
$value
);
}
elsif
(
$str
=~ m/^</) {
$str
=~ s/^<//;
$fh
->
read
(
$str
, 255,
length
(
$str
))
while
(0 >
index
(
$str
,
'>'
));
(
$value
,
$str
) = (
$str
=~ /^(.*?)>(.*)/s);
$result
= PDF::Builder::Basic::PDF::String->from_pdf(
'<'
.
$value
.
'>'
);
}
elsif
(
$str
=~ m/^\[/) {
$str
=~ s/^\[//;
$str
= update(
$fh
,
$str
)
if
$update
;
$result
= PDFArray();
while
(
$str
!~ m/^\]/) {
$str
=~ s/^
$ws_char
+//;
$str
=~ s/^\%[^\015\012]
*$ws_char
+//;
(
$value
,
$str
) =
$self
->readval(
$str
,
%opts
);
$result
->add_elements(
$value
);
$str
= update(
$fh
,
$str
)
if
$update
;
}
$str
=~ s/^\]//;
}
elsif
(
$str
=~ m/^(true|false)(
$irreg_char
|$)/) {
$value
= $1;
$str
=~ s/^(?:true|false)//;
$result
= PDF::Builder::Basic::PDF::Bool->from_pdf(
$value
);
}
elsif
(
$str
=~ m/^([+-.0-9]+)(
$irreg_char
|$)/) {
$value
= $1;
$str
=~ s/^([+-.0-9]+)//;
if
(
$update
and (
$str
=~ /^
$re_whitespace
*$/s or
$str
=~ /^
$re_whitespace
+[0-9]+
$re_whitespace
*$/s)) {
$str
=~ s/^
$re_whitespace
+/ /s;
$str
=~ s/
$re_whitespace
+$/ /s;
$str
= update(
$fh
,
$str
);
if
(
$str
=~ m/^
$re_whitespace
*([0-9]+)
$re_whitespace
+(?:R|obj)/s) {
return
$self
->readval(
"$value $str"
,
%opts
);
}
}
$result
= PDF::Builder::Basic::PDF::Number->from_pdf(
$value
);
}
elsif
(
$str
=~ m/^null(
$irreg_char
|$)/) {
$str
=~ s/^null//;
$result
= PDF::Builder::Basic::PDF::Null->new();
}
else
{
die
"Can't parse `$str' near "
. (
$fh
->
tell
()) .
" length "
.
length
(
$str
) .
"."
;
}
$str
=~ s/^
$ws_char
+//s;
return
(
$result
,
$str
);
}
sub
read_obj {
my
(
$self
,
$objind
,
%opts
) =
@_
;
my
$res
=
$self
->read_objnum(
$objind
->{
' objnum'
},
$objind
->{
' objgen'
},
%opts
) ||
return
;
$objind
->merge(
$res
)
unless
$objind
eq
$res
;
return
$objind
;
}
sub
read_objnum {
my
(
$self
,
$num
,
$gen
,
%opts
) =
@_
;
croak
'Undefined object number in call to read_objnum($num, $gen)'
unless
defined
$num
;
croak
'Undefined object generation in call to read_objnum($num, $gen)'
unless
defined
$gen
;
croak
"Invalid object number '$num' in call to read_objnum"
unless
$num
=~ /^[0-9]+$/;
croak
"Invalid object generation '$gen' in call to read_objnum"
unless
$gen
=~ /^[0-9]+$/;
my
$object_location
=
$self
->locate_obj(
$num
,
$gen
) ||
return
;
my
$object
;
if
(
ref
(
$object_location
)) {
my
(
$object_stream_num
,
$object_stream_pos
) = @{
$object_location
};
my
$object_stream
=
$self
->read_objnum(
$object_stream_num
, 0,
%opts
);
die
'Cannot find the compressed object stream'
unless
$object_stream
;
$object_stream
->read_stream()
if
$object_stream
->{
' nofilt'
};
my
$fh
;
my
$pairs
;
unless
(
$object_stream
->{
' streamfile'
}) {
$pairs
=
substr
(
$object_stream
->{
' stream'
}, 0,
$object_stream
->{
'First'
}->val());
}
else
{
CORE::
open
(
$fh
,
'<'
,
$object_stream
->{
' streamfile'
});
read
(
$fh
,
$pairs
,
$object_stream
->{
'First'
}->val());
}
my
@map
=
split
/\s+/,
$pairs
;
my
$index
=
$object_stream_pos
* 2;
die
"Objind $num does not exist at index $index"
unless
$map
[
$index
] ==
$num
;
my
$start
=
$map
[
$index
+ 1];
my
$last_object_in_stream
=
$map
[-2];
my
$length
;
if
(
$last_object_in_stream
==
$num
) {
if
(
$object_stream
->{
' stream'
}) {
$length
=
length
(
$object_stream
->{
' stream'
}) -
$object_stream
->{
'First'
}->val() -
$start
;
}
else
{
$length
= (-s
$object_stream
->{
' streamfile'
}) -
$object_stream
->{
'First'
}->val() -
$start
;
}
}
else
{
my
$next_start
=
$map
[
$index
+ 3];
$length
=
$next_start
-
$start
;
}
my
$stream
=
"$num 0 obj "
;
unless
(
$object_stream
->{
' streamfile'
}) {
$stream
.=
substr
(
$object_stream
->{
' stream'
},
$object_stream
->{
'First'
}->val() +
$start
,
$length
);
}
else
{
seek
(
$fh
,
$object_stream
->{
'First'
}->val() +
$start
, 0);
read
(
$fh
,
$stream
,
$length
,
length
(
$stream
));
close
$fh
;
}
(
$object
) =
$self
->readval(
$stream
,
%opts
,
update
=> 0);
return
$object
;
}
my
$current_location
=
$self
->{
' INFILE'
}->
tell
();
$self
->{
' INFILE'
}->
seek
(
$object_location
, 0);
(
$object
) =
$self
->readval(
''
,
%opts
);
$self
->{
' INFILE'
}->
seek
(
$current_location
, 0);
return
$object
;
}
sub
new_obj {
my
(
$self
,
$base
) =
@_
;
my
$res
;
if
(
defined
$self
->{
' free'
} and
scalar
@{
$self
->{
' free'
}} > 0) {
$res
=
shift
(@{
$self
->{
' free'
}});
if
(
defined
$base
) {
my
(
$num
,
$gen
) = @{
$self
->{
' objects'
}{
$res
->uid()}};
$self
->remove_obj(
$res
);
$self
->add_obj(
$base
,
$num
,
$gen
);
return
$self
->out_obj(
$base
);
}
else
{
$self
->{
' objects'
}{
$res
->uid()}[2] = 0;
return
$res
;
}
}
my
$tdict
=
$self
;
my
$i
;
while
(
defined
$tdict
) {
$i
=
$tdict
->{
' xref'
}{
defined
(
$i
) ?
$i
:
''
}[0];
while
(
defined
$i
and
$i
!= 0) {
my
(
$ni
,
$ng
) = @{
$tdict
->{
' xref'
}{
$i
}};
unless
(
defined
$self
->locate_obj(
$i
,
$ng
)) {
if
(
defined
$base
) {
$self
->add_obj(
$base
,
$i
,
$ng
);
return
$base
;
}
else
{
$res
=
$self
->test_obj(
$i
,
$ng
) ||
$self
->add_obj(PDF::Builder::Basic::PDF::Objind->new(),
$i
,
$ng
);
$self
->out_obj(
$res
);
return
$res
;
}
}
$i
=
$ni
;
}
$tdict
=
$tdict
->{
' prev'
};
}
$i
=
$self
->{
' maxobj'
}++;
if
(
defined
$base
) {
$self
->add_obj(
$base
,
$i
, 0);
$self
->out_obj(
$base
);
return
$base
;
}
else
{
$res
=
$self
->add_obj(PDF::Builder::Basic::PDF::Objind->new(),
$i
, 0);
$self
->out_obj(
$res
);
return
$res
;
}
}
sub
out_obj {
my
(
$self
,
$obj
) =
@_
;
unless
(
exists
$self
->{
' outlist_cache'
}{
$obj
}) {
push
@{
$self
->{
' outlist'
}},
$obj
;
$self
->{
' outlist_cache'
}{
$obj
} = 1;
}
return
$obj
;
}
sub
free_obj {
my
(
$self
,
$obj
) =
@_
;
push
@{
$self
->{
' free'
}},
$obj
;
$self
->{
' objects'
}{
$obj
->uid()}[2] = 1;
$self
->out_obj(
$obj
);
return
;
}
sub
remove_obj {
my
(
$self
,
$objind
) =
@_
;
delete
$self
->{
' objects'
}{
$objind
->uid()};
delete
$self
->{
' outlist_cache'
}{
$objind
};
delete
$self
->{
' printed_cache'
}{
$objind
};
@{
$self
->{
' outlist'
}} =
grep
{
$_
ne
$objind
} @{
$self
->{
' outlist'
}};
@{
$self
->{
' printed'
}} =
grep
{
$_
ne
$objind
} @{
$self
->{
' printed'
}};
$self
->{
' objcache'
}{
$objind
->{
' objnum'
},
$objind
->{
' objgen'
}} =
undef
if
$self
->{
' objcache'
}{
$objind
->{
' objnum'
},
$objind
->{
' objgen'
}} eq
$objind
;
return
$self
;
}
sub
ship_out {
my
(
$self
,
@objects
) =
@_
;
return
unless
defined
$self
->{
' OUTFILE'
};
my
$fh
=
$self
->{
' OUTFILE'
};
seek
(
$fh
, 0, 2);
@objects
= @{
$self
->{
' outlist'
}}
unless
scalar
@objects
> 0;
foreach
my
$objind
(
@objects
) {
next
unless
$objind
->is_obj(
$self
);
my
$j
= -1;
for
(
my
$i
= 0;
$i
<
scalar
@{
$self
->{
' outlist'
}};
$i
++) {
if
(
$self
->{
' outlist'
}[
$i
] eq
$objind
) {
$j
=
$i
;
last
;
}
}
next
if
$j
< 0;
splice
(@{
$self
->{
' outlist'
}},
$j
, 1);
delete
$self
->{
' outlist_cache'
}{
$objind
};
next
if
grep
{
$_
eq
$objind
} @{
$self
->{
' free'
}};
map
{
$fh
->
print
(
"\% $_ \n"
) }
split
(/
$cr
/,
$objind
->{
' comments'
})
if
$objind
->{
' comments'
};
$self
->{
' locs'
}{
$objind
->uid()} =
$fh
->
tell
();
my
(
$objnum
,
$objgen
) = @{
$self
->{
' objects'
}{
$objind
->uid()}}[0..1];
$fh
->
printf
(
'%d %d obj '
,
$objnum
,
$objgen
);
$objind
->outobjdeep(
$fh
,
$self
);
$fh
->
print
(
"\nendobj\n"
);
unless
(
exists
$self
->{
' printed_cache'
}{
$objind
}) {
push
@{
$self
->{
' printed'
}},
$objind
;
$self
->{
' printed_cache'
}{
$objind
}++;
}
}
return
$self
;
}
sub
copy {
my
(
$self
,
$outpdf
,
$filter
) =
@_
;
my
(
$obj
,
$minl
,
$mini
,
$ming
);
foreach
my
$key
(
grep
{ not m/^[\s\-]/ }
keys
%$self
) {
$outpdf
->{
$key
} =
$self
->{
$key
}
unless
defined
$outpdf
->{
$key
};
}
my
$tdict
=
$self
;
while
(
defined
$tdict
) {
foreach
my
$i
(
sort
{
$a
<=>
$b
}
keys
%{
$tdict
->{
' xref'
}}) {
my
(
$nl
,
$ng
,
$nt
) = @{
$tdict
->{
' xref'
}{
$i
}};
next
unless
$nt
eq
'n'
;
if
(
$nl
<
$minl
or
$mini
== 0) {
$mini
=
$i
;
$ming
=
$ng
;
$minl
=
$nl
;
}
unless
(
$obj
=
$self
->test_obj(
$i
,
$ng
)) {
$obj
= PDF::Builder::Basic::PDF::Objind->new();
$obj
->{
' objnum'
} =
$i
;
$obj
->{
' objgen'
} =
$ng
;
$self
->add_obj(
$obj
,
$i
,
$ng
);
$obj
->{
' parent'
} =
$self
;
weaken
$obj
->{
' parent'
};
$obj
->{
' realised'
} = 0;
}
$obj
->realise();
my
$res
=
defined
$filter
? &{
$filter
}(
$obj
) :
$obj
;
$outpdf
->new_obj(
$res
)
unless
(!
$res
||
$res
->is_obj(
$outpdf
));
}
$tdict
=
$tdict
->{
' prev'
};
}
$obj
=
$self
->test_obj(
$mini
,
$ming
);
if
(
$obj
->isa(
'PDF::Builder::Basic::PDF::Dict'
) &&
$obj
->{
'Linearized'
}) {
$outpdf
->free_obj(
$obj
);
}
return
$self
;
}
sub
locate_obj {
my
(
$self
,
$num
,
$gen
) =
@_
;
my
$tdict
=
$self
;
while
(
defined
$tdict
) {
if
(
ref
$tdict
->{
' xref'
}{
$num
}) {
my
$ref
=
$tdict
->{
' xref'
}{
$num
};
return
$ref
unless
scalar
(
@$ref
) == 3;
if
(
$ref
->[1] ==
$gen
) {
return
$ref
->[0]
if
$ref
->[2] eq
'n'
;
return
;
}
}
$tdict
=
$tdict
->{
' prev'
};
}
return
;
}
sub
update {
my
(
$fh
,
$str
,
$instream
) =
@_
;
print
STDERR
'fpos='
.
tell
(
$fh
) .
' strlen='
.
length
(
$str
) .
"\n"
if
$readDebug
;
if
(
$instream
) {
while
(
$str
!~ m/endstream/ and not
$fh
->
eof
()) {
print
STDERR
'fpos='
.
tell
(
$fh
) .
' strlen='
.
length
(
$str
) .
"\n"
if
$readDebug
;
$fh
->
read
(
$str
, 314,
length
(
$str
));
}
}
else
{
$str
=~ s/^
$ws_char
*//;
while
(
$str
!~ m/
$cr
/ and not
$fh
->
eof
()) {
print
STDERR
'fpos='
.
tell
(
$fh
) .
' strlen='
.
length
(
$str
) .
"\n"
if
$readDebug
;
$fh
->
read
(
$str
, 314,
length
(
$str
));
$str
=~ s/^
$ws_char
*//so;
}
while
(
$str
=~ m/^\%/) {
print
STDERR
'fpos='
.
tell
(
$fh
) .
' strlen='
.
length
(
$str
) .
"\n"
if
$readDebug
;
$fh
->
read
(
$str
, 314,
length
(
$str
))
while
(
$str
!~ m/
$cr
/ and not
$fh
->
eof
());
$str
=~ s/^\%[^\015\012]
*$ws_char
*//so;
}
}
return
$str
;
}
sub
test_obj {
my
(
$self
,
$num
,
$gen
) =
@_
;
return
$self
->{
' objcache'
}{
$num
,
$gen
};
}
sub
add_obj {
my
(
$self
,
$obj
,
$num
,
$gen
) =
@_
;
$self
->{
' objcache'
}{
$num
,
$gen
} =
$obj
;
$self
->{
' objects'
}{
$obj
->uid()} = [
$num
,
$gen
];
return
$obj
;
}
sub
_unpack_xref_stream {
my
(
$self
,
$width
,
$data
) =
@_
;
if
(
$width
== 3) {
$data
=
"\x00$data"
;
$width
= 4;
}
elsif
(
$width
== 5) {
$data
=
"\x00\x00\x00$data"
;
$width
= 8;
}
elsif
(
$width
== 6) {
$data
=
"\x00\x00$data"
;
$width
= 8;
}
elsif
(
$width
== 7) {
$data
=
"\x00$data"
;
$width
= 8;
}
return
unpack
(
'C'
,
$data
)
if
$width
== 1;
return
unpack
(
'n'
,
$data
)
if
$width
== 2;
return
unpack
(
'N'
,
$data
)
if
$width
== 4;
if
(
$width
== 8) {
if
(
substr
(
$data
, 0, 4) eq
"\x00\x00\x00\x00"
) {
return
unpack
(
'N'
,
substr
(
$data
, 4, 4));
}
else
{
return
unpack
(
'Q>'
,
$data
);
}
}
die
"Unsupported field width: $width. 1-8 supported."
;
}
sub
readxrtr {
my
(
$self
,
$xpos
,
%options
) =
@_
;
if
(
defined
$options
{
'-diags'
} && !
defined
$options
{
'diags'
}) {
$options
{
'diags'
} =
delete
(
$options
{
'-diags'
}); }
my
(
$tdict
,
$buf
,
$xmin
,
$xnum
,
$xdiff
);
my
$fh
=
$self
->{
' INFILE'
};
$fh
->
seek
(
$xpos
, 0);
$fh
->
read
(
$buf
, 22);
$buf
= update(
$fh
,
$buf
);
my
$xlist
= {};
if
(
$buf
=~ s/^xref
$cr
//i) {
my
$subsection_count
= 0;
my
$entry_format_error
= 0;
my
$xrefListEmpty
= 0;
while
(
$buf
=~ m/^
$ws_char
*([0-9]+)
$ws_char
+([0-9]+)
$ws_char
*$cr
(.*?)$/s) {
my
$old_buf
=
$buf
;
$xmin
= $1;
$xnum
= $2;
$buf
= $3;
$subsection_count
++;
unless
(
$old_buf
=~ /^[0-9]+ [0-9]+
$cr
/) {
if
(
$options
{
'diags'
} == 1) {
warn
"Malformed xref: subsection header needs a single\n"
.
"ASCII space between the numbers and no extra spaces.\n"
;
}
}
$xdiff
=
length
(
$buf
);
if
(
$xnum
< 1) {
if
(
$options
{
'diags'
} == 1) {
warn
"Xref subsection has 0 entries. Skipped.\n"
;
}
$xrefListEmpty
= 1;
next
;
}
my
$entry_size
= 20;
$fh
->
read
(
$buf
,
$entry_size
* 1 -
$xdiff
+ 15,
$xdiff
);
if
(
$buf
=~ m/^(.*?)
$cr
/) {
$entry_size
=
length
($1) + 2;
}
if
(
$entry_size
!= 20 &&
$options
{
'diags'
} == 1) {
warn
"Xref entries supposed to be 20 bytes long, are $entry_size.\n"
;
}
$xdiff
=
length
(
$buf
);
$fh
->
read
(
$buf
,
$entry_size
*
$xnum
-
$xdiff
+ 15,
$xdiff
);
while
(
$xnum
-- > 0 and
$buf
=~ m/^
$ws_char
*(\d+)
$ws_char
+(\d+)
$ws_char
+([nf])
$ws_char
*$cr
/) {
if
(
$buf
=~ m/^\d{10} \d{5} [nf]
$cr
/ ||
$entry_format_error
) {
}
else
{
if
(
$options
{
'diags'
} == 1) {
warn
"Xref entry readable, but doesn't meet PDF spec.\n"
;
}
$entry_format_error
++;
}
$buf
=~ s/^
$ws_char
*(\d+)
$ws_char
+(\d+)
$ws_char
+([nf])
$ws_char
*$cr
//;
if
(
exists
$xlist
->{
$xmin
}) {
if
(
$options
{
'diags'
} == 1) {
warn
"Duplicate object number $xmin in xref table ignored.\n"
;
}
}
else
{
$xlist
->{
$xmin
} = [$1, $2, $3];
if
(
$xmin
== 0 &&
$subsection_count
> 1 &&
$options
{
'diags'
} == 1) {
warn
"Xref object 0 entry not in first subsection.\n"
;
}
}
$xmin
++;
}
}
if
(!
exists
$xlist
->{
'0'
} && !
$xrefListEmpty
) {
if
(
$subsection_count
== 1 &&
exists
$xlist
->{
'1'
}) {
if
(
$xlist
->{
'1'
}[0] == 0 &&
$xlist
->{
'1'
}[1] == 65535 &&
$xlist
->{
'1'
}[2] eq
'f'
) {
if
(
$options
{
'diags'
} == 1) {
warn
"xref appears to be mislabeled starting with 1. Shift down all elements.\n"
;
}
my
$next
= 1;
while
(
exists
$xlist
->{
$next
}) {
$xlist
->{
$next
- 1} =
$xlist
->{
$next
};
$next
++;
}
delete
$xlist
->{--
$next
};
}
else
{
if
(
$options
{
'diags'
} == 1) {
warn
"Xref appears to be missing object 0. Insert a new one.\n"
;
}
$xlist
->{
'0'
} = [0, 65535,
'f'
];
}
}
else
{
if
(
$options
{
'diags'
} == 1) {
warn
"Malformed cross reference list in PDF file $self->{' fname'} -- no object 0 (free list head)\n"
;
}
$xlist
->{
'0'
} = [0, 65535,
'f'
];
}
}
my
@free_list
;
foreach
(
sort
{
$a
<=>
$b
}
keys
%{
$xlist
}) {
if
(
$xlist
->{
$_
}[2] eq
'f'
) {
if
(
$xlist
->{
$_
}[1] <= 0 &&
$options
{
'diags'
} == 1) {
warn
"Xref free list entry $_ with bad next generation number.\n"
;
}
else
{
push
@free_list
,
$_
;
}
}
elsif
(
$xlist
->{
$_
}[2] eq
'n'
) {
if
(
$xlist
->{
$_
}[0] <= 0 &&
$options
{
'diags'
} == 1) {
warn
"Xref active object $_ entry with bad length "
.(
$xlist
->{
$_
}[1]).
"\n"
;
}
if
(
$xlist
->{
$_
}[1] < 0 &&
$options
{
'diags'
} == 1) {
warn
"Xref active object $_ entry with bad generation number "
.(
$xlist
->{
$_
}[1]).
"\n"
;
}
}
else
{
if
(
$options
{
'diags'
} == 1) {
warn
"Xref entry has flag that is not 'f' or 'n'.\n"
;
}
}
}
my
$next_free
= 0;
if
(
$xlist
->{
'0'
}[1] != 65535 &&
$options
{
'diags'
} == 1) {
warn
"Object 0 next generation is not 65535.\n"
;
}
do
{
if
(
$xlist
->{
$next_free
}[2] ne
'f'
) {
if
(
$options
{
'diags'
} == 1) {
warn
"Corrupted free object list: next=$next_free is not a free object.\n"
;
}
$next_free
= 0;
}
else
{
$next_free
=
$xlist
->{
$next_free
}[0];
}
splice
(
@free_list
,
index
(
@free_list
,
$next_free
), 1);
}
while
(
$next_free
&&
exists
$xlist
->{
$next_free
});
if
(
scalar
@free_list
&&
$options
{
'diags'
} == 1) {
warn
"Corrupted xref list: object(s) @free_list marked as free, but are not in free chain.\n"
;
}
if
(
$buf
!~ /^\s
*trailer
\b/i &&
$options
{
'diags'
} == 1) {
warn
"Malformed trailer in PDF file $self->{' fname'} at "
. (
$fh
->
tell
() -
length
(
$buf
));
}
$buf
=~ s/^\s
*trailer
\b//i;
(
$tdict
,
$buf
) =
$self
->readval(
$buf
);
}
elsif
(
$buf
=~ m/^(\d+)\s+(\d+)\s+obj/i) {
my
(
$xref_obj
,
$xref_gen
) = ($1, $2);
$PDF::Builder::global_pdf
->verCheckOutput(1.5,
"importing cross-reference stream"
);
(
$tdict
,
$buf
) =
$self
->readval(
$buf
);
unless
(
$tdict
->{
' stream'
}) {
if
(
$options
{
'diags'
} == 1) {
warn
"Malformed XRefStm at $xref_obj $xref_gen obj in PDF file $self->{' fname'}"
;
}
}
$tdict
->read_stream(1);
my
$stream
=
$tdict
->{
' stream'
};
my
@widths
=
map
{
$_
->val() } @{
$tdict
->{
'W'
}->val()};
my
$start
= 0;
my
$last
;
my
@index
;
if
(
defined
$tdict
->{
'Index'
}) {
@index
=
map
{
$_
->val() } @{
$tdict
->{
'Index'
}->val()};
}
else
{
@index
= (0,
$tdict
->{
'Size'
}->val());
}
while
(
scalar
@index
) {
$start
=
shift
(
@index
);
$last
=
$start
+
shift
(
@index
) - 1;
for
my
$i
(
$start
...
$last
) {
$xmin
=
$i
;
my
@cols
;
for
my
$w
(
@widths
) {
my
$data
;
$data
=
$self
->_unpack_xref_stream(
$w
,
substr
(
$stream
, 0,
$w
,
''
))
if
$w
;
push
@cols
,
$data
;
}
$cols
[0] = 1
unless
defined
$cols
[0];
if
(
$cols
[0] > 2 &&
$options
{
'diags'
} == 1) {
warn
"Invalid XRefStm entry type ($cols[0]) at $xref_obj $xref_gen obj"
;
}
next
if
exists
$xlist
->{
$xmin
};
my
@objind
= (
$cols
[1],
defined
(
$cols
[2]) ?
$cols
[2] : (
$xmin
? 0 : 65535));
push
@objind
, (
$cols
[0] == 0?
'f'
:
'n'
)
if
$cols
[0] < 2;
$xlist
->{
$xmin
} = \
@objind
;
}
}
}
else
{
if
(
$options
{
'diags'
} == 1) {
warn
"Malformed xref in PDF file $self->{' fname'}"
;
}
}
$xmin
||= 0;
$tdict
->{
' loc'
} =
$xpos
;
$tdict
->{
' xref'
} =
$xlist
;
$self
->{
' maxobj'
} =
$xmin
+ 1
if
$xmin
+ 1 >
$self
->{
' maxobj'
};
$tdict
->{
' prev'
} =
$self
->readxrtr(
$tdict
->{
'Prev'
}->val(),
%options
)
if
(
defined
$tdict
->{
'Prev'
} and
$tdict
->{
'Prev'
}->val() != 0);
delete
$tdict
->{
' prev'
}
unless
defined
$tdict
->{
' prev'
};
return
$tdict
;
}
sub
out_trailer {
my
(
$self
,
$tdict
,
$update
) =
@_
;
my
$fh
=
$self
->{
' OUTFILE'
};
while
(@{
$self
->{
' outlist'
}}) {
$self
->ship_out();
}
$tdict
->{
'Size'
} = PDFNum(
$self
->{
' maxobj'
});
my
$tloc
=
$fh
->
tell
();
my
@out
;
my
@xreflist
=
sort
{
$self
->{
' objects'
}{
$a
->uid()}[0] <=>
$self
->{
' objects'
}{
$b
->uid()}[0] } (@{
$self
->{
' printed'
} || []}, @{
$self
->{
' free'
} || []});
my
(
$i
,
$j
,
$k
);
unless
(
$update
) {
$i
= 1;
for
(
$j
= 0;
$j
<
@xreflist
;
$j
++) {
my
@inserts
;
$k
=
$xreflist
[
$j
];
while
(
$i
<
$self
->{
' objects'
}{
$k
->uid()}[0]) {
my
(
$n
) = PDF::Builder::Basic::PDF::Objind->new();
$self
->add_obj(
$n
,
$i
, 0);
$self
->free_obj(
$n
);
push
(
@inserts
,
$n
);
$i
++;
}
splice
(
@xreflist
,
$j
, 0,
@inserts
);
$j
+=
@inserts
;
$i
++;
}
}
my
@freelist
=
sort
{
$self
->{
' objects'
}{
$a
->uid()}[0] <=>
$self
->{
' objects'
}{
$b
->uid()}[0] } @{
$self
->{
' free'
} || []};
$j
= 0;
my
$first
= -1;
$k
= 0;
for
(
$i
= 0;
$i
<=
$#xreflist
+ 1;
$i
++) {
if
(
$i
>
$#xreflist
||
$self
->{
' objects'
}{
$xreflist
[
$i
]->uid()}[0] !=
$j
+ 1) {
push
@out
, (
$first
== -1 ?
"0 "
:
"$self->{' objects'}{$xreflist[$first]->uid()}[0] "
) . (
$i
-
$first
) .
"\n"
;
if
(
$first
== -1) {
push
@out
,
sprintf
(
"%010d 65535 f \n"
,
defined
$freelist
[
$k
] ?
$self
->{
' objects'
}{
$freelist
[
$k
]->uid()}[0] : 0);
$first
= 0;
}
for
(
$j
=
$first
;
$j
<
$i
;
$j
++) {
my
$xref
=
$xreflist
[
$j
];
if
(
defined
$freelist
[
$k
] &&
defined
$xref
&&
"$freelist[$k]"
eq
"$xref"
) {
$k
++;
push
(
@out
,
pack
(
"A10AA5A4"
,
sprintf
(
"%010d"
, (
defined
$freelist
[
$k
] ?
$self
->{
' objects'
}{
$freelist
[
$k
]->uid()}[0] : 0)),
" "
,
sprintf
(
"%05d"
,
$self
->{
' objects'
}{
$xref
->uid()}[1] + 1),
" f \n"
));
}
else
{
push
(
@out
,
pack
(
"A10AA5A4"
,
sprintf
(
"%010d"
,
$self
->{
' locs'
}{
$xref
->uid()}),
" "
,
sprintf
(
"%05d"
,
$self
->{
' objects'
}{
$xref
->uid()}[1]),
" n \n"
));
}
}
$first
=
$i
;
$j
=
$self
->{
' objects'
}{
$xreflist
[
$i
]->uid()}[0]
if
(
$i
<
scalar
@xreflist
);
}
else
{
$j
++;
}
}
if
(
exists
$tdict
->{
'Type'
} and
$tdict
->{
'Type'
}->val() eq
'XRef'
) {
my
(
@index
,
@stream
);
for
(
@out
) {
my
@a
=
split
;
@a
== 2 ?
push
@index
,
@a
:
push
@stream
, \
@a
;
}
my
$i
=
$self
->{
' maxobj'
}++;
$self
->add_obj(
$tdict
,
$i
, 0);
$self
->out_obj(
$tdict
);
push
@index
,
$i
, 1;
push
@stream
, [
$tloc
, 0,
'n'
];
my
$len
=
$tloc
> 0xFFFF ? 4 : 2;
my
$tpl
=
$tloc
> 0xFFFF ?
'CNC'
:
'CnC'
;
my
$sstream
=
''
;
my
@prev
= ( 0 ) x (
$len
+ 2 );
for
(
@stream
) {
$_
->[ 1 ] = 0
if
$_
->[ 1 ] == 65535 and
$_
->[ 2 ] eq
'f'
;
if
(
$_
->[1] > 0xFF) {
print
"generation number "
.(
$_
->[1]).
" in entry '$_->[0] $_->[1] $_->[2]' exceeds 256, reduced to "
.(
$_
->[1] & 0x00FF).
"\n"
;
}
$_
->[ 1 ] &= 0x00FF;
my
@line
=
unpack
'C*'
,
pack
$tpl
,
$_
->[ 2 ] eq
'n'
? 1 : 0, @{
$_
}[ 0 .. 1 ];
$sstream
.=
pack
'C*'
, 2,
map
{(
$line
[
$_
] -
$prev
[
$_
] + 256) % 256} 0 ..
$#line
;
@prev
=
@line
;
}
$tdict
->{
'Size'
} = PDFNum(
$i
+ 1);
$tdict
->{
'Index'
} = PDFArray(
map
{ PDFNum(
$_
) }
@index
);
$tdict
->{
'W'
} = PDFArray(
map
{ PDFNum(
$_
) } 1,
$len
, 1);
$tdict
->{
'Filter'
} = PDFName(
'FlateDecode'
);
$tdict
->{
'DecodeParms'
} = PDFDict();
$tdict
->{
'DecodeParms'
}->val()->{
'Predictor'
} = PDFNum(12);
$tdict
->{
'DecodeParms'
}->val()->{
'Columns'
} = PDFNum(
$len
+ 2);
$sstream
= PDF::Builder::Basic::PDF::Filter::FlateDecode->new()->outfilt(
$sstream
, 1);
$tdict
->{
' stream'
} =
$sstream
;
$tdict
->{
' nofilt'
} = 1;
delete
$tdict
->{
'Length'
};
$self
->ship_out();
}
else
{
delete
$tdict
->{
'XRefStm'
};
$fh
->
print
(
"xref\n"
,
@out
,
"trailer\n"
);
$tdict
->outobjdeep(
$fh
,
$self
);
$fh
->
print
(
"\n"
);
}
$fh
->
print
(
"startxref\n$tloc\n%%EOF\n"
);
return
;
}
sub
_new {
my
$class
=
shift
();
my
$self
= {};
bless
$self
,
$class
;
$self
->{
' outlist'
} = [];
$self
->{
' outlist_cache'
} = {};
$self
->{
' maxobj'
} = 1;
$self
->{
' objcache'
} = {};
$self
->{
' objects'
} = {};
return
$self
;
}
1;
=head1 AUTHOR
Martin Hosken Martin_Hosken
@sil
.org
Copyright Martin Hosken 1999
No warranty or expression of effectiveness, least of all regarding anyone's
safety, is implied in this software or documentation.