Merge FDF data into a PDF file using PHP - php

Is it possible to merge FDF data with a PDF file using PHP alone? Or is there no option but to use a 3rd party command line tool to achieve this?
If that is the case can someone point me in the direction of one?
I am currently outputting the FDF file to the browser in the hope that it will redirect the user to the filled in PDF but for some people that is not the case. The FDF contents is being output to the screen, even though I am using header('Content-type: application/vnd.fdf');

For future reference, it looks like there isn't a reliable way of doing it without a 3rd party app. Pdftk (http://www.accesspdf.com/pdftk/) ended up being my solution.
I first generated the FDF file as before, and then merged it into my PDF file using the following PHP code
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="Download.pdf"');
passthru("pdftk file.pdf fill_form data.fdf output - ");
exit;
It was much easier than I thought it would be. This instantly eliminates the need to hack around with headers and file extensions to ensure all browsers handle an FDF properly, as it simply makes the browser download the PDF file.
If you want the PDF output file to no longer be editable, use
passthru("pdftk file.pdf fill_form data.fdf output - flatten");
Apologies if this is basic stuff, just thought I'd put it all in one place so that people don't go through the headache that I endured.
N.B. If your PATH variable is not set, you will need to use the full path to pdftk i.e.
passthru("/usr/local/bin/pdftk file.pdf fill_form data.fdf output - flatten");

There is another way to day that not using passthru nor pdftk but just a script made in 2004 but still working well : forge_fdf
it helps you to build a fdf that you can incoporate straithly in your pdf, it means that you
Save this in a php file, let's say generatePdf.php
require_once('forge_fdf.php');
// leave this blank if we're associating the FDF w/ the PDF via URL
$pdf_form_url= "";
// default data; these two arrays must ultimately list all of the fields
// you desire to alter, even if you just want to set the 'hidden' flag;
//
//
$fdf_data_names= array(); // none of these in this example
$fdf_data_strings= array(); // none of these in this example
$fdf_data_strings['email']=mb_strtolower($row_delivreur['firstname']).'.'.mb_strtolower($row_delivreur['lastname']).'#gmail.com';
$fields_hidden= array();
$fields_readonly= array();
// set this to retry the previous state
$retry_b= false;
header( 'content-type: application/vnd.fdf' );
echo forge_fdf( $pdf_form_url,
$fdf_data_strings,
$fdf_data_names,
$fields_hidden,
$fields_readonly );
Making a link to Pathtoyourpdf/nameofpdffile.pdf#FDF=generatePdf.php will open your PDF file in browser (alternatively there is a way to save it to disk i think i remember) and the field email will be filled with data from MYSQL : mb_strtolower($row_delivreur['firstname']).'.'.mb_strtolower($row_delivreur['lastname']).'#gmail.com'
It works with checkboxes, radio button,... It opens well in firefox, it has to be tested with other browsers.
More infos on PDF HACKS

by far the easiest way I have found to install pdftk on centos:
Install rpmforge repo - http://wiki.centos.org/AdditionalResources/Repositories/RPMForge#head-5aabf02717d5b6b12d47edbc5811404998926a1b
then
yum install pdftk
and thats it!

http://www.setasign.de/products/pdf-php-solutions/fpdi/demos/concatenate-fake/
This works , the classes download are linked to from the web site too,
It requires no passthru/exec command and no additional extensions.
Edited to say, this doesn't work with newer pdf versions 1.5+, reverted to PDFTK, fiddly but works with all pdfs using the 'exec' command.

Related

Create a zip file but provide it as a download without saving it on server [duplicate]

