what I'm trying to accomplish is population of PDF form with PHP.
I tried many ways, I found that FPDM (FPDF) is working well when I create a new form, or use the form from source file they provided.
My problem is when I'm using already created PDF form, the form has restrictions such as Owner password, document is signed and certified. I used the app to remove those restrictions, some of them are left. In picture below you can see how my current PDF looks like.
That PDF also was compressed, and because FPDM was throwing the error that 'Object Stream' is not supported I decompressed it through PDFTK, so file went from 1.48 Mb to 6.78 Mb.
To get all form field names I used also PDFTK, so I have them in txt file.
There are two ways I can do by the instructions of FPDM:
First way is only to send an array field_name => value along with PDF I want to change and that's it. So when I use PDF described above I get error:
'FPDF-Merge Error: field form1[0].#subform[0].Line1_GivenName[0] not found'
Just to remind that I have all names and this name exists.
<?php
require('fpdm.php');
$fields = array(
'form1[0].#subform[0].Line1_GivenName[0]' => 'my name'
);
$pdf = new FPDM('test.pdf');
$pdf->Load($fields, false); // second parameter: false if field values are in ISO-8859- 1, true if UTF-8
$pdf->Merge();
$pdf->Output('new_pdf.pdf', 'F');
?>
The other way is that I create FDF file with createXFDF function and then use FPDM to merge FDF to PDF. This solution creates 'new_file.pdf' like I want but empty :)
function createXFDF($file, $info, $enc = 'UTF-8') {
$data = '<?xml version="1.0" encoding="'.$enc.'"?>' . "\n" .
'<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">' . "\n" .
'<fields>' . "\n";
foreach($info as $field => $val) {
$data .= '<field name="' . $field . '">' . "\n";
if(is_array($val)) {
foreach( $val as $opt )
$data .= '<value>' .
htmlentities( $opt, ENT_COMPAT, $enc ) .
'</value>' . "\n";
} else {
$data .= '<value>' .
htmlentities( $val, ENT_COMPAT, $enc ) .
'</value>' . "\n";
}
$data .= '</field>' . "\n";
}
$data .= '</fields>' . "\n" .
'<ids original="' . md5( $file ) . '" modified="' .
time() . '" />' . "\n" .
'<f href="' . $file . '" />' . "\n" .
'</xfdf>' . "\n";
return $data;
}
require('fpdm.php');
$pdf = new FPDM('test.pdf', 'posted-0.fdf');
$pdf->Merge();
$pdf->Output('new_file.pdf', 'F');
One more thing, if I try to open FDF file in Acrobat I get a message
'The file you are attempting to open contains comments or form data that are supposed to be placed on test.pdf. This document cannot be found. It may have been moved, or deleted. Would you like to browse to attempt to locate this document?'
but the file is there, not moved or deleted. When I find it manually the form populates.
If anyone has experience with this, any help or advice would help a lot.
Thank you in advance, Vukasin
EDIT:
More info about the PDF file
I have spent more than a complete day working through issues with FPDM, and was hard pressed to find someone who had similar issues.
The following format worked for me: PDF 1.4 (Acrobat 5). I had to
actually go to Save As -> choose Adobe PDF Optimized, then click the
Settings button. From there I had to choose the version from the
drop-down/fly-out menu.
I received the error: 'not compatible with fast web view' or similar. If in the PDF Optimized settings option you click 'clean up' on the left side you can untoggle fast web view.
Now I am receiving the error, PDF-Merge Error: field 'fieldname' not found. When I run it through pdftk hoping to resolve this, I receive the error: FPDF-Merge Error: Number of objects (35) differs with number of xrefs (36), something , pdf xref table is corrupted :(
To fix this issue, I had to download and install pdftk server utility on ubuntu
sudo apt-get install pdftk
After install, I ran this command to repair a PDF’s corrupted XREF table and stream lengths, if possible:
pdftk broken.pdf output fixed.pdf
When I open fixed.pdf it has no issues whatsoever and populates the fields correctly. Hallelujah this was the most annoying issue in the world. To summarize, I had to take the pdf and put it through the following steps:
Edit PDF to preference
Save As > .pdf optimized > Settings > Acrobat 5 > uncheck fast web under cleanup
Open file in pdftk and resave
Install command line pdftk via ubuntu
run command: pdftk broken.pdf output fixed.pdf
done.
After revealing the creator/producer info the problem is clear.
You do not have a real PDF form, but you have a XFA form (created by LiveCycle Designer), wrapped in a PDF wrapper so that Adobe Reader can display it.
XFA forms do not support (X)FDF. You have to import data using XML. You can try to export the data from a filled version, and then use this as a sample for creating the import XML.
Note that the XML export/import format XFA forms use is not the same as XFDF (which is simply an XML representation of FDF, the PDF-native forms data format).
Thanks to Kyle's answer, I resolved a similar issue.
I have a pdf created in Adobe Acrobat Pro DC and need to populate its form fields from web input.
On my development machine, I created an fdf file from my web form data, and merged it into the pdf using pdftk (called from php with exec() ).
But I couldn't put that on the cloud linux webserver where my site is hosted because it requires deprecated libraries.
So I switched to FPDM and had the following errors:
'Fast Web View mode is not supported'. I fixed that by setting
preferences in 'save as' in Adobe Acrobat Pro (save as -> pdf
optimized -> settings -> clean up -> uncheck Fast Web View).
'Object streams are not supported' - again, fixed in the 'save as'
preferences (clean up -> object compression options -> remove
compression).
'Incremental updates are not supported' - again, fixed using 'save
as' in Acrobat.
Then FPDM ran, but couldn't read any field names.
The One-Step Solution:
Take the original file pdf - with incremental updates, object compression and fast web view - and pass it through pdftk on Windows, exactly as Kyle describes.
> pdftk broken.pdf output fixed.pdf
Now FPDM populates the fields correctly from the fdf file.
I found a tool called Scribus after examining the template used in the fpdf example. You can use it to create pdf templates and the format created plays nice with fpdm. It isn't a complicated program and allows you to create form fields with permissions/parameters around them (like making a form field read-only after you populate data in it from an online form). For my application, I needed to have some fields pre-populated from values in a database that were non-editable, have other fields that were pre-populated, but still editable and some fields that were empty and required completion (force required). It was all possible using the template that Scribus has generated.
MAGNIFICENT work!
The One-Step Solution:
Take the original file pdf - with incremental updates, object compression and fast web view - and pass it through pdftk on Windows, exactly as Kyle describes.
pdftk broken.pdf output fixed.pdf
Now FPDM populates the fields correctly from the fdf file.
I created a PDF with Acrobat, then "fixed" it with pdftk, and FPDM the class merged the data perfectly...
Related
I am building an Angular 4 app with PHP API. Within the app users are able generate some sort of "magazine". This allows them to sort pages, edit content and add images but not in a WYSIWYG way, rather in a step by step "this option I selected is what I want" way.
So I end up with a lot of data stored in a MySQL database kind of "describing" how the final PDF should look like.
The problem is I have absolutely no idea how to generate a PDF. I know there are things like pdfmake or jsPDF as client side solutions or tcpdf (which seems to be in version transition for ever?!) as server side solution. But all of these are limited.
I think the best solution would be to generate some LaTeX code and generating some PDF out of it, due to the ability to use the variety of LaTeX commands instead of the limited commands for jsPDF or pdfmake.
Is there any kind of standard or best way to manage compiling LaTeX code using angular?
Which way to go? Server side or client side? The LaTeX and PDFs which are going to be created contain lots of images, and about 100-200 pages ...
For everyone else searching
CLSI seems to be a way of managing it. There is an still being maintained open source api to compile LaTeX files: CLSI ShareLaTeX
Thanks to mike42
Another very interesting example of compiling LaTeX with PHP ... which actually is the way to go in my case ... is to generate a .tex file which is a valid LaTeX file and a valid PHP file at once so the code ends up something like this:
% This file is a valid PHP file and also a valid LaTeX file
% When processed with LaTeX, it will generate a blank template
% Loading with PHP will fill it with details
\documentclass{article}
% Required for proper escaping
\usepackage{textcomp} % Symbols
\usepackage[T1]{fontenc} % Input format
% Because Unicode etc.
\usepackage{fontspec} % For loading fonts
\setmainfont{Liberation Serif} % Has a lot more symbols than Computer Modern
% Make placeholders visible
\newcommand{\placeholder}[1]{\textbf{$<$ #1 $>$}}
% Defaults for each variable
\newcommand{\test}{\placeholder{Data here}}
% Fill in
% <?php echo "\n" . "\\renewcommand{\\test}{" . LatexTemplate::escape($data['test']) . "}\n"; ?>
\begin{document}
\section{Data From PHP}
\test{}
\end{document}
If PHP safe mode is disabled and the server has xelatex / pdflatex installed execute the command on a file directly ...
First the filled-in LaTeX code needs to be stored in a temporary file by doing something like this:
/**
* Generate a PDF file using xelatex and pass it to the user
*/
public static function download($data, $template_file, $outp_file) {
// Pre-flight checks
if(!file_exists($template_file)) {
throw new Exception("Could not open template");
}
if(($f = tempnam(sys_get_temp_dir(), 'tex-')) === false) {
throw new Exception("Failed to create temporary file");
}
$tex_f = $f . ".tex";
$aux_f = $f . ".aux";
$log_f = $f . ".log";
$pdf_f = $f . ".pdf";
// Perform substitution of variables
ob_start();
include($template_file);
file_put_contents($tex_f, ob_get_clean());
}
After that the engine of choice should be executed to generate the output files:
// Run xelatex (Used because of native unicode and TTF font support)
$cmd = sprintf("xelatex -interaction nonstopmode -halt-on-error %s",
escapeshellarg($tex_f));
chdir(sys_get_temp_dir());
exec($cmd, $foo, $ret);
// No need for these files anymore
#unlink($tex_f);
#unlink($aux_f);
#unlink($log_f);
// Test here
if(!file_exists($pdf_f)) {
#unlink($f);
throw new Exception("Output was not generated and latex returned: $ret.");
}
I have a DB system built in PHP/MySql. I'm fairly new at this. The system allows the user to upload an invoice. Others give permission to pay the invoice. The accounting person uploads the check. After check is uploaded, it generates a PDF as a cover, then uses PDFTK (using Ben Squire's PDFTK-PHP-Library) to combine all of the files together and present the user with a single PDF to download.
Some users upload PDF files which cause PDFTK to hang indefinitely when it tries to combine the PDF with others (but most of the time it works fine). No returned error, just hangs. In order to get back onto the sytem, user must clear cache and re-log in. There are no error messages logged by the server, it just freezes. The only difference I can find in the files that do or do not work in looking at them with Acrobat is that the bad files are legal sized (8.5 x 14) ... but if I create my own legal sized file and try that, it works fine.
Using Putty I've gone to command line and replicated the same problem, PDFTK can't read the file, it hangs on the command line as well. I tried using PDFMerge which uses FPDF to combine the files and get an error with the file as well (The error I get back from this is: FPDF error: Unable to find object (4, 0) at expected location). On the command line I was able to use ImageMagick to convert PDF to JPG, but it gives me an error: "Warning: File has an invalid xref entry: 2. Rebuilding xref table." and then it converts it to a jpg but gives a few other less helpful warnings.
If I could get PHP to check the PDF file to determine if is valid without hanging the system, I could use ImageMagick to convert the file and then convert it back to a PDF, but I don't want to do this to all files. How can I get it to check the validity of the file when uploaded to see if it needs to be converted without causing the system to hang?
Here is a link to a file that is causing problems: http://www.cssc-testing.org/accounting/school_9/20130604-a1atransportation-1.pdf
Thanks in advance for any guidance you can offer!
My Code (which I'm guessing is not very clean, as I'm new):
$pdftk = new pdftk();
if($create_cover) { $pdftk->setInputFile(array("filename" => $cover_page['server'])); }
// Load a list of attachments
$sql = "SELECT * FROM actg_attachments WHERE trans_id = {$trans_id}";
$attachments = Attachment::find_by_sql($sql);
foreach($attachments as $attachment) {
// Check if the file exists from the attachments
$attachment->set_variables();
$file = $attachment->abs_path . DS . $attachment->filename;
if(file_exists($file)){
// Use the pdftk tool to attach the documents to this PDF
$pdftk->setInputFile(array("filename" => $file));
}
}
$pdftk->setOutputFile($save_file);
$pdftk->_renderPdf();
the $pdftk class it is calling is from: https://github.com/bensquire/php-pdtfk-toolkit
You could possibly use Ghostscript using exec() to check the file.
The non-accepted answer here may help:
How can you find a problem with a programmatically generated PDF?
I wont say this is an appropriate/best fix, but it may resolve your problem,
In: pdf_parser.php, comment out the line:
$this->error("Unable to find object ({$obj_spec[1]}, {$obj_spec[2]}) at expected location");
It should be near line 544.
You'll also likely need to replace:
if (!is_array($kids))
$this->error('Cannot find /Kids in current /Page-Dictionary');
with:
if (!is_array($kids)){
// $this->error('Cannot find /Kids in current /Page-Dictionary');
return;
}
in the fpdi_pdf_parser.php file
Hope that helps. It worked for me.
I am converting a classic ASP application to PHP.
In the Classic ASP app, we are programatically populating fields that have been created in PDF documents using a component called ASPpdf.
I need to reproduce this behavior in PHP, but need to know if PHP can populate PDF fields on its own or if any third party plug in is needed.
Is this functionlity posible in PHP with or without a plug in?
Thanks.
Note: I already have the PDFs created, I do not need to create the actual PDF. I need to grab a preexisting PDF with form fields, populate those form fields and then save that custom PDF.
This is the first google search result I got, is there a reason this doesn't work for you?
http://koivi.com/fill-pdf-form-fields/tutorial.php
UPDATE
After reading a little further, this generates an FDF (which Acrobat can read). To generate an actual PDF you'll need to use this: http://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/
The resolution to my problem was to use the same COM component I used in Classic ASP in my new PHP apps. The COM object give me tremendous control over PDF documents.
The component I use is AspPdf
<?php
$pdf = new COM("Persits.Pdf") or die("Unable to instantiate Word");
$pdfdoc = $pdf->OpenDocument( "pdf.pdf" );
$pdffont = $pdfdoc->Fonts("Helvetica-Bold");
$pdfdoc->Form->RemoveXFA();
$pdffield = $pdfdoc->Form->FindField("FirstName");
$pdffield->SetFieldValue("PHP Text", $pdffont);
$pdffile = $pdfdoc->save( "php.pdf" , false);
echo $pdf->version;
$pdf = null;
?>
It looks as though the pdftk PDF toolkit may be able to do this. Reading the manual:
fill_form < FDF data filename | XFDF data filename | - | PROMPT >
Fills the single input PDF’s form fields with the data from an FDF file, XFDF file or stdin. Enter the data filename after fill_form, or use - to pass the data via stdin, like so:
pdftk form.pdf fill_form data.fdf output form.filled.pdf
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.
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...)