Link to home
Start Free TrialLog in
Avatar of Joseph Longo
Joseph Longo

asked on

PHP: Filling Out/Creating a PDF

I am not sure how to accomplish this...

I have been tasked with automating my employer's car seat rental form. Is PHP capable of filling out a predesigned PDF that was created in Adobe? Or is it easier to create/generate a PDF file at run-time?

Either way, the PDF will need to be emailed to a few different people and the renter's information will need to be stored in a MySQL database.
SOLUTION
Avatar of Peos John
Peos John
Flag of Malaysia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Joseph Longo
Joseph Longo

ASKER

Here is an example of the rental form:
User generated image
I plan on using a PHP form and a MySQL database. Should I use HTML tables to recreate the form, and then have PHP plug-in the information from the database, after storing it? I kind of wanted it to be in a PDF format, so the renter cannot modify the rental agreement. Is it possibly to store the user's information, create the form using HTML tables, email the appropriate people and then echo the form out to the browser all in the same click? Or should I have the script store the user's information in one click, create the rental agreement with another button and then email the appropriate people with a third button?
Thanks for the posting that, Since you want to store the renter's information in the database.

1.  You can create a php form where the renter can fill in all the details that you have show in the screen sample form.

For exp: Name, Address, Phone number etc.

2.  When you send this form link to this renter you can also send the PDF file(Rental Agreement). This can be manually signed and uploaded in the same form link you are sending.

3. So the php form you are sending will have 'browse' button to upload the rental agreement.

4. Usually when the files are uploaded using the forms, we upload the files in any folder, and store file path in the database.

All this can be done on a single php form.
The user won't have much interaction with the form, other than receiving a copy of it, after it's been completed. The form will be filled out by the receptionist, and then emailed to herself, the renter and the fiscal department.

Same process as you mentioned or would things change?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
O.K. got it.  

It will be the same process, so your php form will have will have only the browse button to upload the PDF file.

Additionally, if you can keep any unique value (Something like Email address of renter, invoice number) to know who has uploaded the file.

Here is the sample code:

Database connection:
<?php
$servername = "hostname";
$username = "username";
$password = "password";
$dbname = "your_databasename";

// Create connection
$conn = mysqli_connect($servername, $username, $password, $dbname);
// Check connection
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}


?>

Open in new window



HTML form where you have the browse button to upload the file

<h2>Upload Rental Agreement(PDF) :</h2>


<form enctype="multipart/form-data" action="<?php print $_SERVER['PHP_SELF']?>" method="post">


<p>

Renter's Email : <input 	type="text" name="email" /><br /><br />

Inovice Number : <input 	type="text" name="invoice_number" /><br /><br />


<input type="hidden" name="MAX_FILE_SIZE" value="200000" /> 
<input 	type="file" name="pdfFile" /><br /><br />


<br />
<input type="submit" value="Click here to upload" /></p>
</form>

Open in new window


Code to upload the PDF file and Insert the information in Mysql table

<?php
if ( isset( $_FILES['pdfFile'] ) ) 
{
	if ($_FILES['pdfFile']['type'] == "application/pdf")
	{
		$source_file = $_FILES['pdfFile']['tmp_name'];
		$dest_file = "upload/".$_FILES['pdfFile']['name'];//YOUR UPLOAD FOLDER

		if (file_exists($dest_file)) 
		{
			print "The file name already exists!!";
		}
		else 
		{
			move_uploaded_file( $source_file, $dest_file )
			or die ("Error!!");
			if($_FILES['pdfFile']['error'] == 0) {
				print "Pdf file uploaded successfully!";
				print "<b><u>Details : </u></b><br/>";
				print "File Name : ".$_FILES['pdfFile']['name']."<br.>"."<br/>";
				print "File Size : ".$_FILES['pdfFile']['size']." bytes"."<br/>";
				print "File location : upload/".$_FILES['pdfFile']['name']."<br/>";
				
				$pdf_file_name = $_FILES['pdfFile']['name'];
				
				
				//INSERT FILE INFORMATION TO DATABASE				
 				$sql = "INSERT INTO tbl_rental_agreement(email,invoice_number,rental_agreement) VALUES ('".$_POST['email']."', '".$_POST['invoice_number']."', '".$pdf_file_name."')";

				if (mysqli_query($conn, $sql)) {
				echo "Record inserted to DB";
				} else {
				echo "Error: " . $sql . "<br>" . mysqli_error($conn);
				}

				mysqli_close($conn);	
				
				
			}
		}
	}
	else 
	{
		if ( $_FILES['pdfFile']['type'] != "application/pdf") 
		{
			print "Error occured while uploading file : ".$_FILES['pdfFile']['name']."<br/>";
			print "Invalid  file extension, should be pdf !!"."<br/>";
			print "Error Code : ".$_FILES['pdfFile']['error']."<br/>";
		}
	}
}
?>