I am trying to generate an archive on-the-fly in PHP and send it to the user immediately (without saving it). I figured that there would be no need to create a file on disk as the data I'm sending isn't persistent anyway, however, upon searching the web, I couldn't find out how. I also don't care about the file format.
So, the question is:
Is it possible to create and manipulate a file archive in memory within a php script without creating a tempfile along the way?
I had the same problem but finally found a somewhat obscure solution and decided to share it here.
I came accross the great zip.lib.php/unzip.lib.php scripts which come with phpmyadmin and are located in the "libraries" directory.
Using zip.lib.php worked as a charm for me:
require_once(LIBS_DIR . 'zip.lib.php');
...
//create the zip
$zip = new zipfile();
//add files to the zip, passing file contents, not actual files
$zip->addFile($file_content, $file_name);
...
//prepare the proper content type
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=my_archive.zip");
header("Content-Description: Files of an applicant");
//get the zip content and send it back to the browser
echo $zip->file();
This script allows downloading of a zip, without the need of having the files as real files or saving the zip itself as a file.
It is a shame that this functionality is not part of a more generic PHP library.
Here is a link to the zip.lib.php file from the phpmyadmin source:
https://github.com/phpmyadmin/phpmyadmin/blob/RELEASE_4_5_5_1/libraries/zip.lib.php
UPDATE:
Make sure you remove the following check from the beginning of zip.lib.php as otherwise the script just terminates:
if (! defined('PHPMYADMIN')) {
exit;
}
UPDATE:
This code is available on the CodeIgniter project as well:
https://github.com/patricksavalle/CodeIgniter/blob/439ac3a87a448ae6c2cbae0890c9f672efcae32d/system/helpers/zip_helper.php
what are you using to generate the archive? You might be able to use the stream php://temp or php://memory to read and write to/from the archive.
See http://php.net/manual/en/wrappers.php.php
Regarding your comment that php://temp works for you except when you close it, try keeping it open, flushing the output, then rewind it back to 0 and read it.
Look here for more examples: http://us.php.net/manual/en/function.tmpfile.php
Also research output buffering and capturing: http://us.php.net/manual/en/function.ob-start.php
You need to use ZipArchive::addFromString - if you use addFile() the file is not actually added until you go to close it. (Horrible bug IMHO, what if you are trying to move files into a zip and you delete them before you close the zip...)
The addFromString() method adds it to the archive immediately.
Is there really a performance issue here, or does it just offend your sense of rightness? A lot of processes write temporary files and delete them, and often they never hit the disk due to caching.
A tempfile is automatically deleted when closed. That's it's nature.
There are only two ways I can think of to create a zip file in memory and serve it and both are probably more trouble than they are worth.
use a ram disk.
modify the ziparchive class to add a method that does everything the close() method does, except actually close the file. (Or add a leave-open parameter to close()).
This might not even be possible depending on the underlying C libraries.

Clean up after creating the pdf

I'm generating a pdf file with html2fpdf.
$pdf = new HTML2FPDF();
$pdf->HTML2FPDF("P","mm","A4");
$pdf->AddPage();
$pdf->WriteHTML($html);
$pdf->output('sample.pdf');
This sample works great. But:
How do I delete the pdf after the output? I just want to have links in my tool, the users can download the pdf and after that it shoud be deleted on the server.
How can I 'clean up' after generating the pdf?
You can use PHP's file deletion function called unlink()
Call this function with the full path to the generated PDF file (or any file for that matter) and PHP will delete that file.
http://php.net/manual/en/function.unlink.php
You don't necessarily have to delete the file immediately after the user has downloaded it. You can just as easily place all the generated files in one central folder and have a cron job execute a more general clean up script simply removing the older files.
One method could be -
Scan the contents of the folder using scandir().
Iterate over its files in a foreach loop..
Inspect the creation time of each file using filemtime().
If the creation time was over hour ago, delete the file using unlink().
Because you are generating the PDF file yourself within your PHP code, I didn't mention the permissions consideration. Here would be a good place to mention that your PHP must have the correct file system permissions in order to perform any action on the file system. You are creating a PDF file so it's safe to assume that you have the correct permissions to make changes to the file system but if you plan on using this unlink() function in other scripts make sure that the files you are dealing with have the correct permissions set.
If you don't add the 'F' flag to the output function there will be no pdf files stored on the server at all:
$pdf->output('sample.pdf', 'F'); //stores PDF on server
In your case the script itself behaves like an actual pdf file. So, creating a link to the script is just like a link to the pdf, except that the PDF is created every time the script is requested. To tell the browser it's a PDF the content-type response header must be set to application/pdf:
content-type: application/pdf
This way the broser knows that it's a pdf even if the URL is ending in a .php. You can use rewrite engine to make it end in pdf or whatever else.
Sending the headers is done by the fpdf/tcpdf. In short: you don't have to do any cleanup, because no pdf file is stored on the server.
If you wonder what the name is for than, try saving the pdf file. The recommanded name when saving will be sample.pdf.
Reference:
PHP header() function, at the examples there is one for sending pdf
FPDF::Output()
TCPDF::Output()

