How to maintain image quality with FPDF and PHP? - php

I'm using FPDF with PHP to add an image to a PDF. But the image quality in the PDF is much worse than the original image, as you can see here:
Relevant code:
$image_height = 40;
$image_width = 40;
$pdf = new FPDF();
$pdf->AddPage();
$start_x = $pdf->GetX();
$start_y = $pdf->GetY();
$pdf->Image('./images/ds_pexeso_ros_0_17.jpg', $pdf->GetX(), $pdf->GetY(), $image_height, $image_width);
$pdf->Output("pexeso".date("Y-m-d"),"I");
The original image is 150x150 pixels.

I faced the same problem in projects for customers.
Blurry pictures in a generated pdf document even with hires images.
It took me a couple of hours, but this is what worked for me.
I have a taken a look at the code and saw that there was a scale factor being set in the constructor of the pdf document:
//Scale factor
if($unit=='pt')
$this->k=1;
elseif($unit=='mm')
$this->k=72/25.4;
elseif($unit=='cm')
$this->k=72/2.54;
elseif($unit=='in')
$this->k=72;
else
$this->Error('Incorrect unit: '.$unit);
The scalefactor is depending on the value given in the constructor of the pdf document:
function FPDF($orientation='P',$unit='mm',$format='A4')
The default is 'mm'. In most of my documents I initiate a pdf document like:
$pdf = new PDF('P');
This means that there will be a scalefactor of 72/25.4 = 2.83 used.
When I placed an image before I just used:
$this->Image('path/to/file', 0, 0);
This way I got the blurry images.
It is also possible to give the width of the image in the command
$this->Image('path/to/file', 0, 0, 200); // for a image width 200
This gave me an image that was far too large. But - and here comes the trick - when you divide the real width by the scalefactor (in my case 2.83) and put this in this statement it gives a perfectly sharp image:
$this->Image('path/to/file', 0, 0, 71); // for a image width 200 / 2.83 = app 71
I hope this works for you too!

I think the problem could be related to:
$image_height = 40;
$image_width = 40;
With these two instructions your are setting the dimensions the image will have in the pdf.
But if the original image is bigger than 40x40 the scaling of the image can cause quality problem.
So what i suggest:
Do a correct resize of the image (php provides GD library). Resize it to 40x40. The GD function imagecopyresampled is your friend: resize and resample the image! Complete reference: http://www.php.net/manual/en/function.imagecopyresampled.php
Insert now the image in the pdf

FPDF with a statement like this to set the user unit to mm
$pdf=new FPDF('P','mm','Letter');
<?php
require_once('fpdf.php');
$image_height = 40;
$image_width = 40;
$pdf = new FPDF('P','mm','Letter');
$pdf->AddPage();
$start_x = $pdf->GetX();
$start_y = $pdf->GetY();
$pdf->Image('./images/ds_pexeso_ros_0_17.jpg',$start_x+0,$start_y-2,40);
$pdf->Output("pexeso".date("Y-m-d"),"I");
?>
FPDF made a very good looking result.

Related

tcpdf portrait custom page size

