our
$VERSION
=
"1.00"
;
our
$DEBUG
= 0;
use
Carp
qw(confess croak cluck carp)
;
our
(
@ISA
,
@EXPORT
,
@EXPORT_OK
,
@EXPORT_FAIL
);
@ISA
=
qw(Exporter)
;
@EXPORT
=
qw()
;
@EXPORT_OK
=
qw($DEBUG $VERSION)
;
sub
new {
my
$this
=
shift
;
my
%params
=
@_
;
my
$class
=
ref
(
$this
) ||
$this
;
my
$self
= {};
bless
$self
,
$class
;
$self
->{
'data'
} =
''
;
$self
->{
'version'
} =
$params
{
'Version'
} ||
"1.2"
;
$self
->{
'trailer'
} = {};
$self
->{
'pages'
} = new PDF::Create::Page();
$self
->{
'current_page'
} =
$self
->{
'pages'
};
$self
->{
'pages'
}->{
'pdf'
} =
$self
;
$self
->{
'page_count'
} = 0;
$self
->{
'outline_count'
} = 0;
$self
->{
'crossreftblstartaddr'
} = 0;
$self
->{
'generation_number'
} = 0;
$self
->{
'object_number'
} = 0;
if
(
defined
$params
{
'fh'
}) {
$self
->{
'fh'
} =
$params
{
'fh'
};
}
elsif
(
defined
$params
{
'filename'
}) {
$self
->{
'filename'
} =
$params
{
'filename'
};
my
$fh
= new FileHandle
"> $self->{'filename'}"
;
carp
"PDF::Create.pm: $self->{'filename'}: $!\n"
unless
defined
$fh
;
binmode
$fh
;
$self
->{
'fh'
} =
$fh
;
}
$self
->{
'catalog'
} = {};
$self
->{
'catalog'
}{
'PageMode'
} =
$params
{
'PageMode'
}
if
defined
$params
{
'PageMode'
};
$self
->add_version;
$self
->{
'Author'
} =
$params
{
'Author'
}
if
defined
$params
{
'Author'
};
$self
->{
'Creator'
} =
$params
{
'Creator'
}
if
defined
$params
{
'Creator'
};
$self
->{
'Title'
} =
$params
{
'Title'
}
if
defined
$params
{
'Title'
};
$self
->{
'Subject'
} =
$params
{
'Subject'
}
if
defined
$params
{
'Subject'
};
$self
->{
'Keywords'
} =
$params
{
'Keywords'
}
if
defined
$params
{
'Keywords'
};
if
(
defined
$params
{
'CreationDate'
}) {
$self
->{
'CreationDate'
} =
sprintf
"D:4u%0.2u%0.2u%0.2u%0.2u%0.2u"
,
$params
{
'CreationDate'
}->[5] + 1900,
$params
{
'CreationDate'
}->[4] + 1,
$params
{
'CreationDate'
}->[3],
$params
{
'CreationDate'
}->[2],
$params
{
'CreationDate'
}->[1],
$params
{
'CreationDate'
}->[0];
}
return
$self
;
}
sub
close
{
my
$self
=
shift
;
my
%params
=
@_
;
$self
->debug(
"Closing PDF"
);
$self
->page_stream;
$self
->add_outlines
if
defined
$self
->{
'outlines'
};
$self
->add_catalog;
$self
->add_pages;
$self
->add_info;
$self
->add_crossrefsection;
$self
->add_trailer;
$self
->{
'fh'
}->
close
if
defined
$self
->{
'fh'
} &&
defined
$self
->{
'filename'
};
$self
->{
'data'
};
}
sub
debug {
return
unless
$DEBUG
;
my
$self
=
shift
;
my
$msg
=
shift
;
my
$s
=
scalar
@_
?
sprintf
$msg
,
@_
:
$msg
;
warn
"PDF DEBUG: $s\n"
;
}
sub
version {
my
$self
=
shift
;
my
$v
=
shift
;
if
(
defined
$v
) {
$self
->{
'version'
} =
$v
;
}
$self
->{
'version'
};
}
sub
add {
my
$self
=
shift
;
my
$data
=
join
''
,
@_
;
$self
->{
'size'
} +=
length
$data
;
if
(
defined
$self
->{
'fh'
}) {
my
$fh
=
$self
->{
'fh'
};
print
$fh
$data
;
}
else
{
$self
->{
'data'
} .=
$data
;
}
}
sub
position {
my
$self
=
shift
;
$self
->{
'size'
};
}
sub
reserve {
my
$self
=
shift
;
my
$name
=
shift
;
my
$type
=
shift
||
$name
;
die
"Error: an object has already been reserved using this name '$name' "
if
defined
$self
->{
'reservations'
}{
$name
};
$self
->{
'object_number'
}++;
$self
->{
'reservations'
}{
$name
} = [
$self
->{
'object_number'
},
$self
->{
'generation_number'
},
$type
];
[
$self
->{
'object_number'
},
$self
->{
'generation_number'
} ];
}
sub
add_version {
my
$self
=
shift
;
$self
->debug(
"adding version"
);
$self
->add(
"%PDF-"
.
$self
->{
'version'
});
$self
->cr;
}
sub
add_comment {
my
$self
=
shift
;
my
$comment
=
shift
||
''
;
$self
->debug(
"adding comment"
);
$self
->add(
"%"
.
$comment
);
$self
->cr;
}
sub
encode {
my
$type
=
shift
;
my
$val
=
shift
;
if
(
$DEBUG
) {
if
(
$val
) {
warn
(
"encode: $type $val"
);}
else
{
warn
(
"encode: $type (no val)"
);}}
if
(!
$type
) {cluck
"PDF::Create::encode: empty argument, called by "
;
return
1}
(
$type
eq
'null'
||
$type
eq
'number'
) &&
do
{
1;
} ||
$type
eq
'cr'
&&
do
{
$val
=
"\n"
;
} ||
$type
eq
'boolean'
&&
do
{
$val
=
$val
eq
'true'
?
$val
:
$val
eq
'false'
?
$val
:
$val
eq
'0'
?
'false'
:
'true'
;
} ||
$type
eq
'string'
&&
do
{
$val
=
"($val)"
;
} ||
$type
eq
'number'
&&
do
{
$val
=
"$val"
;
} ||
$type
eq
'name'
&&
do
{
$val
=
"/$val"
;
} ||
$type
eq
'array'
&&
do
{
my
$s
=
'['
;
for
my
$v
(
@$val
) {
$s
.=
&encode
(
$$v
[0],
$$v
[1]) .
" "
;
}
chop
$s
;
$val
=
$s
.
"]"
;
} ||
$type
eq
'dictionary'
&&
do
{
my
$s
=
'<<'
.
&encode
(
'cr'
);
for
my
$v
(
keys
%$val
) {
$s
.=
&encode
(
'name'
,
$v
) .
" "
;
$s
.=
&encode
(${
$$val
{
$v
}}[0], ${
$$val
{
$v
}}[1]);
$s
.=
&encode
(
'cr'
);
}
$val
=
$s
.
">>"
;
} ||
$type
eq
'object'
&&
do
{
my
$s
=
&encode
(
'number'
,
$$val
[0]) .
" "
.
&encode
(
'number'
,
$$val
[1]) .
" obj"
;
$s
.=
&encode
(
'cr'
);
$s
.=
&encode
(
$$val
[2][0],
$$val
[2][1]);
$s
.=
&encode
(
'cr'
);
$val
=
$s
.
"endobj"
;
} ||
$type
eq
'ref'
&&
do
{
my
$s
=
&encode
(
'number'
,
$$val
[0]) .
" "
.
&encode
(
'number'
,
$$val
[1]) .
" R"
;
$val
=
$s
;
} ||
$type
eq
'stream'
&&
do
{
my
$data
=
delete
$$val
{
'Data'
};
my
$s
=
'<<'
.
&encode
(
'cr'
);
for
my
$v
(
keys
%$val
) {
$s
.=
&encode
(
'name'
,
$v
) .
" "
;
$s
.=
&encode
(${
$$val
{
$v
}}[0], ${
$$val
{
$v
}}[1]);
$s
.=
&encode
(
'cr'
);
}
$s
.=
">>"
.
&encode
(
'cr'
) .
"stream"
.
&encode
(
'cr'
);
$s
.=
$data
.
&encode
(
'cr'
);
$val
=
$s
.
"endstream"
.
&encode
(
'cr'
);
} || confess
"Error: unknown type '$type'"
;
$val
;
}
sub
add_object {
my
$self
=
shift
;
my
$v
=
shift
;
my
$val
=
&encode
(
@$v
);
$self
->add(
$val
);
$self
->cr;
[
$$v
[1][0],
$$v
[1][1] ];
}
sub
null {
my
$self
=
shift
;
[
'null'
,
'null'
];
}
sub
boolean {
my
$self
=
shift
;
my
$val
=
shift
;
[
'boolean'
,
$val
];
}
sub
number {
my
$self
=
shift
;
my
$val
=
shift
;
[
'number'
,
$val
];
}
sub
name {
my
$self
=
shift
;
my
$val
=
shift
;
[
'name'
,
$val
];
}
sub
string {
my
$self
=
shift
;
my
$val
=
shift
;
[
'string'
,
$val
];
}
sub
array {
my
$self
=
shift
;
[
'array'
, [
@_
] ];
}
sub
dictionary {
my
$self
=
shift
;
[
'dictionary'
, {
@_
} ];
}
sub
indirect_obj {
my
$self
=
shift
;
my
(
$id
,
$gen
);
my
$name
=
$_
[1];
my
$type
=
$_
[0][1]{
'Type'
}[1]
if
defined
$_
[0][1] &&
ref
$_
[0][1] eq
'HASH'
&&
defined
$_
[0][1]{
'Type'
};
if
(
defined
$name
&&
defined
$self
->{
'reservations'
}{
$name
}) {
(
$id
,
$gen
) = @{
$self
->{
'reservations'
}{
$name
}};
delete
$self
->{
'reservations'
}{
$name
};
}
elsif
(
defined
$type
&&
defined
$self
->{
'reservations'
}{
$type
}) {
(
$id
,
$gen
) = @{
$self
->{
'reservations'
}{
$type
}};
delete
$self
->{
'reservations'
}{
$type
};
}
else
{
$id
= ++
$self
->{
'object_number'
};
$gen
=
$self
->{
'generation_number'
};
}
push
@{
$self
->{
'crossrefsubsection'
}{
$gen
}}, [
$id
,
$self
->position, 1 ];
[
'object'
, [
$id
,
$gen
,
@_
] ];
}
sub
indirect_ref {
my
$self
=
shift
;
[
'ref'
, [
@_
] ];
}
sub
stream {
my
$self
=
shift
;
[
'stream'
, {
@_
} ];
}
sub
add_info {
my
$self
=
shift
;
$self
->debug(
"add_info"
);
my
%params
=
@_
;
$params
{
'Author'
} =
$self
->{
'Author'
}
if
defined
$self
->{
'Author'
};
$params
{
'Creator'
} =
$self
->{
'Creator'
}
if
defined
$self
->{
'Creator'
};
$params
{
'Title'
} =
$self
->{
'Title'
}
if
defined
$self
->{
'Title'
};
$params
{
'Subject'
} =
$self
->{
'Subject'
}
if
defined
$self
->{
'Subject'
};
$params
{
'Keywords'
} =
$self
->{
'Keywords'
}
if
defined
$self
->{
'Keywords'
};
$params
{
'CreationDate'
} =
$self
->{
'CreationDate'
}
if
defined
$self
->{
'CreationDate'
};
$self
->{
'info'
} =
$self
->reserve(
'Info'
);
my
$content
= {
'Producer'
=>
$self
->string(
"PDF::Create version $VERSION"
),
'Type'
=>
$self
->name(
'Info'
) };
$$content
{
'Author'
} =
$self
->string(
$params
{
'Author'
})
if
defined
$params
{
'Author'
};
$$content
{
'Creator'
} =
$self
->string(
$params
{
'Creator'
})
if
defined
$params
{
'Creator'
};
$$content
{
'Title'
} =
$self
->string(
$params
{
'Title'
})
if
defined
$params
{
'Title'
};
$$content
{
'Subject'
} =
$self
->string(
$params
{
'Subject'
})
if
defined
$params
{
'Subject'
};
$$content
{
'Keywords'
} =
$self
->string(
$params
{
'Keywords'
})
if
defined
$params
{
'Keywords'
};
$$content
{
'CreationDate'
} =
$self
->string(
$params
{
'CreationDate'
})
if
defined
$params
{
'CreationDate'
};
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(
%$content
)),
'Info'
);
$self
->cr;
}
sub
add_catalog {
my
$self
=
shift
;
$self
->debug(
"add_catalog"
);
my
%params
= %{
$self
->{
'catalog'
}};
$self
->{
'catalog'
} =
$self
->reserve(
'Catalog'
);
my
$content
= {
'Type'
=>
$self
->name(
'Catalog'
) };
my
$pages
=
$self
->reserve(
'Pages'
);
$$content
{
'Pages'
} =
$self
->indirect_ref(
@$pages
);
$self
->{
'pages'
}{
'id'
} =
$$content
{
'Pages'
}[1];
$$content
{
'Outlines'
} =
$self
->indirect_ref(@{
$self
->{
'outlines'
}->{
'id'
}})
if
defined
$self
->{
'outlines'
};
$$content
{
'PageMode'
} =
$self
->name(
$params
{
'PageMode'
})
if
defined
$params
{
'PageMode'
};
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(
%$content
)));
$self
->cr;
}
sub
add_outlines {
my
$self
=
shift
;
$self
->debug(
"add_outlines"
);
my
%params
=
@_
;
my
$outlines
=
$self
->reserve(
"Outlines"
);
my
(
$First
,
$Last
);
my
@list
=
$self
->{
'outlines'
}->list;
my
$i
= -1;
for
my
$outline
(
@list
) {
$i
++;
my
$name
=
$outline
->{
'name'
};
$First
=
$outline
->{
'id'
}
unless
defined
$First
;
$Last
=
$outline
->{
'id'
};
my
$content
= {
'Title'
=>
$self
->string(
$outline
->{
'Title'
}) };
if
(
defined
$outline
->{
'Kids'
} &&
scalar
@{
$outline
->{
'Kids'
}}) {
my
$t
=
$outline
->{
'Kids'
};
$$content
{
'First'
} =
$self
->indirect_ref(@{
$$t
[0]->{
'id'
}});
$$content
{
'Last'
} =
$self
->indirect_ref(@{
$$t
[
$#$t
]->{
'id'
}});
}
my
$brothers
=
$outline
->{
'Parent'
}->{
'Kids'
};
my
$j
= -1;
for
my
$brother
(
@$brothers
) {
$j
++;
last
if
$brother
==
$outline
;
}
$$content
{
'Next'
} =
$self
->indirect_ref(@{
$$brothers
[
$j
+ 1]->{
'id'
}})
if
$j
<
$#$brothers
;
$$content
{
'Prev'
} =
$self
->indirect_ref(@{
$$brothers
[
$j
- 1]->{
'id'
}})
if
$j
;
$outline
->{
'Parent'
}->{
'id'
} =
$outlines
unless
defined
$outline
->{
'Parent'
}->{
'id'
};
$$content
{
'Parent'
} =
$self
->indirect_ref(@{
$outline
->{
'Parent'
}->{
'id'
}});
$$content
{
'Dest'
} =
$self
->array(
$self
->indirect_ref(@{
$outline
->{
'Dest'
}->{
'id'
}}),
$self
->name(
'Fit'
),
$self
->null,
$self
->null,
$self
->null);
my
$count
=
$outline
->count;
$$content
{
'Count'
} =
$self
->number(
$count
)
if
$count
;
my
$t
=
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(
%$content
),
$name
));
$self
->cr;
}
my
$content
= {
'Type'
=>
$self
->name(
'Outlines'
) };
my
$count
=
$self
->{
'outlines'
}->count;
$$content
{
'Count'
} =
$self
->number(
$count
)
if
$count
;
$$content
{
'First'
} =
$self
->indirect_ref(
@$First
);
$$content
{
'Last'
} =
$self
->indirect_ref(
@$Last
);
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(
%$content
)));
$self
->cr;
}
sub
new_outline {
my
$self
=
shift
;
my
%params
=
@_
;
unless
(
defined
$self
->{
'outlines'
}) {
$self
->{
'outlines'
} = new PDF::Create::Outline();
$self
->{
'outlines'
}->{
'pdf'
} =
$self
;
$self
->{
'outlines'
}->{
'Status'
} =
'opened'
;
}
my
$parent
=
$params
{
'Parent'
} ||
$self
->{
'outlines'
};
my
$name
=
"Outline "
. ++
$self
->{
'outline_count'
};
$params
{
'Destination'
} =
$self
->{
'current_page'
}
unless
defined
$params
{
'Destination'
};
my
$outline
=
$parent
->add(
$self
->reserve(
$name
,
"Outline"
),
$name
,
%params
);
$outline
;
}
sub
get_page_size {
my
$self
=
shift
;
my
$name
=
lc
(
shift
);
my
%pagesizes
= (
'A0'
=> [ 0, 0, 2380, 3368 ],
'A1'
=> [ 0, 0, 1684, 2380 ],
'A2'
=> [ 0, 0, 1190, 1684 ],
'A3'
=> [ 0, 0, 842, 1190 ],
'A4'
=> [ 0, 0, 595, 842 ],
'A4L'
=> [ 0, 0, 842, 595 ],
'A5'
=> [ 0, 0, 421, 595 ],
'A6'
=> [ 0, 0, 297, 421 ],
'LETTER'
=> [ 0, 0, 612, 792 ],
'BROADSHEET'
=> [ 0, 0, 1296, 1584 ],
'LEDGER'
=> [ 0, 0, 1224, 792 ],
'TABLOID'
=> [ 0, 0, 792, 1224 ],
'LEGAL'
=> [ 0, 0, 612, 1008 ],
'EXECUTIVE'
=> [ 0, 0, 522, 756 ],
'36X36'
=> [ 0, 0, 2592, 2592 ],
);
if
(!
$pagesizes
{
uc
(
$name
)}) {
$name
=
"A4"
;
}
$pagesizes
{
uc
(
$name
)};
}
sub
new_page {
my
$self
=
shift
;
my
%params
=
@_
;
my
$parent
=
$params
{
'Parent'
} ||
$self
->{
'pages'
};
my
$name
=
"Page "
. ++
$self
->{
'page_count'
};
my
$page
=
$parent
->add(
$self
->reserve(
$name
,
"Page"
),
$name
);
$page
->{
'resources'
} =
$params
{
'Resources'
}
if
defined
$params
{
'Resources'
};
$page
->{
'mediabox'
} =
$params
{
'MediaBox'
}
if
defined
$params
{
'MediaBox'
};
$page
->{
'cropbox'
} =
$params
{
'CropBox'
}
if
defined
$params
{
'CropBox'
};
$page
->{
'artbox'
} =
$params
{
'ArtBox'
}
if
defined
$params
{
'ArtBox'
};
$page
->{
'trimbox'
} =
$params
{
'TrimBox'
}
if
defined
$params
{
'TrimBox'
};
$page
->{
'bleedbox'
} =
$params
{
'BleedBox'
}
if
defined
$params
{
'BleedBox'
};
$page
->{
'rotate'
} =
$params
{
'Rotate'
}
if
defined
$params
{
'Rotate'
};
$self
->{
'current_page'
} =
$page
;
$page
;
}
sub
add_pages {
my
$self
=
shift
;
$self
->debug(
"add_pages"
);
my
%params
=
@_
;
my
$content
= {
'Type'
=>
$self
->name(
'Pages'
) };
my
$t
=
$self
->{
'pages'
}->kids;
die
"Error: document MUST contains at least one page. Abort."
unless
scalar
@$t
;
my
$kids
= [];
map
{
push
@$kids
,
$self
->indirect_ref(
@$_
) }
@$t
;
$$content
{
'Kids'
} =
$self
->array(
@$kids
);
$$content
{
'Count'
} =
$self
->number(
$self
->{
'pages'
}->count);
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(
%$content
)));
$self
->cr;
for
my
$font
(
sort
keys
%{
$self
->{
'fonts'
}}) {
$self
->debug(
"add_pages: font: $font"
);
$self
->{
'fontobj'
}{
$font
} =
$self
->reserve(
'Font'
);
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(%{
$self
->{
'fonts'
}{
$font
}}),
'Font'
));
$self
->cr;
}
for
my
$xobject
(
sort
keys
%{
$self
->{
'xobjects'
}}) {
$self
->debug(
"add_pages: object: $xobject"
);
$self
->{
'xobj'
}{
$xobject
} =
$self
->reserve(
'XObject'
);
$self
->add_object(
$self
->indirect_obj(
$self
->stream(%{
$self
->{
'xobjects'
}{
$xobject
}}),
'XObject'
));
$self
->cr;
if
(
defined
$self
->{
'reservations'
}{
"ImageColorSpace$xobject"
}) {
$self
->add_object(
$self
->indirect_obj(
$self
->stream(%{
$self
->{
'xobjects_colorspace'
}{
$xobject
}}),
"ImageColorSpace$xobject"
));
$self
->cr;
}
}
for
my
$page
(
$self
->{
'pages'
}->list) {
my
$name
=
$page
->{
'name'
};
$self
->debug(
"add_pages: page: $name"
);
my
$type
=
'Page'
.
(
defined
$page
->{
'Kids'
} &&
scalar
@{
$page
->{
'Kids'
}} ?
's'
:
''
);
my
$content
= {
'Type'
=>
$self
->name(
$type
) };
my
$resources
= {};
for
my
$k
(
keys
%{
$page
->{
'resources'
}}) {
my
$v
=
$page
->{
'resources'
}{
$k
};
(
$k
eq
'ProcSet'
) &&
do
{
my
$l
= [];
if
(
ref
(
$v
) eq
'ARRAY'
) {
map
{
push
@$l
,
$self
->name(
$_
) }
@$v
;
}
else
{
push
@$l
,
$self
->name(
$v
);
}
$$resources
{
'ProcSet'
} =
$self
->array(
@$l
);
} ||
(
$k
eq
'fonts'
) &&
do
{
my
$l
= {};
map
{
$$l
{
"F$_"
} =
$self
->indirect_ref(@{
$self
->{
'fontobj'
}{
$_
}});
}
keys
%{
$page
->{
'resources'
}{
'fonts'
}};
$$resources
{
'Font'
} =
$self
->dictionary(
%$l
);
} ||
(
$k
eq
'xobjects'
) &&
do
{
my
$l
= {};
map
{
$$l
{
"Image$_"
} =
$self
->indirect_ref(@{
$self
->{
'xobj'
}{
$_
}});
}
keys
%{
$page
->{
'resources'
}{
'xobjects'
}};
$$resources
{
'XObject'
} =
$self
->dictionary(
%$l
);
};
}
if
(
defined
(
$$resources
{
'XObject'
} ) ) {
my
$r
=
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(
%$resources
)));
$self
->cr;
$$content
{
'Resources'
} = [
'ref'
, [
$$r
[0],
$$r
[1] ] ];
}
else
{
$$content
{
'Resources'
} =
$self
->dictionary(
%$resources
)
if
scalar
keys
%$resources
;
}
for
my
$K
(
'MediaBox'
,
'CropBox'
,
'ArtBox'
,
'TrimBox'
,
'BleedBox'
) {
my
$k
=
lc
$K
;
if
(
defined
$page
->{
$k
}) {
my
$l
= [];
map
{
push
@$l
,
$self
->number(
$_
) } @{
$page
->{
$k
}};
$$content
{
$K
} =
$self
->array(
@$l
);
}
}
$$content
{
'Rotate'
} =
$self
->number(
$page
->{
'rotate'
})
if
defined
$page
->{
'rotate'
};
if
(
$type
eq
'Page'
) {
$$content
{
'Parent'
} =
$self
->indirect_ref(@{
$page
->{
'Parent'
}{
'id'
}});
if
(
defined
$page
->{
'contents'
}) {
my
$contents
= [];
map
{
push
@$contents
,
$self
->indirect_ref(
@$_
);
} @{
$page
->{
'contents'
}};
$$content
{
'Contents'
} =
$self
->array(
@$contents
);
}
}
else
{
my
$kids
= [];
map
{
push
@$kids
,
$self
->indirect_ref(
@$_
) } @{
$page
->kids};
$$content
{
'Kids'
} =
$self
->array(
@$kids
);
$$content
{
'Parent'
} =
$self
->indirect_ref(@{
$page
->{
'Parent'
}{
'id'
}})
if
defined
$page
->{
'Parent'
};
$$content
{
'Count'
} =
$self
->number(
$page
->count);
}
$self
->add_object(
$self
->indirect_obj(
$self
->dictionary(
%$content
),
$name
));
$self
->cr;
}
}
sub
add_crossrefsection {
my
$self
=
shift
;
$self
->debug(
"adding cross reference section"
);
$self
->{
'crossrefstartpoint'
} =
$self
->position;
$self
->add(
'xref'
);
$self
->cr;
die
"Fatal error: should contains at least one cross reference subsection."
unless
defined
$self
->{
'crossrefsubsection'
};
for
my
$subsection
(
sort
keys
%{
$self
->{
'crossrefsubsection'
}}) {
$self
->add_crossrefsubsection(
$subsection
);
}
}
sub
add_crossrefsubsection {
my
$self
=
shift
;
my
$subsection
=
shift
;
$self
->debug(
"adding cross reference subsection"
);
$self
->add(0,
' '
,
1 +
scalar
@{
$self
->{
'crossrefsubsection'
}{
$subsection
}});
$self
->cr;
$self
->add(
sprintf
"%010d %05d %s "
, 0, 65535,
'f'
);
$self
->cr;
for
my
$entry
(
sort
{
$$a
[0] <=>
$$b
[0] }
@{
$self
->{
'crossrefsubsection'
}{
$subsection
}}) {
$self
->add(
sprintf
"%010d %05d %s "
,
$$entry
[1],
$subsection
,
$$entry
[2] ?
'n'
:
'f'
);
$self
->cr;
}
}
sub
add_trailer {
my
$self
=
shift
;
$self
->debug(
"adding trailer"
);
my
@keys
= (
'Size'
,
'Prev'
,
'Root'
,
'Info'
,
'ID'
,
'Encrypt'
);
$self
->add(
'trailer'
);
$self
->cr;
$self
->add(
'<<'
);
$self
->cr;
$self
->{
'trailer'
}{
'Size'
} = 1;
map
{
$self
->{
'trailer'
}{
'Size'
} +=
scalar
@{
$self
->{
'crossrefsubsection'
}{
$_
}}
}
keys
%{
$self
->{
'crossrefsubsection'
}};
$self
->{
'trailer'
}{
'Root'
} =
&encode
(@{
$self
->indirect_ref(@{
$self
->{
'catalog'
}})});
$self
->{
'trailer'
}{
'Info'
} =
&encode
(@{
$self
->indirect_ref(@{
$self
->{
'info'
}})})
if
defined
$self
->{
'info'
};
for
my
$k
(
@keys
) {
next
unless
defined
$self
->{
'trailer'
}{
$k
};
$self
->add(
"/$k "
,
ref
$self
->{
'trailer'
}{
$k
} eq
'ARRAY'
?
join
(
' '
, @{
$self
->{
'trailer'
}{
$k
}}) :
$self
->{
'trailer'
}{
$k
});
$self
->cr;
}
$self
->add(
'>>'
);
$self
->cr;
$self
->add(
'startxref'
);
$self
->cr;
$self
->add(
$self
->{
'crossrefstartpoint'
});
$self
->cr;
$self
->add(
'%%EOF'
);
$self
->cr;
}
sub
cr {
my
$self
=
shift
;
$self
->add(
&encode
(
'cr'
));
}
sub
page_stream {
my
$self
=
shift
;
my
$page
=
shift
;
if
(
defined
$self
->{
'reservations'
}{
'stream_length'
}) {
$self
->cr,
return
if
defined
$page
&&
defined
$self
->{
'stream_page'
} &&
$page
==
$self
->{
'current_page'
} &&
$self
->{
'stream_page'
} ==
$page
;
my
$len
=
$self
->position -
$self
->{
'stream_pos'
} + 1;
$self
->cr;
$self
->add(
'endstream'
);
$self
->cr;
$self
->add(
'endobj'
);
$self
->cr;
$self
->cr;
$self
->add_object(
$self
->indirect_obj(
$self
->number(
$len
),
'stream_length'
));
$self
->cr;
}
if
(
defined
$page
) {
my
$obj
=
$self
->reserve(
'stream'
);
delete
$self
->{
'reservations'
}{
'stream'
};
my
$stream_length
=
$self
->reserve(
'stream_length'
);
push
@$stream_length
,
'R'
;
push
@{
$page
->{
'contents'
}},
$obj
;
push
@{
$self
->{
'crossrefsubsection'
}{
$$obj
[1]}},
[
$$obj
[0],
$self
->position, 1 ];
$self
->add(
"$$obj[0] $$obj[1] obj"
);
$self
->cr;
$self
->add(
'<<'
);
$self
->cr;
$self
->add(
'/Length '
,
join
(
' '
,
@$stream_length
));
$self
->cr;
$self
->add(
'>>'
);
$self
->cr;
$self
->add(
'stream'
);
$self
->cr;
$self
->{
'stream_pos'
} =
$self
->position;
$self
->{
'stream_page'
} =
$page
;
}
}
sub
font {
my
$self
=
shift
;
my
%params
=
@_
;
my
$num
= 1 +
scalar
keys
%{
$self
->{
'fonts'
}};
$self
->{
'fonts'
}{
$num
} = {
'Subtype'
=>
$self
->name(
$params
{
'Subtype'
} ||
'Type1'
),
'Encoding'
=>
$self
->name(
$params
{
'Encoding'
} ||
'WinAnsiEncoding'
),
'BaseFont'
=>
$self
->name(
$params
{
'BaseFont'
} ||
'Helvetica'
),
'Name'
=>
$self
->name(
"F$num"
),
'Type'
=>
$self
->name(
"Font"
),
};
$num
;
}
sub
image {
my
$self
=
shift
;
my
$filename
=
shift
;
my
$num
= 1 +
scalar
keys
%{
$self
->{
'xobjects'
}};
my
$image
;
my
$colorspace
;
my
@a
;
my
$s
;
if
(
$filename
=~/\.gif$/i) {
$self
->{
'images'
}{
$num
} = GIFImage->new();
}
elsif
(
$filename
=~/\.jpg$/i ||
$filename
=~/\.jpeg$/i) {
$self
->{
'images'
}{
$num
} = JPEGImage->new();
}
$image
=
$self
->{
'images'
}{
$num
};
if
(!
$image
->Open(
$filename
)) {
print
$image
->{error} .
"\n"
;
return
0;
}
$self
->{
'xobjects'
}{
$num
} = {
'Subtype'
=>
$self
->name(
'Image'
),
'Name'
=>
$self
->name(
"Image$num"
),
'Type'
=>
$self
->name(
'XObject'
),
'Width'
=>
$self
->number(
$image
->{width}),
'Height'
=>
$self
->number(
$image
->{height}),
'BitsPerComponent'
=>
$self
->number(
$image
->{bpc}),
'Data'
=>
$image
->ReadData(),
'Length'
=>
$self
->number(
$image
->{imagesize}),
};
if
(
$image
->{colorspacesize}) {
$colorspace
=
$self
->reserve(
"ImageColorSpace$num"
);
$self
->{
'xobjects_colorspace'
}{
$num
} = {
'Data'
=>
$image
->{colorspacedata},
'Length'
=>
$self
->number(
$image
->{colorspacesize}),
};
$self
->{
'xobjects'
}{
$num
}->{
'ColorSpace'
} =
$self
->array(
$self
->name(
'Indexed'
),
$self
->name(
$image
->{colorspace}),
$self
->number(255),
$self
->indirect_ref(
@$colorspace
));
}
else
{
$self
->{
'xobjects'
}{
$num
}->{
'ColorSpace'
} =
$self
->array(
$self
->name(
$image
->{colorspace}));
}
$#a
= -1;
foreach
$s
(@{
$image
->{filter}}) {
push
@a
,
$self
->name(
$s
);
}
if
(
$#a
>= 0) {
$self
->{
'xobjects'
}{
$num
}->{
'Filter'
} =
$self
->array(
@a
);
}
$#a
= -1;
foreach
$s
(
keys
%{
$image
->{decodeparms}}) {
push
@a
,
$s
;
push
@a
,
$self
->number(
$image
->{decodeparms}{
$s
});
}
$self
->{
'xobjects'
}{
$num
}->{
'DecodeParms'
} =
$self
->array(
$self
->dictionary(
@a
));
if
(
$image
->{transparent}) {
$self
->{
'xobjects'
}{
$num
}->{
'Mask'
} =
$self
->array(
$self
->number(
$image
->{mask}),
$self
->number(
$image
->{mask}));
}
{
'num'
=>
$num
,
'width'
=>
$image
->{width},
'height'
=>
$image
->{height} };
}
sub
uses_font {
my
$self
=
shift
;
my
$page
=
shift
;
my
$font
=
shift
;
$page
->{
'resources'
}{
'fonts'
}{
$font
} = 1;
$page
->{
'resources'
}{
'ProcSet'
} = [
'PDF'
,
'Text'
];
$self
->{
'fontobj'
}{
$font
} = 1;
}
sub
uses_xobject {
my
$self
=
shift
;
my
$page
=
shift
;
my
$xobject
=
shift
;
$page
->{
'resources'
}{
'xobjects'
}{
$xobject
} = 1;
$page
->{
'resources'
}{
'ProcSet'
} = [
'PDF'
,
'Text'
];
$self
->{
'xobj'
}{
$xobject
} = 1;
}
sub
get_data {
shift
->{
'data'
};
}
1;