cort has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I tried to post this on beginners@perl.org, but it didn't make it (sometimes my mails to them get lost). Hopefully it will get here.

I am trying to print a canvas using the postscript method, but it's a really big canvas (not too wide, just high / long), and always comes out scaled to a single page. Any easy way to get the postscript method to make it a multi-page document, or should I just start hacking the resulting eps? I've tried playing with all the options to the postscript method, but haven't gotten anything to help.

Example code:

#!/usr/bin/perl -w # written/testing on MSwin ###################################### use Tk; use strict; my $temp = "C:\\temp"; my $file="$temp\\psfile.ps"; chomp(my @files = `dir/b $temp\\psfile.* 2>&1`); for (@files) { # get rid of all psfile.* next if /file not found/i; if (-e "$temp\\$_") { unlink "$temp\\$_"; } } my $cw=595; # 595.44 printer points A4 my $ch=841; # 841.68 printer points A4 my $mw= tkinit; my $c = $mw->Scrolled('Canvas', -scrollregion => [0,0,'596 p','842 p'], )->pack(-expand => 1, -fill => 'both', ); # create "page size" box and text in each corner $c->createRectangle(0,0,$cw.'p',$ch.'p',-outline=>'blue'); $c->createText(0,0, -text => 'nw', -anchor => 'nw'); $c->createText($cw.'p',0, -text => 'ne', -anchor => 'ne'); $c->createText($cw.'p',$ch.'p',-text => 'se', -anchor => 'se'); $c->createText( 0,$ch.'p',-text => 'sw', -anchor => 'sw'); # create numbers down the middle (3 "pages" worth) for (my $i = 0; $i <= (3*$ch); $i += 72) { $c->createText(($cw/2).'p',$i.'p',-text => $i); } my $b = $mw->Button(-text=>'output',-command=>\&ps)->pack; MainLoop; sub ps { $c->postscript( -colormode=> 'color', -file=> $file, -width=> $cw.'p', -height=> (3*$ch).'p', # height of canvas to print -x=> 0, -y=> 0, -pagex=> 0, -pagey=> 0, -pageanchor=> 'nw', -pagewidth=> $cw.'p', -pageheight=> (3*$ch).'p', # all 3 pages on single page # -pageheight=> $ch.'p', # all 3 pages on single page ); }

I also haven't had much luck getting things to change using -pagex, -pagey or -pageanchor. Exhaustive searching on the web for using the postscript method has mostly resulted in the same info as in Learning (or Mastering) Perl/Tk. Options explained but not many examples of use.

Any help / advice appreciated, but using a different widget set is not an option.

Thanks, Cort

Replies are listed 'Best First'.
Re: multi-page from Tk::Canvas
by zentara (Cardinal) on Nov 05, 2008 at 13:02 UTC
    Here is the basics of how to do it. Notice the change in the @capture region. Turning it into a multipage document, will probably need another module like shown in the second script. You can convert the ps to pdf with ImageMagic, by set('magick'=>'pdf'), and it can be done in blobs(in memory) to avoid temp ps files. Possibly there is a module that will manipulate ps directly, without the pdf conversion.
    #!/usr/bin/perl use warnings; use strict; use Tk; my $mw=MainWindow->new(-title=>"Print out the canvas"); my $c=$mw->Scrolled("Canvas")->pack(-expand=>1,-fill=>'both'); my $canvas=$c->Subwidget("canvas"); $canvas->createLine(100,100,20,20,-arrow=>'both',-arrowshape=>[10,10,1 +0]); $canvas->createOval(100,80,65,75,-fill=>"blue"); $canvas->createRectangle(30,50,60,80, -fill=>"red",-width=>5); $canvas->createText(150,1100,-text=>"check postscript",-justify=>"left +"); $canvas->createOval(150,1150,300,1300,-fill=>"blue"); $canvas->createRectangle(150,1300,400,1350, -fill=>"red",-width=>5); $canvas->configure(-scrollregion=>[$canvas->bbox("all")]); $canvas->update(); my $button=$canvas->Button(-text=>"Print",-command=>\&buttonDo) ->pack(-side=>"bottom"); MainLoop; sub buttonDo{ $canvas->update; my @capture=(); my ($x0,$y0,$x1,$y1)=$canvas->bbox('all'); my $width = $x1-$x0; my $height = ($y1-$y0)/2; print "$width $height\n"; @capture=('-x'=>$x0,'-y'=>$y0,-height=>$height,-width=>$width +); $canvas -> postscript(-colormode=>'color', -file=>"$0a.ps", -rotate=>0, # -width=> 500, # -height=>500, @capture); @capture=('-x'=>$x0,'-y'=>$height ,-height=>$height,-width=>$ +width); print $y0 + ($y1-$y0/2),"\n"; $canvas -> postscript(-colormode=>'color', -file=>"$0b.ps", -rotate=>0, # -width=> 500, # -height=>500, @capture); print "saved\n"; }
    #!/usr/bin/perl use warnings; use strict; use PDF::API2; my $pdf=PDF::API2->new; # Create a new top-level PDF document my $font = $pdf->corefont('Helvetica'); my $page1 = $pdf->page(); # Create 1st page $page1->mediabox(500, 700); # Set the page dimensions my $text1 = $page1->text(); $text1->translate(50, 650); # Position the text $text1->font($font,12); $text1->text("Page 1"); my $page2 = $pdf->page(); # Create 2nd page $page2->mediabox(500, 700); # Set the page dimensions my $text2 = $page2->text(); $text2->translate(50, 650); # Position the text $text2->font($font,12); $text2->text("Page 2"); $pdf->saveas("$0.pdf"); $pdf->end; exit;

    I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: multi-page from Tk::Canvas
