Copy link to clipboard
Copied
I have a form I have built and want to submit the entire pdf to a url. I have added a submit button with an action on MouseDown to submit a form. When I open the pdf in Acrobat DC or in Acrobat Reader DC it works perfectly. However, when I open the pdf in the browser (and not in the actual acrobat reader stand-alone software) I get an "Adobe Acrobat" error stating "Error opening URL to submit this form". I am 100% sure I am using the adobe acrobat reader plug-ins in the browser and I have tried opening the file in the browser on both the local server machine and on a remote machine accessing the webpage.
Is there some reason why I am getting this error in the browser acrobat reader but not in the stand-alone software? Any ideas would be helpful.
On a side note, I am doing this because I need to save a copy of the pdf (filled in by the user) to the server and the only way I know how to is to submit the pdf...and from there I have a php handler that takes it from there. If there is no fix for the above question is there another way to trigger the pdf to save to the server after it has been modified (not on the page load)?
1 Correct answer
Ahh, I think I found the issue.. You may need to supply the URL of the PDF form that's sitting on your website, so when it responds with FDF it will load the PDF from the URL; otherwise; the FDF doesn't know what form to populate. Also; be sure to select IE or FF as the default web browser in your OS; because, it may automatically open the PDF with the default browser.
CHANGE FROM: Found in (public function make_pdf)
$fdf = $this->forge_fdf('', $fdf_data_strings, $fdf_data_names, $fields_hidden,
...Copy link to clipboard
Copied
Did you extend usage rights to the PDF? In order for Adobe Reader end-users to submit the whole PDF format; the PDF needs Extended Usage Rights...
Copy link to clipboard
Copied
Yes, user rights are extended. Still the same problem.
Copy link to clipboard
Copied
If the server-side PHP script is responding with a FDF file, be sure to include the /F key in the FDF response file outputted by the server. If the form is submitted from a web browser it will look for the /F key in the FDF file. If the form is submitted from Standalone Adobe Reader, then it doesn't need the /F file parameter in the FDF response.
If you are redirecting to a URL, be sure to include http:// at the beginning of the URL instead of starting he URL with www.
By the way; where did you get the script? Did you develop it or purchase it?
Let me know if this works.
Copy link to clipboard
Copied
View instructions on how to enable Adobe Reader as the default PDF browser plug-in:
http://www.nk-inc.com/blog/96/2015/02/13/setting-adober-reader-default-pdf-browser-plug-in
Note: Chrome and maybe Edge no longer officially support NPAPI plugins such as Adobe Reader. Firefox and IE may be your best choices for viewing PDFs with the Adobe Reader browser plugin.
Copy link to clipboard
Copied
So I double checked the browser plug-in status and Adobe Reader is definitely good. By the way, IE is the browser I am using for this project...if that matters.
The URL is good.
By looking at pdftk-php.php the /F key is included (as I understand it, I am still learning this pdf generation/form filling). I am using pdftk-php for the script.
What is so confusing to me is that I can generate the pdf via pdftk, save it and then submit the form in the stand-alone but it doesn't submit in the browser.
Copy link to clipboard
Copied
I can't help fix what's happening without a link to the PDF or viewing any source code..
Copy link to clipboard
Copied
How and where are the extended usge rights setup
Copy link to clipboard
Copied
You should use Mouse Up, not Mouse Down. This probably isn't the source of the problem, but you should change it anyway.
Copy link to clipboard
Copied
Well, thanks for the help. With some further investigation it is defiantly the php-pdftk interface. Removing the file from processing by pdftk works just fine. That doesnt solve the problem but it helps immensely. Thank you NKOWA555, your question about the /F key pointed me in the right direction.
I will post a response if I am able to resolve the problem of "breaking the submitForm()" javascript function in a form when populating a form with php-pdftk.
And thank you George_Johnson for the information on setting the action. I will keep that in mind for future projects too.
Copy link to clipboard
Copied
Below is the php-pdftk.php. Can you tell me if I need to make a change...specifically to the /F key issue, which I do not really understand? I have submitted a form to this page and it pulls the fillable pdf and merges the file. The problem is that when I press the submit button (which is a generic submit button form acrobat.pdf...and which works 100% outside of the browser) it throws the error I stated in the opening post of this thread.
<?php
##################################################################################################################
#
# pdftk-php Class
# http://code.google.com/p/pdftk-php/
# http://www.pdfhacks.com/forge_fdf/
#
# License: Released under New BSD license - http://www.opensource.org/licenses/bsd-license.php
#
# Purpose: Contains functions used to inject data from MySQL into an empty PDF form
#
# Authors: Andrew Heiss (www.andrewheiss.com), Sid Steward (http://www.oreillynet.com/pub/au/1754)
#
# History:
# 8/26/08 - Initial programming
#
# Usage:
# $pdfmaker = new pdftk_php;
#
# $fdf_data_strings = array();
# $fdf_data_names = array();
# $fields_hidden = array();
# $fields_readonly = array();
# $pdf_original = "string"; = filename of the original, empty pdf form
# $pdf_filename = "string"; = filename to be used for the output pdf
#
# $pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_filename);
#
##################################################################################################################
class pdftk_php {
#############################################################################
#
# Function name: makePDF
#
# Purpose: Generate an FDF file from db data, inject the FDF in an empty PDF form
#
# Incoming parameters:
# $fetched_array - one row of fetched MySQL data saved as a variable
#
# Returns: Downloaded PDF file
#
# Notes:
# * For text fields, combo boxes and list boxes, add field values as a name => value pair to $fdf_data_strings. An example of $fdf_data_strings is given in /example/download.php
# * For check boxes and radio buttons, add field values as a name => value pair to $fdf_data_names. Typically, true and false correspond to the (case sensitive) names "Yes" and "Off".
# * Any field added to the $fields_hidden or $fields_readonly array must also be a key in $fdf_data_strings or $fdf_data_names; this might be changed in the future
# * Any field listed in $fdf_data_strings or $fdf_data_names that you want hidden or read-only must have its field name added to $fields_hidden or $fields_readonly; do this even if your form has these bits set already
#
#############################################################################
public function make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename) {
// Create the fdf file
$fdf = $this->forge_fdf('', $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
// Save the fdf file temporarily - make sure the server has write permissions in the folder you specify in tempnam()
$fdf_fn = tempnam(".", "fdf");
$fp = fopen($fdf_fn, 'w');
if($fp) {
fwrite($fp, $fdf);
fclose($fp);
// Send a force download header to the browser with a file MIME type
// header("Content-Type: application/force-download");
// header("Content-Type: application/pdf");
// header("Content-Disposition: attachment; filename=\"$pdf_filename\"");
// Actually make the PDF by running pdftk - make sure the path to pdftk is correct
// The PDF will be output directly to the browser - apart from the original PDF file, no actual PDF wil be saved on the server.
passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - ");
// delete temporary fdf file
unlink( $fdf_fn );
}
else { // error
echo 'Error: unable to write temp fdf file: '. $fdf_fn;
}
} // end of make_pdf()
protected function forge_fdf($pdf_form_url, &$fdf_data_strings, &$fdf_data_names, &$fields_hidden, &$fields_readonly) {
/* forge_fdf, by Sid Steward
version 1.1
visit: www.pdfhacks.com/forge_fdf/
PDF can be particular about CR and LF characters, so I spelled them out in hex: CR == \x0d : LF == \x0a
*/
$fdf = "%FDF-1.2\x0d%\xe2\xe3\xcf\xd3\x0d\x0a"; // header
$fdf.= "1 0 obj\x0d<< "; // open the Root dictionary
$fdf.= "\x0d/FDF << "; // open the FDF dictionary
$fdf.= "/Fields [ "; // open the form Fields array
$fdf_data_strings = $this->burst_dots_into_arrays( $fdf_data_strings );
$this->forge_fdf_fields_strings( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly );
$fdf_data_names= $this->burst_dots_into_arrays( $fdf_data_names );
$this->forge_fdf_fields_names( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly );
$fdf.= "] \x0d"; // close the Fields array
// the PDF form filename or URL, if given
if( $pdf_form_url ) {
$fdf.= "/F (".$this->escape_pdf_string($pdf_form_url).") \x0d";
}
$fdf.= ">> \x0d"; // close the FDF dictionary
$fdf.= ">> \x0dendobj\x0d"; // close the Root dictionary
// trailer; note the "1 0 R" reference to "1 0 obj" above
$fdf.= "trailer\x0d<<\x0d/Root 1 0 R \x0d\x0d>>\x0d";
$fdf.= "%%EOF\x0d\x0a";
return $fdf;
}
public function escape_pdf_string( $ss ) {
$backslash= chr(0x5c);
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii})== 0x28 || // open paren
ord($ss{$ii})== 0x29 || // close paren
ord($ss{$ii})== 0x5c ) // backslash
{
$ss_esc.= $backslash.$ss{$ii}; // escape the character w/ backslash
}
else if( ord($ss{$ii}) < 32 || 126 < ord($ss{$ii}) ) {
$ss_esc.= sprintf( "\\%03o", ord($ss{$ii}) ); // use an octal code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
protected function escape_pdf_name( $ss ) {
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii}) < 33 || 126 < ord($ss{$ii}) ||
ord($ss{$ii})== 0x23 ) // hash mark
{
$ss_esc.= sprintf( "#%02x", ord($ss{$ii}) ); // use a hex code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
// In PDF, partial form field names are combined using periods to
// yield the full form field name; we'll take these dot-delimited
// names and then expand them into nested arrays, here; takes
// an array that uses dot-delimited names and returns a tree of arrays;
//
protected function burst_dots_into_arrays( &$fdf_data_old ) {
$fdf_data_new= array();
foreach( $fdf_data_old as $key => $value ) {
$key_split= explode( '.', (string)$key, 2 );
if( count($key_split)== 2 ) { // handle dot
if( !array_key_exists( (string)($key_split[0]), $fdf_data_new ) ) {
$fdf_data_new[ (string)($key_split[0]) ]= array();
}
if( gettype( $fdf_data_new[ (string)($key_split[0]) ] )!= 'array' ) {
// this new key collides with an existing name; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)($key_split[0]) ]=
array( '' => $fdf_data_new[ (string)($key_split[0]) ] );
}
$fdf_data_new[ (string)($key_split[0]) ][ (string)($key_split[1]) ]= $value;
}
else { // no dot
if( array_key_exists( (string)($key_split[0]), $fdf_data_new ) &&
gettype( $fdf_data_new[ (string)($key_split[0]) ] )== 'array' )
{ // this key collides with an existing array; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)$key ]['']= $value;
}
else { // simply copy
$fdf_data_new[ (string)$key ]= $value;
}
}
}
foreach( $fdf_data_new as $key => $value ) {
if( gettype($value)== 'array' ) {
$fdf_data_new[ (string)$key ]= $this->burst_dots_into_arrays( $value ); // recurse
}
}
return $fdf_data_new;
}
protected function forge_fdf_fields_flags( &$fdf,
$field_name,
&$fields_hidden,
&$fields_readonly ) {
if( in_array( $field_name, $fields_hidden ) )
$fdf.= "/SetF 2 "; // set
else
$fdf.= "/ClrF 2 "; // clear
if( in_array( $field_name, $fields_readonly ) )
$fdf.= "/SetFf 1 "; // set
else
$fdf.= "/ClrFf 1 "; // clear
}
protected function forge_fdf_fields( &$fdf,
&$fdf_data,
&$fields_hidden,
&$fields_readonly,
$accumulated_name,
$strings_b ) // true <==> $fdf_data contains string data
//
// string data is used for text fields, combo boxes and list boxes;
// name data is used for checkboxes and radio buttons, and
// /Yes and /Off are commonly used for true and false
{
if( 0< strlen( $accumulated_name ) ) {
$accumulated_name.= '.'; // append period seperator
}
foreach( $fdf_data as $key => $value ) {
// we use string casts to prevent numeric strings from being silently converted to numbers
$fdf.= "<< "; // open dictionary
if( gettype($value)== 'array' ) { // parent; recurse
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") "; // partial field name
$fdf.= "/Kids [ "; // open Kids array
// recurse
$this->forge_fdf_fields( $fdf,
$value,
$fields_hidden,
$fields_readonly,
$accumulated_name. (string)$key,
$strings_b );
$fdf.= "] "; // close Kids array
}
else {
// field name
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") ";
// field value
if( $strings_b ) { // string
$fdf.= "/V (".$this->escape_pdf_string( (string)$value ).") ";
}
else { // name
$fdf.= "/V /".$this->escape_pdf_name( (string)$value ). " ";
}
// field flags
$this->forge_fdf_fields_flags( $fdf,
$accumulated_name. (string)$key,
$fields_hidden,
$fields_readonly );
}
$fdf.= ">> \x0d"; // close dictionary
}
}
protected function forge_fdf_fields_strings( &$fdf,
&$fdf_data_strings,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly,
'',
true ); // true => strings data
}
protected function forge_fdf_fields_names( &$fdf,
&$fdf_data_names,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly,
'',
false ); // false => names data
}
}
?>
Copy link to clipboard
Copied
Ahh, I think I found the issue.. You may need to supply the URL of the PDF form that's sitting on your website, so when it responds with FDF it will load the PDF from the URL; otherwise; the FDF doesn't know what form to populate. Also; be sure to select IE or FF as the default web browser in your OS; because, it may automatically open the PDF with the default browser.
CHANGE FROM: Found in (public function make_pdf)
$fdf = $this->forge_fdf('', $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
TO:
$fdf = $this->forge_fdf('http://www.your-domain.com/path/to/pdfdoc.pdf', $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
And be sure to change 'http://www.your-domain.com/path/to/pdfdoc.pdf' to your actual URL of the blank PDF sitting on your website; and the PDF is accessible to user submitting the form.
Copy link to clipboard
Copied
I want to thank you so very much for taking the time to work with me on this. It works perfectly!
I am just learning how to deal with these pdf and fdf projects. Can I ask you how you learned all this. Did you get a formal education or just teach yourself?
One more question....in that php-pdftk.php I used to have it password encrypt the output pdf. I did this by placing "user_pw $password" ($password being the chosen password without the "$") to the line:
passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - user_pw passwordGoesHere");
However, now it does not add the password when I add that bit of code. Is there something I need to do differently to add a password or is that not possible with the changes? It is not critical, not nearly as important as what you already solved.
Thank you again.
Copy link to clipboard
Copied
According to the documentation found at the following website:
github.com/andrewheiss/pdftk-php/blob/master/pdftk-php.php
Parameters:
owner_pw (required)
user_pw (optional)
The owner_pw (modify) is required in order to set the user_pw (open).
The "owner" password encrypts the PDF and it can restrict printing, filling in fields, comments, extracting pages and modifying the document.
The "user" password sets the password for opening the document in Adobe Reader and Acrobat. It also provides an important extra layer of protection from unlocking the document using unethical procedures.
Note: Double-encrypted documents with both owner and user passwords are incapable of being decrypted using normal procedures. If both are unknown then the document can not be unlocked. If either the user or the owner passwords are known or if one is empty; then the document can be unlocked.
CHANGE THE SCRIPT TO:
<?php
//https://www.pdflabs.com/docs/pdftk-man-page/
########################################################################################## ########################
#
# pdftk-php Class
# http://code.google.com/p/pdftk-php/
# http://www.pdfhacks.com/forge_fdf/
#
# License: Released under New BSD license - http://www.opensource.org/licenses/bsd-license.php
#
# Purpose: Contains functions used to inject data from MySQL into an empty PDF form
#
# Authors: Andrew Heiss (www.andrewheiss.com), Sid Steward (http://www.oreillynet.com/pub/au/1754)
#
# History:
# 8/26/08 - Initial programming
#
# Usage:
# $pdfmaker = new pdftk_php;
#
# $fdf_data_strings = array();
# $fdf_data_names = array();
# $fields_hidden = array();
# $fields_readonly = array();
# $pdf_original = "string"; = filename of the original, empty pdf form
# $pdf_filename = "string"; = filename to be used for the output pdf
#
# $pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_filename);
#
########################################################################################## ########################
class pdftk_php {
#############################################################################
#
# Function name: makePDF
#
# Purpose: Generate an FDF file from db data, inject the FDF in an empty PDF form
#
# Incoming parameters:
# $fetched_array - one row of fetched MySQL data saved as a variable
#
# Returns: Downloaded PDF file
#
# Notes:
# * For text fields, combo boxes and list boxes, add field values as a name => value pair to $fdf_data_strings. An example of $fdf_data_strings is given in /example/download.php
# * For check boxes and radio buttons, add field values as a name => value pair to $fdf_data_names. Typically, true and false correspond to the (case sensitive) names "Yes" and "Off".
# * Any field added to the $fields_hidden or $fields_readonly array must also be a key in $fdf_data_strings or $fdf_data_names; this might be changed in the future
# * Any field listed in $fdf_data_strings or $fdf_data_names that you want hidden or read-only must have its field name added to $fields_hidden or $fields_readonly; do this even if your form has these bits set already
#
#############################################################################
public function make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename) {
//START MODIFY
//Detects if submitted from web browser (referrer) or standalone reader (no-referrer)
//Sends PDFLocation back to web browser if referrer exists, or empty URL for standalone reader users.
// MODIFY THE FOLLOWING PASSWORDS
$ownerPW = "owner";
$userPW = "password";
// SET THE URL to Empty
$pdfLocation = "";
// GET REFERRER (Stadalone = Empty)
$refererX = "".$_SERVER['HTTP_REFERER']."";
$posComma = strpos($refererX,",");
if($posComma>0){
// Fix for comma referrer
$commas=explode(",", $refererX);
$refererX="".$commas[0]."";
}
$pos1 = strpos($refererX, 'http://');
$pos2 = strpos($refererX, 'https://');
$pos3 = strpos($refererX, 'ftp://');
$posX = $pos1 + $pos2 + $pos3;
if($posX<=0){
// referrerEmpty = submitted from standalone PDF = pdflocation not needed
$pdfLocation="";
}
else{
// has referrer = submitted from Web Browser (URL)
// send back to the original PDF document which was submitted from web browser plug-in
$pdfLocation="".$refererX."";
}
$fdf = $this->forge_fdf($pdfLocation, $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
//END MODIFY
// Create the fdf file
// $fdf = $this->forge_fdf('', $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
// Save the fdf file temporarily - make sure the server has write permissions in the folder you specify in tempnam()
$fdf_fn = tempnam(".", "fdf");
$fp = fopen($fdf_fn, 'w');
if($fp) {
fwrite($fp, $fdf);
fclose($fp);
// Send a force download header to the browser with a file MIME type
// header("Content-Type: application/force-download");
// header("Content-Type: application/pdf");
// header("Content-Disposition: attachment; filename=\"$pdf_filename\"");
// Actually make the PDF by running pdftk - make sure the path to pdftk is correct
// The PDF will be output directly to the browser - apart from the original PDF file, no actual PDF wil be saved on the server.
passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - encrypt_40bit owner_pw $ownerPW user_pw $userPW allow AllFeatures");
// delete temporary fdf file
unlink( $fdf_fn );
}
else { // error
echo 'Error: unable to write temp fdf file: '. $fdf_fn;
}
} // end of make_pdf()
protected function forge_fdf($pdf_form_url, &$fdf_data_strings, &$fdf_data_names, &$fields_hidden, &$fields_readonly) {
/* forge_fdf, by Sid Steward
version 1.1
visit: www.pdfhacks.com/forge_fdf/
PDF can be particular about CR and LF characters, so I spelled them out in hex: CR == \x0d : LF == \x0a
*/
$fdf = "%FDF-1.2\x0d%\xe2\xe3\xcf\xd3\x0d\x0a"; // header
$fdf.= "1 0 obj\x0d<< "; // open the Root dictionary
$fdf.= "\x0d/FDF << "; // open the FDF dictionary
$fdf.= "/Fields [ "; // open the form Fields array
$fdf_data_strings = $this->burst_dots_into_arrays( $fdf_data_strings );
$this->forge_fdf_fields_strings( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly );
$fdf_data_names= $this->burst_dots_into_arrays( $fdf_data_names );
$this->forge_fdf_fields_names( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly );
$fdf.= "] \x0d"; // close the Fields array
// the PDF form filename or URL, if given
if( $pdf_form_url ) {
$fdf.= "/F (".$this->escape_pdf_string($pdf_form_url).") \x0d";
}
$fdf.= ">> \x0d"; // close the FDF dictionary
$fdf.= ">> \x0dendobj\x0d"; // close the Root dictionary
// trailer; note the "1 0 R" reference to "1 0 obj" above
$fdf.= "trailer\x0d<<\x0d/Root 1 0 R \x0d\x0d>>\x0d";
$fdf.= "%%EOF\x0d\x0a";
return $fdf;
}
public function escape_pdf_string( $ss ) {
$backslash= chr(0x5c);
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii})== 0x28 || // open paren
ord($ss{$ii})== 0x29 || // close paren
ord($ss{$ii})== 0x5c ) // backslash
{
$ss_esc.= $backslash.$ss{$ii}; // escape the character w/ backslash
}
else if( ord($ss{$ii}) < 32 || 126 < ord($ss{$ii}) ) {
$ss_esc.= sprintf( "\\%03o", ord($ss{$ii}) ); // use an octal code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
protected function escape_pdf_name( $ss ) {
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii}) < 33 || 126 < ord($ss{$ii}) ||
ord($ss{$ii})== 0x23 ) // hash mark
{
$ss_esc.= sprintf( "#%02x", ord($ss{$ii}) ); // use a hex code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
// In PDF, partial form field names are combined using periods to
// yield the full form field name; we'll take these dot-delimited
// names and then expand them into nested arrays, here; takes
// an array that uses dot-delimited names and returns a tree of arrays;
//
protected function burst_dots_into_arrays( &$fdf_data_old ) {
$fdf_data_new= array();
foreach( $fdf_data_old as $key => $value ) {
$key_split= explode( '.', (string)$key, 2 );
if( count($key_split)== 2 ) { // handle dot
if( !array_key_exists( (string)($key_split[0]), $fdf_data_new ) ) {
$fdf_data_new[ (string)($key_split[0]) ]= array();
}
if( gettype( $fdf_data_new[ (string)($key_split[0]) ] )!= 'array' ) {
// this new key collides with an existing name; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)($key_split[0]) ]=
array( '' => $fdf_data_new[ (string)($key_split[0]) ] );
}
$fdf_data_new[ (string)($key_split[0]) ][ (string)($key_split[1]) ]= $value;
}
else { // no dot
if( array_key_exists( (string)($key_split[0]), $fdf_data_new ) &&
gettype( $fdf_data_new[ (string)($key_split[0]) ] )== 'array' )
{ // this key collides with an existing array; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)$key ]['']= $value;
}
else { // simply copy
$fdf_data_new[ (string)$key ]= $value;
}
}
}
foreach( $fdf_data_new as $key => $value ) {
if( gettype($value)== 'array' ) {
$fdf_data_new[ (string)$key ]= $this->burst_dots_into_arrays( $value ); // recurse
}
}
return $fdf_data_new;
}
protected function forge_fdf_fields_flags( &$fdf,
$field_name,
&$fields_hidden,
&$fields_readonly ) {
if( in_array( $field_name, $fields_hidden ) )
$fdf.= "/SetF 2 "; // set
else
$fdf.= "/ClrF 2 "; // clear
if( in_array( $field_name, $fields_readonly ) )
$fdf.= "/SetFf 1 "; // set
else
$fdf.= "/ClrFf 1 "; // clear
}
protected function forge_fdf_fields( &$fdf,
&$fdf_data,
&$fields_hidden,
&$fields_readonly,
$accumulated_name,
$strings_b ) // true <==> $fdf_data contains string data
//
// string data is used for text fields, combo boxes and list boxes;
// name data is used for checkboxes and radio buttons, and
// /Yes and /Off are commonly used for true and false
{
if( 0< strlen( $accumulated_name ) ) {
$accumulated_name.= '.'; // append period seperator
}
foreach( $fdf_data as $key => $value ) {
// we use string casts to prevent numeric strings from being silently converted to numbers
$fdf.= "<< "; // open dictionary
if( gettype($value)== 'array' ) { // parent; recurse
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") "; // partial field name
$fdf.= "/Kids [ "; // open Kids array
// recurse
$this->forge_fdf_fields( $fdf,
$value,
$fields_hidden,
$fields_readonly,
$accumulated_name. (string)$key,
$strings_b );
$fdf.= "] "; // close Kids array
}
else {
// field name
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") ";
// field value
if( $strings_b ) { // string
$fdf.= "/V (".$this->escape_pdf_string( (string)$value ).") ";
}
else { // name
$fdf.= "/V /".$this->escape_pdf_name( (string)$value ). " ";
}
// field flags
$this->forge_fdf_fields_flags( $fdf,
$accumulated_name. (string)$key,
$fields_hidden,
$fields_readonly );
}
$fdf.= ">> \x0d"; // close dictionary
}
}
protected function forge_fdf_fields_strings( &$fdf,
&$fdf_data_strings,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly,
'',
true ); // true => strings data
}
protected function forge_fdf_fields_names( &$fdf,
&$fdf_data_names,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly,
'',
false ); // false => names data
}
}
?>
Let me know if this works for you.
FYI: I hold a degree in programming; but I have gained most my PDF knowledge while helping others.
Regards, Nick K.
Copy link to clipboard
Copied
I like your changes. I have tried to set the owner_pw and user_pw both together, separately and with your modified script and the original and it still is not setting a password.
For some reason, when I set that url it stopped setting a password. Does that make any sense?
Copy link to clipboard
Copied
UPDATE:
I changed the code to pass variables ($ownerPW, $userPW, $pdfLocation) in the make_pdf() method.
See comments in code below..
Let me know if this works for you.
Regards, Nick
<?php
//https://www.pdflabs.com/docs/pdftk-man-page/
########################################################################################## ########################
#
# pdftk-php Class
# http://code.google.com/p/pdftk-php/
# http://www.pdfhacks.com/forge_fdf/
#
# License: Released under New BSD license - http://www.opensource.org/licenses/bsd-license.php
#
# Purpose: Contains functions used to inject data from MySQL into an emptyempty PDF form
#
# Authors: Andrew Heiss (www.andrewheiss.com), Sid Steward (http://www.oreillynet.com/pub/au/1754)
#
# History:
# 8/26/08 - Initial programming
#
# Usage:
# $pdfmaker = new pdftk_php;
#
# $fdf_data_strings = array();
# $fdf_data_names = array();
# $fields_hidden = array();
# $fields_readonly = array();
# $pdf_original = "string"; = filename of the original, emptyempty pdf form
# $pdf_filename = "string"; = filename to be used for the output pdf
#
#
#
# // START MODIFY - Nick K.
#
# $ownerPW = "string"; = Owner (Modify) Password (Required for User Password)
# $userPW = "string"; = User (Open) Password
# $pdfLocation = "string"; Do not set (leave blank) - The script will pull the URL from the web submission referrrer or it will leave it blank for standalone reader
#
# passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - owner_pw $ownerPW user_pw $userPW allow AllFeatures");
#
# $pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_filename, $ownerPW, $userPW, $pdfLocation);
#
########################################################################################## ########################
class pdftk_php {
#############################################################################
#
# Function name: makePDF
#
# Purpose: Generate an FDF file from db data, inject the FDF in an emptyempty PDF form
#
# Incoming parameters:
# $fetched_array - one row of fetched MySQL data saved as a variable
#
# Returns: Downloaded PDF file
#
# Notes:
# * For text fields, combo boxes and list boxes, add field values as a name => value pair to $fdf_data_strings. An example of $fdf_data_strings is given in /example/download.php
# * For check boxes and radio buttons, add field values as a name => value pair to $fdf_data_names. Typically, true and false correspond to the (case sensitive) names "Yes" and "Off".
# * Any field added to the $fields_hidden or $fields_readonly array must also be a key in $fdf_data_strings or $fdf_data_names; this might be changed in the future
# * Any field listed in $fdf_data_strings or $fdf_data_names that you want hidden or read-only must have its field name added to $fields_hidden or $fields_readonly; do this even if your form has these bits set already
#
#############################################################################
public function make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename, $ownerPW, $userPW, $pdfLocation) {
//Detects if submitted from web browser (referrer) or standalone reader (no-referrer)
//Sends PDFLocation back to web browser if referrer exists, or empty URL for standalone reader users.
// GET REFERRER (Standalone = Empty)
$refererX = "".$_SERVER['HTTP_REFERER']."";
$posComma = strpos($refererX,",");
if($posComma>0){
// Fix for comma referrer
$commas=explode(",", $refererX);
$refererX="".$commas[0]."";
}
$pos1 = strpos($refererX, 'http://');
$pos2 = strpos($refererX, 'https://');
$pos3 = strpos($refererX, 'ftp://');
$posX = $pos1 + $pos2 + $pos3;
if($posX<=0){
// referrerEmpty = submitted from standalone PDF = pdflocation not needed
$pdfLocation="";
}
else{
// has referrer = submitted from Web Browser (URL)
// send back to the original PDF document which was submitted from web browser plug-in
$pdfLocation="".$refererX."";
}
$fdf = $this->forge_fdf($pdfLocation, $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
// Create the fdf file
// $fdf = $this->forge_fdf('', $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
// Save the fdf file temporarily - make sure the server has write permissions in the folder you specify in tempnam()
$fdf_fn = tempnam(".", "fdf");
$fp = fopen($fdf_fn, 'w');
if($fp) {
fwrite($fp, $fdf);
fclose($fp);
// Send a force download header to the browser with a file MIME type
// header("Content-Type: application/force-download");
// header("Content-Type: application/pdf");
// header("Content-Disposition: attachment; filename=\"$pdf_filename\"");
// Nick K - The PDF will be output directly to the browser - apart from the original PDF file, no actual PDF wil be saved on the server.
// Nick K - Remove 40-bit encryption so it defaults to 128-bit
// Actually make the PDF by running pdftk - make sure the path to pdftk is correct
passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - owner_pw $ownerPW user_pw $userPW allow AllFeatures");
// END MODIFY - Nick K.
// delete temporary fdf file
unlink( $fdf_fn );
}
else { // error
echo 'Error: unable to write temp fdf file: '. $fdf_fn;
}
} // end of make_pdf()
protected function forge_fdf($pdf_form_url, &$fdf_data_strings, &$fdf_data_names, &$fields_hidden, &$fields_readonly) {
/* forge_fdf, by Sid Steward
version 1.1
visit: www.pdfhacks.com/forge_fdf/
PDF can be particular about CR and LF characters, so I spelled them out in hex: CR == \x0d : LF == \x0a
*/
$fdf = "%FDF-1.2\x0d%\xe2\xe3\xcf\xd3\x0d\x0a"; // header
$fdf.= "1 0 obj\x0d<< "; // open the Root dictionary
$fdf.= "\x0d/FDF << "; // open the FDF dictionary
$fdf.= "/Fields [ "; // open the form Fields array
$fdf_data_strings = $this->burst_dots_into_arrays( $fdf_data_strings );
$this->forge_fdf_fields_strings( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly );
$fdf_data_names= $this->burst_dots_into_arrays( $fdf_data_names );
$this->forge_fdf_fields_names( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly );
$fdf.= "] \x0d"; // close the Fields array
// the PDF form filename or URL, if given
if( $pdf_form_url ) {
$fdf.= "/F (".$this->escape_pdf_string($pdf_form_url).") \x0d";
}
$fdf.= ">> \x0d"; // close the FDF dictionary
$fdf.= ">> \x0dendobj\x0d"; // close the Root dictionary
// trailer; note the "1 0 R" reference to "1 0 obj" above
$fdf.= "trailer\x0d<<\x0d/Root 1 0 R \x0d\x0d>>\x0d";
$fdf.= "%%EOF\x0d\x0a";
return $fdf;
}
public function escape_pdf_string( $ss ) {
$backslash= chr(0x5c);
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii})== 0x28 || // open paren
ord($ss{$ii})== 0x29 || // close paren
ord($ss{$ii})== 0x5c ) // backslash
{
$ss_esc.= $backslash.$ss{$ii}; // escape the character w/ backslash
}
else if( ord($ss{$ii}) < 32 || 126 < ord($ss{$ii}) ) {
$ss_esc.= sprintf( "\\%03o", ord($ss{$ii}) ); // use an octal code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
protected function escape_pdf_name( $ss ) {
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii}) < 33 || 126 < ord($ss{$ii}) ||
ord($ss{$ii})== 0x23 ) // hash mark
{
$ss_esc.= sprintf( "#%02x", ord($ss{$ii}) ); // use a hex code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
// In PDF, partial form field names are combined using periods to
// yield the full form field name; we'll take these dot-delimited
// names and then expand them into nested arrays, here; takes
// an array that uses dot-delimited names and returns a tree of arrays;
//
protected function burst_dots_into_arrays( &$fdf_data_old ) {
$fdf_data_new= array();
foreach( $fdf_data_old as $key => $value ) {
$key_split= explode( '.', (string)$key, 2 );
if( count($key_split)== 2 ) { // handle dot
if( !array_key_exists( (string)($key_split[0]), $fdf_data_new ) ) {
$fdf_data_new[ (string)($key_split[0]) ]= array();
}
if( gettype( $fdf_data_new[ (string)($key_split[0]) ] )!= 'array' ) {
// this new key collides with an existing name; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)($key_split[0]) ]=
array( '' => $fdf_data_new[ (string)($key_split[0]) ] );
}
$fdf_data_new[ (string)($key_split[0]) ][ (string)($key_split[1]) ]= $value;
}
else { // no dot
if( array_key_exists( (string)($key_split[0]), $fdf_data_new ) &&
gettype( $fdf_data_new[ (string)($key_split[0]) ] )== 'array' )
{ // this key collides with an existing array; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)$key ]['']= $value;
}
else { // simply copy
$fdf_data_new[ (string)$key ]= $value;
}
}
}
foreach( $fdf_data_new as $key => $value ) {
if( gettype($value)== 'array' ) {
$fdf_data_new[ (string)$key ]= $this->burst_dots_into_arrays( $value ); // recurse
}
}
return $fdf_data_new;
}
protected function forge_fdf_fields_flags( &$fdf,
$field_name,
&$fields_hidden,
&$fields_readonly ) {
if( in_array( $field_name, $fields_hidden ) )
$fdf.= "/SetF 2 "; // set
else
$fdf.= "/ClrF 2 "; // clear
if( in_array( $field_name, $fields_readonly ) )
$fdf.= "/SetFf 1 "; // set
else
$fdf.= "/ClrFf 1 "; // clear
}
protected function forge_fdf_fields( &$fdf,
&$fdf_data,
&$fields_hidden,
&$fields_readonly,
$accumulated_name,
$strings_b ) // true <==> $fdf_data contains string data
//
// string data is used for text fields, combo boxes and list boxes;
// name data is used for checkboxes and radio buttons, and
// /Yes and /Off are commonly used for true and false
{
if( 0< strlen( $accumulated_name ) ) {
$accumulated_name.= '.'; // append period seperator
}
foreach( $fdf_data as $key => $value ) {
// we use string casts to prevent numeric strings from being silently converted to numbers
$fdf.= "<< "; // open dictionary
if( gettype($value)== 'array' ) { // parent; recurse
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") "; // partial field name
$fdf.= "/Kids [ "; // open Kids array
// recurse
$this->forge_fdf_fields( $fdf,
$value,
$fields_hidden,
$fields_readonly,
$accumulated_name. (string)$key,
$strings_b );
$fdf.= "] "; // close Kids array
}
else {
// field name
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") ";
// field value
if( $strings_b ) { // string
$fdf.= "/V (".$this->escape_pdf_string( (string)$value ).") ";
}
else { // name
$fdf.= "/V /".$this->escape_pdf_name( (string)$value ). " ";
}
// field flags
$this->forge_fdf_fields_flags( $fdf,
$accumulated_name. (string)$key,
$fields_hidden,
$fields_readonly );
}
$fdf.= ">> \x0d"; // close dictionary
}
}
protected function forge_fdf_fields_strings( &$fdf,
&$fdf_data_strings,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly,
'',
true ); // true => strings data
}
protected function forge_fdf_fields_names( &$fdf,
&$fdf_data_names,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly,
'',
false ); // false => names data
}
}
?>
Copy link to clipboard
Copied
The page you modified is included in another page. I should have mentioned that earlier but all previous changes worked seamlessly with the parent page. The most recent change does not...there are errors thrown from line 62 of the below script (the other page).
Can you tell me how I need to modify the other page to see if I can get them to work?
On another note..when I tried to revert back to the previous change that had a working submit button I cannot get it to work again. Still working on that. The "big picture" idea I have is that I have a html form that submits data to the php-pdftk.php. Then the pdftk populates the fillable pdf (which is has some fillable fields and digital signature fields (so flattening the pdf is no good). Then the pdf submits to another php page that saves the entire pdf to the server. Am I making this way to hard or does this seem to be a good way to go?
<?php
require('../../../inc/pdftk-php.php');
include('../../../inc/php_functions.php');
// Initiate the class
$pdfmaker = new pdftk_php;
// Define variables for all the data fields in the PDF form. You need to assign a column in the database to each field that you'll be using in the PDF.
// Example:
// $pdf_column = $data['column'];
// You can also format the MySQL data how you want here. One common example is formatting a date saved in the database. For example:
// $pdf_date = date("l, F j, Y, g:i a", strtotime($data['date']));
$pdf_full_name = $_POST['full_name'];
$pdf_form_date = $_POST['form_date'];
// $fdf_data_strings associates the names of the PDF form fields to the PHP variables you just set above. In order to work correctly the PDF form field name has to be exact. PDFs made in Acrobat generally have simpler names - just the name you assigned to the field. PDFs made in LiveCycle Designer nest their forms in other random page elements, creating a long and hairy field name. You can use pdftk to discover the real names of your PDF form fields: run "pdftk form.pdf dump_data_fields > form-fields.txt" to generate a report.
// Example of field names from a PDF created in LiveCycle:
// $fdf_data_strings= array('form1[0].#subform[0].#area[0].LastName[0]' => $pdf_lastname, 'form1[0].#subform[0].#area[0].FirstName[0]' => $pdf_firstname, 'form1[0].#subform[0].#area[0].EMail[0]' => $pdf_email, );
$fdf_data_strings= array(
'full_name' => $pdf_full_name,
'form_date' => $pdf_form_date,
);
// See the documentation of pdftk-php.php for more explanation of these other variables.
// Used for radio buttons and check boxes
// Example: (For check boxes options are Yes and Off)
// $pdf_checkbox1 = "Yes";
// $pdf_checkbox2 = "Off";
// $pdf_checkbox3 = "Yes";
// $fdf_data_names = array('checkbox1' => $pdf_checkbox1,'checkbox2' => $pdf_checkbox2,'checkbox3' => $pdf_checkbox3,'checkbox4' => $pdf_checkbox4);
//$pdf_option1 = "Off";
//$fdf_data_names = array('checkbox' => $pdf_option1);
$fdf_data_names = array(); // Leave empty if there are no radio buttons or check boxes
$fields_hidden = array(); // Used to hide form fields
$fields_readonly = array(); // Used to make fields read only - however, flattening the output with pdftk will in effect make every field read only. If you don't want a flattened pdf and still want some read only fields, use this variable and remove the flatten flag near line 70 in pdftk-php.php
// Take each REQUEST value and clean it up for fdf creation
foreach( $_REQUEST as $key => $value ) {
// Translate tildes back to periods
$fdf_data_strings[strtr($key, '~', '.')]= $value;
}
// Name of file to be downloaded
$pdf_filename = $pdf_full_name. ".pdf";
// Name/location of original, empty PDF form
$pdf_x = "form.pdf";
$pdf_original = $pdf_x;
// Finally make the actual PDF file!
// $pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename);
$pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename);
echo $pdfmaker;
// The end!
?>
Copy link to clipboard
Copied
Change From:
$pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename);
Change TO: (It was missing the $pdf_original variable)
$ownerPW = "owner"; // Owner (Modify) Password
$userPW = "password"; // User (Open) Password
$pdfLocation = ""; //Do not set (leave blank)
$pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename, $ownerPW, $userPW, $pdfLocation);
Copy link to clipboard
Copied
I got this error:
Catchable fatal error: Object of class pdftk_php could not be converted to string in C:\...........php on line 68.
Which is "echo $pdfmaker;"
Copy link to clipboard
Copied
Change:
echo $pdfmaker
To:
// echo $pdfmaker
Because:
The following line should automatically output the PDF with the "output - " parameter:
passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - owner_pw $ownerPW user_pw $userPW allow AllFeatures");
Copy link to clipboard
Copied
That cured the error but now it just prints gibberish. Below is a bit of it:
%PDF-1.6 %���� 1 0 obj << /Matrix [1 0 0 1 0 0] /Subtype /Form /Filter /FlateDecode /Length 18 /Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI] >> /FormType 1 /BBox [0 0 37.68 12.24] /Type /XObject >> stream x�� �Pp�uVp b � endstream endobj 2 0 obj << /Matrix [1 0 0 1 0 0] /Subtype /Form /Filter /FlateDecode /Length 18 /Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI] >> /FormType 1 /BBox [0 0 96.6 12.36] /Type /XObject >> stream x�� �Pp�uVp b � endstream endobj 3 0 obj << /Matrix [1 0 0 1 0 0] /Subtype /Form /Filter /FlateDecode /Length 18 /Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI] >> /FormType 1 /BBox [0 0 97.8 12.36] /Type /XObject >>
I messaged you on another note.
Copy link to clipboard
Copied
Put the following at the top of the script that outputs the PDF; and make sure the script only outputs the PDF.
<?php
header('Content-type: application/pdf');
Copy link to clipboard
Copied
Here's the old script you said worked before I added the variables:
<?php
//https://www.pdflabs.com/docs/pdftk-man-page/
########################################################################################## ########################
#
# pdftk-php Class
# http://code.google.com/p/pdftk-php/
# http://www.pdfhacks.com/forge_fdf/
#
# License: Released under New BSD license - http://www.opensource.org/licenses/bsd-license.php
#
# Purpose: Contains functions used to inject data from MySQL into an emptyempty PDF form
#
# Authors: Andrew Heiss (www.andrewheiss.com), Sid Steward (http://www.oreillynet.com/pub/au/1754)
#
# History:
# 8/26/08 - Initial programming
#
# Usage:
# $pdfmaker = new pdftk_php;
#
# $fdf_data_strings = array();
# $fdf_data_names = array();
# $fields_hidden = array();
# $fields_readonly = array();
# $pdf_original = "string"; = filename of the original, emptyempty pdf form
# $pdf_filename = "string"; = filename to be used for the output pdf
#
# EDITED BY Nick K.
# $ownerPW = "string"; = Owner (Modify) Password (Required for User Password)
# $userPW = "string"; = User (Open) Password
# $pdfLocation = "string"; Do not set (leave blank) - The script will pull the URL from the web submission referrrer or it will leave it blank for standalone reader
# Defaults to 128-bit encryption - Adds features only available in 128-bit: High Resolution printing and others
# passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - owner_pw $ownerPW user_pw $userPW allow AllFeatures");
#
# $pdfmaker->make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_filename);
#
########################################################################################## ########################
class pdftk_php {
#############################################################################
#
# Function name: makePDF
#
# Purpose: Generate an FDF file from db data, inject the FDF in an emptyempty PDF form
#
# Incoming parameters:
# $fetched_array - one row of fetched MySQL data saved as a variable
#
# Returns: Downloaded PDF file
#
# Notes:
# * For text fields, combo boxes and list boxes, add field values as a name => value pair to $fdf_data_strings. An example of $fdf_data_strings is given in /example/download.php
# * For check boxes and radio buttons, add field values as a name => value pair to $fdf_data_names. Typically, true and false correspond to the (case sensitive) names "Yes" and "Off".
# * Any field added to the $fields_hidden or $fields_readonly array must also be a key in $fdf_data_strings or $fdf_data_names; this might be changed in the future
# * Any field listed in $fdf_data_strings or $fdf_data_names that you want hidden or read-only must have its field name added to $fields_hidden or $fields_readonly; do this even if your form has these bits set already
#
#############################################################################
public function make_pdf($fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly, $pdf_original, $pdf_filename) {
//START MODIFY
//Detects if submitted from web browser (referrer) or standalone reader (no-referrer)
//Sends PDFLocation back to web browser if referrer exists, or empty URL for standalone reader users.
// MODIFY THE FOLLOWING PASSWORDS
$ownerPW = "owner";
$userPW = "password";
// SET THE URL to Empty
$pdfLocation = "";
// GET REFERRER (Stadalone = Empty)
$refererX = "".$_SERVER['HTTP_REFERER']."";
$posComma = strpos($refererX,",");
if($posComma>0){
// Fix for comma referrer
$commas=explode(",", $refererX);
$refererX="".$commas[0]."";
}
$pos1 = strpos($refererX, 'http://');
$pos2 = strpos($refererX, 'https://');
$pos3 = strpos($refererX, 'ftp://');
$posX = $pos1 + $pos2 + $pos3;
if($posX<=0){
// referrerEmpty = submitted from standalone PDF = pdflocation not needed
$pdfLocation="";
}
else{
// has referrer = submitted from Web Browser (URL)
// send back to the original PDF document which was submitted from web browser plug-in
$pdfLocation="".$refererX."";
}
$fdf = $this->forge_fdf($pdfLocation, $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
//END MODIFY
// Create the fdf file
// $fdf = $this->forge_fdf('', $fdf_data_strings, $fdf_data_names, $fields_hidden, $fields_readonly);
// Save the fdf file temporarily - make sure the server has write permissions in the folder you specify in tempnam()
$fdf_fn = tempnam(".", "fdf");
$fp = fopen($fdf_fn, 'w');
if($fp) {
fwrite($fp, $fdf);
fclose($fp);
// Send a force download header to the browser with a file MIME type
// header("Content-Type: application/force-download");
// header("Content-Type: application/pdf");
// header("Content-Disposition: attachment; filename=\"$pdf_filename\"");
// Actually make the PDF by running pdftk - make sure the path to pdftk is correct
// The PDF will be output directly to the browser - apart from the original PDF file, no actual PDF wil be saved on the server.
// Remove 40-bit encryption so it defaults to 128-bit
passthru("C:\PDFtk\bin\pdftk.exe $pdf_original fill_form $fdf_fn output - owner_pw $ownerPW user_pw $userPW allow AllFeatures");
// delete temporary fdf file
unlink( $fdf_fn );
}
else { // error
echo 'Error: unable to write temp fdf file: '. $fdf_fn;
}
} // end of make_pdf()
protected function forge_fdf($pdf_form_url, &$fdf_data_strings, &$fdf_data_names, &$fields_hidden, &$fields_readonly) {
/* forge_fdf, by Sid Steward
version 1.1
visit: www.pdfhacks.com/forge_fdf/
PDF can be particular about CR and LF characters, so I spelled them out in hex: CR == \x0d : LF == \x0a
*/
$fdf = "%FDF-1.2\x0d%\xe2\xe3\xcf\xd3\x0d\x0a"; // header
$fdf.= "1 0 obj\x0d<< "; // open the Root dictionary
$fdf.= "\x0d/FDF << "; // open the FDF dictionary
$fdf.= "/Fields [ "; // open the form Fields array
$fdf_data_strings = $this->burst_dots_into_arrays( $fdf_data_strings );
$this->forge_fdf_fields_strings( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly );
$fdf_data_names= $this->burst_dots_into_arrays( $fdf_data_names );
$this->forge_fdf_fields_names( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly );
$fdf.= "] \x0d"; // close the Fields array
// the PDF form filename or URL, if given
if( $pdf_form_url ) {
$fdf.= "/F (".$this->escape_pdf_string($pdf_form_url).") \x0d";
}
$fdf.= ">> \x0d"; // close the FDF dictionary
$fdf.= ">> \x0dendobj\x0d"; // close the Root dictionary
// trailer; note the "1 0 R" reference to "1 0 obj" above
$fdf.= "trailer\x0d<<\x0d/Root 1 0 R \x0d\x0d>>\x0d";
$fdf.= "%%EOF\x0d\x0a";
return $fdf;
}
public function escape_pdf_string( $ss ) {
$backslash= chr(0x5c);
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii})== 0x28 || // open paren
ord($ss{$ii})== 0x29 || // close paren
ord($ss{$ii})== 0x5c ) // backslash
{
$ss_esc.= $backslash.$ss{$ii}; // escape the character w/ backslash
}
else if( ord($ss{$ii}) < 32 || 126 < ord($ss{$ii}) ) {
$ss_esc.= sprintf( "\\%03o", ord($ss{$ii}) ); // use an octal code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
protected function escape_pdf_name( $ss ) {
$ss_esc= '';
$ss_len= strlen( $ss );
for( $ii= 0; $ii< $ss_len; ++$ii ) {
if( ord($ss{$ii}) < 33 || 126 < ord($ss{$ii}) ||
ord($ss{$ii})== 0x23 ) // hash mark
{
$ss_esc.= sprintf( "#%02x", ord($ss{$ii}) ); // use a hex code
}
else {
$ss_esc.= $ss{$ii};
}
}
return $ss_esc;
}
// In PDF, partial form field names are combined using periods to
// yield the full form field name; we'll take these dot-delimited
// names and then expand them into nested arrays, here; takes
// an array that uses dot-delimited names and returns a tree of arrays;
//
protected function burst_dots_into_arrays( &$fdf_data_old ) {
$fdf_data_new= array();
foreach( $fdf_data_old as $key => $value ) {
$key_split= explode( '.', (string)$key, 2 );
if( count($key_split)== 2 ) { // handle dot
if( !array_key_exists( (string)($key_split[0]), $fdf_data_new ) ) {
$fdf_data_new[ (string)($key_split[0]) ]= array();
}
if( gettype( $fdf_data_new[ (string)($key_split[0]) ] )!= 'array' ) {
// this new key collides with an existing name; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)($key_split[0]) ]=
array( '' => $fdf_data_new[ (string)($key_split[0]) ] );
}
$fdf_data_new[ (string)($key_split[0]) ][ (string)($key_split[1]) ]= $value;
}
else { // no dot
if( array_key_exists( (string)($key_split[0]), $fdf_data_new ) &&
gettype( $fdf_data_new[ (string)($key_split[0]) ] )== 'array' )
{ // this key collides with an existing array; this shouldn't happen;
// associate string value with the special empty key in array, anyhow;
$fdf_data_new[ (string)$key ]['']= $value;
}
else { // simply copy
$fdf_data_new[ (string)$key ]= $value;
}
}
}
foreach( $fdf_data_new as $key => $value ) {
if( gettype($value)== 'array' ) {
$fdf_data_new[ (string)$key ]= $this->burst_dots_into_arrays( $value ); // recurse
}
}
return $fdf_data_new;
}
protected function forge_fdf_fields_flags( &$fdf,
$field_name,
&$fields_hidden,
&$fields_readonly ) {
if( in_array( $field_name, $fields_hidden ) )
$fdf.= "/SetF 2 "; // set
else
$fdf.= "/ClrF 2 "; // clear
if( in_array( $field_name, $fields_readonly ) )
$fdf.= "/SetFf 1 "; // set
else
$fdf.= "/ClrFf 1 "; // clear
}
protected function forge_fdf_fields( &$fdf,
&$fdf_data,
&$fields_hidden,
&$fields_readonly,
$accumulated_name,
$strings_b ) // true <==> $fdf_data contains string data
//
// string data is used for text fields, combo boxes and list boxes;
// name data is used for checkboxes and radio buttons, and
// /Yes and /Off are commonly used for true and false
{
if( 0< strlen( $accumulated_name ) ) {
$accumulated_name.= '.'; // append period seperator
}
foreach( $fdf_data as $key => $value ) {
// we use string casts to prevent numeric strings from being silently converted to numbers
$fdf.= "<< "; // open dictionary
if( gettype($value)== 'array' ) { // parent; recurse
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") "; // partial field name
$fdf.= "/Kids [ "; // open Kids array
// recurse
$this->forge_fdf_fields( $fdf,
$value,
$fields_hidden,
$fields_readonly,
$accumulated_name. (string)$key,
$strings_b );
$fdf.= "] "; // close Kids array
}
else {
// field name
$fdf.= "/T (".$this->escape_pdf_string( (string)$key ).") ";
// field value
if( $strings_b ) { // string
$fdf.= "/V (".$this->escape_pdf_string( (string)$value ).") ";
}
else { // name
$fdf.= "/V /".$this->escape_pdf_name( (string)$value ). " ";
}
// field flags
$this->forge_fdf_fields_flags( $fdf,
$accumulated_name. (string)$key,
$fields_hidden,
$fields_readonly );
}
$fdf.= ">> \x0d"; // close dictionary
}
}
protected function forge_fdf_fields_strings( &$fdf,
&$fdf_data_strings,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_strings,
$fields_hidden,
$fields_readonly,
'',
true ); // true => strings data
}
protected function forge_fdf_fields_names( &$fdf,
&$fdf_data_names,
&$fields_hidden,
&$fields_readonly ) {
return
$this->forge_fdf_fields( $fdf,
$fdf_data_names,
$fields_hidden,
$fields_readonly,
'',
false ); // false => names data
}
}
?>

