Forum How do I...?

using convert3

ghazuria
I am having a problem using the convert3 function.
Should the following code produce a pdf?
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="foo.pdf"');
require_once('prince.php');
$html='<html><body><div>This is a test</div></body></html>';
$pdf = new Prince('c:\\PrincePDF\\Engine\\bin\\prince.exe');
$pdf->convert3($html);

using convert2 and an html file that has the same string does produce a pdf.
On one script where I tried to use convert3, it looks like it is downloading a pdf but comes up with a 0byte file.
On the above code, it says "Internet Explorer Cannot Display Page'

Thanks
kmmv
I'm not sure if PHP interpolates your headers, but the Content-Disposition header should not use quotes, despite what you may read elsewhere.

Content-Disposition: attachment; filename=foo.pdf[CRLF]

Note that while the filename argument may contain spaces according to the spec, it's advisable you not do so. Various browsers have issues (OK, only IE) with spaces in the names, particularly when combined with SSL or cross-domain or mixed security policy settings.
ghazuria
I tried it without the quotes, that creates the pdf in firefox but not in IE. Unfortunately the majority of the people viewing the page will be using IE.

Also, when I tried to create the pdf from the actual html string (a very long string with css) it again came up with a 0 byte file. I would suspect that there is something wrong with the html BUT I also tried it the following way, and it works. (In IE and Firefox)

generated the html string as before
created an html file and inserted html string
closed html file
used prince->convert2 with path to html file and a path for pdf file
redirected user to the newly generated file.

displays.

So, this is the same html that I was trying to use with convert3 without any results.

For now I will continue to use this method to use prince but hopefully there is a way to get the convert3 function working.

Thanks for your help.
mikeday
Try using the setLog method to specify a log file for Prince, then see if any error messages are showing up in the log.
joomlaman
I am having the same problem as this user. I believe I know the cause of this problem. In the sample code, it does not advise you to use a content-length directive. If you do not use content-length when outputting a PDF to Internet Explorer it will not render.

The result of this behavior is very interesting. What happens is, the Prince process starts consuming 100% memory allocation because it gets hung up before it finishes outputting the file (sorry for lack of technical terms, but I am just tired and I have been researching this for a while). I saw a thread or two about users with this exact behavior, and this is exactly what the cause is, IE not getting the content-length, hanging up rudely, and then Prince starts consuming 100% CPU time because it didn't get hung up properly.

The fix that worked for me was suggested by another forum user which was to use the PHP statement ignore_user_abort(TRUE); before the headers were output.

HOWEVER, I have not yet determined how to get the content-length of the pdf before it is output via convert3. If someone else wants to take a shot at this, feel free, and post the solution here, because that will be the fix you need for IE! If I figure it out I will post it here.
mikeday
The only way to get the content-length in advance will be to read the PDF from Prince into a PHP string (or byte array?) until the entire PDF has been read, check the length, set the header, then write out the buffered PDF to the output. This means that the fpassthru() function cannot be used, as it will write the output without buffering it and checking the length first.
joomlaman
Exactly, except, I have no idea how to code this in PHP, unfortunately. 8) Any PHP gurus want to chime in with a fix? :wink:
mdsc
mikeday wrote:
The only way to get the content-length in advance will be to read the PDF from Prince into a PHP string (or byte array?) until the entire PDF has been read, check the length, set the header, then write out the buffered PDF to the output. This means that the fpassthru() function cannot be used, as it will write the output without buffering it and checking the length first.


We've taken to saving the PDF data out to a temporary file, then using filesize() to get the content length for the header, followed by readfile() to open and stream the content to the user, and finally unlink()ing the temporary file before terminating the request. The other benefit here is that for large PDF documents, they aren't all buffered into memory at once, possibly exceeding PHP's memory limit. (Which can be increased, of course.)
mdsc
mikeday wrote:
The only way to get the content-length in advance will be to read the PDF from Prince into a PHP string (or byte array?) until the entire PDF has been read, check the length, set the header, then write out the buffered PDF to the output. This means that the fpassthru() function cannot be used, as it will write the output without buffering it and checking the length first.