Open in new window


Mysql Table to store the detail with file name

CREATE TABLE `tbl_rental_agreement` (
  `id` int(11) NOT NULL,
  `email` varchar(250) NOT NULL,
  `invoice_number` varchar(100) NOT NULL,
  `rental_agreement` text NOT NULL
)

Open in new window



Once the file is uploaded you access the file using folder_path/file_name.pdf

And the form will look something like this

User generated image
Please, folks!  Don't keep telling PHP learners to use die() and especially not with meaningless error messages.  It's 2017, and we know better today!  Here is how it's done in the modern millennium.
https://www.experts-exchange.com/articles/29115/PHP-Error-Handling-Never-Say-die-Again.html

As to the part about getting a PDF uploaded - that may be possible, but the implementation detail that is going to be the central issue will be the process of getting client information into the PDF.  In my experience with FPDF and TCPDF it was easier to build the PDF entirely, rather than try to upload a pre-existing PDF and then try to put data into it.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Julian,

This process might be simple to you, but it is foreign to me. Can you describe the process a little more in-depth? Would I create the blank PDF form in Adobe, first without any information/data on the form? Import the PDF into a graphics program to find the offset entry field? How would one find out the offsets?

Do you have an example program?

Peos John,

I appreciate your sample program, but it's not quite the process I am looking for. I will upload a pic of my HTML form in a little while.
So, here is a VERY simple HTML form:
User generated image
Upon clicking submit, the data will be "processed" and sent to a MySQL database. After the data has been inserted into the database, the page will refresh and a "generate rental agreement" button will appear. Upon clicking the "generate rental agreement", the php script will obtain the necessary information from the MySQL database, such as "invoice number", etc.

The finished PDF file/product will look like this:
User generated image
I am like 85% confident that I know how to make EVERYTHING work, except creating the PDF file with the tables as seen in my embedded image. If there are things I am not thinking about or am overcomplicating this process, please let me know.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
When you get into it you will find it is really simple :) Unfortunately, I am working on a project that is very similar to this and I am under an NDA so I can only provide general guidance.

Would I create the blank PDF form in Adobe, first without any information/data on the form?
Yes - blank form => PDF

How would one find out the offsets?
This is a bit more complicated - you need find this out by trial and error but once you have the factor for one field the rest follow.

The FPDF library is perfectly adequate for this. You load the PDF (setSourceFile), import the page (importPage), set the template size and orientation, useTemplate to use the above.

Then use Text() to output your text to the desired locations (which can come from the DB) - repeat for all fields then output the result with Output()

There are options to change fonts, colours and sizes but you can find those in the FPDF API
Here's a PDF I made using your uploaded form.  Obviously I had to overwrite the existing text in the document, so I did that in red to make it stand out.
temp_pdf.pdf
Here's the script I used to make the PDF.  Most of the constants, such as image resize values and X:Y coordinates were arrived at through trial and error.  The PNG image we started with is too big to fit into a standard Letter-size Landscape document, and FPDF did not like its transparency, so I resized it and sharpened it a little bit to make it fit and look as good as possible.  You could store a smaller image and dispense with this part of the exercise.

