I've written the following code for merging PDFs using this answer
function merge_pdfs() {
$pdfs_array = array('1.pdf', '2.pdf');
$pdf = new FPDI_Protection();
for ($i = 0; $i < count($pdfs_array); $i++ ) {
$pagecount = $pdf->setSourceFile($pdfs_array[$i]);
for($j = 0; $j < $pagecount ; $j++) {
$tplidx = $pdf->importPage(($j +1), '/MediaBox');
$pdf->addPage('P','A4');
$pdf->useTemplate($tplidx, 0, 0, 0, 0, TRUE);
}
}
$dt = new DateTime(NULL, new DateTimeZone($data->user->timezone));
$pdf->SetTitle('PDF, created: '.$dt->format(MYHMRS_DATETIME_FRIENDLY));
$pdf->SetSubject('PDF subject !');
$output = $pdf->Output('', 'S');
$name = "PDF".'-'.$dt->format('ymd').'.pdf';
$this->output
->set_header("Content-Disposition: filename=$name;")
->set_content_type('Application/pdf')
->set_output($output);
}
So, after this I'm getting the following error message
This document (1.pdf) probably uses a compression technique which is not supported by the free parser shipped with FPDI. (See https://www.setasign.com/fpdi-pdf-parser for more details)
I've checked the link and it suggests to set another PDF Parser ( If I understand right )
But I'm not sure how to make it working with Codeigniter and my example
Should I create library and try to use it?
Or maybe you know another solution for merging PDFs
The issue was related to PDF versions
Edit
If you don't know, the PDFs has versions. Yeah, I was surprised as well. Please check them here PDF versions
So, the problem was that I was trying to merge PDF 1.5 version with PDF 1.6
An example. It is simple.
<?php
require_once __DIR__ . '/vendor/autoload.php';
$mpdf = new \Mpdf\Mpdf();
$mpdf->WriteHTML('<h1>Hello world!</h1>');
$mpdf->AddPage('P');
$mpdf->WriteHTML('<h1>More</h1>');
$mpdf->Output();
?>
I am using Intervention Image image manipulation library this library in a project and I am stuck on adding watermark image all over the source image.
Is it possible to repeat the watermark image on all over the source image like in the following example?
I try the following code, but it doesn't work for me.
$thumbnail = $manager->make($name);
$watermark = $manager->make($watermarkSource);
$x = 0;
while ($x < $thumbnail->width()) {
$y = 0;
while($y < $thumbnail->height()) {
$thumbnail->insert($watermarkSource, 'top-left', $x, $y);
$y += $watermark->height();
}
$x += $watermark->width();
}
$thumbnail->save($name, 80);
I've just solved this problem via Intervention Image library using it in Laravel Framework. So here's code snippet.
public function watermarkPhoto(String $originalFilePath,String $filePath2Save ){
$watermark_path='photos/watermark.png';
if(\File::exists($watermark_path)){
$watermarkImg=Image::make($watermark_path);
$img=Image::make($originalFilePath);
$wmarkWidth=$watermarkImg->width();
$wmarkHeight=$watermarkImg->height();
$imgWidth=$img->width();
$imgHeight=$img->height();
$x=0;
$y=0;
while($y<=$imgHeight){
$img->insert($watermark_path,'top-left',$x,$y);
$x+=$wmarkWidth;
if($x>=$imgWidth){
$x=0;
$y+=$wmarkHeight;
}
}
$img->save($filePath2Save);
$watermarkImg->destroy();
$img->destroy(); // to free memory in case you have a lot of images to be processed
}
return $filePath2Save;
}
If you use PHP version prior to 7 remove String type declaration from function arguments. just make it
public function watermarkPhoto($originalFilePath, $filePath2Save ){....}
Also if you are not using Laravel Framework and you don't have File class included just remove redundand check from function.
if(\File::exists($watermark_path))
So the simplest framework-agnostic function would be:
function watermarkPhoto($originalFilePath, $filePath2Save ){
$watermark_path='photos/watermark.png';
$watermarkImg=Image::make($watermark_path);
$img=Image::make($originalFilePath);
$wmarkWidth=$watermarkImg->width();
$wmarkHeight=$watermarkImg->height();
$imgWidth=$img->width();
$imgHeight=$img->height();
$x=0;
$y=0;
while($y<=$imgHeight){
$img->insert($watermark_path,'top-left',$x,$y);
$x+=$wmarkWidth;
if($x>=$imgWidth){
$x=0;
$y+=$wmarkHeight;
}
}
$img->save($filePath2Save);
$watermarkImg->destroy();
$img->destroy();
return $filePath2Save;
}
Also you need watermark image in png format with transparent background.
I'm just adding one thing added to your accepted answer when you try your code and the above-accepted code, the watermark on the images will be very close together and close together with what I tried. like this
So if you want the watermarks like what you want, you need to modify the code with plus numbers on wmarkheight and wmarkwidth like this:
while ($x < $imgWidth) {
$y = 0;
while($y < $imgHeight) {
$imgFileCollection->insert($watermark, 'top-left', $x, $y);
$y += $wmarkHeight+30;
}
$x += $wmarkWidth+40;
}
this line of code is important:
$y += $wmarkHeight+30;
$x += $wmarkWidth+40;
and you will get the result like that below:
I am having issues getting an image into my PDF. I have a form, and on it I have two upload fields. The user can upload a PDF, or an image. When the file is uploaded, it is saved in my temp folder without an extension. I pass my class two things: $fileData, which is the url to the file, and inputArray which is an array of other data from the form (name, address, etc).
My code is like so
private $tplidx;
public function __construct($fileData, $inputArray) {
$pdf = new \FPDI();
$count = 10;
$pdf->AddPage('P');
$pdf->SetFontSize(20);
foreach($inputArray as $input) {
$pdf->SetXY(50, $count);
$pdf->Write(1, $input);
$count = $count + 10;
}
foreach($fileData as $name => $extension){
if($extension == "application/pdf") {
$pagecount = $pdf->setSourceFile($name);
for($i=0; $i<$pagecount; $i++){
$pdf->AddPage();
$this->tplidx = $pdf->importPage($i+1);
$pdf->useTemplate($this->tplidx, 10, 10, 200);
}
} else {
$pdf->AddPage();
$filetype = explode("/",$extension);
$pdf->Image($name.'.'.$filetype[1],30,120,25);
}
}
$pdf->Output('test.pdf', 'F');
}
The first foreach adds the inputs from the field to a page, this works fine.
The next foreach next checks if its a pdf, and if it is, it adds it to another page in the PDF. This also works fine.
My problem is in the else, because I am appending the files extension, I get the error
Can't open image file
If I remove the extension part, I get the error
Image file has no extension and no type was specified
Is there any way to solve this issue?
Thanks
You can pass the image type in the $type parameter of the Image() method.
I'm converting a table of my DB to PDF and I'm using the TCPDF.
First I have to convert my table to HTML and then I can convert to PDF, which use a lot of memory and I have a few resources on the server (256M for PHP max).
How can I pass a table that may have thousands of records to PDF with 256M memory max in PHP?
Can I create a PDF page by page and in the end concatenate all pages?
I have found a way to concatenate the pdf page from this link.
require_once("tcpdf/tcpdf.php"); //ur workspaces
require_once("fpdi/fpdi.php");
class concat_pdf extends FPDI {
var $files = array();
function setFiles($files) {
$this->files = $files;
}
function concat() {
foreach($this->files AS $file) {
$pagecount = $this->setSourceFile($file);
for ($i = 1; $i <= $pagecount; $i++) {
$tplidx = $this->ImportPage($i);
$s = $this->getTemplatesize($tplidx);
$this->AddPage(’P', array($s['w'], $s['h']));
$this->useTemplate($tplidx);
}
}
}
}
Did you try fpdf (http://www.fdpf.org) or the related mpdf (http://www.mpdf1.com) as an alternative? Maybe they use less resources so they can run on your server. They do a good job in creating HTML to PDF output.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
My concept is - there are 10 pdf files in a website. User can select some pdf files and then select merge to create a single pdf file which contains the selected pages. How can i do this with php?
Below is the php PDF merge command.
$fileArray= array("name1.pdf","name2.pdf","name3.pdf","name4.pdf");
$datadir = "save_path/";
$outputName = $datadir."merged.pdf";
$cmd = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$outputName ";
//Add each pdf file to the end of the command
foreach($fileArray as $file) {
$cmd .= $file." ";
}
$result = shell_exec($cmd);
I forgot the link from where I found it, but it works fine.
Note: You should have gs (on linux and probably Mac), or Ghostscript (on windows) installed for this to work.
i suggest PDFMerger from github.com, so easy like ::
include 'PDFMerger.php';
$pdf = new PDFMerger;
$pdf->addPDF('samplepdfs/one.pdf', '1, 3, 4')
->addPDF('samplepdfs/two.pdf', '1-2')
->addPDF('samplepdfs/three.pdf', 'all')
->merge('file', 'samplepdfs/TEST2.pdf'); // REPLACE 'file' WITH 'browser', 'download', 'string', or 'file' for output options
I've done this before. I had a pdf that I generated with fpdf, and I needed to add on a variable amount of PDFs to it.
So I already had an fpdf object and page set up (http://www.fpdf.org/)
And I used fpdi to import the files (http://www.setasign.de/products/pdf-php-solutions/fpdi/)
FDPI is added by extending the PDF class:
class PDF extends FPDI
{
}
$pdffile = "Filename.pdf";
$pagecount = $pdf->setSourceFile($pdffile);
for($i=0; $i<$pagecount; $i++){
$pdf->AddPage();
$tplidx = $pdf->importPage($i+1, '/MediaBox');
$pdf->useTemplate($tplidx, 10, 10, 200);
}
This basically makes each pdf into an image to put into your other pdf. It worked amazingly well for what I needed it for.
$cmd = "gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=".$new." ".implode(" ", $files);
shell_exec($cmd);
A simplified version of Chauhan's answer
Both the accepted answer and even the FDPI homepage seem to give botched or incomplete examples. Here's mine which works and is easy to implement. As expected it requires fpdf and fpdi libraries:
FPDF: http://www.fpdf.org/en/download.php
FPDI: https://www.setasign.com/products/fpdi/downloads
require('fpdf.php');
require('fpdi.php');
$files = ['doc1.pdf', 'doc2.pdf', 'doc3.pdf'];
$pdf = new FPDI();
// iterate over array of files and merge
foreach ($files as $file) {
$pageCount = $pdf->setSourceFile($file);
for ($i = 0; $i < $pageCount; $i++) {
$tpl = $pdf->importPage($i + 1, '/MediaBox');
$pdf->addPage();
$pdf->useTemplate($tpl);
}
}
// output the pdf as a file (http://www.fpdf.org/en/doc/output.htm)
$pdf->Output('F','merged.pdf');
I've had similar problem in my software. We've wanted to merge several PDF files into one PDF file and submit it to an outer service. We've been using the FPDI solution as shown in Christa's solution.
However, the input PDF's we've been using could be in version higher than 1.7. We've decided to evaluate the FPDI commercial add-on. However, it turned out that some of the documents scanned by our office copier were having malformed indexes, which crashed the commercial FPDI add-on. So we've decided to use Ghostscript solution as in Chauhan's answer.
But then we got some strange metadata in the output PDF properties.
Finally we've decided to join two solutions to get PDF's merged and downgraded by Ghostscript, but the metadata is set by FPDI. We don't know yet how it would work with some advanced formatted pdfs, but for scans we use it works just fine. Here's our class excerpt:
class MergedPDF extends \FPDI
{
private $documentsPaths = array();
public function Render()
{
$outputFileName = tempnam(sys_get_temp_dir(), 'merged');
// merge files and save resulting file as PDF version 1.4 for FPDI compatibility
$cmd = "/usr/bin/gs -q -dNOPAUSE -dBATCH -dCompatibilityLevel=1.4 -sDEVICE=pdfwrite -sOutputFile=$outputFileName";
foreach ($this->getDocumentsPaths() as $pdfpath) {
$cmd .= " $pdfpath ";
}
$result = shell_exec($cmd);
$this->SetCreator('Your Software Name');
$this->setPrintHeader(false);
$numPages = $this->setSourceFile($outputFileName);
for ($i = 1; $i <= $numPages; $i++) {
$tplIdx = $this->importPage($i);
$this->AddPage();
$this->useTemplate($tplIdx);
}
unlink($outputFileName);
$content = $this->Output(null, 'S');
return $content;
}
public function getDocumentsPaths()
{
return $this->documentsPaths;
}
public function setDocumentsPaths($documentsPaths)
{
$this->documentsPaths = $documentsPaths;
}
public function addDocumentPath($documentPath)
{
$this->documentsPaths[] = $documentPath;
}
}
The usage of this class is as follows:
$pdf = new MergedPDF();
$pdf->setTitle($pdfTitle);
$pdf->addDocumentPath($absolutePath1);
$pdf->addDocumentPath($absolutePath2);
$pdf->addDocumentPath($absolutePath3);
$tempFileName = tempnam(sys_get_temp_dir(), 'merged');
$content = $pdf->Render();
file_put_contents($tempFileName, $content);
I have tried similar issue and works fine, try it. It can handle different orientations between PDFs.
// array to hold list of PDF files to be merged
$files = array("a.pdf", "b.pdf", "c.pdf");
$pageCount = 0;
// initiate FPDI
$pdf = new FPDI();
// iterate through the files
foreach ($files AS $file) {
// get the page count
$pageCount = $pdf->setSourceFile($file);
// iterate through all pages
for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
// import a page
$templateId = $pdf->importPage($pageNo);
// get the size of the imported page
$size = $pdf->getTemplateSize($templateId);
// create a page (landscape or portrait depending on the imported page size)
if ($size['w'] > $size['h']) {
$pdf->AddPage('L', array($size['w'], $size['h']));
} else {
$pdf->AddPage('P', array($size['w'], $size['h']));
}
// use the imported page
$pdf->useTemplate($templateId);
$pdf->SetFont('Helvetica');
$pdf->SetXY(5, 5);
$pdf->Write(8, 'Generated by FPDI');
}
}
This worked for me on Windows
download PDFtk free from https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/
drop folder (PDFtk) into the root of c:
add the following to your php code where $file1 is the location and name of the first PDF file, $file2 is the location and name of the second and $newfile is the location and name of the destination file
$file1 = ' c:\\\www\\\folder1\\\folder2\\\file1.pdf';
$file2 = ' c:\\\www\\\folder1\\\folder2\\\file2.pdf';
$file3 = ' c:\\\www\\\folder1\\\folder2\\\file3.pdf';
$command = 'cmd /c C:\\\pdftk\\\bin\\\pdftk.exe '.$file1.$file2.$newfile;
$result = exec($command);
I created an abstraction layer over FPDI (might accommodate other engines).
I published it as a Symfony2 bundle depending on a library, and as the library itself.
The bundle
The Library
usage:
public function handlePdfChanges(Document $document, array $formRawData)
{
$oldPath = $document->getUploadRootDir($this->kernel) . $document->getOldPath();
$newTmpPath = $document->getFile()->getRealPath();
switch ($formRawData['insertOptions']['insertPosition']) {
case PdfInsertType::POSITION_BEGINNING:
// prepend
$newPdf = $this->pdfManager->insert($oldPath, $newTmpPath);
break;
case PdfInsertType::POSITION_END:
// Append
$newPdf = $this->pdfManager->append($oldPath, $newTmpPath);
break;
case PdfInsertType::POSITION_PAGE:
// insert at page n: PdfA={p1; p2; p3}, PdfB={pA; pB; pC}
// insert(PdfA, PdfB, 2) will render {p1; pA; pB; pC; p2; p3}
$newPdf = $this->pdfManager->insert(
$oldPath, $newTmpPath, $formRawData['insertOptions']['pageNumber']
);
break;
case PdfInsertType::POSITION_REPLACE:
// does nothing. overrides old file.
return;
break;
}
$pageCount = $newPdf->getPageCount();
$newPdf->renderFile($mergedPdfPath = "$newTmpPath.merged");
$document->setFile(new File($mergedPdfPath, true));
return $pageCount;
}
myokyawhtun's solution worked best for me (using PHP 5.4)
You will still get an error though - I resolved using the following:
Line 269 of fpdf_tpl.php - changed the function parameters to:
function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='',$align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0) {
I also made this same change on line 898 of fpdf.php