Ok, so am working on a simple generator using jquery. A user enters a text of his/her choice, selects a font, font-color and font-size. All this properties are displayed on a separate div in real-time (live preview).
I now need to save the generated preview as a picture. For that, I use php GD library. All works fine but with some fonts, everything is just messed up.
In the first image, everything looks alright but the second image, the line height is just messed up.
This is my php script that am using to processes the properties
<?php
//Width and height of desired image
$width = 320;
$height= 320;
//Create an image with specified height and width
$main_img = ImageCreate($width, $height);
$mx = imagesx($main_img);
$my = imagesy($main_img);
//Capture values from form
$main_text = $_POST['rtext'];
$main_text_size = $_POST['rfsize'];
$color = $_POST['rcolor'];
$mt_f = $_POST['rfont'];
$main_text_x = ($mx/2);
// more code here
//wrap text if text too long
$words = explode(' ', $main_text);
$lines = array($words[0]);
$currentLine = 0;
for($i = 1; $i < count($words); $i++)
{
$lineSize = imagettfbbox($main_text_size, 0, $mt_f, $lines[$currentLine] . ' ' . $words[$i]);
if($lineSize[2] - $lineSize[0] < $mx)
{
$lines[$currentLine] .= ' ' . $words[$i];
}
else
{
$currentLine++;
$lines[$currentLine] = $words[$i];
}
}
$line_count = 1;
// Loop through the lines and place them on the image
foreach ($lines as $line)
{
$line_box = imagettfbbox($main_text_size, 0, $mt_f, "$line");
$line_width = $line_box[0]+$line_box[2];
$line_height = $line_box[1]-$line_box[7];
$line_margin = ($mx-$line_width)/2;
$line_y = (($line_height+12) * $line_count);
imagettftext($main_img, $main_text_size, 0, $line_margin, $line_y, $main_text_color, $mt_f, $line);
// Increment Y so the next line is below the previous line
$line_count ++;
}
header("Content-type: image/png");
//code to download the image
?>
Is there a way I can modify the part of the code where I wrap the text to accomodate all fonts? Like automatically calculate the line height based on the font?
Thanks, any help will be appreciated
I found a very useful class at phpclasses imagefittext.class.php http://www.phpclasses.org/browse/file/41869.html. I also found an example script that is implemented using the class http://www.phpclasses.org/browse/file/41870.html. This is exactly what I wanted.
Worked Perfectly!!!1
Related
I'm trying to make a pdf with fpdf from a form in which the user introduces a file not neccesary a csv but with the structure of a csv.And I want the text to fill to the cell width because i have very long fields and others which are very short,so I want the text to do a line break and continue in the same cell without overlaying other element,so that i can have the large and short information in the same cell width.
EDIT:
I'm trying with multicell but i dont know why im having problems with setY because i dont know why it fails and the pdf cant open but when i commentes everything work fine only that the lines are at different height which is the work of setY
if ($file) {
$csv = [];
while (($data = fgetcsv($file)) !== FALSE) {
$csv[] = $data;
}
fclose($file);
$start_x = $pdf->GetX(); //initial x (start of column position)
$cell_width = 30; //define cell width
$cell_height = 10; //define cell height
foreach ($csv as $columna) {
foreach ($columna as $dato) {
$current_x = $pdf->GetX();
$currenty=$pdf->GetY();
//$cellWidth = $pdf->GetStringWidth(strtok(utf8_decode($dato),','));
//$salto = wordwrap(strtok(utf8_decode($dato), ','), 10, "\n", false);
$pdf->MultiCell($cell_width, $cell_height, strtok(utf8_decode($dato), ','), 1, 'C');
$current_x += $cell_width; //calculate position for next cell
$pdf->SetX($current_x);
$pdf->SetY($currenty);
}
//$pdf->Ln();
$current_x = $start_x; //set x to start_x (beginning of line)
$currenty += $cell_height;
$pdf->SetXY($current_x + $cell_width, $currenty);
}
}
}
i am using php imagick to create a image and than convert to pdf. using php imagick. i coded this:
$image = new Imagick();
$height = 800; //height of the page;
$image->newImage(794, $height, "#f5f5f5");
$image->setImageFormat("jpg");
$card = new Imagick('card.jpg'); ; //get single card
$l_align = 190; //left alignment
for($i=0; $i < 4; $i++) //for creating multiple cards on a page
{
$image->compositeImage($card, Imagick::COMPOSITE_DEFAULT,10, ($l_align*$i)+10);
$image->compositeImage($card, Imagick::COMPOSITE_DEFAULT, 390, ($l_align*$i)+10);
}
$image->setResolution(72, 72);
$image->resetIterator();
$combined = $image->appendImages(true);
$image->setImageFormat("pdf");
$combined->writeImages( 'card.pdf', true );
header("Content-Type: application/pdf");
header('Content-Disposition: attachment; filename="card.pdf"');
echo file_get_contents('card.pdf');
and get something like this
in pdf format . Now i want to page break after every 6 cards print in pdf . i am using imagick . please help me. thanks in advance.
You are using the wrong function for adding images as new pages. You should be using addImage which adds the new image as a separate page, rather than append which just tacks them onto the bottom of the current image.
An example of this working is:
$combined = null;
$images = [
'../../../images/Source1.jpg',
'../../../images/Source2.png',
];
foreach ($images as $image) {
if ($combined == null) {
$combined = new Imagick(realpath($image));
}
else {
$card = new Imagick(realpath($image)); ; //get single card
$combined->addImage($card);
}
}
$combined->setImageFormat("pdf");
$combined->writeImages( './card.pdf', true );
btw there is weird stuff going on in your code example - you're only even attempting to add one image, and what is 'resetIterator' doing in there?
I just discovered this article http://jeffkreeftmeijer.com/2011/comparing-images-and-creating-image-diffs/
It is talking about using regular Ruby and ChunkyPNG to do image diffs.
In particular, the first code of doing a loop through all the pixels.
require 'chunky_png'
images = [
ChunkyPNG::Image.from_file('1.png'),
ChunkyPNG::Image.from_file('2.png')
]
diff = []
images.first.height.times do |y|
images.first.row(y).each_with_index do |pixel, x|
diff << [x,y] unless pixel == images.last[x,y]
end
end
puts "pixels (total): #{images.first.pixels.length}"
puts "pixels changed: #{diff.length}"
puts "pixels changed (%): #{(diff.length.to_f / images.first.pixels.length) * 100}%"
x, y = diff.map{ |xy| xy[0] }, diff.map{ |xy| xy[1] }
images.last.rect(x.min, y.min, x.max, y.max, ChunkyPNG::Color.rgb(0,255,0))
images.last.save('diff.png')
I wonder
a) what would be an ideal PHP equivalent of ChunkyPNG?
b) what would be the implementation of the same code in PHP?
You can do with Imagick extension. Imagick is a native php extension to create and modify images using the ImageMagick API.
Example Using Imagick::compareImages():
Compare images and display the reconstructed image.
<?php
$image1 = new imagick("image1.png");
$image2 = new imagick("image2.png");
$result = $image1->compareImages($image2, Imagick::METRIC_MEANSQUAREERROR);
$result[0]->setImageFormat("png");
header("Content-Type: image/png");
echo $result[0];
?>
OR
A PHP shell script to tell you if two images are visually different by comparing them pixel by pixel. If there's a difference, the script creates a third image - black background with the different pixels in green. Here two GD images are created from the input files, also a third GD image with black background to store the diff. Loop through all the pixels and compare them one by one. If one is different, write a green pixel at the same location of the diff image. [Source : http://www.phpied.com/image-diff/ ]
diff.php
<?php
/**
* Shell script to tell if two images are identical.
* If not, a third image is written - black background with the different pixels painted green
* Code partially inspired by and borrowed from http://pear.php.net/Image_Text test cases
*/
// check if there's enough input
if (empty($argv[1]) || empty($argv[2])) {
echo 'gimme at least two image filenames, please.', "\n";
echo 'e.g. "php idiff.php img1.png img2.png"';
echo "\n", 'third filename is the image diff, optional, default is "diffy.png"';
exit(1);
}
// create images
$i1 = #imagecreatefromstring(file_get_contents($argv[1]));
$i2 = #imagecreatefromstring(file_get_contents($argv[2]));
// check if we were given garbage
if (!$i1) {
echo $argv[1] . ' is not a valid image';
exit(1);
}
if (!$i2) {
echo $argv[2] . ' is not a valid image';
exit(1);
}
// dimensions of the first image
$sx1 = imagesx($i1);
$sy1 = imagesy($i1);
// compare dimensions
if ($sx1 !== imagesx($i2) || $sy1 !== imagesy($i2)) {
echo "The images are not even the same size";
exit(1);
}
// create a diff image
$diffi = imagecreatetruecolor($sx1, $sy1);
$green = imagecolorallocate($diffi, 0, 255, 0);
imagefill($diffi, 0, 0, imagecolorallocate($diffi, 0, 0, 0));
// increment this counter when encountering a pixel diff
$different_pixels = 0;
// loop x and y
for ($x = 0; $x < $sx1; $x++) {
for ($y = 0; $y < $sy1; $y++) {
$rgb1 = imagecolorat($i1, $x, $y);
$pix1 = imagecolorsforindex($i1, $rgb1);
$rgb2 = imagecolorat($i2, $x, $y);
$pix2 = imagecolorsforindex($i2, $rgb2);
if ($pix1 !== $pix2) { // different pixel
// increment and paint in the diff image
$different_pixels++;
imagesetpixel($diffi, $x, $y, $green);
}
}
}
if (!$different_pixels) {
echo "Image is the same";
exit(0);
} else {
if (empty($argv[3])) {
$argv[3] = 'diffy.png'; // default result filename
}
imagepng($diffi, $argv[3]);
$total = $sx1 * $sy1;
echo "$different_pixels/$total different pixels, or ", number_format(100 * $different_pixels / $total, 2), '%';
exit(1);
}
?>
Usage (Command line):
php diff.php img1.png img2.png result.png
I'm trying to read the text from this image:
I want to read the price, e.g. "EUR42721.92"
I tried these libraries:
How to Create a PHP Captcha Decoder with PHP OCR Class: Recognize text & objects in graphical images - PHP Classes
phpOCR: Optical Character Recognizer written in PHP
But they don't work. How can I read the text?
Try this (it worked with me):
$imagick = new Imagick($filePath);
$size = $imagick->getImageGeometry();
$width = $size['width'];
$height = $size['height'];
unset($size);
$textBottomPosition = $height-1;
$textRightPosition = $width;
$black = new ImagickPixel('#000000');
$gray = new ImagickPixel('#C0C0C0');
$textRight = 0;
$textLeft = 0;
$textBottom = 0;
$textTop = $height;
$foundGray = false;
for($x= 0; $x < $width; ++$x) {
for($y = 0; $y < $height; ++$y) {
$pixel = $imagick->getImagePixelColor($x, $y);
$color = $pixel->getColor();
// remove alpha component
$pixel->setColor('rgb(' . $color['r'] . ','
. $color['g'] . ','
. $color['b'] . ')');
// find the first gray pixel and ignore pixels below the gray
if( $pixel->isSimilar($gray, .25) ) {
$foundGray = true;
break;
}
// find the text boundaries
if( $foundGray && $pixel->isSimilar($black, .25) ) {
if( $textLeft === 0 ) {
$textLeft = $x;
} else {
$textRight = $x;
}
if( $y < $textTop ) {
$textTop = $y;
}
if( $y > $textBottom ) {
$textBottom = $y;
}
}
}
}
$textWidth = $textRight - $textLeft;
$textHeight = $textBottom - $textTop;
$imagick->cropImage($textWidth+10, $textHeight+10, $textLeft-5, $textTop-5);
$imagick->scaleImage($textWidth*10, $textHeight*10, true);
$textFilePath = tempnam('/temp', 'text-ocr-') . '.png';
$imagick->writeImage($textFilePath);
$text = str_replace(' ', '', shell_exec('gocr ' . escapeshellarg($textFilePath)));
unlink($textFilePath);
var_dump($text);
You need ImageMagick extension and GOCR installed to run it.
If you can't or don't want to install the ImageMagick extension, I'll send you a GD version with a function to calculate colors distances (it's just an extended Pythagorean Theorem).
Don't forget to set the $filePath value.
The image shows that it looks for a gray pixel to change the $foundGray flag.
After that, it looks for the first and last pixels from the left and from the top.
It crops the image with some padding, the resulting image is resized and it's saved to a temporary file. After that, it's easy to use gocr (or any other OCR command or library). The temporary file can be removed after that.
Improve the quality of the image of the numbers before you start the OCR. Use a drawing program to improve the quality (bigger size, straight lines).
You can either modify the PHP scripts and adapt the pattern recognition to your needs.
https://github.com/ogres/PHP-OCR/blob/master/Image2String.php
Or try out other OCR tools:
https://github.com/thiagoalessio/tesseract-ocr-for-php
This one might be a little confusing. I'm using AMCharts with rails. Amcharts comes with a PHP script to export images called "export.php"
I'm trying to figure out how to take the code in export.php and put it into a controller.
Here is the code:
<?php
// amcharts.com export to image utility
// set image type (gif/png/jpeg)
$imgtype = 'jpeg';
// set image quality (from 0 to 100, not applicable to gif)
$imgquality = 100;
// get data from $_POST or $_GET ?
$data = &$_POST;
// get image dimensions
$width = (int) $data['width'];
$height = (int) $data['height'];
// create image object
$img = imagecreatetruecolor($width, $height);
// populate image with pixels
for ($y = 0; $y < $height; $y++) {
// innitialize
$x = 0;
// get row data
$row = explode(',', $data['r'.$y]);
// place row pixels
$cnt = sizeof($row);
for ($r = 0; $r < $cnt; $r++) {
// get pixel(s) data
$pixel = explode(':', $row[$r]);
// get color
$pixel[0] = str_pad($pixel[0], 6, '0', STR_PAD_LEFT);
$cr = hexdec(substr($pixel[0], 0, 2));
$cg = hexdec(substr($pixel[0], 2, 2));
$cb = hexdec(substr($pixel[0], 4, 2));
// allocate color
$color = imagecolorallocate($img, $cr, $cg, $cb);
// place repeating pixels
$repeat = isset($pixel[1]) ? (int) $pixel[1] : 1;
for ($c = 0; $c < $repeat; $c++) {
// place pixel
imagesetpixel($img, $x, $y, $color);
// iterate column
$x++;
}
}
}
// set proper content type
header('Content-type: image/'.$imgtype);
header('Content-Disposition: attachment; filename="chart.'.$imgtype.'"');
// stream image
$function = 'image'.$imgtype;
if ($imgtype == 'gif') {
$function($img);
}
else {
$function($img, null, $imgquality);
}
// destroy
imagedestroy($img);
?>
There are some versions in existence in a thread I found here: http://www.amcharts.com/forum/viewtopic.php?id=341
But I have a feeling the PHP code above has changed since then - because neither implementation worked for me.
What this code more or less dose is grabs the informations, that were sent to the script (POST).
The informations include the height and width of the picture and the RGB values of every pixel. The script draws every pixel and sends the images at the end to the client.
You can use Rmagick's method to draw a pixel. This will give you the same result.
The incomming post data looks like this:
height = number -> cast to int
width = number -> cast to int
// first row with a repeating part of R:G:B,R:G:B,... (n = width)
r0 = 255:0:0,150:120:0,77:88:99,...
r1 = ...
.
.
r100 = ... -> the row count is the height - 1
Actually, I found a discussion about speeding up pixel by pixel drawing.
So apparently I was running into other errors which made me think the already existing code didnt work. However, the code on the thread I linked to in the original question does in fact work!