I found explicit placement of the text to be the most dependable and straightforward way to get good positioning.

Just an opinion, but I think FPDF is getting a little "long in the tooth" as we say about horses.  You might find TCPDF to be more actively maintained and better documented. Similar principles will apply.
https://github.com/tecnickcom/TCPDF

HTH, ~Ray
<?php // demo/temp_joseph_longo.php
/**
 * https://www.experts-exchange.com/questions/29010988/PHP-Filling-Out-Creating-a-PDF.html#a42063532
 *
 * Demonstrate the basics of an HTML form to populate information in a PDF-image with FPDF
 *
 * http://www.fpdf.org/
 * http://www.fpdf.org/en/doc/index.php
 * http://www.fpdf.org/en/doc/image.htm
 */
error_reporting(E_ALL);

// THE URL OF THE BLANK FORM
$url = 'https://filedb.experts-exchange.com/incoming/2017/03_w12/1152824/Rental-Agreement-Form.png';

// A PLACE TO STORE THE INTERMEDIATE IMAGE
$png = 'storage/temp_joseph_longo.png';

// DETERMINE THE DESIRED SIZE OF THE IMAGE IN THE DOCUMENT (TRIAL AND ERROR)
$img = imageCreateFromPNG($url);
if (!$img) trigger_error("Unable to read $url", E_USER_ERROR);
$img_w = imagesX($img);
$new_w = $img_w / 2.8;
$img_h = imagesY($img);
$new_h = $img_h / 2.8;

// MAKE A NEW IMAGE TO RESIZE AND REMOVE THE ALPHA-TRANSPARENCY CHANNEL
$new = ImageCreateTrueColor($new_w, $new_h);
ImageCopyResampled
( $new        // DESTINATION IMAGE RESOURCE
, $img        // SOURCE IMAGE RESOURCE
, 0           // DESTINATION X
, 0           // DESTINATION Y
, 0           // SOURCE X
, 0           // SOURCE Y
, $new_w      // DESTINATION WIDE
, $new_h      // DESTINATION HIGH
, $img_w      // SOURCE WIDE
, $img_h      // SOURCE HIGH
)
;

// SHARPEN THE NEW IMAGE
$sharpenMatrix = array
( array( -1.2, -1.0, -1.2 )
, array( -1.0, 20.0, -1.0 )
, array( -1.2, -1.0, -1.2 )
)
;
$divisor = array_sum(array_map('array_sum', $sharpenMatrix));
$offset  = 0;
imageConvolution($new, $sharpenMatrix, $divisor, $offset);

// CREATE THE NEW IMAGE THAT WILL BE USED IN THE PDF
imagePNG($new, $png);
imageDestroy($img);
imageDestroy($new);


// IF THERE IS A POST-METHOD REQUEST
if (!empty($_POST))
{
    // GET THE EXTERNAL REQUEST VARS FROM THE HTML FORM
    $rn = !empty($_POST['rn']) ? substr(trim($_POST['rn']),0,32) : 'NO NAME';
    $an = !empty($_POST['an']) ? substr(trim($_POST['an']),0,32) : 'NO ADDRESS';

    // LOAD THE FPDF CLASSES
    require_once('fpdf16/fpdf.php');

    // SYNTHESIZE THE PDF FILE INFORMATION
    $pdf_file_link
    = 'storage'
    . DIRECTORY_SEPARATOR
    . 'temp_pdf'
    . '.pdf'
    ;
    $pdf_file_name
    = getcwd()
    . DIRECTORY_SEPARATOR
    . $pdf_file_link
    ;

    // USE THIS FONT FOR THE PDF
    $font = 'Arial';

    // DO THE WRITING IN RED TO MAKE IT OBVIOUS
    $pdf = new FPDF();
    $pdf->SetMargins(0, 0);
    $pdf->AddPage('L', 'Letter');
    $pdf->SetFont($font, 'B', 18);
    $pdf->SetTextColor(0xCC, 0x00, 0x00);
    $pdf->Image($png, 0, 0);

    // SET THE THE RENTER'S NAME ON THE DOCUMENT
    $x = 50;
    $y = 30;
    $pdf->SetXY($x, $y);
    $pdf->Write(0, $rn);

    // SET THE THE RENTER'S ADDRESS ON THE DOCUMENT
    $x = 50;
    $y = 38;
    $pdf->SetXY($x, $y);
    $pdf->Write(0, $an);

    // WRITE THE PDF TO DISK
    $pdf->Output($pdf_file_name, 'F');

    // PRESENT A LINK
    echo '<a target="my_PDF" href="' . $pdf_file_link . '">See the PDF with ' . $rn . '</a>';
}


