I'm adding some heading in Russian on the image using newPseudoImage and CAPTION.
Heading can have some entities like — etc, so I clean it with html_entity_decode.
In some cases it works well, but in some cases text after decoded entity goes in wrong encoding, like that:
look at second line - there is the problem
Please, help me to find the solution.
If I use annotateImage - everything is nice.
If I use newPseudoImage and LABEL - everything is nice.
The problem appears only with newPseudoImage and CAPTION (but I have to CAPTION, cause I need to scale my headings depending on their length).
At the same time I have to use these entities in headings (i.e. to avoid wrapping of the dash to the next line).
Some headings shows nice:
'Путешествие в Карелию' - is fine, but 'Нуорунен в Карелии' - shows with error.
My code:
$postTitle = 'Нуорунен в Карелии';
$postTitle = html_entity_decode($postTitle);
$mainImage = new Imagick('source.jpg');
$title = new Imagick();
$colorize = new Imagick();
$mainImage->setImageCompressionQuality(85);
$title->setBackgroundColor('transparent');
$title->setFont('/fonts/roboto-slab_semibold.ttf');
$title->setGravity(imagick::GRAVITY_CENTER);
$title->newPseudoImage( 780, 480, "CAPTION:" . $postTitle );
$colorize->newImage(1, 1, new ImagickPixel('#f8bb00'));
$title->clutImage($colorize);
$colorize->destroy();
$mainImage->compositeImage($title, imagick::COMPOSITE_DEFAULT, 210, 110);
$mainImage->writeImage('image.jpg');
Related
With SetaPDF from Setasign, when importing a page from a document and trying to paint a line of text to that page the text shows up upside down, as if it was reflected on the x axis.
I tried drawing the text vía SetaPDF_Core_Text_Block() and also with the canvas text() helper
$reader = new \SetaPDF_Core_Reader_File($original);
$tempWriter = new\SetaPDF_Core_Writer_File(Routes::DOCUMENT_UPLOAD.'test.pdf');
$document = \SetaPDF_Core_Document::load($reader, $tempWriter);
$portada = $document->getCatalog()->getPages()->getPage(1);
$canvas = $portada->getCanvas();
$font = \SetaPDF_Core_Font_Standard_HelveticaBold::create($document);
$text = new \SetaPDF_Core_Text_Block($font, 24);
$text->setText('ABCDEF');
$text->setAlign(\SetaPDF_Core_Text::ALIGN_LEFT);
$text->setBackgroundColor('#FFFFFF');
$text->draw($canvas, 0 ,0);
The result in this image:
Setasign support provided the page in manuals to understand this issue:
https://manuals.setasign.com/setapdf-core-manual/canvas/#index-2-1
TLDR;
I was working with a PDF that was previously rotated (didn't know) so when working with existing pages you have to use:
$pageImported = $document->getCatalog()->getPages()->getPage(1);
$pageImported ->getStreamProxy()->encapsulateExistingContentInGraphicState();
$canvas = $portada->getCanvas();
So you encapsulate the content and do not work with the page original graphic state.
Trying to take a rectangular photo, crop it into a square region, and then mask it into a circular with a transparent background.
//$dims is an array with the width, height, x, y of the region in the rectangular image (whose path on disk is $tempfile)
$circle = new \Imagick();
$circle->newImage($dims['w'], $dims['h'], 'none');
$circle->setimageformat('png');
$circle->setimagematte(true);
$draw = new \ImagickDraw();
$draw->setfillcolor('#ffffff');
$draw->circle($dims['w']/2, $dims['h']/2, $dims['w']/2, $dims['w']);
$circle->drawimage($draw);
$imagick = new \Imagick();
$imagick->readImage($tempfile);
$imagick->setImageFormat( "png" );
$imagick->setimagematte(true);
$imagick->cropimage($dims['w'], $dims['h'], $dims['x'], $dims['y']);
$imagick->compositeimage($circle, \Imagick::COMPOSITE_DSTIN, 0, 0);
$imagick->writeImage($tempfile);
$imagick->destroy();
The result is the rectangular image, uncropped and without being circularized. What am I doing wrong?
Example image:
Example input for $dims = {"x":253,"y":0,"x2":438.5,"y2":185.5,"w":185.5,"h":185.5}
Rough expected output:
Image i'm getting looks roughly like the input image.
For those with an older version of Imagick (setimagematte does not exist in version lower than 6.2.9), I came up with an easy solution. The thing here is to copy opacity from the mask to the original image.
Original Image:
Mask:
Result:
The code:
$base = new Imagick('original.jpg');
$mask = new Imagick('mask.png');
$base->compositeImage($mask, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
$base->writeImage('result.png');
You could use an Imagick black circle as mask but I though it wasn't perfect so I used my own.
Of course you will certainly have to resize / crop your images but that's another story.
Hope this helps.
J.
This works for me:
<?php
//$dims is an array with the width, height, x, y of the region in the rectangular image (whose path on disk is $tempfile)
$tempfile = 'VDSlU.jpg';
$outfile = 'blah.png';
$circle = new Imagick();
$circle->newImage(185.5, 185.5, 'none');
$circle->setimageformat('png');
$circle->setimagematte(true);
$draw = new ImagickDraw();
$draw->setfillcolor('#ffffff');
$draw->circle(185.5/2, 185.5/2, 185.5/2, 185.5);
$circle->drawimage($draw);
$imagick = new Imagick();
$imagick->readImage($tempfile);
$imagick->setImageFormat( "png" );
$imagick->setimagematte(true);
$imagick->cropimage(185.5, 185.5, 253, 0);
$imagick->compositeimage($circle, Imagick::COMPOSITE_DSTIN, 0, 0);
$imagick->writeImage($outfile);
$imagick->destroy();
?>
<img src="blah.png">
I always try to keep the code simple until I get it working and then add all the variables etc. That could be the problem or there could be a problem with your version of Imagick.
It's namespaced
Still do not know what it means! - I am getting a bit behind with php as I do not use it very much these days.
There's also another workaround that I suggest here :
// create an imagick object of your image
$image = new \Imagick('/absolute/path/to/your/image');
// crop square your image from its center (100px witdh/height in my example)
$image->cropThumbnailImage(100, 100);
// then round the corners (0.5x the width and height)
$image->roundCorners(50, 50);
// force the png format for transparency
$image->setImageFormat("png");
// write the new image
$image->writeImage('/absolute/path/to/your/new/image');
// done!
Many thanks to all previous answers and contributors that lead me to this code!
Feel free to test/comment my solution!
I stumbled upon this as I was searching for a similar solution for Ruby on Rails, notice that this Stackoverflow question uses vignette instead which seems to be a much simpler way to solve the problem.
I used vignette to solve my problem with rounded images in Ruby on Rails using Dragonfly.
Loads of answers on how to do it for a command line
convert /path/to/file/file.pdf[3] output.jpg
great... but what if I am using in memory processing, I am generating PDF with PDFlib and then output its buffer to a function that I want to generate jpg preview of selected page. How? My code :
[...]
$buf = $pdf->get_buffer();
//$buff is just a PDF stored in a string now.
$im = new Imagick();
$im->readimageblob($buf);
$im->setImageFormat("jpg");
$im->setimagecompressionquality(60);
$len = strlen($im);
header("Content-type: image/jpeg");
header("Content-Length: $len");
header("Content-Disposition: inline; filename=test.jpg");
echo $im;
This creates a jpeg but always returns last page of the PDF. I want to be able to choose which one will be converted. Is it doable without saving temporary files and using command line (exec('convert /path/to/file/file.pdf[3] output.jpg')) syntax?
Let me add that I tried
$im->readimageblob($buf[2]);
and it did not work :)
For the ones who is still searching for solution of reading specific page number from blob, please check this question Creating array populated by images from a PDF using PHP and ImageMagick
$img_array = array();
$im = new imagick();
$im->setResolution(150,150);
$im->readImageBlob($pdf_in);
$num_pages = $im->getNumberImages();
for($i = 0;$i < $num_pages; $i++)
{
$im->setIteratorIndex($i);
$im->setImageFormat('jpeg');
$img_array[$i] = $im->getImageBlob();
}
$im->destroy();
I'm loading the PDF binary into memory from Amazon S3 and then selecting the specific page I want using setIteratorIndex() followed by getImage()
function get_image_from_pdf($pdf_bytes, $page_num){
$im = new \Imagick();
$im->setResolution(150, 150);
$im->readImageBlob($pdf_bytes);
$im->setIteratorIndex($page_num);
$im = $im->getImage();
$im->setImageFormat('png');
return $im->getImageBlob();
}
version 3.0.1
being on the last image or first image in imagic object
$image_obj = new Imagick("test.pdf"); then you on last image in $image_obj
if you use
fp_pdf = fopen("test.pdf", 'rb');
$image_obj = new Imagick();
$image_obj -> readImageFile($fp_pdf);
then you on the first image in $image_obj
In second case to switch to last image you can do
fp_pdf = fopen("test.pdf", 'rb');
$image_obj = new Imagick();
$image_obj -> readImageFile($fp_pdf,2); // 2 can be any positive number?
then you on the last image in $image_obj
echo $image_obj->getNumberImages() // Returns the number of images in the object
then
if ($image_obj->hadPreviousImage)
$image_obj->previousImage() //Switch to the previous image in the object
}
or
if ($image_obj->hasNextImage()) {
$image_obj->nextImage()) //Switch to the next image in the object
}
e.g. if you have 6 images total and you need 4th then do from the end
$image_obj->previousImage()
$image_obj->previousImage()
$image_obj->setImageFormat("png");
header("Content-Type: image/png");
echo $image_obj;
EDIT: Another find is that you can
foreach($image_obj as $slides) {
echo "<br>".$Obj_img->getImageWidth();
//or wehatever you need to do.
}
EDIT 2: Very simple solution would be to use this function $image_obj->setIteratorIndex(4) count starts with zero.
It's not good news unfortunately, but I can definitively say that, as of time of writing, the ImageMagick (and PHP libraries) don't support the page notation that you're trying to use. (For people from the future finding this: I'm checking php-imagick-3.0.1 and imagemagick-6.6.0.4).
I'm trying to do the exact same thing as you, and I've just spent the last few hours trawling through the source, trying to figure out what it does and how it gets the pages, and it looks like it simply won't use it when reading from a stream (ie. the readBlob() call).
As such, I'm just going to be putting it in a temporary file and reading it from there instead. Not as elegant, but it'll work.
I'm trying to add some text on a Imagick object.
However I use setTextEncoding() function, it still doesn't work.
.......
$draw = new ImagickDraw();
$draw->setTextEncoding('utf-8');
$draw->setFont($fpath.'/process/ARIAL.TTF');
$draw->setFontSize(80);
$draw->setFillColor("#ffffff");
/*** annotate the text on the image ***/
$imageOrg->annotateImage($draw, 60, 100, 0, "onur küçükkeçe");
........
and as a result I get,
onur küçükkeçe
Any idea why it's not working?
Thanks in advance.
UPDATE
if I set a $text variable to something like chr(252) then I get a proper result
$text=chr(252);
$imageOrg->annotateImage($draw, 60, 100, 0, $text);
as a result I get
ü
UPDATE II
Finally I found what causing this.
The problem occurs because the charset of the document is not defined but if set a charset for the script then imagick doesn't work because the type of the document needs to be set to image/png.
But I don't know how can I fix it.
Ok. I found the solution.
php utf8_decode() function solves the problem
.......
$draw = new ImagickDraw();
$draw->setTextEncoding('utf-8');
$draw->setFont($fpath.'/process/ARIAL.TTF');
$draw->setFontSize(80);
$draw->setFillColor("#ffffff");
/*** annotate the text on the image ***/
$imageOrg->annotateImage($draw, 60, 100, 0, utf8_decode("onur küçükkeçe"));
........
I'm in need of drawing continuous lines in PHP. GD apparently can't do it, but imagick has a polyline function. Now my test code:
$image = new Imagick();
$image->newImage(100, 100, new ImagickPixel('white'));
$draw = new ImagickDraw();
$draw->setStrokeWidth(3);
$line = array(
array('x'=>10, 'y'=>10),
array('x'=>50, 'y'=>10),
array('x'=>70, 'y'=>40),
array('x'=>30, 'y'=>60),
);
$draw->polyline($line);
$image->drawImage($draw);
$image->setImageFormat('jpg');
$image->writeImage("test.jpg");
produces a CLOSED (and filled) polygon for reasons I can't fathom. What's going on here and how do I fix it? I want it to stop at the last point and NOT close or fill.
If there's a solution in GD, I'm also happy. No, I can't simply string individual lines together, because I want thickness in the lines.
It is possible to draw an open polyline by drawing in one direction and then back with the same points table, without first and last point (they are not neccessary)
so, if you have
$line = array(
array('x'=>10, 'y'=>10),
array('x'=>50, 'y'=>10),
array('x'=>70, 'y'=>40),
array('x'=>30, 'y'=>60),
);
you should add
$line[] = array('x'=>70, 'y'=>40);
$line[] = array('x'=>50, 'y'=>10);
and then
$draw->polyline($line);
and you would get what you want.
Looks at the documentation of GD polyline. Perhaps it that you want : http://bonzoli.com/sourcecode/index.php/man/GD::Polyline
You should define both the stroke and fill colors, otherwise Imagick will "fill" the area bounded by the line and the start and finish points with the fill color. The defaults are for these values are "white" and "black".
This code will work as intended, i.e. it produces a mirrored "c" shaped object with black lines of 3 pixels width:
$image = new Imagick();
$image->newImage(100, 100, 'white');
$draw = new ImagickDraw();
$draw->setStrokeWidth(3);
$draw->setStrokeColor ("black");
$draw->setFillColor ("none");
$line = array(
array('x'=>10, 'y'=>10),
array('x'=>50, 'y'=>10),
array('x'=>70, 'y'=>40),
array('x'=>30, 'y'=>60),
);
$draw->polyline($line);
$image->drawImage($draw);
$image->writeImage("test.jpg");