diff --git a/lib/Service/SignatureTextService.php b/lib/Service/SignatureTextService.php index d72e280153..e933715c05 100644 --- a/lib/Service/SignatureTextService.php +++ b/lib/Service/SignatureTextService.php @@ -230,7 +230,7 @@ public function signerNameImage( $draw->setTextAlignment($align); $maxCharsPerLine = $this->splitAndGetLongestHalfLength($text); - $wrappedText = wordwrap($text, $maxCharsPerLine, "\n", true); + $wrappedText = $this->mbWordwrap($text, $maxCharsPerLine, "\n", true); $textMetrics = $image->queryFontMetrics($draw, $wrappedText); $lineCount = substr_count($wrappedText, "\n") + 1; @@ -300,6 +300,91 @@ private function splitAndGetLongestHalfLength(string $text): int { return !empty($results) ? max($results) : $length; } + /** + * Multibyte-safe version of wordwrap + * + * @param string $text The text to wrap + * @param int $width The number of characters at which the string will be wrapped + * @param string $break The line break character + * @param bool $cut If true, words longer than $width will be broken + * @return string The wrapped text + */ + private function mbWordwrap(string $text, int $width, string $break = "\n", bool $cut = false): string { + if ($width <= 0) { + return $text; + } + + $lines = []; + $currentLine = ''; + $currentLength = 0; + + $paragraphs = explode("\n", $text); + + foreach ($paragraphs as $paragraphIndex => $paragraph) { + if ($paragraph === '') { + if ($currentLength > 0) { + $lines[] = $currentLine; + $currentLine = ''; + $currentLength = 0; + } + $lines[] = ''; + continue; + } + + $words = explode(' ', $paragraph); + + foreach ($words as $word) { + $wordLength = mb_strlen($word); + + if ($cut && $wordLength > $width) { + if ($currentLength > 0) { + $lines[] = $currentLine; + $currentLine = ''; + $currentLength = 0; + } + + while ($wordLength > $width) { + $lines[] = mb_substr($word, 0, $width); + $word = mb_substr($word, $width); + $wordLength = mb_strlen($word); + } + + if ($wordLength > 0) { + $currentLine = $word; + $currentLength = $wordLength; + } + continue; + } + + $spaceLength = ($currentLength > 0) ? 1 : 0; + if ($currentLength + $spaceLength + $wordLength > $width && $currentLength > 0) { + $lines[] = $currentLine; + $currentLine = $word; + $currentLength = $wordLength; + } else { + if ($currentLength > 0) { + $currentLine .= ' '; + $currentLength++; + } + $currentLine .= $word; + $currentLength += $wordLength; + } + } + + if ($currentLength > 0 && $paragraphIndex < count($paragraphs) - 1) { + $lines[] = $currentLine; + $currentLine = ''; + $currentLength = 0; + } + } + + if ($currentLength > 0) { + $lines[] = $currentLine; + } + + return implode($break, $lines); + } + public function getDefaultTemplate(): string { $collectMetadata = $this->appConfig->getValueBool(Application::APP_ID, 'collect_metadata', false); if ($collectMetadata) { diff --git a/tests/php/Unit/Service/SignatureTextServiceTest.php b/tests/php/Unit/Service/SignatureTextServiceTest.php index d8afa627d6..9cc80b076a 100644 --- a/tests/php/Unit/Service/SignatureTextServiceTest.php +++ b/tests/php/Unit/Service/SignatureTextServiceTest.php @@ -203,6 +203,11 @@ public static function providerSignerNameImage(): array { 'center 175x50 scale 2.5' => ['Secure ✔️', 175, 50, 'center', 2.5], 'left 175x50 scale 3' => ['Sign now', 175, 50, 'left',3], 'right 175x50 scale 4' => ['Signed 🔐', 175, 50, 'right', 4], + + // Portuguese text with accents + 'center 175x101 portuguese' => ['Imagem da assinatura aqui', 175, 101, 'center', 5], + 'right 175x101 portuguese' => ['Imagem da assinatura aqui', 175, 101, 'right', 5], + 'left 350x100 portuguese long' => ['Assinado com LibreSign administrador', 350, 100, 'left', 5], ]; }