0

I am doing a basic system that the staff uploads a pdf file with some description and this data stored in database MySQL.

The admin will view this pdf and click on approval if everything is ok.

An image will be inserted in pdf file with approve logo.

I use fpdf and fpdi class to do this, I manage to do this if PDF file stored on the actual path as shown in the code below.

<?php

use setasign\Fpdi\Fpdi;

require_once('fpdf/fpdf.php');
require_once('fpdi2/src/autoload.php');

// initiate FPDI
$pdf = new Fpdi();
// add a page
$pdf->AddPage();
// set the source file
$pdf->setSourceFile('PdfDocument.pdf');
// import page 1
$tplIdx = $pdf->importPage(1);
// use the imported page and place it at position 10,10 with a width of 100 mm
$pdf->useTemplate($tplIdx, 10, 10, 100);

// now write some text above the imported page
$pdf->SetFont('Helvetica');
$pdf->SetTextColor(255, 0, 0);
$pdf->SetXY(30, 30);
$pdf->Write(0, 'This is just a simple text');

$pdf->Output();

BUT when I try to use $pdf->setSourceFile($string) or other that actual file for example (PDF $content from string (database) or URL) I cannot manage to do that.

    // set the source file
//$pageCount = $pdf->setSourceFile("http://localhost/pdf/getpicture.php?fid=2");

$stream = fopen('data:text/plain,' . urlencode("http://localhost/pdf/getPDF.php?fid=2"), 'rb');
//$reader = new SetaPDF_Core_Reader_Stream($stream);
$pageCount = $pdf->setSourceFile($stream);

My question is how can I import PDF from MySQL string to be edited by fpdf and fpdi or any other free PDF classes.

Note: I try to use stream_wrapper_register with no luck so far. as in this link https://www.setasign.com/support/faq/miscellaneous/using-a-pdf-from-a-php-variable-instead-of-a-file/

Please help me with a simple example as I am not really familiar with PDF classes.

Thank you.

Khalifa Nikzad
  • 1,213
  • 1
  • 6
  • 17
Mahdi
  • 1
  • 1

2 Answers2

0

I think the source of your trouble is in the line:

$stream = fopen('data:text/plain,' . urlencode("http://localhost/pdf/getPDF.php?fid=2"), 'rb');

PHP's fopen function returns a file pointer, and is not giving you the name of the PDF file you want.

So later when you call

$pageCount = $pdf->setSourceFile($stream);

$stream is not a string with a PDF filename.

If your http://localhost/pdf/getPDF.php?fid=2 URL is returning the filename of the PDF, try getting that value with file_get_contents like so:

$pdf_file = file_get_contents('http://localhost/pdf/getPDF.php?fid=2');

and then call

$pdf->setSourceFile($pdf_file);
ebcode
  • 124
  • 1
  • 5
  • `setSourceFile()` doesn't accept a PDF as a string. So this answer is missleading and faulty. – Jan Slabon Dec 04 '18 at 13:53
  • Well, I didn't test this code, so @JanSlabon may be right. If he is, then the documentation [here](https://manuals.setasign.com/fpdi-manual/v2/the-fpdi-class/#c_setasignFpdiFpdiTraitsetSourceFile) is wrong. It says the $file argument may be on of the following: Path to the file or a stream resource or a StreamReader instance. – ebcode Dec 04 '18 at 19:12
  • What's wrong with the documentation? You try to pass the PDF content as a string which is not documented. – Jan Slabon Dec 04 '18 at 20:52
  • @JanSlabon, I think there is a miscommunication. I was not saying to pass the content of the PDF as a string, but the filename of the PDF as a string. If that does not work with setSourceFile(), then the documentation is misleading. – ebcode Dec 04 '18 at 21:19
  • Ok, understood. But this doesn't make sense at all... calling a script on the same host, to get a path to a local file on the same host is absolutely nonsense :-) – Jan Slabon Dec 04 '18 at 22:14
0

You should not use an additional HTTP request to access a file from a database!

FPDI 2 allows you to read from any source through a StreamReader class:

// use a resource
$fh = fopen('a/path/to/a.pdf', 'rb');
$pdf->setSourceFile(new StreamReader($fh));
// same as
$pdf->setSourceFile($fh);
// don't forget to call fclose($fh);

// use a path
$path = 'a/path/to/a.pdf';
$pdf->setSourceFile(StreamReader::createByFile($path));
// same as
$pdf->setSourceFile($path);

// use a string
$pdfString = '%%PDF-1.4...';
$pdf->setSourceFile(StreamReader::createByString($pdfString));

So do not call an external script but query your database for the PDF and pass it e.g. as a string.

PS: You cannot edit a PDF with FPDI!

Jan Slabon
  • 4,736
  • 2
  • 14
  • 29
  • Thank @Jan Slabon for you suggestion. I try to follow your code but I got this error Fatal error: Uncaught Error: Class 'StreamReader' not found in.. My full code as below `use setasign\Fpdi\Fpdi; use setasign\Fpdi\PdfReader; require_once('fpdf/fpdf.php'); require_once('fpdi2/src/autoload.php'); require_once('fpdi2/src/PdfReader/PdfReader.php'); // use a resource $fh = fopen('http://localhost/pdf/invoice2.pdf', 'rb'); $pdf->setSourceFile(new StreamReader($fh)); ` May you help me with full example I am very sorry I am new to PHP programing Thank you – Mahdi Dec 06 '18 at 02:58
  • Add a `use setasign\Fpdi\PdfParser\StreamReader;` to your use statements (see [here](http://php.net/manual/en/language.namespaces.importing.php) for more details). Additionally: DO NOT USE AN HTTP REQUEST TO ACCESS A LOCAL FILE! If you really need to access an external file, you have to download it first and pass it as a string (last example). – Jan Slabon Dec 06 '18 at 07:59
  • I got this error after I added `use setasign\Fpdi\PdfParser\StreamReader;` This error `Fatal error: Uncaught InvalidArgumentException: Given stream is not seekable! in C:\xampp\htdocs\pdf\fpdi2\src\PdfParser\StreamReader.php:112 Stack trace: #0 C:\xampp\htdocs\pdf\fpdi.php(20): setasign\Fpdi\PdfParser\StreamReader->__construct(Resource id #10) #1 {main} thrown in C:\xampp\htdocs\pdf\fpdi2\src\PdfParser\StreamReader.php on line 112` – Mahdi Dec 07 '18 at 02:24
  • I already answered this: "If you really need to access an external file, you have to download it first and pass it as a string (last example)." – Jan Slabon Dec 07 '18 at 18:14