
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-fpmthat 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-pdftthat 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 PdfEditorBelow 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>
@endsectionOnce 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.