Php Download script isn't working

I have a script which uploads files into an online directory and stores the file details in a database. The files when stored are renamed to the id of the entry in the database. Whenever a user requests a download, a simple SQL statement retrieves the file details from the database, the contents of the file are read from the database, and the file is prompted for download. The following is my code:
$one_file = $FILE_OBJECT->get($_GET['id']); // this is an object which just grabs the file details from the database
header("Content-type: ".$one_file['type']); // add here more headers for diff. extensions
header("Content-Disposition: attachment; filename=\"".$one_file["filename"]."\""); // use 'attachment' to force a download
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"".$one_file["filename"]."\"");
readfile(_config('files_path').$_GET['id']);// reading the actual raw file stored in my online directory
Problem is that Im testing using a word document and its uploading perfectly - I've even checked the raw file being uploaded by manually changing its extension and it's uploading perfectly. The problem is that when it's downloaded using the code above, the Word file seems corrupted or something, because when I try to open it, it's all mumbled and jumbled. What's happening? I've used this snippet on a few other sites I've worked on, and they work perfectly fine... Help please!
By default PHP's header function will replace previous headers with the same name, so your first two headers are being overwritten by the second two. Delete the second two and see if that works.
See if this helps:
Webkit and Excel file(PHPexcel)
I was having the same problem: every time I downloaded a file, it was supposedly "corrupt". Turns out I had made a stupid directory path mistake, but the php error was being written into the downloaded file. Which, of course, made it "corrupt".
Actually I solved by reading Ian Wetherbee's comment about testing with a plain text file. Thanks Ian!

Manipulate an Archive in memory with PHP (without creating a temporary file on disk)

I am trying to generate an archive on-the-fly in PHP and send it to the user immediately (without saving it). I figured that there would be no need to create a file on disk as the data I'm sending isn't persistent anyway, however, upon searching the web, I couldn't find out how. I also don't care about the file format.
So, the question is:
Is it possible to create and manipulate a file archive in memory within a php script without creating a tempfile along the way?
I had the same problem but finally found a somewhat obscure solution and decided to share it here.
I came accross the great zip.lib.php/unzip.lib.php scripts which come with phpmyadmin and are located in the "libraries" directory.
Using zip.lib.php worked as a charm for me:
require_once(LIBS_DIR . 'zip.lib.php');
...
//create the zip
$zip = new zipfile();
//add files to the zip, passing file contents, not actual files
$zip->addFile($file_content, $file_name);
...
//prepare the proper content type
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=my_archive.zip");
header("Content-Description: Files of an applicant");
//get the zip content and send it back to the browser
echo $zip->file();
This script allows downloading of a zip, without the need of having the files as real files or saving the zip itself as a file.
It is a shame that this functionality is not part of a more generic PHP library.
Here is a link to the zip.lib.php file from the phpmyadmin source:
https://github.com/phpmyadmin/phpmyadmin/blob/RELEASE_4_5_5_1/libraries/zip.lib.php
UPDATE:
Make sure you remove the following check from the beginning of zip.lib.php as otherwise the script just terminates:
if (! defined('PHPMYADMIN')) {
exit;
}
UPDATE:
This code is available on the CodeIgniter project as well:
https://github.com/patricksavalle/CodeIgniter/blob/439ac3a87a448ae6c2cbae0890c9f672efcae32d/system/helpers/zip_helper.php
what are you using to generate the archive? You might be able to use the stream php://temp or php://memory to read and write to/from the archive.
See http://php.net/manual/en/wrappers.php.php
Regarding your comment that php://temp works for you except when you close it, try keeping it open, flushing the output, then rewind it back to 0 and read it.
Look here for more examples: http://us.php.net/manual/en/function.tmpfile.php
Also research output buffering and capturing: http://us.php.net/manual/en/function.ob-start.php
You need to use ZipArchive::addFromString - if you use addFile() the file is not actually added until you go to close it. (Horrible bug IMHO, what if you are trying to move files into a zip and you delete them before you close the zip...)
The addFromString() method adds it to the archive immediately.
Is there really a performance issue here, or does it just offend your sense of rightness? A lot of processes write temporary files and delete them, and often they never hit the disk due to caching.
A tempfile is automatically deleted when closed. That's it's nature.
There are only two ways I can think of to create a zip file in memory and serve it and both are probably more trouble than they are worth.
use a ram disk.
modify the ziparchive class to add a method that does everything the close() method does, except actually close the file. (Or add a leave-open parameter to close()).
This might not even be possible depending on the underlying C libraries.

