TCPDF: why is the position of a multicell inherited from another element? - php

I an using TCPDF to create a PDF output.
At the left of the page, I'd like a couple of cells or blocks on top of each other with rotated text.
If the text is larger than the width (or actual height, when rotated), the text should be cut off.
I can make a unrotated box in a certain size at a certain position by doing this:
$roleheight=160/count($rolenames);
$x=4;
$y=18;
$w=$roleheight;
$h=20;
$text="The quick brown fox jumps over the lazy dog, The quick brown fox jumps over the lazy dog, The quick brown fox jumps over the lazy dog";
$pdf->MultiCell($w,$h,"",0,"J",true,0,4,18,true,0,false,true,5,'T',false);
$pdf->StartTransform();
$pdf->Rect($x, $y, $w, $h, 'CNZ');
$pdf->writeHTMLCell($w, $h, $x, $y, $text,0,0,false,true,"",false);
$pdf->StopTransform();
I can make a rotated box at a certain size by doing this:
$pdf->StartTransform();
$pdf->Rotate(90);
$pdf->MultiCell($w,$h,$text,0,"J",true,0,$x,$y-50,true,0,false,true,5,'T',false);
$pdf->StopTransform();
But the second box does not honor my position, and the first box cannot be rotated.
I tried adding $pdf->Rotate(90); to the first box before the Rect, between Rect and writeHTMLCell and after writeHTMLCell, but no luck
I tried alter the $x and $y of the second box, by adding or substracting a number (e.g. 100), but that places the box at a very unexpected location.
If I outcomment the FIRST box, the second box won't appear either. So somehow the position of box 2 is inherited from box 1. But how and why?

I still don't why the Multicell is having parameters X and Y, while those do not set the X- and Y-positions. I do have to add a SetXY in front of the multicell.
Furthermore, I don't know why I have to add a +10 at the x and the y to place the boxes at the correct locations.
But this did the trick:
$roleheight=160/count($rolenames);
$x=4;
$y=18;
$y=$y-$roleheight-2;
for ($i=0;$i<count($rolenames);$i++)
{
$x=($x+$roleheight)-$roleheight;
$y=($y+$roleheight)+1;
$w=$roleheight;
$h=20;
$pdf->SetXY($x+10, $y+10);
$pdf->StartTransform();
$pdf->Rotate(90);
$pdf->MultiCell($w,$h,$rolenames[$i],0,"L",true,0,$x,$y,true,0,false,false,5,'T',false);
$pdf->StopTransform();
}

Related

PHP Imagick stroke appears over fill

Attempting to do a simple watermark consisting of white text with black outline on top of an image. Problem is that the stroke color is appearing over (or at least affecting) the fill color. What I mean by "affecting" is that, for example, when I try a red stroke color the fill color becomes a slightly lighter red (ie red + white) because I have a fill color of white.
Image
Code:
$watermark_text = new ImagickDraw();
$watermark_text->setFont($font);
$watermark_text->setFontSize(25);
$watermark_text->setFillColor('white');
$watermark_text->setStrokeColor('#000000');
$watermark_text->setStrokeWidth(3);
$watermark_text->setStrokeOpacity(0.4);
$watermark_text->setStrokeAntialias(false);
$watermark_text->setGravity(Imagick::GRAVITY_SOUTHEAST);
I specified .4 opacity to see/show that the fill and solid are actually there, just not fully in the way I expect.
I am expecting the fill color to be 100% white, any ideas why it's not please?
Thank you
Edit
I see what's happening. The stroke width is such that it overlaps the fill internally. Is there a way to ensure that the fill color appears on top of the stroke so that the white is 100% white?
Looks like your point size is to small for stroke width of 3. The effects your experiencing can be demonstrated with the following example.
foreach(range(1,5) as $strokeWidth) {
// ... your example here ...
$watermark_text->setStrokeWidth($strokeWidth);
$watermark_text->annotation(0, 0, 'Font Width ' . $strokeWidth);
}
The easiest solution would be to clone your ImagickDraw, and draw the same text twice. First being only the outline w/out fill, and the actual filled text over the top of the first. If your not working with vectors, you can also drop the opacity steps in favor of rgba color.
// Create white text w/ common options
$font = 'Helvetica-Neue-Bold';
$watermark_text = new ImagickDraw();
$watermark_text->setFillColor('white');
$watermark_text->setFont($font);
$watermark_text->setFontSize(25);
$watermark_text->setStrokeAntialias(false);
$watermark_text->setStrokeColor('none');
$watermark_text->setStrokeWidth(0);
$watermark_text->setGravity(Imagick::GRAVITY_SOUTHEAST);
// Clone & set stroke attributes
$watermark_outline = clone $watermark_text;
$watermark_outline->setFillColor('none');
$watermark_outline->setStrokeColor('rgba(0,0,0, 0.4)');
$watermark_outline->setStrokeWidth(3);
// Set the text for both, and offset one to match stroke width
$watermark_outline->annotation(0, 0, 'Draw On Top');
$watermark_text->annotation(3, 0, 'Draw On Top');
// Draw stroke, then text
$image = new Imagick();
$image->setSize(200, 35);
$image->readImage('XC:LightGoldenrod');
$image->drawImage($watermark_outline);
$image->drawImage($watermark_text);
$image->writeImage('out.png');

