1
votes

Is it possible to render a pdf in a browser using PERL? What I have is a flash application that sends the rendered pdf binary to perl. The pdf is generated from AlivePDF.

#!C:\Perl\bin\perl.exe
##
BEGIN { $ENV{PATH} = ''; delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; }
use strict;
use warnings;
no warnings qw (redefine closure);
use CGI;
my $CGI = new CGI();

#name=generated.pdf&method=inline these are passed via the URL and are in the environmental variable QUERY_STRING
my %nv_pairs = map{my @tmp = split(/=/,$_);$tmp[0] => $tmp[1] }split(/&/,$ENV{QUERY_STRING});
my $name = $nv_pairs{name};
my $method = $nv_pairs{method};

#Raw Data is stored in the POST Parameter POSTDATA
my $pdf = $CGI->param('POSTDATA');

print "Content-Type: application/pdf\r\n";
print "Content-Length: " . length($pdf) . "\r\n";
print "Content-Disposition :$method\n\n";
print $pdf;

The problem is that I want to actually render what a pdf will look like. I can save that binary code and open it manually in Adobe Reader and it renders properly.

I would like for it to render in the browser, but I don't know how to get it to.

Currently the output (what the browser displays), looks like this:

Content-Type: application/pdf
Content-Length: 432785
Content-disposition:inline; filename="test.pdf"

%PDF-1.5
1 0 obj
<</Type /Pages
/Kids [3 0 R 5 0 R]
/Count 2>>
endobj
3 0 obj
<</Type /Page
/Parent 1 0 R
/MediaBox [0 0 612.00 792.00]
/Resources 2 0 R

This is only part of the displayed file, but I hope this helps. I don't want the code to display, I want it to look graphical. If I download this file, and change the extension to .pdf, it works perfectly.

2
So you want to render what the pdf would look like without rendering the pdf? Can't you just provide hyperlink to the pdf itself? - Ilion
I'm not sure what you mean. I have the code that creates the pdf, but I want it to render (look like an image). I hope that makes sense. - Cam
It's the "look like an image" part that is confusing me. Wouldn't it be best to have the browser simply display the pdf? To me, what you're saying sounds like you want the pdf to be displayed as a jpg or something. - Ilion
I do want it to display the pdf. Please see my edit. - Cam
Why do you parse QUERY_STRING manually? - Alexandr Ciornii

2 Answers

0
votes

I do not have the Flash app that creates the PDF in the request body, but I verified it against the output of a static resource with the same response headers. Content-Disposition is the crucial one. This was tested in Konqueror with the Okular KPart and works, I fully expect other browsers/plug-in combinations to also work.

#!/usr/bin/perl -T
# ↑↑↑↑↑
# on Windows you can just write …
#!perl -T
# … instead, using the Unix shebang however does no harm
use strict;
use warnings FATAL => 'all';
use CGI qw();
use IO::File qw();

# delete @ENV{qw(BASH_ENV CDPATH ENV IFS PATH)};
# ↑↑↑↑↑
# Cleaning path is required for taint-checked programs
# that want to run other programs. It does not affect anything here,
# so I commented it out.

my $c = CGI->new;

# untaint data coming from outside
my ($name) = defined $c->url_param('name') ?
    $c->url_param('name') =~ /\A ([a-zA-Z_-]{1,40}\.pdf) \z/msx : ();
my ($method) = defined $c->url_param('method') ?
    $c->url_param('method') =~ /\A (attachment|inline) \z/msx : ();
die 'invalid parameters' unless $name or $method;

# FIXME: untaint blindly because I don't know how to validate PDF
my ($pdf) = $c->param('POSTDATA') =~ /(.*)/msx;

STDOUT->binmode(':raw');
STDOUT->print($c->header(
    -Content_Type        => 'application/pdf',
    -Content_Length      => length($pdf),
    -Content_Disposition => qq($method; filename="$name"),
));
STDOUT->print($pdf);

Be aware that you are mixing GET and POST parameters. Learn how to write secure CGI programs.

0
votes

You need to add in the following HTTP header

print "Content-Transfer-Encoding: binary\n";

The following is working for me to read a pdf file and display it:

use strict;
use warnings;

my $file = "discover.pdf"; # a pdf I happen to have
my $pdf;

open (my $fh, $file);
binmode $fh; # set the file handle to binary mode
while (<$fh>){ $pdf .= $_; } # read it all into a string;
close ($fh);

showPdf($pdf); # call the display function

sub showPdf {

        my $pdf = shift;
        my $file = shift || "new.pdf"; # if no name is given use this
        my $method = shift || "Content-disposition:inline; filename='$file'"; # default method
        my $size = length($pdf);

        print "Content-Type: application/pdf\n";
        print "Content-Length: $size\n";
        print "$method\n";
        print "Content-Transfer-Encoding: binary\n\n"; # blank line to separate headers

        print $pdf;

}

The same function can be added to the original code and should work like this:

#!C:\Perl\bin\perl.exe
##
BEGIN { $ENV{PATH} = ''; delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; }
use strict;
use warnings;
no warnings qw (redefine closure);
use CGI;
my $CGI = new CGI();

#name=generated.pdf&method=inline these are passed via the URL and are in the environmental variable QUERY_STRING
my %nv_pairs = map{my @tmp = split(/=/,$_);$tmp[0] => $tmp[1] }split(/&/,$ENV{QUERY_STRING});
my $name = $nv_pairs{name};
my $method = $nv_pairs{method};

#Raw Data is stored in the POST Parameter POSTDATA
my $pdf = $CGI->param('POSTDATA');

showPdf($pdf, $name, $method);

sub showPdf {

    my $pdf = shift;
    my $file = shift || "new.pdf"; # if no name is given use this
    my $method = shift || "Content-disposition:inline; filename='$file'"; # default method
    my $size = length($pdf);

    print "Content-Type: application/pdf\n";
    print "Content-Length: $size\n";
    print "$method\n";
    print "Content-Transfer-Encoding: binary\n\n"; # blank line to separate headers

    print $pdf;

}