// PUT UP A FORM TO CAPTURE CLIENT INPUT
$form = <<<EOF
<form method="post">
What do you want in the PDF?
<br>
<input name="rn" placeholder="Renter Name" />
<br>
<input name="an" placeholder="Renter Address" />
<br>
<input type="submit"  value="make PDF" />
</form>
EOF;

echo $form;

Open in new window

Ray Paseur,

Here is the blank PDF I intend on using. Some fields, such as security deposit and total, will be auto-calculated, depending on the amount of car seats and the term(s) the renter intends on renting the car seat(s) for. One feature I like about Adobe is that it has an "auto" size field for the font. Is it possible to accomplish this with FPDF?

Rental-Agreement-Form-without.pdf

Is it possible to "check" the check boxes, too?
I don't think there will be any auto-size in FPDF.  And it would be easier, I think, to start with an image instead of a PDF (maybe Julian has an example that starts with a PDF, but I don't). Other than that, I think all things are more-or-less possible, but telling you that would not be as useful as having an experienced PHP pro work the issues and give you a solution.  Consider creating a project in E-E Gigs.
From here https://github.com/tecnickcom/TCPDF
A new version of this library is under development at https://github.com/tecnickcom/tc-lib-pdf and as a consequence this version will not receive any additional development or support. This version should be considered obsolete, new projects should use the new version as soon it will become stable.
I use FPDF as I found it the most versatile for what I want to do - specifically importing other PDF's for the purpose of overlaying data.

The PDF supplied uses forms. I have not explored this option but apparently FPDF has a form module that will do this http://www.fpdf.org/en/script/script93.php which references FPDM https://github.com/madnh/FPDM

There is also PDFtk which is a separate system that can be invoked by PHP https://www.sitepoint.com/filling-pdf-forms-pdftk-php/
@Julian: If you follow that part about "A new version of this library is under development..." you will see that the link I posted is the current level and the "new version" is not quite ready for prime time yet.  This often happens with open-source projects when they are built by a small community; not everything is 100% "perfect" at all times.  Contrast WordPress, where the community is huge.  I've never used PDFtk, so I can't comment on that.

Anyway, I think the Author will be best served by the E-E Gigs project or maybe by E-E Live.  But if you can show a simple working example that starts by importing his sample PDF, that might save him some money!
Sorry, I forgot to add the link to E-E Gigs.  Here you go...
Here is the code
<?php
require_once "fpdf/fpdf.php";
require_once "fpdi.php";
require_once "fpdf_tpl.php";

$fpdi = new FPDI();
$font = new stdClass;
$font->family = 'Arial';
$font->size = 16;
$font->weight = '';

$fpdi->setSourceFile('t2235.pdf');
$template = $fpdi->importPage(1);
$fpdi->templateSize   = $fpdi->getTemplateSize($template);
$fpdi->templateCenter = $fpdi->templateSize['w'] >> 1;
$orientation = 'P';
$fpdi->AddPage($orientation);
$fpdi->useTemplate($template, 0, 0);

$fpdi->SetFont($font->family, $font->weight, $font->size);