Creating a dynamically sized text overlay with iMagick

I am trying to create a text overlay on an image using php and iMagick. The crucial part is that I want the text size to scale to fill a predefined box, so I do not want to set a fixed font size. I have found the code at ImageMagick - Text into rectangle but need to adapt this code for the iMagick. I seem to be able to do everything except the crucial part -size ${width}x100
Here's the code I have, with a few things that I have tried commented out
$draw = new ImagickDraw();
$draw->setGravity (Imagick::GRAVITY_SOUTHEAST);
$draw->setFont('Bookman-DemiItalic');
//$draw->setFontSize( 30 ); // don't want to set a fixed font size
$draw->setTextUnderColor('#00000075');
//$draw->setSize(0, 0, 200, 200); // error: Call to undefined method ImagickDraw::setSize()
//$draw->setViewBox(200, 200, 800, 800); // appears to do nothing
//$draw->rectangle(200,200,300,300); // this just draws a separate white box
$draw->annotation(100, 100, 'The quick brown fox jumps over the lazy dog'); // works, but it's a default (small) text size and the box simply fits the exact text with no border
$img->drawImage($draw);
Is there a size option that somehow I've missed in the docs?
Note that How can I wrap text using Imagick in PHP so that it is drawn as multiline text? partially addresses this issue, but it doesn't appear to offer scaling of the text to fit a fixed size box, rather a wrapping of text if the selected font size would cause the text to overrun. Though maybe I have misunderstood - I will give it a try!

PHP: imageftbbox

What is the application of a bounding box of a text in PHP imageftbbox?
In the example of the manual, it was used to determine the coordinates for the imagefttext function. Is it necessary? Maybe I am missing something here.
...
// First we create our bounding box
$bbox = imageftbbox(10, 0, $font, 'The PHP Documentation Group');
// This is our cordinates for X and Y
$x = $bbox[0] + (imagesx($im) / 2) - ($bbox[4] / 2) - 5;
$y = $bbox[1] + (imagesy($im) / 2) - ($bbox[5] / 2) - 5;
imagefttext($im, 10, 0, $x, $y, $black, $font, 'The PHP Documentation Group');
...
Flylib has extensive information about imagettfbox().
Here's some relevant information from the linked information:
(image copyright Flylib)
From image above, you can see that there are 4 point with 8 information (as the documentation already stated):
Array information Bounding box coordinate
===================================================================
0 X coordinate of the lower left hand corner
1 Y coordinate of the lower left hand corner
2 X coordinate of the lower right hand corner
3 Y coordinate of the lower right hand corner
4 X coordinate of the upper right hand corner
5 Y coordinate of the upper right hand corner
6 X coordinate of the upper left hand corner
7 Y coordinate of the upper left hand corner
Again, as stated by the documentation, this information is relative to text regardless of the angle. Therefore, if you rotate the text to 90 degree clockwise, the bounding box become:
Array information Bounding box coordinate
===================================================================
0 X coordinate of the upper left hand corner
1 Y coordinate of the upper left hand corner
2 X coordinate of the lower left hand corner
3 Y coordinate of the lower left hand corner
4 X coordinate of the lower right hand corner
5 Y coordinate of the lower right hand corner
6 X coordinate of the upper right hand corner
7 Y coordinate of the upper right hand corner
Another resources that I believe will help you better grasp the basic idea around bounding box:
http://ruquay.com/sandbox/imagettf/
the pair of functions imageftbbox and imagettfbbox allow you to know how much space will your text take on an image (the first one is used for free type text and the second for true type text)
this way, if you have an application that generates some images and writes on them variable text (user input or something like this) you can decide how/where to place that text using functions like imagefttext or imagettftext (same difference - the font)
so you can do something like:
$bbox = imagettfbbox(34, 0, 'myriadb.otf', strtoupper($name)); //font size 34, text in a horizontal line, use myriadb.otf as font, the user name as variable text
$text_width = $bbox[2]; // this is how much space the name will take
$margin = (CARD_WIDTH-($text_width))/2; // a constant knowing the width of the resulting card.. this way we will center the name..
imagettftext($image, 34, 0, $margin, $y, $white, 'myriadb.otf', strtoupper($name));// $image resource, 34-same size, same angle, the margin is the the x coordinate, fixed $y, color, font and the same text

Starting point in coordinate systems

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.

PHP - Bug in polygon GD library?

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.

Categories