diff --git a/.gitignore b/.gitignore index def28a32..5813be0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor/ .vscode/ -.env \ No newline at end of file +.env +.idea \ No newline at end of file diff --git a/README.md b/README.md index 1d553a72..5e38ed76 100644 --- a/README.md +++ b/README.md @@ -120,22 +120,23 @@ Feel free to [open a PR](https://github.com/DenverCoder1/readme-typing-svg/issue ## 🔧 Options -| Parameter | Details | Type | Example | -| :----------: | :-------------------------------------------------------------------------: | :-----: | :---------------------------------: | -| `lines` | Text to display with lines separated by `;` and `+` for spaces | string | `First+line;Second+line;Third+line` | -| `height` | Height of the output SVG in pixels (default: `50`) | integer | Any positive number | -| `width` | Width of the output SVG in pixels (default: `400`) | integer | Any positive number | -| `size` | Font size in pixels (default: `20`) | integer | Any positive number | -| `font` | Font family (default: `monospace`) | string | Any font from Google Fonts | -| `color` | Color of the text (default: `36BCF7`) | string | Hex code without # (eg. `F724A9`) | -| `background` | Background color of the text (default: `00000000`) | string | Hex code without # (eg. `FEFF4C`) | -| `center` | `true` to center text or `false` for left aligned (default: `false`) | boolean | `true` or `false` | -| `vCenter` | `true` to center vertically or `false`(default) to align above the center | boolean | `true` or `false` | -| `multiline` | `true` to wrap lines or `false` to retype on one line (default: `false`) | boolean | `true` or `false` | -| `duration` | Duration of the printing of a single line in milliseconds (default: `5000`) | integer | Any positive number | -| `pause` | Duration of the pause between lines in milliseconds (default: `0`) | integer | Any non-negative number | -| `repeat` | `true` to loop around to the first line after the last (default: `true`) | boolean | `true` or `false` | -| `separator` | Separator used between lines in the lines parameter (default: `;`) | string | `;`, `;;`, `/`, etc. | +| Parameter | Details | Type | Example | +| :-------------: | :-------------------------------------------------------------------------: | :-----: | :---------------------------------------------------------------------------------------------------------------: | +| `lines` | Text to display with lines separated by `;` and `+` for spaces | string | `First+line;Second+line;Third+line` | +| `height` | Height of the output SVG in pixels (default: `50`) | integer | Any positive number | +| `width` | Width of the output SVG in pixels (default: `400`) | integer | Any positive number | +| `size` | Font size in pixels (default: `20`) | integer | Any positive number | +| `font` | Font family (default: `monospace`) | string | Any font from Google Fonts | +| `color` | Color of the text (default: `36BCF7`) | string | Hex code without # (eg. `F724A9`) | +| `background` | Background color of the text (default: `00000000`) | string | Hex code without # (eg. `FEFF4C`) | +| `center` | `true` to center text or `false` for left aligned (default: `false`) | boolean | `true` or `false` | +| `vCenter` | `true` to center vertically or `false`(default) to align above the center | boolean | `true` or `false` | +| `multiline` | `true` to wrap lines or `false` to retype on one line (default: `false`) | boolean | `true` or `false` | +| `duration` | Duration of the printing of a single line in milliseconds (default: `5000`) | integer | Any positive number | +| `pause` | Duration of the pause between lines in milliseconds (default: `0`) | integer | Any non-negative number | +| `repeat` | `true` to loop around to the first line after the last (default: `true`) | boolean | `true` or `false` | +| `separator` | Separator used between lines in the lines parameter (default: `;`) | string | `;`, `;;`, `/`, etc. | +| `letterSpacing` | Letter spacing (default: `normal`) | string | Any css values for the [letter-spacing](https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing) property | ## 📤 Deploying it on your own diff --git a/src/demo/index.php b/src/demo/index.php index 5c9184ca..657e03e4 100644 --- a/src/demo/index.php +++ b/src/demo/index.php @@ -70,6 +70,17 @@ function gtag() { +
+ + + + + + + +
+ + diff --git a/src/demo/js/script.js b/src/demo/js/script.js index d067a193..533c5254 100644 --- a/src/demo/js/script.js +++ b/src/demo/js/script.js @@ -6,6 +6,7 @@ let preview = { color: "36BCF7", background: "00000000", size: "20", + letterSpacing: "normal", center: "false", vCenter: "false", multiline: "false", diff --git a/src/models/RendererModel.php b/src/models/RendererModel.php index 64e08d0e..a8d47e39 100644 --- a/src/models/RendererModel.php +++ b/src/models/RendererModel.php @@ -58,6 +58,9 @@ class RendererModel /** @var string $fontCSS CSS required for displaying the selected font */ public $fontCSS; + /** @var string $letterSpacing Letter spacing */ + public $letterSpacing; + /** @var string $template Path to template file */ public $template; @@ -78,6 +81,7 @@ class RendererModel "repeat" => "true", "separator" => ";", "random" => "false", + "letterSpacing" => "normal", ]; /** @@ -106,6 +110,7 @@ public function __construct($template, $params) $this->pause = $this->checkNumberNonNegative($params["pause"] ?? $this->DEFAULTS["pause"], "pause"); $this->repeat = $this->checkBoolean($params["repeat"] ?? $this->DEFAULTS["repeat"]); $this->fontCSS = $this->fetchFontCSS($this->font, $this->weight, $params["lines"]); + $this->letterSpacing = $this->checkLetterSpacing($params["letterSpacing"] ?? $this->DEFAULTS["letterSpacing"]); } /** @@ -224,4 +229,43 @@ private function fetchFontCSS($font, $weight, $text) // font is not found return ""; } + + /** + * Validate unit for size properties + * + * This method validates if the given unit is a valid CSS size unit. + * It supports various units such as px, em, rem, pt, pc, in, cm, mm, + * ex, ch, vh, vw, vmin, vmax, and percentages. + * + * @param string $unit Unit for validation + * @return bool True if valid, false otherwise + */ + private function isValidUnit($unit) + { + return (bool) preg_match("/^(-?\\d+(\\.\\d+)?(px|em|rem|pt|pc|in|cm|mm|ex|ch|vh|vw|vmin|vmax|%))$/", $unit); + } + + /** + * Validate letter spacing + * + * This method validates the letter spacing property for fonts. + * It allows specific keywords (normal, inherit, initial, revert, revert-layer, unset) + * and valid CSS size units. + * + * @param string $letterSpacing Letter spacing for validation + * @return string Validated letter spacing + */ + private function checkLetterSpacing($letterSpacing) + { + // List of valid keywords for letter-spacing + $keywords = "normal|inherit|initial|revert|revert-layer|unset"; + + // Check if the input matches one of the keywords or a valid unit + if (preg_match("/^($keywords)$/", $letterSpacing) || $this->isValidUnit($letterSpacing)) { + return $letterSpacing; + } + + // Return the default letter spacing value if the input is invalid + return $this->DEFAULTS["letterSpacing"]; + } } diff --git a/src/templates/main.php b/src/templates/main.php index 7664f89b..3d423335 100644 --- a/src/templates/main.php +++ b/src/templates/main.php @@ -57,7 +57,8 @@ + x='' text-anchor='' + letter-spacing=''> diff --git a/src/views/RendererView.php b/src/views/RendererView.php index 7c540003..934ea40c 100644 --- a/src/views/RendererView.php +++ b/src/views/RendererView.php @@ -41,6 +41,7 @@ public function render() "duration" => $this->model->duration, "pause" => $this->model->pause, "repeat" => $this->model->repeat, + "letterSpacing" => $this->model->letterSpacing, ]); // render SVG with output buffering ob_start(); diff --git a/tests/OptionsTest.php b/tests/OptionsTest.php index 48ff687e..6b8411cd 100644 --- a/tests/OptionsTest.php +++ b/tests/OptionsTest.php @@ -367,4 +367,33 @@ public function testRandom(): void $model = new RendererModel("src/templates/main.php", $params); $this->assertEquals(false, $model->random); } + + /** + * Test Letter Spacing + */ + public function testLetterSpacing(): void + { + // default + $params = [ + "lines" => "text", + ]; + $model = new RendererModel("src/templates/main.php", $params); + $this->assertEquals("normal", $model->letterSpacing); + + // invalid + $params = [ + "lines" => "text", + "letterSpacing" => "invalid", + ]; + $model = new RendererModel("src/templates/main.php", $params); + $this->assertEquals("normal", $model->letterSpacing); + + // valid + $params = [ + "lines" => "text", + "letterSpacing" => "10px", + ]; + $model = new RendererModel("src/templates/main.php", $params); + $this->assertEquals("10px", $model->letterSpacing); + } } diff --git a/tests/RendererTest.php b/tests/RendererTest.php index 6093aaf6..0e08a1c0 100644 --- a/tests/RendererTest.php +++ b/tests/RendererTest.php @@ -326,4 +326,24 @@ public function testRandom(): void $this->assertStringContainsString("> $line ", $actualSVG); } } + + /** + * Test Letter Spacing + */ + public function testLetterSpacing() + { + $params = [ + "lines" => implode(";", [ + "Full-stack web and app developer", + "Self-taught UI/UX Designer", + "10+ years of coding experience", + "Always learning new things", + ]), + "letterSpacing" => "10px", + ]; + $controller = new RendererController($params); + $actualSVG = preg_replace("/\s+/", " ", $controller->render()); + $this->assertStringContainsString("letter-spacing='10px'", $actualSVG); + $this->assertStringNotContainsString("letter-spacing='normal'", $actualSVG); + } } diff --git a/tests/svg/test_normal.svg b/tests/svg/test_normal.svg index 7c7b70f3..efcced2b 100644 --- a/tests/svg/test_normal.svg +++ b/tests/svg/test_normal.svg @@ -13,7 +13,8 @@ + x='50%' text-anchor='middle' + letter-spacing='normal'> Full-stack web and app developer @@ -27,7 +28,8 @@ + x='50%' text-anchor='middle' + letter-spacing='normal'> Self-taught UI/UX Designer @@ -41,7 +43,8 @@ + x='50%' text-anchor='middle' + letter-spacing='normal'> 10+ years of coding experience @@ -55,7 +58,8 @@ + x='50%' text-anchor='middle' + letter-spacing='normal'> Always learning new things