Skip to content

Commit b2bfa29

Browse files
committed
refactoring letter spacing in write text methods
1 parent 9a6f48c commit b2bfa29

File tree

3 files changed

+127
-74
lines changed

3 files changed

+127
-74
lines changed

docs/classes/DantSu/PHPImageEditor/Image.md

+15-13
Original file line numberDiff line numberDiff line change
@@ -848,13 +848,14 @@ Write text on the image.
848848
|-----------|------|-------------|
849849
| `string` | **string** | Text to be added on the image |
850850
| `fontPath` | **string** | Path to the TTF file |
851-
| `fontSize` | **int** | Font size |
851+
| `fontSize` | **float** | Font size |
852852
| `color` | **string** | Hexadecimal string color |
853-
| `posX` | **int|string** | Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
854-
| `posY` | **int|string** | Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
855-
| `anchorX` | **int|string** | Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
856-
| `anchorY` | **int|string** | Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
857-
| `rotation` | **int** | Counterclockwise text rotation in degrees |
853+
| `posX` | **float|string** | Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
854+
| `posY` | **float|string** | Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
855+
| `anchorX` | **float|string** | Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
856+
| `anchorY` | **float|string** | Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
857+
| `rotation` | **float** | Counterclockwise text rotation in degrees |
858+
| `letterSpacing` | **float** | add space between letters |
858859

859860

860861
#### Return Value:
@@ -881,18 +882,19 @@ Write text on the image and get the bounding box of the text in the image.
881882
|-----------|------|-------------|
882883
| `string` | **string** | Text to be added on the image |
883884
| `fontPath` | **string** | Path to the TTF file |
884-
| `fontSize` | **int** | Font size |
885+
| `fontSize` | **float** | Font size |
885886
| `color` | **string** | Hexadecimal string color |
886-
| `posX` | **int|string** | Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
887-
| `posY` | **int|string** | Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
888-
| `anchorX` | **int|string** | Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
889-
| `anchorY` | **int|string** | Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
890-
| `rotation` | **int** | Counterclockwise text rotation in degrees |
887+
| `posX` | **float|string** | Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
888+
| `posY` | **float|string** | Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
889+
| `anchorX` | **float|string** | Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT` |
890+
| `anchorY` | **float|string** | Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM` |
891+
| `rotation` | **float** | Counterclockwise text rotation in degrees |
892+
| `letterSpacing` | **float** | add space between letters |
891893

892894

893895
#### Return Value:
894896

895-
**array** : Pixels positions of the
897+
**array** : Bounding box positions of the text
896898

897899

898900

src/Image.php

