From fd4a87759f06e77fca038935008efea92731a0a6 Mon Sep 17 00:00:00 2001 From: Alexander Faust Date: Mon, 24 May 2021 17:16:04 +0200 Subject: [PATCH 1/4] Make EPS & SVG Rendering usable on international locales --- src/Renderer/Image/EpsImageBackEnd.php | 26 +++++++++++++------------- src/Renderer/Image/SvgImageBackEnd.php | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Renderer/Image/EpsImageBackEnd.php b/src/Renderer/Image/EpsImageBackEnd.php index b581b54..2dcc92c 100644 --- a/src/Renderer/Image/EpsImageBackEnd.php +++ b/src/Renderer/Image/EpsImageBackEnd.php @@ -75,7 +75,7 @@ public function scale(float $size) : void throw new RuntimeException('No image has been started'); } - $this->eps .= sprintf("%1\$s %1\$s s\n", round($size, self::PRECISION)); + $this->eps .= sprintf("%1\$1.".self::PRECISION."F %1\$1.".self::PRECISION."F s\n", round($size, self::PRECISION)); } public function translate(float $x, float $y) : void @@ -84,7 +84,7 @@ public function translate(float $x, float $y) : void throw new RuntimeException('No image has been started'); } - $this->eps .= sprintf("%s %s t\n", round($x, self::PRECISION), round($y, self::PRECISION)); + $this->eps .= sprintf("%1.".self::PRECISION."F %1.".self::PRECISION."F t\n", round($x, self::PRECISION), round($y, self::PRECISION)); } public function rotate(int $degrees) : void @@ -176,13 +176,13 @@ private function drawPathOperations(Iterable $ops, &$fromX, &$fromY) : string case $op instanceof Move: $fromX = $toX = round($op->getX(), self::PRECISION); $fromY = $toY = round($op->getY(), self::PRECISION); - $pathData[] = sprintf('%s %s m', $toX, $toY); + $pathData[] = sprintf('%1.".self::PRECISION."F %1.".self::PRECISION."F m', $toX, $toY); break; case $op instanceof Line: $fromX = $toX = round($op->getX(), self::PRECISION); $fromY = $toY = round($op->getY(), self::PRECISION); - $pathData[] = sprintf('%s %s l', $toX, $toY); + $pathData[] = sprintf('%1.".self::PRECISION."F %1.".self::PRECISION."F l', $toX, $toY); break; case $op instanceof EllipticArc: @@ -196,7 +196,7 @@ private function drawPathOperations(Iterable $ops, &$fromX, &$fromY) : string $y2 = round($op->getY2(), self::PRECISION); $fromX = $x3 = round($op->getX3(), self::PRECISION); $fromY = $y3 = round($op->getY3(), self::PRECISION); - $pathData[] = sprintf('%s %s %s %s %s %s c', $x1, $y1, $x2, $y2, $x3, $y3); + $pathData[] = sprintf('%1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F c', $x1, $y1, $x2, $y2, $x3, $y3); break; case $op instanceof Close: @@ -271,7 +271,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa switch ($gradient->getType()) { case GradientType::HORIZONTAL(): $this->eps .= sprintf( - " /Coords [ %s %s %s %s ]\n", + " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", round($x, self::PRECISION), round($y, self::PRECISION), round($x + $width, self::PRECISION), @@ -281,7 +281,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa case GradientType::VERTICAL(): $this->eps .= sprintf( - " /Coords [ %s %s %s %s ]\n", + " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", round($x, self::PRECISION), round($y, self::PRECISION), round($x, self::PRECISION), @@ -291,7 +291,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa case GradientType::DIAGONAL(): $this->eps .= sprintf( - " /Coords [ %s %s %s %s ]\n", + " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", round($x, self::PRECISION), round($y, self::PRECISION), round($x + $width, self::PRECISION), @@ -301,7 +301,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa case GradientType::INVERSE_DIAGONAL(): $this->eps .= sprintf( - " /Coords [ %s %s %s %s ]\n", + " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", round($x, self::PRECISION), round($y + $height, self::PRECISION), round($x + $width, self::PRECISION), @@ -314,7 +314,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa $centerY = ($y + $height) / 2; $this->eps .= sprintf( - " /Coords [ %s %s 0 %s %s %s ]\n", + " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F 0 %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", round($centerX, self::PRECISION), round($centerY, self::PRECISION), round($centerX, self::PRECISION), @@ -354,12 +354,12 @@ private function getColorSetString(ColorInterface $color) : string private function getColorString(ColorInterface $color) : string { if ($color instanceof Rgb) { - return sprintf('%s %s %s', $color->getRed() / 255, $color->getGreen() / 255, $color->getBlue() / 255); + return sprintf("%1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F", $color->getRed() / 255, $color->getGreen() / 255, $color->getBlue() / 255); } if ($color instanceof Cmyk) { return sprintf( - '%s %s %s %s', + "% %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F", $color->getCyan() / 100, $color->getMagenta() / 100, $color->getYellow() / 100, @@ -368,7 +368,7 @@ private function getColorString(ColorInterface $color) : string } if ($color instanceof Gray) { - return sprintf('%s', $color->getGray() / 100); + return sprintf("%1.".self::PRECISION."F", $color->getGray() / 100); } return $this->getColorString($color->toCmyk()); diff --git a/src/Renderer/Image/SvgImageBackEnd.php b/src/Renderer/Image/SvgImageBackEnd.php index 714da6e..d1dc9e9 100644 --- a/src/Renderer/Image/SvgImageBackEnd.php +++ b/src/Renderer/Image/SvgImageBackEnd.php @@ -97,7 +97,7 @@ public function scale(float $size) : void $this->xmlWriter->startElement('g'); $this->xmlWriter->writeAttribute( 'transform', - sprintf('scale(%s)', round($size, self::PRECISION)) + sprintf('scale(%1.'.self::PRECISION.'F)', round($size, self::PRECISION)) ); ++$this->stack[$this->currentStack]; } @@ -111,7 +111,7 @@ public function translate(float $x, float $y) : void $this->xmlWriter->startElement('g'); $this->xmlWriter->writeAttribute( 'transform', - sprintf('translate(%s,%s)', round($x, self::PRECISION), round($y, self::PRECISION)) + sprintf('translate(%1.'.self::PRECISION.'F,%1.'.self::PRECISION.'F)', round($x, self::PRECISION), round($y, self::PRECISION)) ); ++$this->stack[$this->currentStack]; } @@ -222,7 +222,7 @@ private function startPathElement(Path $path) : void switch (true) { case $op instanceof Move: $pathData[] = sprintf( - 'M%s %s', + 'M%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', round($op->getX(), self::PRECISION), round($op->getY(), self::PRECISION) ); @@ -230,7 +230,7 @@ private function startPathElement(Path $path) : void case $op instanceof Line: $pathData[] = sprintf( - 'L%s %s', + 'L%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', round($op->getX(), self::PRECISION), round($op->getY(), self::PRECISION) ); @@ -238,7 +238,7 @@ private function startPathElement(Path $path) : void case $op instanceof EllipticArc: $pathData[] = sprintf( - 'A%s %s %s %u %u %s %s', + 'A%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %u %u %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', round($op->getXRadius(), self::PRECISION), round($op->getYRadius(), self::PRECISION), round($op->getXAxisAngle(), self::PRECISION), @@ -251,7 +251,7 @@ private function startPathElement(Path $path) : void case $op instanceof Curve: $pathData[] = sprintf( - 'C%s %s %s %s %s %s', + 'C%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', round($op->getX1(), self::PRECISION), round($op->getY1(), self::PRECISION), round($op->getX2(), self::PRECISION), From cd891439ba441d8c2346261c14507425f551267d Mon Sep 17 00:00:00 2001 From: Alexander Faust Date: Sat, 23 Mar 2024 18:57:47 +0100 Subject: [PATCH 2/4] amend pr#84 to be inline of the solution of accepted pr#100 but for all functions of svg & eps backends --- src/Renderer/Image/EpsImageBackEnd.php | 35 ++++++++++++++++++-------- src/Renderer/Image/SvgImageBackEnd.php | 15 ++++++++--- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/Renderer/Image/EpsImageBackEnd.php b/src/Renderer/Image/EpsImageBackEnd.php index 3cbbf85..4e57c53 100644 --- a/src/Renderer/Image/EpsImageBackEnd.php +++ b/src/Renderer/Image/EpsImageBackEnd.php @@ -21,6 +21,21 @@ final class EpsImageBackEnd implements ImageBackEndInterface { private const PRECISION = 3; + private const S_SAFE = "%1.".self::PRECISION."F"; + private const SS_SAFE = self::S_SAFE." ".self::S_SAFE; + private const SSS_SAFE = self::SS_SAFE." ".self::S_SAFE; + private const SSSS_SAFE = self::SS_SAFE." ".self::SS_SAFE; + private const SCALE_FORMAT = "%1\$1.".self::PRECISION."F %1\$1.".self::PRECISION."F s\n"; + private const TRANSLATE_FORMAT = self:SS_SAFE." t\n"; + private const MOVE_FORMAT = self::SS_SAFE." m"; + private const LINE_FORMAT = self::SS_SAFE." l"; + private const CURVE_FORMAT = self::SSS_SAFE." ".self::SSS_SAFE." c"; + private const GRADIENTH_FORMAT = " /Coords [ ".self::SSSS_SAFE." ]\n"; + private const GRADIENTV_FORMAT = " /Coords [ ".self::SSSS_SAFE." ]\n"; + private const GRADIENTD_FORMAT = " /Coords [ ".self::SSSS_SAFE." ]\n"; + private const GRADIENTID_FORMAT = " /Coords [ ".self::SSSS_SAFE." ]\n"; + private const GRADIENTR_FORMAT = " /Coords [ ".self::SS_SAFE." 0 ".self::SSS_SAFE." ]\n"; + private ?string $eps; @@ -72,7 +87,7 @@ public function scale(float $size) : void throw new RuntimeException('No image has been started'); } - $this->eps .= sprintf("%1\$1.".self::PRECISION."F %1\$1.".self::PRECISION."F s\n", round($size, self::PRECISION)); + $this->eps .= sprintf(self::SCALE_FORMAT, round($size, self::PRECISION)); } public function translate(float $x, float $y) : void @@ -81,7 +96,7 @@ public function translate(float $x, float $y) : void throw new RuntimeException('No image has been started'); } - $this->eps .= sprintf("%1.".self::PRECISION."F %1.".self::PRECISION."F t\n", round($x, self::PRECISION), round($y, self::PRECISION)); + $this->eps .= sprintf(self::TRANSLATE_FORMAT, round($x, self::PRECISION), round($y, self::PRECISION)); } public function rotate(int $degrees) : void @@ -173,13 +188,13 @@ private function drawPathOperations(Iterable $ops, &$fromX, &$fromY) : string case $op instanceof Move: $fromX = $toX = round($op->getX(), self::PRECISION); $fromY = $toY = round($op->getY(), self::PRECISION); - $pathData[] = sprintf('%1.".self::PRECISION."F %1.".self::PRECISION."F m', $toX, $toY); + $pathData[] = sprintf(self::MOVE_FORMAT, $toX, $toY); break; case $op instanceof Line: $fromX = $toX = round($op->getX(), self::PRECISION); $fromY = $toY = round($op->getY(), self::PRECISION); - $pathData[] = sprintf('%1.".self::PRECISION."F %1.".self::PRECISION."F l', $toX, $toY); + $pathData[] = sprintf(self::LINE_FORMAT, $toX, $toY); break; case $op instanceof EllipticArc: @@ -193,7 +208,7 @@ private function drawPathOperations(Iterable $ops, &$fromX, &$fromY) : string $y2 = round($op->getY2(), self::PRECISION); $fromX = $x3 = round($op->getX3(), self::PRECISION); $fromY = $y3 = round($op->getY3(), self::PRECISION); - $pathData[] = sprintf('%1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F c', $x1, $y1, $x2, $y2, $x3, $y3); + $pathData[] = sprintf(self::CURVE_FORMAT, $x1, $y1, $x2, $y2, $x3, $y3); break; case $op instanceof Close: @@ -268,7 +283,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa switch ($gradient->getType()) { case GradientType::HORIZONTAL(): $this->eps .= sprintf( - " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", + self::GRADIENTH_FORMAT, round($x, self::PRECISION), round($y, self::PRECISION), round($x + $width, self::PRECISION), @@ -278,7 +293,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa case GradientType::VERTICAL(): $this->eps .= sprintf( - " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", + self::GRADIENTV_FORMAT, round($x, self::PRECISION), round($y, self::PRECISION), round($x, self::PRECISION), @@ -288,7 +303,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa case GradientType::DIAGONAL(): $this->eps .= sprintf( - " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", + self::GRADIENTD_FORMAT, round($x, self::PRECISION), round($y, self::PRECISION), round($x + $width, self::PRECISION), @@ -298,7 +313,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa case GradientType::INVERSE_DIAGONAL(): $this->eps .= sprintf( - " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", + self::GRADIENTID_FORMAT, round($x, self::PRECISION), round($y + $height, self::PRECISION), round($x + $width, self::PRECISION), @@ -311,7 +326,7 @@ private function createGradientFill(Gradient $gradient, float $x, float $y, floa $centerY = ($y + $height) / 2; $this->eps .= sprintf( - " /Coords [ %1.".self::PRECISION."F %1.".self::PRECISION."F 0 %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F ]\n", + self::GRADIENTR_FORMAT, round($centerX, self::PRECISION), round($centerY, self::PRECISION), round($centerX, self::PRECISION), diff --git a/src/Renderer/Image/SvgImageBackEnd.php b/src/Renderer/Image/SvgImageBackEnd.php index 357bcbe..57c4b15 100644 --- a/src/Renderer/Image/SvgImageBackEnd.php +++ b/src/Renderer/Image/SvgImageBackEnd.php @@ -19,8 +19,15 @@ final class SvgImageBackEnd implements ImageBackEndInterface { private const PRECISION = 3; + private const S_SAFE = '%.' . self::PRECISION . 'F'; + private const SS_SAFE = self::S_SAFE.' 'self::S_SAFE; + private const SSS_SAFE = self::S_SAFE.' 'self::S_SAFE; private const SCALE_FORMAT = 'scale(%.' . self::PRECISION . 'F)'; private const TRANSLATE_FORMAT = 'translate(%.' . self::PRECISION . 'F,%.' . self::PRECISION . 'F)'; + private const MOVE_FORMAT = 'M'.self::SS_SAFE; + private const LINE_FORMAT = 'L'.self::SS_SAFE; + private const ARC_FORMAT = 'A'.self::SSS_SAFE.' %u %u '.self::SS_SAFE; + private const CURVE_FORMAT = 'C'.self::SSS_SAFE.' '.self::SSS_SAFE; private ?XMLWriter $xmlWriter; @@ -212,7 +219,7 @@ private function startPathElement(Path $path) : void switch (true) { case $op instanceof Move: $pathData[] = sprintf( - 'M%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', + self::MOVE_FORMAT, round($op->getX(), self::PRECISION), round($op->getY(), self::PRECISION) ); @@ -220,7 +227,7 @@ private function startPathElement(Path $path) : void case $op instanceof Line: $pathData[] = sprintf( - 'L%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', + self::LINE_FORMAT, round($op->getX(), self::PRECISION), round($op->getY(), self::PRECISION) ); @@ -228,7 +235,7 @@ private function startPathElement(Path $path) : void case $op instanceof EllipticArc: $pathData[] = sprintf( - 'A%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %u %u %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', + self::ARC_FORMAT, round($op->getXRadius(), self::PRECISION), round($op->getYRadius(), self::PRECISION), round($op->getXAxisAngle(), self::PRECISION), @@ -241,7 +248,7 @@ private function startPathElement(Path $path) : void case $op instanceof Curve: $pathData[] = sprintf( - 'C%1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F %1.'.self::PRECISION.'F', + self::CURVE_FORMAT, round($op->getX1(), self::PRECISION), round($op->getY1(), self::PRECISION), round($op->getX2(), self::PRECISION), From d45c2ac38b25f8d19191f32ac104a0b388c203e2 Mon Sep 17 00:00:00 2001 From: Alexander Faust Date: Sat, 23 Mar 2024 19:03:52 +0100 Subject: [PATCH 3/4] fix color formats --- src/Renderer/Image/EpsImageBackEnd.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Renderer/Image/EpsImageBackEnd.php b/src/Renderer/Image/EpsImageBackEnd.php index 4e57c53..028d2fd 100644 --- a/src/Renderer/Image/EpsImageBackEnd.php +++ b/src/Renderer/Image/EpsImageBackEnd.php @@ -35,6 +35,8 @@ final class EpsImageBackEnd implements ImageBackEndInterface private const GRADIENTD_FORMAT = " /Coords [ ".self::SSSS_SAFE." ]\n"; private const GRADIENTID_FORMAT = " /Coords [ ".self::SSSS_SAFE." ]\n"; private const GRADIENTR_FORMAT = " /Coords [ ".self::SS_SAFE." 0 ".self::SSS_SAFE." ]\n"; + private const RGBCOLOR_FORMAT = self::SSS_SAFE; + private const CMYKCOLOR_FORMAT = self::SSSS_SAFE; private ?string $eps; @@ -366,12 +368,12 @@ private function getColorSetString(ColorInterface $color) : string private function getColorString(ColorInterface $color) : string { if ($color instanceof Rgb) { - return sprintf("%1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F", $color->getRed() / 255, $color->getGreen() / 255, $color->getBlue() / 255); + return sprintf(self::RGBCOLOR_FORMAT, $color->getRed() / 255, $color->getGreen() / 255, $color->getBlue() / 255); } if ($color instanceof Cmyk) { return sprintf( - "% %1.".self::PRECISION."F %1.".self::PRECISION."F %1.".self::PRECISION."F", + self::CMYKCOLOR_FORMAT, $color->getCyan() / 100, $color->getMagenta() / 100, $color->getYellow() / 100, From 45082c488585082d4b9cb6f20fbff36ebecf534a Mon Sep 17 00:00:00 2001 From: Alexander Faust Date: Sat, 23 Mar 2024 19:05:54 +0100 Subject: [PATCH 4/4] fix typos --- src/Renderer/Image/EpsImageBackEnd.php | 2 +- src/Renderer/Image/SvgImageBackEnd.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Renderer/Image/EpsImageBackEnd.php b/src/Renderer/Image/EpsImageBackEnd.php index 028d2fd..1e12230 100644 --- a/src/Renderer/Image/EpsImageBackEnd.php +++ b/src/Renderer/Image/EpsImageBackEnd.php @@ -26,7 +26,7 @@ final class EpsImageBackEnd implements ImageBackEndInterface private const SSS_SAFE = self::SS_SAFE." ".self::S_SAFE; private const SSSS_SAFE = self::SS_SAFE." ".self::SS_SAFE; private const SCALE_FORMAT = "%1\$1.".self::PRECISION."F %1\$1.".self::PRECISION."F s\n"; - private const TRANSLATE_FORMAT = self:SS_SAFE." t\n"; + private const TRANSLATE_FORMAT = self::SS_SAFE." t\n"; private const MOVE_FORMAT = self::SS_SAFE." m"; private const LINE_FORMAT = self::SS_SAFE." l"; private const CURVE_FORMAT = self::SSS_SAFE." ".self::SSS_SAFE." c"; diff --git a/src/Renderer/Image/SvgImageBackEnd.php b/src/Renderer/Image/SvgImageBackEnd.php index 57c4b15..c40ebcc 100644 --- a/src/Renderer/Image/SvgImageBackEnd.php +++ b/src/Renderer/Image/SvgImageBackEnd.php @@ -20,8 +20,8 @@ final class SvgImageBackEnd implements ImageBackEndInterface { private const PRECISION = 3; private const S_SAFE = '%.' . self::PRECISION . 'F'; - private const SS_SAFE = self::S_SAFE.' 'self::S_SAFE; - private const SSS_SAFE = self::S_SAFE.' 'self::S_SAFE; + private const SS_SAFE = self::S_SAFE.' '.self::S_SAFE; + private const SSS_SAFE = self::S_SAFE.' '.self::S_SAFE; private const SCALE_FORMAT = 'scale(%.' . self::PRECISION . 'F)'; private const TRANSLATE_FORMAT = 'translate(%.' . self::PRECISION . 'F,%.' . self::PRECISION . 'F)'; private const MOVE_FORMAT = 'M'.self::SS_SAFE;