diff --git a/src/Rasterization/Renderers/TextRenderer.php b/src/Rasterization/Renderers/TextRenderer.php index 11e4c21..83d9c29 100644 --- a/src/Rasterization/Renderers/TextRenderer.php +++ b/src/Rasterization/Renderers/TextRenderer.php @@ -25,7 +25,7 @@ class TextRenderer extends MultiPassRenderer */ protected function prepareRenderParams(array $options, Transform $transform, ?FontRegistry $fontRegistry): ?array { - // this assumes there is no rotation or skew, but that's fine, we can't deal with that anyway + // only the rotational impact of skew is used $size1 = $options['fontSize']; $size2 = $size1; $transform->resize($size1, $size2); @@ -55,12 +55,16 @@ protected function prepareRenderParams(array $options, Transform $transform, ?Fo $y = $options['y']; $transform->map($x, $y); + //get clockwise rotation result from transform matrix + $rotation = $transform->rotation(); + return [ 'x' => $x - $anchorOffset, 'y' => $y, 'size' => $size, 'fontPath' => $fontPath, 'text' => $options['text'], + 'rotation' => $rotation ]; } @@ -72,7 +76,7 @@ protected function renderFill($image, $params, int $color): void imagettftext( $image, $params['size'], - 0, + $params['rotation'] * -1, //counterclockwise $params['x'], $params['y'], $color, @@ -92,7 +96,16 @@ protected function renderStroke($image, $params, int $color, float $strokeWidth) for ($c1 = ($x - abs($px)); $c1 <= ($x + abs($px)); $c1++) { for ($c2 = ($y - abs($px)); $c2 <= ($y + abs($px)); $c2++) { - imagettftext($image, $params['size'], 0, $c1, $c2, $color, $params['fontPath'], $params['text']); + imagettftext( + $image, + $params['size'], + $params['rotation'] * -1, //counterclockwise + $c1, + $c2, + $color, + $params['fontPath'], + $params['text'] + ); } } } diff --git a/src/Rasterization/Transform/Transform.php b/src/Rasterization/Transform/Transform.php index e92af4a..d03ba2a 100644 --- a/src/Rasterization/Transform/Transform.php +++ b/src/Rasterization/Transform/Transform.php @@ -202,6 +202,34 @@ public function rotate(float $radians): void $this->matrix[3] = $d; } + /** + * Calculate the resulting rotation using the yaw rotation matrix + * + * @return float clockwise rotation + */ + public function rotation(): float + { + /* + * This is the counterclockwise rotation matrices, + * although the $radians in the function rotate() is a clockwise value, + * the matrix presents is counterclockwise, + * using these matrices, the result is clockwise. + * + * |cos(θ) -sin(θ) 0| |a -c 0| + * R = |sin(θ) cos(θ) 0| = |b d 0| + * |0 0 1| |0 0 1| + * + * Note: atan2(y, x) takes params in the y, x order. + * First we calculate the rotation effect of skewX/c and scaleX/a into radians: atan2(-c, a) + * Then we calculate the rotation effect of skewY/b and scaleY/d into radians: atan2(b, d) + * Last we average that and convert it to degree rotation: Ave(caRadian, bdRadian) * 180 / M_PI + */ + + $caRadian = atan2(-$this->matrix[2], $this->matrix[0]); + $bdRadian = atan2($this->matrix[1], $this->matrix[3]); + return ($caRadian + $bdRadian) / 2 * 180 / M_PI; + } + /** * Apply a horizontal skew to this transform. This object will be mutated as a result of this operation. * This is the same as post-multiplying this transform with another transform representing a pure horizontal skew.