// Assumes an array of PDF text items made up of
// x: x offset of start of text
// y: y offset of top of text
// text: text to output

if ($pdfitems = getPDFItems()) {
	foreach($pdfitems as $item) {
		$fpdi->Text($item->x,$item->y,$item->text);
	}

	$clientnumber = rand(1000,2000);
	$filename = "Rental-Agreement-Form-{$clientnumber}.pdf";

	$fpdi->Output($filename, 'D');
}
/*
** Emulate getting data for PDF by getting from POST
*/
function getPDFItems()
{
	$name = isset($_POST['name']) ? $_POST['name'] : false;
	
	if ($name) {
		$items = array();
		$newitem = new stdClass;
		$newitem->text = $name;
		$newitem->x = 24;
		$newitem->y = 17;
		
		$items[] = $newitem;
	}
	else {
		$items = false;
	}
	
	return $items;
}
?>
<!doctype html>
<html>
<body>
<form method="post">
	<label>Full Name</label><input type="text" name="name" /> <input type="submit" />
</form>
<a href="t2235.pdf">Sample input PDF</a>

</body>
</html>

Open in new window

Working sample here
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
That shows the difference in rendering quality
NB: The image used was taken from the scanned image the author submitted and then using Gimp to take out the writing. In reality you create the clean PDF template - and there is NO quality loss over creating the template from scratch with PHP.