We've taken to saving the PDF data out to a temporary file (using convert_string_to_file()), then using filesize() to get the content length for the header, followed by readfile() to open and stream the content to the user, and finally unlink()ing the temporary file before terminating the request. The other benefit here is that for large PDF documents, they aren't all buffered into memory at once, possibly exceeding PHP's memory limit. (Which can be increased, of course.)
joomlaman
Cool idea - I will try to implement this today. Is there a PHP method for generating a temporary file handle, or do you have to arbitrarily make one up (what I usually use, just some random string I generate in /tmp) ?

Maybe if it works, we can suggest it for addition to the prince PHP library?
mdsc
joomlaman wrote:
Cool idea - I will try to implement this today. Is there a PHP method for generating a temporary file handle, or do you have to arbitrarily make one up (what I usually use, just some random string I generate in /tmp) ?

Maybe if it works, we can suggest it for addition to the prince PHP library?


Something like this probably doesn't belong in the Prince library, as it's more of an application-specific implementation issue. We use the PHP tempnam() function to create a unique temporary file in /tmp and pass that to either the convert_string_to_file() or the convert_file_to_file() methods of the Prince class.

There's one caveat, however, and something that should probably be fixed in the Prince interface. Line 193 in prince.php, in the method convert_file_to_file(), needs to look like this:

$pathAndArgs .= '"' . $xmlPath . '" -o "' . $pdfPath . '"';


Note the addition of the -o argument. Otherwise, with Prince 6, the call will fail, because the temporary file name doesn't have a .pdf extension, and Prince interprets the filename as an input file and not the output file. Note that convert_string_to_file() correctly uses the -o argument.
joomlaman
Are your saying your fix doesn't belong in the Prince PHP library, or that any fix doesn't belong in the Prince PHP library? Aren't IE browsers something like 70% of the world internet population?
mdsc
joomlaman wrote:
Are your saying your fix doesn't belong in the Prince PHP library, or that any fix doesn't belong in the Prince PHP library? Aren't IE browsers something like 70% of the world internet population?


I consider my "save to a file first" method as too specific to my particular development platform and circumstances, that's all. In addition, the "fix", whether using my method or the previously offered output buffering method, is only necessary and applicable within the context of a web application. It's outside the scope of the Prince PHP interface in my opinion.
joomlaman
mdsc wrote:
joomlaman wrote:
Are your saying your fix doesn't belong in the Prince PHP library, or that any fix doesn't belong in the Prince PHP library? Aren't IE browsers something like 70% of the world internet population?


I consider my "save to a file first" method as too specific to my particular development platform and circumstances, that's all. In addition, the "fix", whether using my method or the previously offered output buffering method, is only necessary and applicable within the context of a web application. It's outside the scope of the Prince PHP interface in my opinion.


Ok, thanks so much for your help. I just wanted to help other PHP developers along with this pitfall also, that's all. Hopefully they will see this thread or they have fixed their applications should they be using this functionality in this particular way.
mikeday
(Just to clarify, mdsc and mikeday are two different people).

I think that a suitable solution would belong in the Prince PHP library, but I would also like to present the option of buffering the PDF file in memory to avoid hassles with temporary files.
joomlaman
Small update - for me, I found that even having content-length header specified was not enough to placate Internet Explorer. This may be because I use session_start() before I output a PDF file. However, after reading hours of forum posts, tweaking, hair pulling frustration, I found this PHP code to finally resolve the issue for me:

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: must-revalidate");

These headers along with content-type, disposition (and specifying the filename), and content-length, all combined, should be enough to placate the Beast. Was able to test on IE 7 Vista, and IE6 XP, but don't have access to anything else at the moment.