—————————#! /usr/bin/perl
package
PostScript::Simple::EPS;
use
strict;
use
Exporter;
use
Carp;
use
PostScript::Simple;
@ISA
=
qw(Exporter)
;
@EXPORT
=
qw()
;
$VERSION
=
"0.01"
;
=head1 NAME
PostScript::Simple::EPS - EPS support for PostScript::Simple
=head1 SYNOPSIS
use PostScript::Simple;
# create a new PostScript object
$p = new PostScript::Simple(papersize => "A4",
colour => 1,
units => "in");
# create a new page
$p->newpage;
# add an eps file
$p->add_eps({xsize => 3}, "test.eps", 1,1);
$p->add_eps({yscale => 1.1, xscale => 1.8}, "test.eps", 4,8);
# create an eps object
$e = new PostScript::Simple::EPS(file => "test.eps");
$e->rotate(90);
$e->xscale(0.5);
$p->add_eps($e, 3, 3); # add eps object to postscript object
$e->xscale(2);
$p->add_eps($e, 2, 5); # add eps object to postscript object again
# write the output to a file
$p->output("file.ps");
=head1 DESCRIPTION
PostScript::Simple::EPS allows you to add EPS files into PostScript::Simple
objects. Included EPS files can be scaled and rotated, and placed anywhere
inside a PostScript::Simple page.
=head1 PREREQUISITES
This module requires C<PostScript::Simple>, C<strict>, C<Carp> and C<Exporter>.
=head2 EXPORT
None.
=cut
=head1 CONSTRUCTOR
=over 4
=item C<new(options)>
Create a new PostScript::Simple::EPS object. The options
that can be set are:
=over 4
=item file
EPS file to be included. This must exist when the C<new> method is called.
=item clip
Set to 0 to disable clipping to the EPS bounding box. Default is to clip.
=back
Example:
$ps = new PostScript::Simple(landscape => 1,
eps => 0,
xsize => 4,
ysize => 3,
units => "in");
$eps = new PostScript::Simple::EPS(file => "test.eps");
$eps->scale(0.5);
Scale the EPS file by x0.5 in both directions.
$ps->newpage();
$ps->importeps($eps, 1, 1);
Add the EPS file to the PostScript document at coords (1,1).
$ps->importepsfile("another.eps", 1, 2, 4, 4);
Easily add an EPS file to the PostScript document using bounding box (1,2),(4,4).
The methods C<importeps> and C<importepsfile> are described in the documentation
of C<PostScript::Simple>.
=back
=cut
sub
new
# {{{
{
my
(
$class
,
%data
) =
@_
;
my
$self
= {
file
=>
undef
,
# filename of the eps file
xsize
=>
undef
,
ysize
=>
undef
,
units
=>
"bp"
,
# measuring units (see below)
clip
=> 1,
# clip to the bounding box
bbx1
=> 0,
# Bounding Box definitions
bby1
=> 0,
bbx2
=> 0,
bby2
=> 0,
epsprefix
=> [],
epsfile
=>
undef
,
epspostfix
=> [],
};
foreach
(
keys
%data
)
{
$self
->{
$_
} =
$data
{
$_
};
}
croak
"must provide file"
if
(!
defined
$self
->{
"file"
});
bless
$self
,
$class
;
$self
->init();
return
$self
;
}
# }}}
sub
init
# {{{
{
my
$self
=
shift
;
my
$foundbbx
= 0;
open
EPS,
"< $$self{file}"
|| croak
"can't open eps file $$self{file}"
;
SCAN:
while
(<EPS>)
{
if
(/^\%\
%BoundingBox
:\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)$/)
{
$$self
{bbx1} = $1;
$$self
{bby1} = $2;
$$self
{bbx2} = $3;
$$self
{bby2} = $4;
$foundbbx
= 1;
last
SCAN;
}
}
close
EPS;
croak
"EPS file must contain a BoundingBox"
if
(!
$foundbbx
);
if
((
$$self
{bbx2} -
$$self
{bbx1} == 0) ||
(
$$self
{bby2} -
$$self
{bby1} == 0)) {
$self
->_error(
"PostScript::Simple::EPS: Bounding Box has zero dimension"
);
return
0;
}
$self
->
reset
();
}
# }}}
=head1 OBJECT METHODS
All object methods return 1 for success or 0 in some error condition
(e.g. insufficient arguments). Error message text is also drawn on
the page.
=over 4
=item C<get_bbox>
Returns the EPS bounding box, as specified on the %%BoundingBox line
of the EPS file. Units are standard PostScript points.
Example:
($x1, $y1, $x2, $y2) = $eps->get_bbox();
=cut
sub
get_bbox
# {{{
{
my
$self
=
shift
;
return
(
$$self
{bbx1},
$$self
{bby1},
$$self
{bbx2},
$$self
{bby2});
}
# }}}
=item C<scale(x, y)>
Scales the EPS file. To scale in one direction only, specify 1 as the
other scale. To scale the EPS file the same in both directions, you
may use the shortcut of just specifying the one value.
Example:
$eps->scale(1.2, 0.8); # make wider and shorter
$eps->scale(0.5); # shrink to half size
=cut
sub
scale
# {{{
{
my
$self
=
shift
;
my
(
$x
,
$y
) =
@_
;
$y
=
$x
if
(!
defined
$y
);
croak
"bad arguments to scale"
if
(!
defined
$x
);
push
@{
$$self
{epsprefix}},
"$x $y scale"
;
return
1;
}
# }}}
=item C<rotate(deg)>
Rotates the EPS file by C<deg> degrees anti-clockwise. The EPS file is rotated
about it's own origin (as defined by it's bounding box). To rotate by a particular
co-ordinate (again, relative to the EPS file, not the main PostScript document),
use translate, too.
Example:
$eps->rotate(180); # turn upside-down
To rotate 30 degrees about point (50,50):
$eps->translate(50, 50);
$eps->rotate(30);
$eps->translate(-50, -50);
=cut
sub
rotate
# {{{
{
my
$self
=
shift
;
my
(
$d
) =
@_
;
croak
"bad arguments to rotate"
if
(!
defined
$d
);
push
@{
$$self
{epsprefix}},
"$d rotate"
;
return
1;
}
# }}}
=item C<translate(x, y)>
Move the EPS file by C<x>,C<y> PostScript points.
Example:
$eps->translate(10, 10); # move 10 points in both directions
=cut
sub
translate
# {{{
{
my
$self
=
shift
;
my
(
$x
,
$y
) =
@_
;
croak
"bad arguments to translate"
if
(!
defined
$y
);
push
@{
$$self
{epsprefix}},
"$x $y translate"
;
return
1;
}
# }}}
=item C<reset>
Clear all translate, rotate and scale operations.
Example:
$eps->reset();
=cut
sub
reset
# {{{
{
my
$self
=
shift
;
@{
$$self
{
"epsprefix"
}} = ();
return
1;
}
# }}}
=item C<load>
Reads the EPS file into memory, to save reading it from file each time if
inserted many times into a document. Can not be used with C<preload>.
=cut
sub
load
# {{{
{
my
$self
=
shift
;
local
*EPS
;
return
1
if
(
defined
$$self
{
"epsfile"
});
$$self
{
"epsfile"
} =
"\%\%BeginDocument: $$self{file}\n"
;
open
EPS,
"< $$self{file}"
|| croak
"can't open eps file $$self{file}"
;
while
(<EPS>)
{
$$self
{
"epsfile"
} .=
$_
;
}
close
EPS;
$$self
{
"epsfile"
} .=
"\%\%EndDocument\n"
;
return
1;
}
# }}}
=item C<preload(object)>
Experimental: defines the EPS at in the document prolog, and just runs a
command to insert it each time it is used. C<object> is a PostScript::Simple
object. If the EPS file is included more than once in the PostScript file then
this will probably shrink the filesize quite a lot.
Can not be used at the same time as C<load>.
Example:
$p = new PostScript::Simple();
$e = new PostScript::Simple::EPS(file => "test.eps");
$e->preload($p);
=cut
sub
preload
# {{{
{
my
$self
=
shift
;
my
$ps
=
shift
;
my
$randcode
=
""
;
croak
"already loaded"
if
(
defined
$$self
{
"epsfile"
});
croak
"no PostScript::Simple module provided"
if
(!
defined
$ps
);
for
my
$i
(0..7)
{
$randcode
.=
chr
(
int
(
rand
()*26)+65);
# yuk
}
$$self
{
"epsfile"
} =
"eps$randcode\n"
;
$$ps
{
"psprolog"
} .=
"/eps$randcode {\n"
;
$$ps
{
"psprolog"
} .=
"\%\%BeginDocument: $$self{file}\n"
;
open
EPS,
"< $$self{file}"
|| croak
"can't open eps file $$self{file}"
;
while
(<EPS>)
{
$$ps
{
"psprolog"
} .=
$_
;
}
close
EPS;
$$ps
{
"psprolog"
} .=
"\%\%EndDocument\n"
;
$$ps
{
"psprolog"
} .=
"} def\n"
;
return
1;
}
# }}}
### PRIVATE
sub
_get_include_data
# {{{
{
my
$self
=
shift
;
my
(
$x
,
$y
) =
@_
;
my
$data
=
""
;
croak
"argh... internal error (incorrect arguments)"
if
(
scalar
@_
!= 2);
foreach
my
$line
(@{
$$self
{
"epsprefix"
}}) {
$data
.=
"$line\n"
;
}
if
(
$$self
{
"clip"
}) {
$data
.= "newpath
$$self
{bbx1}
$$self
{bby1} moveto
$$self
{bbx2}
$$self
{bby1} lineto
$$self
{bbx2}
$$self
{bby2} lineto
$$self
{bbx1}
$$self
{bby2} lineto closepath clip\n";
}
if
(
defined
$$self
{
"epsfile"
}) {
$data
.=
$$self
{
"epsfile"
};
}
else
{
$data
.=
"\%\%BeginDocument: $$self{file}\n"
;
open
EPS,
"< $$self{file}"
|| croak
"can't open eps file $$self{file}"
;
while
(<EPS>) {
$data
.=
$_
;
}
close
EPS;
$data
.=
"\%\%EndDocument\n"
;
}
foreach
my
$line
(@{
$$self
{
"epspostfix"
}}) {
$data
.=
"$line\n"
;
}
return
$data
;
}
# }}}
sub
_error {
# {{{
my
$self
=
shift
;
my
$msg
=
shift
;
$self
->{pspages} .=
"(error: $msg\n) print flush\n"
;
}
# }}}
=back
=head1 BUGS
This is software in development; some current functionality may not be as
expected, and/or may not work correctly.
=head1 AUTHOR
The PostScript::Simple::EPS module was written by Matthew Newton, after prods
for such a feature from several people around the world. A useful importeps
function that provides scaling and aspect ratio operations was gratefully
received from Glen Harris, and merged into this module.
=head1 SEE ALSO
L<PostScript::Simple>
=cut
1;
# vim:foldmethod=marker: