I've got a problem with rotating text on PDF.
My form before any rotation looks like this:
The positions seems to be correct, when I set rotation to 0.
But the rotation may vary from the item and can be anything.
Rotation from the data is, that LEFT has -90 degrees, right +90 degrees and bottom +180 degrees.
When I set it to it, the only correct text is TOP (because rotation is 0). The rest of text items are totally wrong, some even not visible.
I've managed to do the rotation with images, example:
The code I've used for image rotation is:
$pdf->StartTransform();
$pdf->Rotate(
-$item['rotation'],
$imagePosition['x'] + $imageSize['x'] / 2,
$imagePosition['y'] + $imageSize['y'] / 2);
$pdf->Image(
$filename,
$imagePosition['x'],
$imagePosition['y'],
$imageSize['x'],
$imageSize['y'],
strtoupper($imagick->getImageFormat()),
'',
'',
false,
(int)$dpi,
'',
false,
false,
0,
false,
false,
false
);
$pdf->StopTransform();
For image it's easy, because I have position and size.
How to do the propper Text rotation?
I was trying to write that text in Imagick and from there get text width and height and try to do the same like for the image, but without any success.
My code for text rotation:
$pdf->StartTransform();
$pdf->Rotate(
-$item['rotation'],
$fontPosition['x'] - $textSize['x'] / 2,
$fontPosition['y'] - $textSize['y'] / 2
);
list($r, $g, $b) = sscanf($item['attributes']['color'], "#%02x%02x%02x");
$pdf->SetTextColor($r, $g, $b);
$pdf->SetFont($fontName, '', $fontSize, '', false);
$pdf->Text($fontPosition['x'], $fontPosition['y'], $item['value'], 0, 0);
$pdf->StopTransform();
Note that Text size variable is probably not correct.
Any ideas how to write image rotated to PDF without so many complications in TCPDF?
For FPDF I found extension:
http://www.fpdf.org/en/script/script31.php
When I've tried to implement it to TCPDF, then the text was squares, instead of the real content.
PLZ HELP :D
You need to keep track of the current "origin" of the coordinate system, where 0,0 is after the current transformation.
You also need to keep in mind that transformations are combined, and earlier transformations affect later ones.
If you rotate 90 degrees clockwise, then move in +Y (up), you actually move in +X (right)... because the entire coordinate system has rotated.
If on the other hand, you moved (translated), then rotated, your new origin around which you would rotate would be wherever it was you wanted +Y.
So if you do your transformations in the correct order, and know which direction things are drawn, you can place things wherever you want.
If you rotate an untranslated image 90 degrees clockwise, it's rotated around the origin (0,0) and therefore disappears. On the other hand, if you move the origin to take the image's movement around the origin into account, it'll land right where you wanted.
One of the tricks I used when messing around with PDF transformation features/bugs was\s to expand the page by a couple thousand units in every direction. IIRC, an 8.5"x11" page has a media box of [0 0 612 792] (wow, I actually remembered that correctly: go me). If you set the media box to [-2000 -2000 2612 2792], then you have to work hard to screw up your transformation so badly that you can't see where your text/image/whatever landed (though it can, and has, been done).
Note that if your page also has a crop box, you have to mess with it too.
Ok, I've sort it out. Working code for text rotation should be like this:
$pdf->StartTransform();
$pdf->Rotate(
-$item['rotation'],
$fontPosition['x'] + $textSize['x'] / 2,
$fontPosition['y'] + $textSize['y'] / 2
);
list($r, $g, $b) = sscanf($item['attributes']['color'], "#%02x%02x%02x");
$pdf->SetTextColor($r, $g, $b);
$pdf->SetFont($fontName, '', $fontSize, '', false);
$pdf->Text($fontPosition['x'], $fontPosition['y'], $item['value'], 0, 0);
$pdf->StopTransform();
Wrong was:
$fontPosition['x'] - $textSize['x'] / 2,
$fontPosition['y'] - $textSize['y'] / 2
Should be:
$fontPosition['x'] + $textSize['x'] / 2,
$fontPosition['y'] + $textSize['y'] / 2
I've tried this combination before, but my textSize wasn't correct (I was calculating it with imagick). In my case, I've counted percentage of our draw area that text is taking and after that I correctly counted it in mm.
Hope it helps someone else.
Related
I have an 8 bit PNG (original is here: https://i.ibb.co/s3024Y8/1000x1500-autumn-leaves.png) and I want to change the colour of the leaves.
I have a list of the hex colours that need to be changed and a list of the colours they need to be changed to - all to be done dynamically. I'm working towards a solution that has to work off those lists - but that can wait. For this question, being able to change the colour of just one leaf will do.
After changing the colour of the leaf I need the image to retain its transparent areas (so I can lay it on top of another image - that part I can do).
I am quite new to the GD library and am getting confused. I understand that an 8-bit PNG (as created in Photoshop) has full alpha - and that needs to be preserved.
That leads me to the start of my code:
$im = ImageCreateFromPNG('https://i.ibb.co/s3024Y8/1000x1500-autumn-leaves.png');
ImageAlphaBlending($im, false);
imagesavealpha($im, true);
Everything I've tried after that fails miserably. By that, I mean that the colour doesn't change. No error message, just no change in colour. It seems that the following has no effect when using that image:
$myColourToChange = imagecolorallocatealpha($im, 100, 24, 11, 0); // colour of dark brown leaf
imagecolorset($im, $myColourToChange, 255, 0, 0, 0); // try changing it to red, fully opaque
I've even tried (which gives 6559755)
$myColourToChange = imagecolorresolvealpha($im, 100, 24, 11, 0);
I think things are failing (ie the colour doesn't change) because the image I'm using isn't a paletted image in the way that GD needs it to be, in order for me to use imagecolorset
I've tried converting it to one using imagetruecolortopalette but that gives unpredictable results on the image, with all sorts of artifacts not visibly present in the original image - and the colour values change slightly.
Note that the following are representations if you want to experiment, use the image link above.
Original image before imagetruecolortopalette - dark brown leaf colour: #64180B
After imagetruecolortopalette - dark brown leaf colour: #631A0C
So how do I specify the colour that needs to be changed and then change it to the given colour? If it means I must use imagetruecolortopalette how do I deal with the artifacts and the colour change I get when using it?
(I won't know in advance the location of colours that need changing, so imagecolorat won't help)
Thanks in advance :)
I have the following php code:
<?php
$image = imagecreatefrompng("captcha_background.png");
$imgcolor = imagecolorallocate($image, 0, 0, 0);
imagesetthickness($image, 2);
imageline($image, 0, 25, 40, 90, $imgcolor);
?>
The method "imageline" draws a straight line on my image from the coordinates 0 (x) 25 (y) to 40 (x) 90 (y).
The result is the following image:
What I'm confused about is the reverse of the bottom and the top when using coordinate systems in php.
Normally 0 (The starting point) would be in the lower left corner, but when assigning coordinates in the method "imageline" the 0 (Starting point) is located in the upper left corner?
Expected result:
(The image is 300x100 pixels)
Could someone please explain why this is happening?
This is not a mathematical graph. The typical coordinate system used in development (as far as I know) is to have the first quadrant at the lower right. That is, 0x0 is at the top left. This applies to all html elements that have widths and heights (the elements drop down, they do not fall up).
The motivation appears to be the fact that it's hard to tell how much height you have to work with without knowing the absolute height of the image, which you may not know at any given time, and which may change frequently.
That's how the coordinates are defined in GD, nothing to worry about.
http://www.php.net/manual/en/function.imagedashedline.php :
y1: Upper left y coordinate 0, 0 is the top left corner of the image.
I believe this is the standard for the GD image library as they define the natural origin as the top-left corner.
I've run into another problem with GD and PHP.
I'm successfully writing text to an image.
However, I've encountered a case where it would be beneficial to place the text - instead of directly on the image - on a rectangle (or any shape) to create a solid background for the text where the image it's being placed on might not allow the text to be read very easily.
My two ideas are, in order of preference:
Fill the background with the color as it writes the text
Write the text to an appropriately sized image with a solid background, and then overlay the image onto the target
I can't figure out how to do #1 and #2 seems overly complex and I don't know how to determine the dimensions of the text so that I can create a new image for it.
For clarity, here is the output that isn't very good:
And here's how I'd like it to look, with a tight box behind the text of any color:
I'm open to any suggestions, but drawing the color on the fly without any other images or hackiness would obviously be my first choice.
Update:
After #Dan suggested using `imagettftext', I decided that it was high time I added support for that function to my library. Everything is working as would be expected except for one major issue.
The text that's written to the image is still transparent, even when written to a solid background (0 transparency).
Here's a script I wrote to test:
<?php
set_include_path('backbone:global:jquery');
require_once('Image.php');
$scout = new Image();
$scout->source = "scout.jpg";
$result = $scout->Write->Font(25, 25, "A Fairly Long String", 12, "#FF0000", 0, "LiberationSans-Regular.ttf", 1, "#FFFF00", .4, 4);
if( !isset($_GET['dev']) )
{
header("Content-Type: " . $scout->contentType());
}
if( $result )
{
$scout->output();
}
?>
The files I used/required:
1. scout
2. liberation font
3. Image Manipulation Library
- Image
- ImageBase
- ImageCombine
- ImageDraw
- ImageManipulate
- ImageWrite
I apologize about all the files, it really only uses Image, ImageBase, ImageCombine, and ImageWrite, but the others are require_onceed by the loader.
Here's a sample of the output from the script above:
And here's output with zero transparency (fully opaque):
$result = $scout->Write->Font(25, 25, "A Fairly Long String", 12, "#FF0000", 0, "LiberationSans-Regular.ttf", 1, "#FFFF00", 1, 4);
Any ideas what could be causing this? It's EXTREMELY possible that it's my code somewhere, but it seems strange that it would work exactly as I thought it should except for this one bug.
In searching desperately for the answer to this problem, I stumbled upon this SO question that's tangentially related to my problem, and contains the answer.
I've never fully understood why you tell imagealphablending false when you want transparency, so I guess my failure to properly understand the code I'm using has led to this issue.
In any case, the following modified code works like a charm in my one single test case.
To write text to a background without forced 100% transparency for the character boxes, you must turn alpha blending ON while writing the text:
imagealphablending($text->handle, true);
$bool = imagettftext($text->handle, $textSize, $angle, $padding, abs($size[5]) + $padding, $this->AllocateColor($rgba[0]['r'], $rgba[0]['g'], $rgba[0]['b'], $rgba[0]['alpha']), $font, $string);
imagealphablending($text->handle, false);
Maybe this will do the job for you if you switch to ttf. http://php.net/manual/en/function.imagettfbbox.php
I am placing text on pdf something like this using Zend_Pdf:
$page1->drawText( 'Hello World!', 100, 100 );
But now I want to rotate text to 90 degree on pdf. How is it possible ??
Thanks
How about:
$page->rotate(0, 0, M_PI/12);
$page->drawText('Hello world!', 100, 100);
See also: Zend PDF tutorial
You can use rotate() which uses Radians to determine the amount to rotate.
For example:
// Rotate at the X and Y coordinates
// and 90 Degrees Counter-Clockwise (which is 1.5708 Radians)
$page->rotate(300, 300, 1.5708);
Use this tool calculate the value from degrees. Whilst you can use the constants in PHP and divide (as the other answer suggests) I would not recommend that personally (as I find it much harder to work out what it equates to).
You do need to rotate back before continuing with the next lines (assuming you want them to be horizontal).
I have run into some trouble with the gd library's imagefilledpolygon().
For some reason some of my lines were ending up 1px out of place so I decided to debug it using imagepixelset to set the colour of my shapes points to red.
alt text http://www.degreeshowcase.com/other/1.gif
if you look at the picture you can see some of the points are inside the shape ... some are outside....its very illogical.
(the picture has been scaled up to make it more visible)
Does anyone have a solution?
Update:
My points for the shape above were: 0,0 40,0 40,20 20,20 20,40 0,40
I require that the height and width of the shape produced should be a multiple of 20.... but for some reason some of the shape ends up 21 px high or wide.
I have made a script to work out what the points would be to get the shape I wanted but I can not work out why and so I can't work out a script to correct all my shapes.
<?php
// set up array of points for polygon
$values = array(0,0, 39,0, 39,20, 19,20, 19,39, 0,39);
//My original values were 0,0 40,0 40,20 20,20 20,40 0,40
//I do not understand why some values require minus 1 and others can remain as they were (a multiple of 20)
// create image
$image = imagecreatetruecolor(40, 40);
// allocate colors
$bg = imagecolorallocate($image, 200, 200, 200);
$blue = imagecolorallocate($image, 0, 0, 255);
// fill the background
imagefilledrectangle($image, 0, 0, 39, 39, $bg);
// draw a polygon
imagefilledpolygon($image, $values, 6, $blue);
// flush image
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
?>
My guess is that you're mixing up width with position.
For example a line from 0px to 9px is 10px long... if you used length as the second parameter instead of position, it would end up 11px long.
If I could see some code I could confirm this.
Normal polygon rendering ensures that each pixel can only be in one polygon, if the 2 polygons share an edge. If you imagine drawing 2 squares, next to each other, so they share a common edge, you don't want to render the pixels along that edge twice.
There is an explanation of determining which pixels on the edge of a polygon should be considered inside the polygon here: http://www.gameprogrammer.com/5-poly.html
A common solution is to say that "pixels on the left and top edges of a polygon belong to the polygon and pixels on the right and bottom edges don't". I am not 100% sure what solution GD uses, as I could not find any documentation on it, but I expect it is something similar to this.
I spoke to the guy who currently develops the GD library he explained that it follows the 'Winding number algorithm' - can be found here. Having looked at my example image, it does match how the 'winding number algorithm' works, however the function should take this into account and produce the shape that was input.
As far as I can see the only way to accurately (to the pixel) generate a concave polygon with this function is to write another function that also applies the winding rule to your coordinates and adjusts them accordingly and then put it into the function.