by almut (Canon) on Nov 05, 2008 at 17:16 UTC

    If all else fails, there's also the pstops utility to postprocess the EPS file.  For example

    $ pstops '1:0@1(0,0),0@1(0,1h),0@1(0,2h)' in.ps out.ps

    would turn a single-page, 3*A4 long document into 3 separate A4 pages. (0,0) / (0,1h) / (0,2h) in the above command specify the position of the new pages relative to the orginal page (h = unit is page-height, by default typically A4).  See the manpage for the syntax details.

Re: multi-page from Tk::Canvas
by rocklee (Beadle) on Nov 05, 2008 at 11:00 UTC

    I've never actually done this, so please take the reply with a grain of salt. It does however seem to me, from reading the Tk documentation, that this should be possible.

    http://www.tcl.tk/man/tcl8.4/TkCmd/canvas.htm#M60

    -x position
    Specifies the x-coordinate of the left edge of the area of the canvas that is to be printed, in canvas coordinates, not window coordinates. Defaults to the coordinate of the left edge of the window.

    -y position
    Specifies the y-coordinate of the top edge of the area of the canvas that is to be printed, in canvas coordinates, not window coordinates. Defaults to the coordinate of the top edge of the window.

    -width size
    Specifies the width of the area of the canvas to print. Defaults to the width of the canvas window.

    -height size Specifies the height of the area of the canvas to print. Defaults to the height of the canvas window.

    I may be wrong, but it should then be possible to divide the canvas height by some factor, resulting in a number of pages, and then adjusting the -x -y -width -height parameters and doing one ->postscript() call for each page.

    YMMV. Good luck :-)

Re: multi-page from Tk::Canvas
by cort (Novice) on Nov 06, 2008 at 09:17 UTC
    Thanks to all for your help.

    The program is a little more complicated than shown in my question. When I stated the canvas is long, it can be really long, so in actuality I have coded 2 canvases working together. One acts as a "header" and sits on top of the "data" canvas, allowing the "data" canvas to be scrolled vertically alone, or both canvases to be scrolled horizontally together (think spreadsheets).

    I have already looped to break the "data" canvas into paper-sized chunks (resulting in multiple postscript files), but am still working on splicing the "header" part onto each "data" part and then combining all files into 1 file.

    I think the pstops will help the most, not in itself, but as an example. I am only able use the standard perl installation (no PDF::API2) and it must be runnable on *nix and Windows.

    I guess I really need to learn how to hack postscript.

      I'll be the first to say that learning PostScript is no small task; I'm pretty sure you can get around this in cheaper ways.

      One way that needs no external tools; What about re-creating the widget(s) making up the header canvas on the data canvas? Place them on the right coordinates for page one, call postscript(), move them to coords for page two, etc.. and just destroy them once you're done printing.

      Another possibility is using ImageMagick; you can overlay images using the command-line 'composite' program (may be avaliable through a Perl binding also). This assumes, of course, that you supply a header.ps by either generating it from the header canvas or optionally supplied as a static file from Acrobat or some other tool.
      composite header.ps data.ps page.ps
      There is also most likely a way to do this using some tool/script in the 'psutils' package http://www.tardis.ed.ac.uk/~ajcd/psutils/. You can use this package to combine several .ps to one file.

      Just my two cents. If you post your "dual canvas" code, it's easier to make helpful suggestions :-)