=head1 NAME PostScript::Columns - Squeeze a text file into multiple columns. =head1 SYNOPSIS use PostScript::ColDoc; $psdoc= pscolumns( -margins => [30,20], # NSEW or NS,EW or N,EW,S or N,E,W,S (like CSS) -headfont => 'NimbusMonL-Bold', -headsize => 12, -head => $head, -font => 'NimbusMonL-Regu', -size => 10, -text => $text, # default font/size for foot: -foot => "Page \$p of \$pp", # will interpolate later :) ); # use all defaults, no footer $doc= pscolumns( -size => 5, -head => "Left\nLeft Also\tTest Document\tRight", -text => $text, -foot => scalar(localtime)."\tFoot\tPage \$p of \$pp", ); =head1 DESCRIPTION Creates a PostScript document with a user-defined header and footer, then attempts to squeeze the data into as many columns as possible. =head1 AVAILABLE FONTS Only the monospace PostScript fonts are available: =over 4 =item C =item C =item C =item C =back =head1 OPTIONS =over 4 =item -margins Array ref that specifies page margins, in I (1/72 of an inch). North, East, West South are expressed as four elements: [ N, E, S, W ], three elements [ N, E_W, S ], two elements [ N_S, E_W ], or one element [ N_S_E_W ]. (This is the same order that CSS uses.) B Different printers may require drastically different margins. You'll have to experiment each time you use this module with a new printer. =item -headfont Name of the font to use for the header (see L<"AVAILABLE FONTS">). =item -headsize Size of the font to use for the header (in points). =item -head String to use as header. Upper-right, centered, and upper-left fields are tab-separated. In the string, C<$p> will be replaced by the current page number, and C<$pp> with the total number of pages. =item -font Name of the font to use for the text (see L<"AVAILABLE FONTS">). =item -size Size of the font to use for the text (in points). =item -text Columnar text. =item -footfont Name of the font to use for the footer (see L<"AVAILABLE FONTS">). =item -footsize Size of the font to use for the footer (in points). =item -foot String to use as footer. Lower-right, centered, and lower-left fields are tab-separated. In the string, C<$p> will be replaced by the current page number, and C<$pp> with the total number of pages. =back =head1 AUTHOR v, Efive@rant.scriptmania.comE =head1 SEE ALSO perl(1). =cut package PostScript::Columns; use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); use vars qw(%wratio); $VERSION = '1.23'; require Exporter; @ISA = qw(Exporter); @EXPORT = qw(pscolumns); sub pscolumns { # Hey! I know what's mine! ;) my %arg= @_; my($now,$who)= (scalar localtime, getlogin); ## initial metrics my($margin_N,$margin_E,$margin_S,$margin_W)= @{$arg{-margins}||[]}; $margin_N= 30 unless defined $margin_N; $margin_E= 20 unless defined $margin_E; $margin_S= $margin_N unless defined $margin_S; $margin_W= $margin_E unless defined $margin_W; my $font= ( $wratio{$arg{-font}} ? $arg{-font} : 'NimbusMonL-Regu' ); my $font_Y= $arg{-size} || 7; my $font_X= $font_Y * $wratio{$font}; my $line_Y= $arg{-linewidth} || 0.2; ## head metrics my @head_right= split /\t/, $arg{-head}; my @head_left= split /\n/, $head_right[0]; my @head_center= split /\n/, $head_right[1]; @head_right= split /\n/, $head_right[2]; my($head_lines)= sort {$b<=>$a} (scalar @head_left, scalar @head_center, scalar @head_right); my $head_font= $arg{-headfont} || 'NimbusMonL-Bold'; my $head_font_Y= $arg{-headsize} || 10; my $head_font_X= $head_font_Y * $wratio{$head_font}; my $head_Y= $head_font_Y * $head_lines; ## foot metrics my @foot_right= split /\t/, $arg{-foot}; my @foot_left= split /\n/, $foot_right[0]; my @foot_center= split /\n/, $foot_right[1]; @foot_right= split /\n/, $foot_right[2]; my($foot_lines)= sort {$b<=>$a} (scalar @foot_left, scalar @foot_center, scalar @foot_right); my $foot_font= $arg{-footfont} || 'NimbusMonL-Regu'; my $foot_font_Y= $arg{-footsize} || 8; my $foot_font_X= $foot_font_Y * $wratio{$foot_font}; my $foot_Y= $foot_font_Y * $foot_lines; ## text metrics local $_= $arg{-text} || ' '; s/\t/ /g; my @text= split /\n/; my($maxlen)= sort {$b<=>$a} map {length} @text; my $col_X= $maxlen * $font_X; my $paper_Y= 792; my $paper_X= 612; my $right= $paper_X-$margin_E; my $head_top= $paper_Y-$margin_N; my $head_line_top= $head_top-$head_Y+($head_font_Y/2); my $text_top= $head_top-$head_Y-($font_Y/2); my $foot_top= $foot_Y+$margin_S; my $foot_line_top= $foot_top+($foot_font_Y); my $text_Y= $text_top-$foot_line_top-4; my $text_X= $paper_X-$margin_E-$margin_W; my $rows= int( $text_Y / $font_Y ); my $cols= int( $text_X / $col_X ) || 1; $col_X= $text_X / $cols; my $pagelines= $rows * $cols; my $pp= int( ( @text / $pagelines ) +0.999 ); my $ps= <<"."; %!PS-Adobe-3.0 %%Title: Columnar Document %%Creator: PostScript::Columns v$VERSION %%CreationDate: $now %%For: $who %%BoundingBox: 0 0 $paper_X $paper_Y %%Pages: $pp %Columns: $cols @ $col_X pt %%EndComments . my $ps_head= "/$head_font findfont $head_font_Y scalefont setfont\n"; for my $y (map {$head_top-$head_font_Y*$_} reverse (0..$head_lines-1)) { local $_= pop @head_left; s/(\(|\)|\\)/\\$1/g; $ps_head.= "$margin_W $y moveto ($_) show\n" if $_; $_= pop @head_center; my $x= ( $paper_X - ($head_font_X * length) )/2; s/(\(|\)|\\)/\\$1/g; $ps_head.= "$x $y moveto ($_) show\n" if $_; $_= pop @head_right; $x= $paper_X - $margin_E - ($head_font_X * length); s/(\(|\)|\\)/\\$1/g; $ps_head.= "$x $y moveto ($_) show\n" if $_; } $ps_head.= "$margin_W $head_line_top moveto $right $head_line_top ". "lineto $line_Y setlinewidth stroke\n". "/$foot_font findfont $foot_font_Y scalefont setfont\n"; for my $y (map {$foot_top-$foot_font_Y*$_} (0..$foot_lines-1)) { local $_= shift @foot_left; s/(\(|\)|\\)/\\$1/g; $ps_head.= "$margin_W $y moveto ($_) show\n" if $_; $_= shift @foot_center; my $x= ( $paper_X - ($foot_font_X * length) )/2; s/(\(|\)|\\)/\\$1/g; $ps_head.= "$x $y moveto ($_) show\n" if $_; $_= shift @foot_right; $x= $paper_X - $margin_E - ($foot_font_X * length); s/(\(|\)|\\)/\\$1/g; $ps_head.= "$x $y moveto ($_) show\n" if $_; } $ps_head.= "$margin_W $foot_line_top moveto $right $foot_line_top ". "lineto $line_Y setlinewidth stroke\n"; $ps_head.= "/$font findfont $font_Y scalefont setfont\n"; $ps_head=~ s/\$pp\b/$pp/g; my $p; PAGE: while(@text) { $p++; (my $thishead= $ps_head)=~ s/\$p\b/$p/g; $ps.= "\n%%Page: (Page $p) $p\n".$thishead; for my $x (map {$margin_W+$col_X*$_} (0..$cols-1)) { for my $y (map {$text_top-$font_Y*$_} (0..$rows-1)) { next unless local $_= shift @text; last if /\x0c/; # end col at formfeed s/(\(|\)|\\)/\\$1/g; $ps.= "$x $y moveto ($_) show\n"; last PAGE unless @text; } } $ps.= "showpage\n"; } $ps.= "showpage\n"; return $ps; } %wratio= ( # Hmmm.... not a lot of variance. Keep? 'NimbusMonL-Regu' => 0.6, 'NimbusMonL-Bold' => 0.6, 'NimbusMonL-ReguObli' => 0.6, 'NimbusMonL-BoldObli' => 0.6, ); 1;