I strongly recommend you DO NOT create from scratch - there is no benefit to this whatsoever - quality is identical and you don't have to go through the pain of layout. Layout has already been done - simply dump that to a PDF and use it as the background - obviously don't use a photo-shopped (gimp'd) scanned image - I used that as I did not have access to any other template.
PHP7 note for FPDF.  PHP7 now warns about PHP4-style constructors, and you may find that FPDF has the old constructor (method name == class name).  It's an easy fix - just change the FPDF method name to __construct() and you'll get past the warning.  The properties and methods do not declare their visibility, which means they are all public.  The deprecation of the var keyword was voted down last year, so it will remain almost the same meaning as public.

When I've used FDPF before, I did all of the document layout with the class methods, and I didn't find it too hard - just time consuming the first time through, and since you have to get the positioning right for the content, positioning the template is not much different.  Here's a script fragment and a link to the resulting document showing how I've done it.

Link to PDF: https://iconoun.com/demo/storage/temp_pdf.pdf
<?php // demo/temp_joseph_longo.php
/**
 * https://www.experts-exchange.com/questions/29010988/PHP-Filling-Out-Creating-a-PDF.html#a42063532
 *
 * Demonstrate the basics of an HTML form to populate information in a PDF-image with FPDF
 *
 * http://www.fpdf.org/
 * http://www.fpdf.org/en/doc/index.php
 * http://www.fpdf.org/en/doc/image.htm
 */
error_reporting(E_ALL);


// PRETEND TO GET THE EXTERNAL REQUEST VARS FROM THE HTML FORM
$rn = 'NO NAME';
$an = 'NO ADDRESS';


// LOAD THE FPDF CLASSES
require_once('fpdf16/fpdf.php');

// SYNTHESIZE THE PDF FILE INFORMATION
$pdf_file_link
= 'storage'
. DIRECTORY_SEPARATOR
. 'temp_pdf'
. '.pdf'
;
$pdf_file_name
= getcwd()
. DIRECTORY_SEPARATOR
. $pdf_file_link
;

// USE THIS FONT FOR THE PDF
$font = 'Arial';

$pdf = new FPDF();
$pdf->SetMargins(0, 0);
$pdf->SetDrawColor(0);
$pdf->AddPage('L', 'Letter');

// FOR CAPTIONS
$pdf->SetFont($font);

// BORDER AROUND DOCUMENT IS EQUIVALENT TO FOUR LINES
//  $pdf->Line(  4,   4, 275,   4);  // TOP
//  $pdf->Line(  4,   4,   4, 212);  // LEFT
//  $pdf->Line(275,   4, 275, 212);  // RIGHT
//  $pdf->Line(  4, 212, 275, 212);  // BOTTOM
$pdf->Rect(4,4,272,208);

// BOXES FOR NAME, ADDRESS, PHONE
$pdf->Rect(  4,  4,  272/2, 12);
$pdf->Rect(  4,  4,  272/2, 24);
$pdf->Rect(  4,  4,  272/2, 36);
$pdf->Rect(  4,  4,  272/2, 48);

// CAPTIONS FOR NAME, ADDRESS, PHONE
$pdf->setXY( 5, 8 );
$pdf->Write(0, 'Name');
$pdf->setXY( 5, 20);
$pdf->Write(0, 'Address');
$pdf->setXY( 5, 44);
$pdf->Write(0, 'Phone No.');

// BOXES FOR TERMS AND DATES
$pdf->Rect(275-64, 16, 65, 12);
$pdf->Rect(275-64, 16, 65, 24);
$pdf->Rect(275-64, 16, 65, 36);

// BOX FOR A CHECKMARK
$pdf->Rect(110, 140,  8, 8);


// DO THE WRITING IN RED TO MAKE IT OBVIOUS
$pdf->SetFont($font, 'B', 18);
$pdf->SetTextColor(0xCC, 0x00, 0x00);


// SET THE THE RENTER'S NAME ON THE DOCUMENT
$x = 24;
$y = 10;
$pdf->SetXY($x, $y);
$pdf->Write(0, $rn);

// SET THE THE RENTER'S ADDRESS ON THE DOCUMENT
$x = 24;
$y = 22;
$pdf->SetXY($x, $y);
$pdf->Write(0, $an);

// CHECK A CHECKBOX ON THE DOCUMENT
$x = 111;
$y = 144;
$pdf->SetXY($x, $y);
$pdf->Write(0, 'X');

// WRITE THE PDF TO DISK
$pdf->Output($pdf_file_name, 'F');

// PRESENT A LINK
echo '<a target="my_PDF" href="' . $pdf_file_link . '">See the PDF with ' . $rn . '</a>';

Open in new window

We are getting too focused on FPDF here - it is a diverse library that can do N things - we only want it to do a very small subset of that

1. Load a PDF page to use as a template
2. Overlay text on it

There are other PDF libraries out there that do the same thing. I made it clear at the start of this thread I am limited by NDA on what I can provide. I was asked to provide code - I provided code that demonstrates the concept.

If FPDF does not work for you then there are other libraries to choose from - however I believe the code provided demonstrates quite clearly the principle of the solution.
Agreed - we could probably do this lots of different ways, but I think FPDF is a good enough solution, and it's almost as easy to create the PDF from FPDF commands as it is to try to get a credible image by loading an external PDF.  I've gotten good results when loading PNG images, but aside from that, I've gotten the best results by letting FPDF do all the work.
https://iconoun.com/demo/temp_joseph_longo.php

In any case, this question is well-enough answered that our Author can choose a path and proceed, or can show the question and comments to a Gigs engagement.  

Over and out...
Awesome!

I apologize. I haven't updated this post in a few days. I have been busy with work, church and other projects.

Ray,

For the image you created, which program did you use? Did you use Photoshop or a similar program to create a PNG image first, and input the data onto the PNG image, using fpdf?
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I am getting closer to achieving my end result. I have began create the PDF and laying out the document accordingly. It is a lot easier than I originally thought it was going to be.

Ray,

In your code, I have a question. How or where did you come up with the number 272? Was that kind of just a trial and error number, or did you calculate the width of the letter size in millimeters?
The FPDF constructor assumes the values are given in millimeters, unless you tell it otherwise, so yes, the number 272 was a distance in millimeters.  That does not eliminate some trial and error, but it makes the measurements more predictable.

FWIW, 8.5" x 11" is pretty close to 216mm x 280mm. so a width of 272mm, starting from a left margin of 4mm gives a 4mm empty border on both sides of an 11" wide document.