I wish to create a custom page size in TCPDF. For that I have the following piece of code:
$width = 80;
$height = 58;
$pageLayout = array($width, $height);
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, $pageLayout, true, 'UTF-8', false);
I am expecting a page of 80mm x 58mm. But the above code creates a page of 58mm width and 80mm height.
So instead of PDF_PAGE_ORIENTATION, I gave 'L' and it gave me the result right. But when I print the page from the browser, the print out comes rotated as would be expected of a Landmark orientation.
Does TCPDF create automatic portrait orientation by adjusting the width and the height?
Any help?
As written in TCPDF docs, you can set PDF_PAGE_ORIENTATION with 'P', or 'L', but also with an emptystring '', which, is the parameter to autoselect page orientation.
TCPDF docs
TCPDF, just set the page orientation from your TCPDF settings.
Hope this helps...
Old question, but IMHO not a complete answer.
Just encountered this problem with custom label sizes.
Yes, you can set the page orientation from within your code.
But TCPDF likes to be in landscape modus when width > height and swaps the dimensions allthough you want portrait orientation.
See these lines in tcpdf->setPageOrientation()
if ($this->fwPt > $this->fhPt) {
...
if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
// swap X and Y coordinates (change page orientation)
A good workaround to create printable pdfs on 57x32mm labels is to set page orientation to landscape yourself, then the generated pdf is printable from within a webbrowser and Acrobat Reader.
$width = 80;
$height = 58;
$orientation = 'P';
$pageLayout = array($width, $height);
if($width > $height){
$orientation = 'L';
// optionally swap dimensions yourself and rotate it:
// $pageLayout = array($height, $width, 'Rotate' => 90);
}
$pdf = new TCPDF($orientation, PDF_UNIT, $pageLayout, true, 'UTF-8', false);
Note: my label printer is set to print in portrait modus, the labels come out with the longest side first (landscape). To print the above created pdf, I have to select 'portrait modus' (or 'automatic') in the print dialog from the webbrowser or Acrobat Reader, allthough the pdf itself is 'landscape orientated'. When you swap the dimensions and rotate, I'll have to select 'landscape' (or 'automatic') in the print dialog.

Rotate image on top of background-image

I have two images. one is a jpg image of a rotated polaroid frame polaroid.jpg. The other is just an ordinary image image.jpg.
I'm trying to rotate the image, and then put it on top of the polaroid-image, and then show the merged images as one jpg-image.
I think I'm pretty close with the following code, but I can't manage to get the transparancy working. The uncovered zone of the rotated image is black instead of transparent. What am I doing wrong? I've added a comment to the lines that are relevant for getting a transparent background for the top-image.
$bg_src = "polaroid.jpg";
$img_src = "image.jpg";
$outputImage = imagecreatefromjpeg($bg_src);
$img = imagecreatefromjpeg($img_src);
// This should create transparent background.
$bgd_color = imagecolorallocatealpha($img, 0, 0, 0, 127);
// This should assign the transparent background to the uncovered zone after rotation
$img = imagerotate($img, 10, $bgd_color);
// This should make sure the alpha transparency gets saved
imagesavealpha($img, true);
$img_x = imagesx($img);
$img_y = imagesy($img);
imagecopymerge($outputImage,$img,156,50,0,0,$img_x,$img_y,100);
header('Content-type: image/jpeg');
imagejpeg($outputImage);
imagedestroy($outputImage);
Figured it out after some heave searching. Turns out to be real simple.
I just changed this line:
imagesavealpha($img, true);
to this:
imagecolortransparent($img,$bgd_color);
yay! :)

How to crop and image before adding an overlay? (GD) (PHP)

