Today I’m going to present a use case of a client who requires a PHP application that can export PDF files filled with collected data.
To illustrate, I will present and small example project in Laravel.
Let’s start creating a Laravel project
composer create-project laravel/laravel PDFTest
The next step is create a docker container for this project.
version: '3.6'
services:
webserver:
image: nginx:latest
container_name: pdf_web
env_file: ./docker/environment.env
volumes:
- ./:/code
- ./docker/nginx/site.conf:/etc/nginx/conf.d/site.conf
- ./docker/nginx/laravel.site.conf:/etc/nginx/conf.d/api.conf
- ./docker/nginx/fastcgi.conf:/etc/nginx/fastcgi.conf
ports:
- "80:80"
- "8888:80"
php:
image: erdiko/php-fpm:latest
container_name: pdf_php
env_file: ./docker/environment.env
volumes:
- ./:/code
ports:
- "9000:9000"
After some research, I opted for pdftk, since it seems to be the most widely used.
Pdftk is a cross-platform binary that provides a bunch of handy command line options to manipulate PDFs like get plain text content, list fields, list fields data, fill in forms, etc.
A simple entrypoint.sh script is used to install the pdftk.
#!/bin/bash
apt update && apt install -y pdftk;
php-fpm
that has to vi mapped and executed in the php services, getting something like this
php: image: erdiko/php-fpm:latest container_name: pdf_php env_file: ./docker/environment.env volumes: - ./:/code - ./docker/entrypoint.sh:/usr/local/etc/php/entrypoint.sh ports: - "9000:9000" entrypoint: /usr/local/etc/php/entrypoint.sh
Here we will install php-pdftk package. Php-pdftk is a php package that relays on pdftk to manipulate PDF documents from php applications.
composer require mikehaertl/php-pdft
that provides a PHP class, named PDF, with all needed methods to work with PDF files.
The first step is create controller, which I named it PdfEditor.
php artisan make:controller PdfEditor
Below is how the implementation looks like,
<?php
namespace App\Http\Controllers;
use mikehaertl\pdftk\Pdf;
class PdfEditor extends Controller
{
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('pdftest.upload');
}
public function store(Request $request)
{
$uploadedFile = $request->file('pdf_file');
$filename = $uploadedFile->getClientOriginalName();
$request->session()->put('current_pdf', $filename);
Storage::disk('local')->putFileAs(
'files/'.$filename,
$uploadedFile,
$filename
);
return redirect()->back()->with('success', 'Your file is submitted Successfully');
}
public function preview()
{
if(Session::has('current_pdf')) {
$filepath = Storage::disk('local')->getDriver()->getAdapter()->getPathPrefix() . 'files/'. Session::get('current_pdf');
$filename = $filepath . '/' . Session::get('current_pdf');
return response(file_get_contents($filename))->withHeaders([
'Content-Type' => 'application/pdf'
]);
}
return redirect()->back()->with('error', 'Sorry. File not found.');
}
public function edit()
{
$filepath = Storage::disk('local')->getDriver()->getAdapter()->getPathPrefix() . 'files/'. Session::get('current_pdf');
$filename = $filepath . '/' . Session::get('current_pdf');
$pdf = new Pdf($filename);
$fields = $pdf->getDataFields(true)->__toArray();
return view('pdftest.edit')->with('fields',$fields);
}
public function save(Request $request)
{
$filepath = Storage::disk('local')->getDriver()->getAdapter()->getPathPrefix() . 'files/'. Session::get('current_pdf');
$filename = $filepath . '/' . Session::get('current_pdf');
$target = $filepath . '/filled_' . Session::get('current_pdf');
$pdf = new Pdf($filename);
$inputs = $request->all();
unset($inputs['_token']);
foreach ($inputs as $field=>$value) {
$data[str_replace('_',' ',$field)] = $value;
}
$pdf->fillForm($data)
->needAppearances();
if (!$pdf->saveAs($target)) {
$error = $pdf->getError();
dd($error);
}
return response(file_get_contents($target))->withHeaders([
'Content-Type' => 'application/pdf'
]);
}
}
We will also need two views, one to upload the PDF and the other to create a dynamic form that will allow us to edit the content of the PDF form.
Below is how the edit form looks like
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">PDF Test</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
@if (session()->has('success'))
<div class="alert alert-success">
<ul>
<li>{!! session()->get('success') !!}</li>
</ul>
</div>
@endif
<form method="POST" action="/pdftest/save" aria-label="{{ __('Save Changes') }}">
@csrf
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>Name</td>
<td>Type</td>
<td>Value</td>
</tr>
</thead>
@foreach($fields as $field)
<tbody>
<tr>
<td>{{$field['FieldName']}}</td>
<td>{{$field['FieldType']}}</td>
<td>{{json_encode($field['FieldValue'])}}</td>
</tr>
</tbody>
@endforeach
</table>
{{--@foreach($fields as $field)--}}
{{--<div class="form-group row">--}}
{{--<label for="{{$field['FieldName']}}">{{ __($field['FieldName']) }}</label>--}}
{{--@switch($field['FieldType'])--}}
{{--@case('Text')--}}
{{--<div class="col-md-6">--}}
{{--<input type="text" id="{{$field['FieldName']}}" name="{{$field['FieldName']}}" value="{{json_encode($field['FieldValue'])}}">--}}
{{--</div>--}}
{{--@break--}}
{{--@case('Choice')--}}
{{--<div class="col-md-6">--}}
{{--<select id="{{$field['FieldName']}}" name="{{$field['FieldName']}}" value="{{json_encode($field['FieldValue'])}}">--}}
{{--<option value="{{$field['FieldValue']}}">{{$field['FieldValue']}}</option>--}}
{{--@foreach($field["FieldStateOption"] as $option)--}}
{{--<option value="{{$option}}" >{{__($option)}}</option>--}}
{{--@endforeach--}}
{{--</select>--}}
{{--</div>--}}
{{--@break--}}
{{--@case('Button')--}}
{{--<div class="col-md-6">--}}
{{--@if($field['FieldValue']=="1")--}}
{{--<input type="checkbox" id="{{$field['FieldName']}}" name="{{$field['FieldName']}}" value="{{$field['FieldValue']}}" checked>--}}
{{--@else--}}
{{--<input type="checkbox" id="{{$field['FieldName']}}" name="{{$field['FieldName']}}" value="{{$field['FieldValue']}}">--}}
{{--@endif--}}
{{--</div>--}}
{{--@break--}}
{{--@endswitch--}}
{{--</div>--}}
{{--@endforeach--}}
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Save changes') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
Once you finish just click on Save Changes button that will fill the form and save a new filled PDF.
I hope this simple example illustrate on how to handle projects that requires PDF form edition.
Thank you for reading and see in the next post.
Tags: Laravel, pdf, pdf forms, pdf-tk, php, programming
Categories: PHP, Programming
Lets talk!
Join our mailing list, we promise not to spam.