+88-61
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ public function curl(string $url, array $curlOptions = [], bool $failOnError = f
318318

319319
$image = \curl_exec($curl);
320320

321-
if($failOnError && \curl_errno($curl)){
321+
if ($failOnError && \curl_errno($curl)) {
322322
throw new \Exception(\curl_error($curl));
323323
}
324324

@@ -635,22 +635,22 @@ private static function formatColor(string $stringColor): string
635635
* Allocate a new color to the image.
636636
*
637637
* @param string $color Hexadecimal string color
638-
* @return int Color id
638+
* @return int|false Color id
639639
*/
640-
private function colorAllocate(string $color): int
640+
private function colorAllocate(string $color)
641641
{
642642
$color = static::formatColor($color);
643643
$red = \hexdec(\substr($color, 0, 2));
644644
$green = \hexdec(\substr($color, 2, 2));
645645
$blue = \hexdec(\substr($color, 4, 2));
646646
$alpha = \floor(\hexdec(\substr($color, 6, 2)) / 2);
647647

648-
$newColor = \imagecolorexactalpha($this->image, $red, $green, $blue, $alpha);
649-
if ($newColor === -1) {
650-
$newColor = \imagecolorallocatealpha($this->image, $red, $green, $blue, $alpha);
648+
$colorId = \imagecolorexactalpha($this->image, $red, $green, $blue, $alpha);
649+
if ($colorId === -1) {
650+
$colorId = \imagecolorallocatealpha($this->image, $red, $green, $blue, $alpha);
651651
}
652652

653-
return $newColor;
653+
return $colorId;
654654
}
655655

656656

@@ -801,18 +801,19 @@ public function grayscale(): Image
801801
*
802802
* @param string $string Text to be added on the image
803803
* @param string $fontPath Path to the TTF file
804-
* @param int $fontSize Font size
804+
* @param float $fontSize Font size
805805
* @param string $color Hexadecimal string color
806-
* @param int|string $posX Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
807-
* @param int|string $posY Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
808-
* @param int|string $anchorX Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
809-
* @param int|string $anchorY Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
810-
* @param int $rotation Counterclockwise text rotation in degrees
806+
* @param float|string $posX Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
807+
* @param float|string $posY Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
808+
* @param float|string $anchorX Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
809+
* @param float|string $anchorY Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
810+
* @param float $rotation Counterclockwise text rotation in degrees
811+
* @param float $letterSpacing add space between letters
811812
* @return $this Fluent interface
812813
*/
813-
public function writeText(string $string, string $fontPath, int $fontSize, string $color = '#ffffff', $posX = 0, $posY = 0, string $anchorX = Image::ALIGN_CENTER, string $anchorY = Image::ALIGN_MIDDLE, int $rotation = 0, int $letter_spacing = 0): Image
814+
public function writeText(string $string, string $fontPath, float $fontSize, string $color = 'ffffff', $posX = 0, $posY = 0, $anchorX = Image::ALIGN_CENTER, $anchorY = Image::ALIGN_MIDDLE, float $rotation = 0, float $letterSpacing = 0): Image
814815
{
815-
$this->writeTextAndGetBoundingBox($string, $fontPath, $fontSize, $color, $posX, $posY, $anchorX, $anchorY, $rotation, $letter_spacing);
816+
$this->writeTextAndGetBoundingBox($string, $fontPath, $fontSize, $color, $posX, $posY, $anchorX, $anchorY, $rotation, $letterSpacing);
816817
return $this;
817818
}
818819

@@ -821,16 +822,17 @@ public function writeText(string $string, string $fontPath, int $fontSize, strin
821822
*
822823
* @param string $string Text to be added on the image
823824
* @param string $fontPath Path to the TTF file
824-
* @param int $fontSize Font size
825+
* @param float $fontSize Font size
825826
* @param string $color Hexadecimal string color
826-
* @param int|string $posX Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
827-
* @param int|string $posY Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
828-
* @param int|string $anchorX Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
829-
* @param int|string $anchorY Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
830-
* @param int $rotation Counterclockwise text rotation in degrees
831-
* @return array Pixels positions of the
832-
*/
833-
public function writeTextAndGetBoundingBox(string $string, string $fontPath, int $fontSize, string $color = '#ffffff', $posX = 0, $posY = 0, string $anchorX = Image::ALIGN_CENTER, string $anchorY = Image::ALIGN_MIDDLE, int $rotation = 0, int $letter_spacing = 0): array
827+
* @param float|string $posX Left position in pixel. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
828+
* @param float|string $posY Top position in pixel. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
829+
* @param float|string $anchorX Horizontal anchor of the text. You can use `Image::ALIGN_LEFT`, `Image::ALIGN_CENTER`, `Image::ALIGN_RIGHT`
830+
* @param float|string $anchorY Vertical anchor of the text. You can use `Image::ALIGN_TOP`, `Image::ALIGN_MIDDLE`, `Image::ALIGN_BOTTOM`
831+
* @param float $rotation Counterclockwise text rotation in degrees
832+
* @param float $letterSpacing add space between letters
833+
* @return array Bounding box positions of the text
834+
*/
835+
public function writeTextAndGetBoundingBox(string $string, string $fontPath, float $fontSize, string $color = 'ffffff', $posX = 0, $posY = 0, $anchorX = Image::ALIGN_CENTER, $anchorY = Image::ALIGN_MIDDLE, float $rotation = 0, float $letterSpacing = 0): array
834836
{
835837
if (!$this->isImageDefined()) {
836838
return [];
@@ -858,7 +860,7 @@ public function writeTextAndGetBoundingBox(string $string, string $fontPath, int
858860
) {
859861
if (
860862
($newImg = \imagecreatetruecolor(1, 1)) === false ||
861-
($posText = $this->imagettftextWithSpacing($newImg, $fontSize, $rotation, 0, 0, $color, $fontPath, $string, $letter_spacing)) === false
863+
($posText = $this->imagettftextWithSpacing($newImg, $fontSize, $rotation, 0, 0, $color, $fontPath, $string, $letterSpacing)) === false
862864
) {
863865
return [];
864866
}
@@ -884,6 +886,7 @@ public function writeTextAndGetBoundingBox(string $string, string $fontPath, int
884886
}
885887

886888
$sizeWidth = $xMax - $xMin;
889+
$sizeHeight = $yMax - $yMin;
887890

888891
switch ($anchorX) {
889892
case static::ALIGN_LEFT :
@@ -898,17 +901,18 @@ public function writeTextAndGetBoundingBox(string $string, string $fontPath, int
898901
}
899902
switch ($anchorY) {
900903
case static::ALIGN_TOP :
901-
$posY = $posY + $fontSize;
904+
$posY = $posY - $yMin;
902905
break;
903906
case static::ALIGN_MIDDLE :
904-
$posY = $posY + $fontSize / 2;
907+
$posY = $posY - $sizeHeight / 2 - $yMin;
905908
break;
906909
case static::ALIGN_BOTTOM :
910+
$posY = $posY - $sizeHeight - $yMin;
907911
break;
908912
}
909913
}
910914

911-
$posText = $this->imagettftextWithSpacing($this->image, $fontSize, $rotation, $posX, $posY, $color, $fontPath, $string, $letter_spacing);
915+
$posText = $this->imagettftextWithSpacing($this->image, $fontSize, $rotation, $posX, $posY, $color, $fontPath, $string, $letterSpacing);
912916

913917
if ($posText === false) {
914918
return [];
@@ -943,35 +947,52 @@ public function writeTextAndGetBoundingBox(string $string, string $fontPath, int
943947

944948
/**
945949
* @param $image
946-
* @param $size
947-
* @param $angle
948-
* @param $x
949-
* @param $y
950-
* @param $color
951-
* @param $font
952-
* @param $text
953-
* @param int $spacing
954-
* @return array
955-
*/
956-
private function imagettftextWithSpacing($image, float $size, float $angle, float $x, float $y, int $color, string $font, string $text, int $spacing = 0)
950+
* @param float $size
951+
* @param float $angle
952+
* @param float $x
953+
* @param float $y
954+
* @param int $color
955+
* @param string $font
956+
* @param string $text
957+
* @param float $spacing
958+
* @return array|false
959+
*/
960+
private function imagettftextWithSpacing($image, float $size, float $angle, float $x, float $y, int $color, string $font, string $text, float $spacing = 0)
957961
{
958-
if ($spacing == 0)
959-
{
962+
if ($spacing == 0) {
960963
return \imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
961-
}
962-
else
963-
{
964-
$temp_x = $x;
965-
$temp_y = $y;
966-
$posText = [];
967-
for ($i = 0; $i < \mb_strlen($text); ++$i)
968-
{
969-
$posText = \imagettftext($image, $size, $angle, $temp_x, $temp_y, $color, $font, $text[$i]);
964+
} else {
965+
$length = \mb_strlen($text);
966+
967+
if ($length == 0) {
968+
return false;
969+
}
970+
971+
$letterPos = ['x' => $x, 'y' => $y];
972+
$textWidth = $spacing * ($length - 1);
973+
$top = 0;
974+
$bottom = 0;
975+
976+
for ($i = 0; $i < $length; ++$i) {
977+
\imagettftext($image, $size, $angle, $letterPos['x'], $letterPos['y'], $color, $font, $text[$i]);
970978
$bbox = \imagettfbbox($size, 0, $font, $text[$i]);
971-
$temp_x += \cos(\deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
972-
$temp_y -= \sin(\deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
979+
$letterPos = Geometry2D::getDstXY($letterPos['x'], $letterPos['y'], $angle, $spacing + $bbox[2]);
980+
981+
$textWidth += $bbox[2];
982+
if ($top > $bbox[5]) {
983+
$top = $bbox[5];
984+
}
985+
if ($bottom < $bbox[1]) {
986+
$bottom = $bbox[1];
987+
}
973988
}
974-
return $posText;
989+
990+
$bottomLeft = Geometry2D::getDstXY($x, $y, $angle - 90, $bottom);
991+
$bottomRight = Geometry2D::getDstXY($bottomLeft['x'], $bottomLeft['y'], $angle, $textWidth);
992+
$topLeft = Geometry2D::getDstXY($x, $y, $angle + 90, \abs($top));
993+
$topRight = Geometry2D::getDstXY($topLeft['x'], $topLeft['y'], $angle, $textWidth);
994+
995+
return [$bottomLeft['x'], $bottomLeft['y'], $bottomRight['x'], $bottomRight['y'], $topRight['x'], $topRight['y'], $topLeft['x'], $topLeft['y']];
975996
}
976997
}
977998

@@ -1010,7 +1031,7 @@ public function drawRectangle(int $left, int $top, int $right, int $bottom, stri
10101031
* @param string $color Hexadecimal string color
10111032
* @return $this Fluent interface
10121033
*/
1013-
public function drawPolygon(array $points, string $color = '#000000', $antialias = false): Image
1034+
public function drawPolygon(array $points, string $color = '000000', $antialias = false): Image
10141035
{
10151036
if (!$this->isImageDefined()) {
10161037
return $this;
@@ -1022,14 +1043,14 @@ public function drawPolygon(array $points, string $color = '#000000', $antialias
10221043
return $this;
10231044
}
10241045

1025-
if($antialias) {
1046+
if ($antialias) {
10261047
\imageantialias($this->image, true);
10271048
\imagepolygon($this->image, $points, \count($points) / 2, $color);
10281049
}
10291050

10301051
\imagefilledpolygon($this->image, $points, \count($points) / 2, $color);
10311052

1032-
if($antialias) {
1053+
if ($antialias) {
10331054
\imageantialias($this->image, false);
10341055
}
10351056

@@ -1280,10 +1301,10 @@ private function getData(callable $imgFunction): string
12801301

12811302
\ob_start();
12821303
$imgFunction();
1283-
$image_data = \ob_get_contents();
1304+
$imageData = \ob_get_contents();
12841305
\ob_end_clean();
12851306

1286-
return $image_data;
1307+
return $imageData;
12871308
}
12881309

12891310
/**
@@ -1293,7 +1314,9 @@ private function getData(callable $imgFunction): string
12931314
*/
12941315
public function getDataPNG(): string
12951316
{
1296-
return $this->getData(function () {$this->displayPNG();});
1317+
return $this->getData(function () {
1318+
$this->displayPNG();
1319+
});
12971320
}
12981321

12991322
/**
@@ -1304,7 +1327,9 @@ public function getDataPNG(): string
13041327
*/
13051328
public function getDataJPG(int $quality = -1): string
13061329
{
1307-
return $this->getData(function () use ($quality) {$this->displayJPG($quality);});
1330+
return $this->getData(function () use ($quality) {
1331+
$this->displayJPG($quality);
1332+
});
13081333
}
13091334

13101335
/**
@@ -1314,7 +1339,9 @@ public function getDataJPG(int $quality = -1): string
13141339
*/
13151340
public function getDataGIF(): string
13161341
{
1317-
return $this->getData(function () {$this->displayGIF();});
1342+
return $this->getData(function () {
1343+
$this->displayGIF();
1344+
});
13181345
}
13191346

13201347
/**

src/samples/sample6.php

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
require_once '../Geometry2D.php';
4+
require_once '../Image.php';
5+
6+
use \DantSu\PHPImageEditor\Image;
7+
8+
\header('Content-type: image/png');
9+
10+
$image1 = Image::newCanvas(500, 500);
11+
$bbox = $image1->writeTextAndGetBoundingBox('I got the power !', __DIR__ . '/resources/font.ttf', 40, '#FFFFFF', Image::ALIGN_RIGHT, Image::ALIGN_BOTTOM, Image::ALIGN_RIGHT, Image::ALIGN_BOTTOM, 0, 1);
12+
13+
Image::newCanvas(500, 500)
14+
->drawPolygon(
15+
[
16+
$bbox['top-left']['x'], $bbox['top-left']['y'],
17+
$bbox['top-right']['x'], $bbox['top-right']['y'],
18+
$bbox['bottom-right']['x'], $bbox['bottom-right']['y'],
19+
$bbox['bottom-left']['x'], $bbox['bottom-left']['y']
20+
],
21+
'00000088'
22+
)
23+
->pasteOn($image1)
24+
->displayPNG();

0 commit comments

Comments
 (0)