Dynamically insert content into pdf files with php

I have an ebook in word that I convert to PDF before distributing to my clients. I'd like to dynamically insert their email address into all links in the ebook to allow them access to the members-only content on my site, and I'd like to do this on the fly, as part of the book download process.
I've briefly looked at http://us.php.net/pdf and FPDF, but I was wondering what specific technique I'd use to insert this data.
I was thinking I'd insert an email token string where I want the email address to go, and then use some function to update those tokens in the PDF document.
Can anyone point me in the right direction? I have php experience, but not with editing / generating pdf documents from php.
EDIT: Yes, this commercial script http://www.setasign.de/products/pdf-php-solutions/setapdf-linkreplacer/ does exactly what I needed.
So far it's looking like this is my best bet:
http://www.setasign.de/products/pdf-php-solutions/setapdf-linkreplacer/
Trying an eval copy of it, will update post with results.
you can do this with FPDI extension for FPDF http://www.setasign.de/products/pdf-php-solutions/fpdi/
it enables fpdf to import existing pdf files, though I'm not sure how can one replace links.
I'd say your best shot would be to generate the whole thing in php, or just save it in html, replace links in html, then convert html to pdf.
Without using the Adobe LiveCycle Designer, the easiest way to generate a custom PDF is to use an FDF file. There are tons of ways to do this, one of which is to download binaries from Adobe and install them on your server. But none of that is really needed. All you need is a pdf with fillable forms and a simple script that makes and FDF file. The FDF simply holds the data that needs to be filled in and a pointer to the pdf file to be filled in. I use this for our timesheets at work. The data goes into a web form, but must come out static and ugly and using a paper from from 30 years ago. Here's what your fdf file will look like (both with code and raw):
$file = "http://www.example.com/blankpdfform.pdf";
$data = "%FDF-1.2\n%âãÏÓ\n1 0 obj\n<< \n/FDF << /Fields [ ";
foreach($datafields as $field => $val) {
$data.='<< /T ('.$field.') /V ('.trim($val).')>> ';
}
$data.="] \n/F (".$file.") /ID [ <".md5(time()).">\n] >>".
" \n>> \nendobj\ntrailer\n".
"<<\n/Root 1 0 R \n\n>>\n%%EOF\n";
The end result being:
%FDF-1.2\n%âãÏÓ\n1 0 obj\n<< \n/FDF << /Fields [<< /T (email) /V (email#address.com)>>
/F ("http://www.example.com/blankpdfform.pdf") /ID [ <"SomeUniqueID">
] >> \nendobj\ntrailer<<
/Root 1 0 R
>>
%%EOF
If you have the PDF template with the email token stored in a file on the webserver, you can do this fairly easily. First you need to read in the file using PHP. You can do this using the file_get_contents method. Then use str_replace to replace the email token with the actual email. Finally, serve the file with the correct content-type.
$pdf = file_get_contents( 'template.pdf' );
$pdf = str_replace( '__EMAIL__TEMPLATE__', $userEmail, $pdf );
header( 'Content-type: application/pdf' );
print $pdf;
Doc links:
http://ca2.php.net/manual/en/function.file-get-contents.php
http://ca2.php.net/manual/en/function.str-replace.php
(I haven't actually tried this and you may run into some issues since PDF is a binary format, but in theory it should work...)

Categories