use
5.008;
our
$VERSION
=
'2.16'
;
our
$AUTHORITY
=
'cpan:NIGELM'
;
sub
mm {
$_
[0] * 72 / 25.4; }
sub
in {
$_
[0] * 72; }
my
%PaperSizes
= (
A3
=> [ mm(297), mm(420) ],
A4
=> [ mm(210), mm(297) ],
A5
=> [ mm(148), mm(210) ],
B4
=> [ 729, 1032 ],
B5
=> [ 516, 729 ],
Letter
=> [ in(8.5), in(11) ],
Legal
=> [ in(8.5), in(14) ],
Executive
=> [ in(7.5), in(10) ],
Tabloid
=> [ in(11), in(17) ],
Statement
=> [ in(5.5), in(8.5) ],
Folio
=> [ in(8.5), in(13) ],
"10x14"
=> [ in(10), in(14) ],
Quarto
=> [ 610, 780 ],
);
my
%FontFamilies
= (
Courier
=> [
qw(Courier
Courier-Bold
Courier-Oblique
Courier-BoldOblique)
],
Helvetica
=> [
qw(Helvetica
Helvetica-Bold
Helvetica-Oblique
Helvetica-BoldOblique)
],
Times
=> [
qw(Times-Roman
Times-Bold
Times-Italic
Times-BoldItalic)
],
);
my
@FontSizes
= ( 5, 6, 8, 10, 12, 14, 18, 24, 32 );
sub
BOLD { 0x01; }
sub
ITALIC { 0x02; }
my
%param
= (
papersize
=>
'papersize'
,
paperwidth
=>
'paperwidth'
,
paperheight
=>
'paperheigth'
,
leftmargin
=>
'lmW'
,
rightmargin
=>
'rmW'
,
horizontalmargin
=>
'mW'
,
topmargin
=>
'tmH'
,
bottommargin
=>
'bmH'
,
verticalmargin
=>
'mH'
,
no_prolog
=>
'no_prolog'
,
no_trailer
=>
'no_trailer'
,
pageno
=>
'printpageno'
,
startpage
=>
'startpage'
,
fontfamily
=>
'family'
,
fontscale
=>
'fontscale'
,
leading
=>
'leading'
,
);
sub
new {
my
$class
=
shift
;
my
$self
=
$class
->SUPER::new(
@_
);
$self
->{title} =
""
;
$self
->{psfontid} =
""
;
$self
->{hspace} =
undef
;
$self
->{encoder} = find_encoding(
'iso-8859-1'
);
$self
;
}
sub
default_values {
(
shift
->SUPER::default_values(),
family
=>
"Times"
,
mH
=> mm(40),
mW
=> mm(20),
printpageno
=> 1,
startpage
=> 1,
fontscale
=> 1,
leading
=> 0.1,
papersize
=>
'A4'
,
paperwidth
=> mm(210),
paperheight
=> mm(297),
);
}
sub
configure {
my
(
$self
,
$hash
) =
@_
;
my
(
$key
,
$val
);
while
( (
$key
,
$val
) =
each
%$hash
) {
$key
=
lc
$key
;
croak
"Illegal parameter ($key => $val)"
unless
exists
$param
{
$key
};
$key
=
$param
{
$key
};
{
$key
eq
"family"
&&
do
{
$val
=
"\u\L$val"
;
croak
"Unknown font family ($val)"
unless
exists
$FontFamilies
{
$val
};
$self
->{family} =
$val
;
last
;
};
$key
eq
"papersize"
&&
do
{
$self
->papersize(
$val
)
|| croak
sprintf
"Unknown papersize '%s'.\nThe knowns are: %s.\nAborting"
,
$val
,
join
(
', '
,
sort
keys
%PaperSizes
);
last
;
};
$self
->{
$key
} =
lc
$val
;
}
}
}
sub
papersize {
my
(
$self
,
$val
) =
@_
;
$val
=
"\u\L$val"
;
my
(
$width
,
$height
) = @{
$PaperSizes
{
$val
} ||
return
0 };
return
0
unless
defined
$width
;
$self
->{papersize} =
$val
;
$self
->{paperwidth} =
$width
;
$self
->{paperheight} =
$height
;
1;
}
sub
fontsize {
my
$self
=
shift
;
my
$size
=
$self
->{font_size}[-1];
$size
= 8
if
$size
> 8;
$size
= 3
if
$size
< 0;
$FontSizes
[
$size
] *
$self
->{fontscale};
}
sub
setfont {
my
(
$self
,
$plain_with_size
) =
@_
;
my
$index
= 0;
my
$family
=
$self
->{family} ||
'Times'
;
my
$size
=
$plain_with_size
;
unless
(
$plain_with_size
) {
$index
|= BOLD
if
$self
->{bold};
$index
|= ITALIC
if
$self
->{italic} ||
$self
->{underline};
$family
=
'Courier'
if
$self
->{teletype};
$size
=
$self
->fontsize;
}
my
$font
=
$FontFamilies
{
$family
}[
$index
];
my
$font_with_size
=
"$font-$size"
;
if
(
$self
->{currentfont} eq
$font_with_size
) {
return
$self
->{currentfontid};
}
$self
->{currentfont} =
$font_with_size
;
$self
->{pointsize} =
$size
;
my
$fontmod
=
"Font::Metrics::$font"
;
$fontmod
=~ s/-//g;
my
$fontfile
=
$fontmod
.
".pm"
;
$fontfile
=~ s,::,/,g;
require
$fontfile
;
{
no
strict
'refs'
;
$self
->{wx} = \@{
"${fontmod}::wx"
};
}
$font
=
$self
->{fonts}{
$font_with_size
} ||
do
{
my
$fontID
=
"F"
. ++
$self
->{fno};
$self
->{fonts}{
$font_with_size
} =
$fontID
;
$fontID
;
};
$self
->{currentfontid} =
$font
;
return
$font
;
}
sub
switchfont {
my
(
$self
,
$fontid
) =
@_
;
if
(
$self
->{psfontid} eq
$fontid
) {
return
""
;
}
else
{
$self
->{psfontid} =
$fontid
;
return
"$fontid SF"
;
}
}
sub
findfont {
my
(
$self
,
$plain_with_size
) =
@_
;
return
$self
->switchfont(
$self
->setfont(
$plain_with_size
) );
}
sub
width {
my
$self
=
shift
;
my
$str
=
shift
;
my
$w
= 0;
my
$wx
=
$self
->{wx};
my
$sz
=
$self
->{pointsize};
for
(
unpack
(
"C*"
,
$self
->encode_string(
$str
) ) ) {
$w
+= ( (
$_
> $
}
$w
;
}
sub
begin {
my
$self
=
shift
;
$self
->SUPER::begin;
$self
->{lm} =
$self
->{lmW} ||
$self
->{mW};
$self
->{rm} =
$self
->{paperwidth} - (
$self
->{rmW} ||
$self
->{mW} );
$self
->{tm} =
$self
->{paperheight} - (
$self
->{tmH} ||
$self
->{mH} );
$self
->{bm} =
$self
->{bmH} ||
$self
->{mH};
$self
->{
'orig_margins'
} =
[
map
{
sprintf
"%.1f"
,
$_
} @{
$self
}{
qw(lm bm rm tm)
} ];
$self
->{fno} = 0;
$self
->{fonts} = {};
$self
->{en} = 0.55 *
$self
->fontsize(3);
$self
->{xpos} =
$self
->{lm};
$self
->{ypos} =
$self
->{tm};
$self
->{pageno} = 1;
$self
->{visible_page_number} =
$self
->{startpage};
$self
->{line} =
""
;
$self
->{showstring} =
""
;
$self
->{currentfont} =
""
;
$self
->{prev_currentfont} =
""
;
$self
->{largest_pointsize} = 0;
$self
->newpage;
}
sub
end {
my
$self
=
shift
;
$self
->showline;
$self
->endpage
if
$self
->{
'out'
};
my
$pages
=
$self
->{pageno} - 1;
my
@prolog
= ();
push
(
@prolog
,
"%!PS-Adobe-3.0\n"
);
push
(
@prolog
,
"%%Creator: "
.
$self
->version_tag .
"\n"
);
push
(
@prolog
,
"%%CreationDate: "
.
localtime
() .
"\n"
);
push
(
@prolog
,
"%%Pages: $pages\n"
);
push
(
@prolog
,
"%%PageOrder: Ascend\n"
);
push
(
@prolog
,
"%%Orientation: Portrait\n"
);
my
(
$pw
,
$ph
) =
map
{
int
(
$_
); } @{
$self
}{
qw(paperwidth paperheight)
};
push
(
@prolog
,
"%%DocumentMedia: Plain $pw $ph 0 white ()\n"
);
push
(
@prolog
,
"%%DocumentNeededResources: \n"
);
my
%seenfont
;
for
my
$full
(
sort
keys
%{
$self
->{fonts} } ) {
$full
=~ s/-\d+$//;
next
if
$seenfont
{
$full
}++;
push
(
@prolog
,
"%%+ font $full\n"
);
}
push
(
@prolog
,
"%%DocumentSuppliedResources: procset newencode 1.0 0\n"
);
push
(
@prolog
,
"%%+ encoding ISOLatin1Encoding\n"
);
push
(
@prolog
,
"%%EndComments\n"
);
push
(
@prolog
,
<<'EOT');
%%BeginProlog
/S/show load def
/M/moveto load def
/SF/setfont load def
%%BeginResource: encoding ISOLatin1Encoding
systemdict /ISOLatin1Encoding known not {
/ISOLatin1Encoding [
/space /space /space /space /space /space /space /space
/space /space /space /space /space /space /space /space
/space /space /space /space /space /space /space /space
/space /space /space /space /space /space /space /space
/space /exclam /quotedbl /numbersign /dollar /percent /ampersand
/quoteright
/parenleft /parenright /asterisk /plus /comma /minus /period /slash
/zero /one /two /three /four /five /six /seven
/eight /nine /colon /semicolon /less /equal /greater /question
/at /A /B /C /D /E /F /G
/H /I /J /K /L /M /N /O
/P /Q /R /S /T /U /V /W
/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore
/quoteleft /a /b /c /d /e /f /g
/h /i /j /k /l /m /n /o
/p /q /r /s /t /u /v /w
/x /y /z /braceleft /bar /braceright /asciitilde /space
/space /space /space /space /space /space /space /space
/space /space /space /space /space /space /space /space
/dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent
/dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron
/space /exclamdown /cent /sterling /currency /yen /brokenbar /section
/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen
/registered /macron
/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph
/periodcentered
/cedillar /onesuperior /ordmasculine /guillemotright /onequarter
/onehalf /threequarters /questiondown
/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex
/Idieresis
/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn
/germandbls
/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex
/idieresis
/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn
/ydieresis
] def
} if
%%EndResource
%%BeginResource: procset newencode 1.0 0
/NE { %def
findfont begin
currentdict dup length dict begin
{ %forall
1 index/FID ne {def} {pop pop} ifelse
} forall
/FontName exch def
/Encoding exch def
currentdict dup
end
end
/FontName get exch definefont pop
} bind def
%%EndResource
%%EndProlog
EOT
push
(
@prolog
,
"\n%%BeginSetup\n"
);
for
my
$full
(
sort
keys
%{
$self
->{fonts} } ) {
my
$short
=
$self
->{fonts}{
$full
};
$full
=~ s/-(\d+)$//;
my
$size
= $1;
push
(
@prolog
,
"ISOLatin1Encoding/$full-ISO/$full NE\n"
);
push
(
@prolog
,
"/$short/$full-ISO findfont $size scalefont def\n"
);
}
push
(
@prolog
,
"%%EndSetup\n"
);
$self
->collect(
"\n%%Trailer\n%%EOF\n"
)
unless
$self
->{
'no_trailer'
};
unshift
( @{
$self
->{output} },
@prolog
)
unless
$self
->{
'no_prolog'
};
}
sub
header_start {
my
(
$self
,
$level
) =
@_
;
$self
->vspace( 1 + ( 6 -
$level
) * 0.4 );
$self
->{bold}++;
push
( @{
$self
->{font_size} }, 8 -
$level
);
1;
}
sub
header_end {
my
(
$self
) =
@_
;
$self
->vspace(1);
$self
->{bold}--;
pop
( @{
$self
->{font_size} } );
1;
}
sub
hr_start {
my
$self
=
shift
;
$self
->showline;
$self
->vspace(0.5);
$self
->skip_vspace;
my
$lm
=
$self
->{lm};
my
$rm
=
$self
->{rm};
my
$y
=
$self
->{ypos};
$self
->collect(
sprintf
"newpath %.1f %.1f M %.1f %.1f lineto stroke\n"
,
$lm
,
$y
,
$rm
,
$y
);
$self
->vspace(0.5);
}
sub
skip_vspace {
my
$self
=
shift
;
if
(
defined
$self
->{vspace} ) {
$self
->showline;
if
(
$self
->{
'out'
} ) {
$self
->{ypos} -=
$self
->{vspace} * 10 *
$self
->{fontscale};
if
(
$self
->{ypos} <
$self
->{bm} ) {
$self
->newpage;
}
else
{
}
}
else
{
}
$self
->{xpos} =
$self
->{lm};
$self
->{vspace} =
undef
;
$self
->{hspace} =
undef
;
}
else
{
}
return
;
}
sub
show {
my
$self
=
shift
;
my
$str
=
$self
->{showstring};
$str
=~
tr
/\x01//d;
return
unless
length
$str
;
$str
=~ s/([\(\)\\])/\\$1/g;
$self
->{line} .=
"("
.
$self
->encode_string(
$str
) .
")S\n"
;
$self
->{showstring} =
""
;
}
sub
showline {
my
$self
=
shift
;
$self
->show;
my
$line
=
$self
->{line};
unless
(
length
$line
) {
return
;
}
$self
->{ypos} -=
$self
->{largest_pointsize} ||
$self
->{pointsize};
if
(
$self
->{ypos} <
$self
->{bm} ) {
$self
->newpage;
$self
->{ypos} -=
$self
->{pointsize};
my
$font
=
$self
->{prev_currentfont};
if
(
$font
) {
$self
->collect(
"$self->{fonts}{$font} SF\n\n"
);
}
}
my
$lm
=
$self
->{lm};
my
$x
=
$lm
;
if
(
$self
->{center} ) {
my
$linewidth
=
$self
->{xpos} -
$lm
;
$x
+= (
$self
->{rm} -
$lm
-
$linewidth
) / 2;
}
$self
->collect(
sprintf
"%.1f %.1f M\n"
,
$x
,
$self
->{ypos} );
$line
=~ s/\s\)S$/)S/;
$self
->collect(
$line
);
$self
->{
'out'
}++;
if
(
$self
->{bullet} ) {
my
$bullet
=
$self
->{bullet};
if
(
$bullet
eq
'*'
) {
my
$radius
=
$self
->{pointsize} / 8;
$self
->collect(
sprintf
"newpath %.1f %.1f %.1f 0 360 arc fill\n"
,
$self
->{bullet_pos} +
$radius
,
$self
->{ypos} +
$radius
* 2,
$radius
,
);
}
else
{
$self
->collect(
sprintf
"%.1f (%s) stringwidth pop sub %.1f add %.1f M\n"
,
$self
->{bullet_pos},
$bullet
,
$self
->{pointsize} * 0.62,
$self
->{ypos},
);
$self
->collect(
"($bullet)S\n"
);
}
$self
->{bullet} =
''
;
}
$self
->{prev_currentfont} =
$self
->{currentfont};
$self
->{largest_pointsize} = 0;
$self
->{line} =
""
;
$self
->{xpos} =
$lm
;
$self
->{ypos} -=
$self
->{leading} *
$self
->{pointsize};
return
;
}
sub
endpage {
my
$self
=
shift
;
$self
->collect(
"showpage\n"
);
$self
->{visible_page_number}++;
$self
->{pageno}++;
}
sub
newpage {
my
$self
=
shift
;
local
$self
->{
'pointsize'
} =
$self
->{
'pointsize'
};
if
(
$self
->{
'out'
} ) {
$self
->endpage;
$self
->collect(
sprintf
"%% %s has sent %s write-events to the above page.\n"
,
ref
(
$self
),
$self
->{
'out'
}, );
}
$self
->{
'out'
} = 0;
my
$pageno
=
$self
->{pageno};
my
$visible_page_number
=
$self
->{visible_page_number};
$self
->collect(
"\n%%Page: $pageno $pageno\n"
);
if
(
$self
->{printpageno} ) {
$self
->collect(
"%% Title and pageno\n"
);
my
$f
=
$self
->findfont(8);
$self
->collect(
"$f\n"
)
if
$f
;
my
$x
=
$self
->{paperwidth};
if
(
$x
) {
$x
-= 30; }
else
{
$x
= 30; }
$self
->collect(
sprintf
"%.1f 30.0 M($visible_page_number)S\n"
,
$x
);
$x
=
$self
->{lm};
$self
->{title} =~
tr
/\x01//d;
$self
->collect(
sprintf
"%.1f 30.0 M($self->{title})S\n"
,
$x
);
}
else
{
}
$self
->collect(
"\n"
);
$self
->{xpos} =
$self
->{lm};
$self
->{ypos} =
$self
->{tm};
}
sub
encode_string {
my
(
$self
,
$str
) =
@_
;
if
( utf8::is_utf8(
$str
) ) {
$str
=~
tr
/\x{2018}\x{2019}\x{201A}\x{201C}\x{201D}\x{201F}\x{2033}\x{2036}/`',
""
""
"/;
}
return
$self
->{encoder}->encode(
$str
);
}
sub
out {
my
(
$self
,
$text
) =
@_
;
$text
=~
tr
/\xA0\xAD/ /d;
if
(
$self
->{collectingTheTitle} ) {
$text
=~ s/([\(\)\\])/\\$1/g;
$self
->{title} .=
$text
;
return
;
}
my
$fontid
=
$self
->setfont();
my
$w
=
$self
->width(
$text
);
if
(
$text
=~ /^\s*$/ ) {
$self
->{hspace} = [
" "
,
$fontid
,
$w
];
return
;
}
$self
->skip_vspace;
if
(
$self
->{hspace} ) {
my
(
$stext
,
$sfont
,
$swidth
) = @{
$self
->{hspace} };
if
(
$self
->{xpos} +
$swidth
+
$w
>
$self
->{rm} ) {
$self
->showline;
}
else
{
$self
->show_with_font(
$stext
,
$sfont
,
$swidth
);
}
$self
->{hspace} =
undef
;
}
$self
->show_with_font(
$text
,
$fontid
,
$w
);
}
sub
show_with_font {
my
(
$self
,
$text
,
$fontid
,
$w
) =
@_
;
my
$fontps
=
$self
->switchfont(
$fontid
);
if
(
length
$fontps
) {
$self
->show;
$self
->{line} .=
"$fontps\n"
;
}
$self
->{xpos} +=
$w
;
$self
->{showstring} .=
$text
;
$self
->{largest_pointsize} =
$self
->{pointsize}
if
$self
->{largest_pointsize} <
$self
->{pointsize};
$self
->{
'out'
}++;
}
sub
pre_out {
my
(
$self
,
$text
) =
@_
;
$self
->skip_vspace;
$self
->tt_start;
my
$font
=
$self
->findfont();
if
(
length
$font
) {
$self
->show;
$self
->{line} .=
"$font\n"
;
}
while
(
$text
=~ s/(.*)\n// ) {
$self
->{
'out'
}++;
$self
->{showstring} .= $1;
$self
->showline;
}
$self
->{showstring} .=
$text
;
$self
->tt_end;
1;
}
sub
bullet {
my
(
$self
,
$bullet
) =
@_
;
$self
->{bullet} =
$bullet
;
$self
->{bullet_pos} =
$self
->{lm};
}
sub
adjust_lm {
my
$self
=
shift
;
$self
->showline;
$self
->{lm} +=
$_
[0] *
$self
->{en};
1;
}
sub
adjust_rm {
my
$self
=
shift
;
$self
->showline;
$self
->{rm} +=
$_
[0] *
$self
->{en};
}
sub
head_start { 1; }
sub
head_end { 1; }
sub
title_start {
my
(
$self
) =
@_
;
$self
->{collectingTheTitle} = 1;
1;
}
sub
title_end {
my
(
$self
) =
@_
;
$self
->{collectingTheTitle} = 0;
1;
}
my
(
$counter
,
$last_state_filename
);
sub
dump_state {
my
$self
=
shift
;
++
$counter
;
my
$filename
=
sprintf
(
"state%04d.txt"
,
$counter
);
my
$state
= IO::File->new(
$filename
,
'w'
) or
die
"Can't write-open $filename: $!"
;
$state
->
printf
(
"%s line %s\n"
, (
caller
(1) )[ 3, 2 ] );
{
local
(
$self
->{
'wx'
} ) =
'<SUPPRESSED>'
;
local
(
$self
->{
'output'
} ) =
'<SUPPRESSED>'
;
$state
->
print
( Data::Dumper::Dumper(
$self
) );
}
$state
->
close
;
sleep
0;
if
(
$last_state_filename
) {
system
(
"perl -S diff.bat $last_state_filename $filename > $filename.diff"
);
}
$last_state_filename
=
$filename
;
return
1;
}
1;