My code so far (it creates an overlay to a youtube thumbnail):
<?php
header("Content-type:image/png");
$background=imagecreatefromjpeg("http://img.youtube.com/vi/".$_GET['v']."/default.jpg");
$insert=imagecreatefrompng("play.png");
imagecolortransparent($insert,imagecolorat($insert,0,0));
$insert_x=imagesx($insert);
$insert_y=imagesy($insert);
imagecopymerge($background,$insert,5,57,0,0,$insert_x,$insert_y,100);
imagepng($background,NULL);
The output image is 120x90px, but i need it to have 90x90px.
Anyone knows how this is possible?
Thanks in advance!
http://www.johnconde.net/blog/cropping-an-image-with-php-and-the-gd-library/
<?php
header("Content-type:image/png");
$background = imagecreatefromjpeg("http://img.youtube.com/vi/".$_GET['v']."/default.jpg");
$insert = imagecreatefrompng("play.png");
imagecolortransparent($insert,imagecolorat($insert,0,0));
list($current_width, $current_height) = getimagesize($background);
$left = 0;
$top = 0;
$crop_width = 90;
$crop_height = 90;
$canvas = imagecreatetruecolor($crop_width, $crop_height);
$current_image = $background;
imagecopy($canvas, $current_image, 0, 0, $left, $top, $current_width, $current_height);
imagecopymerge($canvas,$insert,5,57,0,0,$current_width,$current_height,100);
imagepng($canvas);
?>
try that it should work if not comment as to otherwise.
This is taken from a function I wrote to create square thumbnails. You may find the commentary I wrote for myself helpful. Depending on your needs (i.e if you can't afford to make assumptions about the type and dimensions of incoming images) you may need to add additional checks. To avoid smashed or stretched thumbnails we take a central part of the image which is most likely to contain something distinguishable as source co-ordinates.
The basic idea is: Since imagecopyresampled or imagecopyresized also allow you to specify destination and source coordinates, you can copy only part of your original image to a new canvas and save that (or directly output to browser). To avoid smashed or stretched dimensions we take a central part of the original image which is most likely to contain distinguishable content.
//assuming you have already merged your $background image with your overlay at this point
//original dimensions
$original_width = imagesx($background);
$original_height = imagesy($background);
//new dimensions
$new_width = 90;
$new_height = 90;
//the center of the rectangular image
$center = $original_width/2, $original_height/2
//the coordinates from where to start on the original image
//assuming you want to crop the center part
$src_x = floor(($original_width/2) - ($new_width/2));
$src_y = floor(($original_height/2) - ($new_height/2));
//create a new canvas with the new desired width, height
$temp = imagecreatetruecolor(90,90);
//copy the large image to this new canvas
imagecopyresampled($temp,$background,0,0,$src_x,$src_y,$new_width,$new_height,$original_width,$original_height);
//from right to left: source image, destination image, dest x, dest y,
//source x, source y, new width, new height, original width, original height
//save the canvas as an image
imagepng($temp);
This could be improved to handle larger images by first taking a central part relative to it's size and then scaling it down to the new canvas.

fixed size thumbnails like twitpic.com

How do you create a fixed size (height / width) of images/thumbnails In GD?
I know there is a lot of php scripts out there but that just scale it and height/width will always be different size.
I like the thumbnail like twitpic.com and facebook
You need to get the height and width of the image using getimagesize
and then resize it using imagecopyresized
The rest is all the same basic work done with GD to load and save the image.
Here's a basic example, if you want to take into account height/width ratios, then you have to do some additional maths.
<?php
header("Content-type: image/png");
$size = getimagesize($filename);
$image = imagecreatefrompng($filename);
$thumbnail = imagecreate(100,100);
imagecopyresized($thumbnail, $image, 0, 0, 0, 0, 100, 100, $size[0], $size[1]);
imagepng($thumbnail);
imagedestroy($image);
imagedestroy($thumbnail);
It's easy with Thumbnailer:
$th=new Thumbnailer("your-photo.jpg");
$th->thumbSquare(100)->save("thumb.jpg");

Add styled backgrounds to images in PHP

I am working on improving one of my Facebook apps by allowing the user to upload an image and apply a styled border, or frame to it (i.e. clouds, stars, sky etc). The user chould also be able to save the image, with the border after it has been applied. This explains a little better what I need:
http://zbrowntechnology.info/ImgDisp/imgdisp.php
If you have any other questions or need more details, please let me know.. I'll edit this post.
Use imagecopy(). The example on that page is done using the transparency option with imagecopymerge() but I don't think you need that.
Using imagecopy() you'll specify the X/Y coordinates to use for positioning:
imagecopy( $borderimage, $topimage, 20, 20, 0, 0, $width, $height);
Where $width and $height will be the entire width and height of the top image. You'll want to replace 20 and 20 with the measurement for how much of the border image will be showing around the borders. You will probably have to resize the top image to the exact dimensions you want, or else it might overlap the border a little too far to the right or bottom. (see imagecopyresampled())
Edit:
Here's a rough way to do the whole process (assuming chosenborder.png is the border they chose, and uploadedimage.png is the image they uploaded. If it's a different image type you'll use the corresponding function).
$borderx = 20; // The width of our border
$border = imagecreatefrompng("chosenborder.png");
$topimage = imagecreatefrompng("uploadedimage.png");
$bordersize = getimagesize($border);
$topimagesize = getimagesize($topimage);
/* The new dimensions of topimage. $borderx*2 means we account for
the border on both left and right, top and bottom. */
$newx = $bordersize[0] - ($borderx*2);
$newy = $bordersize[1] - ($borderx*2);
imagecopyresampled( $topimage_scaled, $topimage, 0, 0, 0, 0,
$newx, $newy, $topimagesize[0], $topimagesize[1]);
/* Merge the images */
imagecopy( $border, $topimage_scaled, $borderx, $borderx,
0, 0, $width, $height);
/* Output the image */
imagepng($border, "newimage.png");
/* Free up the memory occupied by the image resources */
imagedestroy($border);
imagedestroy($topimage);
After the user uploads their image, find chosenborder.png and uploadedimage.png, run the above script, then display newimage.png to the user and you're good to go. Just make sure you call imagedestroy() on the temporary image resources or they'll eat up memory.
If you don't want to keep the generated image on your server, you can omit the second argument to imagepng() which will make it send the image information directly as an image to the browser, in which case you'll want to write the correct image HTTP headers.
Client-side solution by using css3:
checkout the css3 property border-image
(dosen't meet the requirement of saving the img with the border)
Server-side solution by merging 2 different images:
<?php
$imgFile = 'img.jpg';
$brdFile = 'brd.jpg';
$img = addBorder($imgFile,$brdFile);
outputImage($img);
function addBorder($imgFile,$brdFile)
{
$img=imagecreatefromjpeg($imgFile);
$brd=imagecreatefromjpeg($brdFile);
$imgSize = getimagesize($imgFile);
$brdSize = getimagesize($brdFile);
//NOTE: the border img MUST be bigger then the src img
$dst_x = ceil(($brdSize[0] - $imgSize[0])/2);
$dst_y = ceil(($brdSize[1] - $imgSize[1])/2);
imagecopymerge ( $brd, $img, $dst_x, $dst_y, 0, 0, $imgSize[0], $imgSize[1] ,100 );
return $brd;
}
function outputImage($img)
{
header('Content-type: image/png');
imagepng($img);
}
?>

Categories