Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ext/gd/gd.c
Original file line number Diff line number Diff line change
Expand Up @@ -3823,7 +3823,7 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int
{
zval *IM, *EXT = NULL;
gdImagePtr im=NULL;
long col = -1, x = -1, y = -1;
long col = -1, x = 0, y = 0;
int str_len, fontname_len, i, brect[8];
double ptsize, angle;
char *str = NULL, *fontname = NULL;
Expand Down
123 changes: 68 additions & 55 deletions ext/gd/libgd/gdft.c
Original file line number Diff line number Diff line change
Expand Up @@ -748,9 +748,9 @@ static char * gdft_draw_bitmap (gdCache_head_t *tc_cache, gdImage * im, int fg,
}

static int
gdroundupdown (FT_F26Dot6 v1, int updown)
gdroundupdown (FT_F26Dot6 v1, int roundup)
{
return (!updown) ? (v1 < 0 ? ((v1 - 63) >> 6) : v1 >> 6) : (v1 > 0 ? ((v1 + 63) >> 6) : v1 >> 6);
return (!roundup) ? v1 >> 6 : (v1 + 63) >> 6;
}

void gdFontCacheShutdown()
Expand Down Expand Up @@ -820,8 +820,6 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
double cos_a = cos (angle);
int len, i = 0, ch;
int x1 = 0, y1 = 0;
int xb = x, yb = y;
int yd = 0;
font_t *font;
fontkey_t fontkey;
char *next;
Expand Down Expand Up @@ -982,9 +980,6 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
penf.y = (penf.y - 32) & -64; /* round to next pixel row */
x1 = (int)(- penf.y * sin_a + 32) / 64;
y1 = (int)(- penf.y * cos_a + 32) / 64;
xb = x + x1;
yb = y + y1;
yd = 0;
pen.x = pen.y = 0;
previous = 0; /* clear kerning flag */
continue;
Expand Down Expand Up @@ -1074,23 +1069,32 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
/* retrieve kerning distance and move pen position */
if (use_kerning && previous && glyph_index) {
FT_Get_Kerning(face, previous, glyph_index, ft_kerning_default, &delta);
pen.x += delta.x;
pen.x += (int)(delta.x * cos_a);
pen.y -= (int)(delta.x * sin_a);
penf.x += delta.x;
}

/* load glyph image into the slot (erase previous one) */
if (FT_Load_Glyph(face, glyph_index, render_mode)) {
if (tmpstr) {
gdFree(tmpstr);
if (brect) { /* only if need brect */
/* load glyph image into the slot (erase previous one) */
if (FT_Load_Glyph(face, glyph_index, render_mode | FT_LOAD_IGNORE_TRANSFORM)) {
if (tmpstr) {
gdFree(tmpstr);
}
gdCacheDelete(tc_cache);
gdMutexUnlock(gdFontCacheMutex);
return "Problem loading glyph";
}

/* transform glyph image */
if (FT_Get_Glyph(slot, &image)) {
if (tmpstr) {
gdFree(tmpstr);
}
gdCacheDelete(tc_cache);
gdMutexUnlock(gdFontCacheMutex);
return "Problem loading glyph";
}
gdCacheDelete(tc_cache);
gdMutexUnlock(gdFontCacheMutex);
return "Problem loading glyph";
}

/* transform glyph image */
FT_Get_Glyph(slot, &image);
if (brect) { /* only if need brect */
FT_Glyph_Get_CBox(image, ft_glyph_bbox_gridfit, &glyph_bbox);
glyph_bbox.xMin += penf.x;
glyph_bbox.yMin += penf.y;
Expand All @@ -1100,17 +1104,11 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
glyph_bbox.xMax += slot->metrics.horiAdvance;
}
if (!i) { /* if first character, init BB corner values */
yd = slot->metrics.height - slot->metrics.horiBearingY;
bbox.xMin = glyph_bbox.xMin;
bbox.yMin = glyph_bbox.yMin;
bbox.xMax = glyph_bbox.xMax;
bbox.yMax = glyph_bbox.yMax;
} else {
FT_Pos desc;

if ( (desc = (slot->metrics.height - slot->metrics.horiBearingY)) > yd) {
yd = desc;
}
if (bbox.xMin > glyph_bbox.xMin) {
bbox.xMin = glyph_bbox.xMin;
}
Expand All @@ -1127,7 +1125,35 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
i++;
}

/* increment (unrotated) pen position */
penf.x += slot->metrics.horiAdvance;

if (render) {
if (!brect || angle != 0) {
/* reload the rotated glyph (for bbox we needed FT_LOAD_IGNORE_TRANSFORM - bbox is rotated later) */
FT_Done_Glyph(image);

/* load glyph image into the slot (erase previous one) */
if (FT_Load_Glyph(face, glyph_index, render_mode)) {
if (tmpstr) {
gdFree(tmpstr);
}
gdCacheDelete(tc_cache);
gdMutexUnlock(gdFontCacheMutex);
return "Problem loading glyph";
}

/* transform glyph image */
if (FT_Get_Glyph(slot, &image)) {
if (tmpstr) {
gdFree(tmpstr);
}
gdCacheDelete(tc_cache);
gdMutexUnlock(gdFontCacheMutex);
return "Problem loading glyph";
}
}

if (image->format != ft_glyph_format_bitmap && FT_Glyph_To_Bitmap(&image, ft_render_mode_normal, 0, 1)) {
FT_Done_Glyph(image);
if (tmpstr) {
Expand All @@ -1150,8 +1176,6 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
pen.x += image->advance.x >> 10;
pen.y -= image->advance.y >> 10;

penf.x += slot->metrics.horiAdvance;

FT_Done_Glyph(image);
}

Expand All @@ -1160,36 +1184,25 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi
double d1 = sin (angle + 0.78539816339744830962);
double d2 = sin (angle - 0.78539816339744830962);

/* make the center of rotation at (0, 0) */
FT_BBox normbox;

normbox.xMin = 0;
normbox.yMin = 0;
normbox.xMax = bbox.xMax - bbox.xMin;
normbox.yMax = bbox.yMax - bbox.yMin;

brect[0] = brect[2] = brect[4] = brect[6] = (int) (yd * sin_a);
brect[1] = brect[3] = brect[5] = brect[7] = (int)(- yd * cos_a);

/* rotate bounding rectangle */
brect[0] += (int) (normbox.xMin * cos_a - normbox.yMin * sin_a);
brect[1] += (int) (normbox.xMin * sin_a + normbox.yMin * cos_a);
brect[2] += (int) (normbox.xMax * cos_a - normbox.yMin * sin_a);
brect[3] += (int) (normbox.xMax * sin_a + normbox.yMin * cos_a);
brect[4] += (int) (normbox.xMax * cos_a - normbox.yMax * sin_a);
brect[5] += (int) (normbox.xMax * sin_a + normbox.yMax * cos_a);
brect[6] += (int) (normbox.xMin * cos_a - normbox.yMax * sin_a);
brect[7] += (int) (normbox.xMin * sin_a + normbox.yMax * cos_a);
/* rotate bounding rectangle (at 0, 0) */
brect[0] = (int) (bbox.xMin * cos_a - bbox.yMin * sin_a);
brect[1] = (int) (bbox.xMin * sin_a + bbox.yMin * cos_a);
brect[2] = (int) (bbox.xMax * cos_a - bbox.yMin * sin_a);
brect[3] = (int) (bbox.xMax * sin_a + bbox.yMin * cos_a);
brect[4] = (int) (bbox.xMax * cos_a - bbox.yMax * sin_a);
brect[5] = (int) (bbox.xMax * sin_a + bbox.yMax * cos_a);
brect[6] = (int) (bbox.xMin * cos_a - bbox.yMax * sin_a);
brect[7] = (int) (bbox.xMin * sin_a + bbox.yMax * cos_a);

/* scale, round and offset brect */
brect[0] = xb + gdroundupdown(brect[0], d2 > 0);
brect[1] = yb - gdroundupdown(brect[1], d1 < 0);
brect[2] = xb + gdroundupdown(brect[2], d1 > 0);
brect[3] = yb - gdroundupdown(brect[3], d2 > 0);
brect[4] = xb + gdroundupdown(brect[4], d2 < 0);
brect[5] = yb - gdroundupdown(brect[5], d1 > 0);
brect[6] = xb + gdroundupdown(brect[6], d1 < 0);
brect[7] = yb - gdroundupdown(brect[7], d2 < 0);
brect[0] = x + gdroundupdown(brect[0], d2 > 0);
brect[1] = y - gdroundupdown(brect[1], d1 < 0);
brect[2] = x + gdroundupdown(brect[2], d1 > 0);
brect[3] = y - gdroundupdown(brect[3], d2 > 0);
brect[4] = x + gdroundupdown(brect[4], d2 < 0);
brect[5] = y - gdroundupdown(brect[5], d1 > 0);
brect[6] = x + gdroundupdown(brect[6], d1 < 0);
brect[7] = y - gdroundupdown(brect[7], d2 < 0);
}

if (tmpstr) {
Expand Down
34 changes: 18 additions & 16 deletions ext/gd/tests/bug43073_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ $delta_t = 360.0 / 16; # Make 16 steps around
$g = imagecreate(800, 800);
$bgnd = imagecolorallocate($g, 255, 255, 255);
$black = imagecolorallocate($g, 0, 0, 0);
$red = imagecolorallocate($g, 255, 0, 0);
$x = 100;
$y = 0;
$cos_t = cos(deg2rad($delta_t));
$sin_t = sin(deg2rad($delta_t));
for ($angle = 0.0; $angle < 360.0; $angle += $delta_t) {
$bbox = imagettftext($g, 24, $angle, 400+$x, 400+$y, $black, $font, 'ABCDEF');
imagepolygon($g, $bbox, 4, $red);
$s = vsprintf("(%d, %d), (%d, %d), (%d, %d), (%d, %d)\n", $bbox);
echo $s;
$temp = $cos_t * $x + $sin_t * $y;
Expand All @@ -33,19 +35,19 @@ imagepng($g, "$cwd/bug43073.png");
--CLEAN--
<?php @unlink(dirname(__FILE__) . '/bug43073.png'); ?>
--EXPECTF--
(500, 400), (610, 400), (610, 376), (500, 376)
(492, 363), (591, 322), (580, 295), (480, 336)
(470, 331), (548, 254), (527, 233), (449, 310)
(439, 309), (483, 202), (461, 193), (416, 299)
(400, 300), (400, 183), (380, 183), (380, 300)
(362, 307), (316, 195), (291, 205), (337, 318)
(330, 329), (246, 244), (224, 265), (308, 350)
(308, 360), (202, 316), (190, 344), (296, 388)
(300, 400), (187, 400), (187, 425), (300, 425)
(306, 437), (195, 483), (206, 510), (318, 464)
(328, 469), (240, 557), (260, 578), (349, 491)
(359, 491), (312, 607), (334, 616), (382, 501)
(400, 500), (400, 618), (419, 618), (419, 500)
(436, 493), (483, 607), (507, 597), (461, 482)
(468, 471), (555, 558), (577, 538), (490, 450)
(490, 440), (600, 485), (611, 457), (502, 412)
(501, 400), (611, 400), (611, 376), (501, 376)
(492, 361), (595, 319), (586, 296), (483, 338)
(470, 329), (549, 251), (531, 233), (453, 312)
(439, 307), (481, 204), (458, 195), (416, 297)
(400, 299), (400, 189), (376, 189), (376, 299)
(361, 307), (319, 204), (296, 213), (338, 316)
(329, 329), (251, 250), (233, 267), (311, 346)
(307, 360), (204, 318), (195, 341), (297, 383)
(299, 400), (189, 400), (189, 424), (299, 424)
(307, 438), (204, 480), (213, 503), (316, 461)
(329, 470), (250, 548), (267, 566), (346, 488)
(360, 492), (318, 595), (341, 604), (383, 502)
(400, 501), (400, 611), (424, 611), (424, 501)
(438, 492), (480, 595), (503, 586), (461, 483)
(470, 470), (548, 549), (566, 532), (488, 453)
(492, 439), (595, 481), (604, 458), (502, 416)
2 changes: 1 addition & 1 deletion ext/gd/tests/bug48732.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ echo 'Left Bottom: (' . $bbox[0] . ', ' . $bbox[1] . ')';
--CLEAN--
<?php @unlink(dirname(__FILE__) . '/bug48732.png'); ?>
--EXPECTF--
Left Bottom: (0, 47)
Left Bottom: (0, 46)
8 changes: 4 additions & 4 deletions ext/gd/tests/bug48801_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ echo '(' . $bbox[4] . ', ' . $bbox[5] . ")\n";
echo '(' . $bbox[6] . ', ' . $bbox[7] . ")\n";
?>
--EXPECTF--
(-1, 15)
(15%d, 15)
(15%d, -48)
(-1, -48)
(4, 15)
(161, 15)
(161, -47)
(4, -47)
101 changes: 101 additions & 0 deletions ext/gd/tests/bug53504.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
--TEST--
Bug #53504 imagettfbbox/imageftbbox gives incorrect values for bounding box
--SKIPIF--
<?php
if(!extension_loaded('gd')){ die('skip gd extension not available'); }
if(!function_exists('imageftbbox')) die('skip imageftbbox() not available');

include dirname(__FILE__) . '/func.inc';
if(version_compare(get_freetype_version(), '2.4.10') == -1) die('skip for freetype < 2.4.10');
?>
--FILE--
<?php
$cwd = dirname(__FILE__);
$font = "$cwd/Tuffy.ttf";

$g = imagecreate(800, 800);
$bgnd = imagecolorallocate($g, 255, 255, 255);
$black = imagecolorallocate($g, 0, 0, 0);
$red = imagecolorallocate($g, 255, 0, 0);
$blue = imagecolorallocate($g, 0, 0, 255);

$tests = [
// Kerning examples (unfortunately not available in "Tuffy" test font):
['fontSize' => 50, 'angle' => 0, 'x' => 20, 'y' => 70, 'text' => 'AV Teg'],
['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 350, 'text' => 'AV Teg'],
['fontSize' => 50, 'angle' => 40, 'x' => 130, 'y' => 280, 'text' => 'AV Teg'],

// Shift-Test:
['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 110, 'text' => 'H-Shift'],

// Small/single chars:
['fontSize' => 100, 'angle' => 0, 'x' => 350, 'y' => 220, 'text' => '-'],
['fontSize' => 100, 'angle' => 0, 'x' => 430, 'y' => 220, 'text' => ','],
['fontSize' => 100, 'angle' => 0, 'x' => 510, 'y' => 220, 'text' => '.'],
['fontSize' => 100, 'angle' => 0, 'x' => 590, 'y' => 220, 'text' => '|'],
['fontSize' => 100, 'angle' => 0, 'x' => 670, 'y' => 220, 'text' => 'g'],

// Multi-Line + rotation:
['fontSize' => 30, 'angle' => 0, 'x' => 20, 'y' => 400, 'text' => "Multi\nLine\nTest"],
['fontSize' => 30, 'angle' => 40, 'x' => 150, 'y' => 420, 'text' => "Multi\nLine\nTest"],
['fontSize' => 30, 'angle' => 90, 'x' => 250, 'y' => 340, 'text' => "Multi\nLine\nTest"],

// Some edge case glyphs:
['fontSize' => 50, 'angle' => 90, 'x' => 70, 'y' => 750, 'text' => "iiiiiiiiiiii"],
['fontSize' => 50, 'angle' => 90, 'x' => 150, 'y' => 750, 'text' => "~~~~~~~"],
['fontSize' => 50, 'angle' => 50, 'x' => 210, 'y' => 750, 'text' => "iiiiiiiiiiii"],
['fontSize' => 50, 'angle' => 50, 'x' => 300, 'y' => 750, 'text' => "~~~~~~~"],
['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 650, 'text' => "iiiiiiiiiiii"],
['fontSize' => 50, 'angle' => 0, 'x' => 430, 'y' => 750, 'text' => "~~~~~~~"],

// "Big" test:
['fontSize' => 200, 'angle' => 0, 'x' => 400, 'y' => 500, 'text' => "Big"],
];

foreach ($tests as $test) {
$bbox = imageftbbox($test['fontSize'], $test['angle'], $font, $test['text']);
vprintf("(%d, %d), (%d, %d), (%d, %d), (%d, %d)\n", $bbox);

$bboxDrawn = imagefttext($g, $test['fontSize'], $test['angle'],
$test['x'], $test['y'], $black, $font, $test['text']);

// check if both bboxes match when adding x/y offset:
for ($i = 0; $i < count($bbox); $i += 2) {
if ($bbox[$i] + $test['x'] !== $bboxDrawn[$i]) echo "imageftbbox and imagefttext differ!\n";
if ($bbox[$i + 1] + $test['y'] !== $bboxDrawn[$i + 1]) echo "imageftbbox and imagefttext differ!\n";
}

// draw bounding box:
imagepolygon($g, $bboxDrawn, 4, $red);

// draw baseline:
$width = sqrt(pow($bboxDrawn[2] - $bboxDrawn[0], 2) + pow($bboxDrawn[3] - $bboxDrawn[1], 2));
imageline($g, $test['x'], $test['y'],
$test['x'] + $width * cos(deg2rad($test['angle'])),
$test['y'] - $width * sin(deg2rad($test['angle'])), $blue);
}

imagepng($g, "$cwd/bug53504.png");
?>
--CLEAN--
<?php @unlink(dirname(__FILE__) . '/bug53504.png'); ?>
--EXPECTF--
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor issue: this doesn't need to be EXPECTF; EXPECT would be sufficient.

(2, 15), (208, 15), (208, -48), (2, -48)
(15, -1), (15, -208), (-48, -208), (-48, -2)
(11, 11), (169, -122), (129, -171), (-30, -39)
(8, 2), (385, 2), (385, -97), (8, -97)
(7, -37), (51, -37), (51, -46), (7, -46)
(7, 15), (21, 15), (21, -13), (7, -13)
(7, 1), (21, 1), (21, -13), (7, -13)
(8, 0), (17, 0), (17, -95), (8, -95)
(5, 29), (60, 29), (60, -72), (5, -72)
(2, 107), (80, 107), (80, -29), (2, -29)
(70, 81), (131, 31), (43, -74), (-18, -24)
(107, -1), (107, -80), (-29, -80), (-29, -2)
(0, -4), (0, -165), (-47, -165), (-47, -4)
(-19, -2), (-18, -167), (-29, -167), (-29, -2)
(3, -3), (107, -127), (70, -157), (-34, -33)
(-13, -13), (93, -141), (85, -147), (-21, -20)
(4, 0), (165, 0), (165, -47), (4, -47)
(2, -19), (167, -19), (167, -29), (2, -29)
(16, 59), (330, 59), (330, -190), (16, -190)