@@ -325,4 +325,128 @@ public function testGetTemplateVariablesMetadata(): void {
325325 $ this ->assertSame ('string ' , $ metadata ['uuid ' ]['type ' ]);
326326 $ this ->assertArrayHasKey ('default ' , $ metadata ['signedBy ' ]);
327327 }
328+
329+ #[DataProvider('dataAccentedCharactersInFooter ' )]
330+ public function testAccentedCharactersInFooterVariablesAreRenderedCorrectly (
331+ string $ testName ,
332+ string $ signedByText ,
333+ array $ expectedSubstrings ,
334+ array $ forbiddenSubstrings
335+ ): void {
336+ $ this ->appConfig ->setValueBool (Application::APP_ID , 'add_footer ' , true );
337+ $ this ->appConfig ->setValueBool (Application::APP_ID , 'write_qrcode_on_footer ' , false );
338+ $ this ->appConfig ->deleteKey (Application::APP_ID , 'footer_template ' );
339+
340+ $ dimensions = [['w ' => 595 , 'h ' => 100 ]];
341+ $ this ->l10n = $ this ->l10nFactory ->get (Application::APP_ID , 'en ' );
342+
343+ $ pdf = $ this ->getClass ()
344+ ->setTemplateVar ('uuid ' , 'test-uuid ' )
345+ ->setTemplateVar ('signedBy ' , $ signedByText )
346+ ->setTemplateVar ('linkToSite ' , 'https://libresign.coop ' )
347+ ->getFooter ($ dimensions );
348+
349+ $ this ->assertNotEmpty ($ pdf );
350+
351+ $ parser = new \Smalot \PdfParser \Parser ();
352+ $ pdfParsed = $ parser ->parseContent ($ pdf );
353+ $ text = $ pdfParsed ->getText ();
354+
355+ foreach ($ expectedSubstrings as $ expected ) {
356+ $ this ->assertStringContainsString ($ expected , $ text , "Expected to find ' {$ expected }' for test: {$ testName }" );
357+ }
358+
359+ foreach ($ forbiddenSubstrings as $ forbidden ) {
360+ $ this ->assertStringNotContainsString ($ forbidden , $ text , "Should not find ' {$ forbidden }' for test: {$ testName }" );
361+ }
362+ }
363+
364+ public static function dataAccentedCharactersInFooter (): array {
365+ return [
366+ 'French accents ' => [
367+ 'testName ' => 'French accents ' ,
368+ 'signedByText ' => 'Signé numériquement par LibreSign ' ,
369+ 'expectedSubstrings ' => ['Signé ' , 'numériquement ' ],
370+ 'forbiddenSubstrings ' => ['é ' , '& ' , 'é ' ],
371+ ],
372+ 'Portuguese accents and cedilla ' => [
373+ 'testName ' => 'Portuguese accents ' ,
374+ 'signedByText ' => 'Assinado digitalmente por João da Silva ' ,
375+ 'expectedSubstrings ' => ['João ' , 'Silva ' ],
376+ 'forbiddenSubstrings ' => ['ã ' , '& ' , 'ã ' ],
377+ ],
378+ 'Spanish ñ and accents ' => [
379+ 'testName ' => 'Spanish characters ' ,
380+ 'signedByText ' => 'Firmado digitalmente por José Muñoz ' ,
381+ 'expectedSubstrings ' => ['José ' , 'Muñoz ' ],
382+ 'forbiddenSubstrings ' => ['ñ ' , 'é ' , '& ' ],
383+ ],
384+ 'German umlauts ' => [
385+ 'testName ' => 'German umlauts ' ,
386+ 'signedByText ' => 'Digital signiert von Müller & Söhne ' ,
387+ 'expectedSubstrings ' => ['Müller ' , 'Söhne ' ],
388+ 'forbiddenSubstrings ' => ['ü ' , 'ö ' , '& ' ],
389+ ],
390+ 'Multiple special characters ' => [
391+ 'testName ' => 'Multiple accents ' ,
392+ 'signedByText ' => 'Signé par Renée & José ' ,
393+ 'expectedSubstrings ' => ['Signé ' , 'Renée ' , 'José ' ],
394+ 'forbiddenSubstrings ' => ['é ' , '& ' , '&# ' ],
395+ ],
396+ 'Greek characters ' => [
397+ 'testName ' => 'Greek characters ' ,
398+ 'signedByText ' => 'Υπογραφή από Αθήνα ' ,
399+ 'expectedSubstrings ' => ['Υπογραφή ' , 'Αθήνα ' ],
400+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
401+ ],
402+ 'Cyrillic characters ' => [
403+ 'testName ' => 'Cyrillic characters ' ,
404+ 'signedByText ' => 'Подписано Москва ' ,
405+ 'expectedSubstrings ' => ['Подписано ' , 'Москва ' ],
406+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
407+ ],
408+ 'Arabic characters (RTL) ' => [
409+ 'testName ' => 'Arabic (RTL) ' ,
410+ 'signedByText ' => 'توقيع رقمي من القاهرة ' ,
411+ 'expectedSubstrings ' => ['عيقوت ' , 'ةرهاقلا ' ], // RTL text appears reversed in extracted PDF
412+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
413+ ],
414+ 'Hebrew characters (RTL) ' => [
415+ 'testName ' => 'Hebrew (RTL) ' ,
416+ 'signedByText ' => 'חתום דיגיטלית מירושלים ' ,
417+ 'expectedSubstrings ' => ['םותח ' , 'םילשורימ ' ], // RTL text appears reversed in extracted PDF
418+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
419+ ],
420+ 'Chinese characters ' => [
421+ 'testName ' => 'Chinese (CJK) ' ,
422+ 'signedByText ' => '数字签名 北京 ' ,
423+ 'expectedSubstrings ' => ['数字签名 ' , '北京 ' ],
424+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
425+ ],
426+ 'Japanese characters ' => [
427+ 'testName ' => 'Japanese (CJK) ' ,
428+ 'signedByText ' => 'デジタル署名 東京 ' ,
429+ 'expectedSubstrings ' => ['デジタル署名 ' , '東京 ' ],
430+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
431+ ],
432+ 'Korean characters ' => [
433+ 'testName ' => 'Korean (CJK) ' ,
434+ 'signedByText ' => '디지털 서명 서울 ' ,
435+ 'expectedSubstrings ' => ['디지털 ' , '서울 ' ],
436+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
437+ ],
438+ 'Emoji characters ' => [
439+ 'testName ' => 'Emoji ' ,
440+ 'signedByText ' => 'Signed ✍️ by LibreSign 🔒 ' ,
441+ 'expectedSubstrings ' => ['Signed ' , 'LibreSign ' ],
442+ 'forbiddenSubstrings ' => ['& ' , '&# ' ],
443+ ],
444+ 'Mixed emoji and accents ' => [
445+ 'testName ' => 'Emoji with accents ' ,
446+ 'signedByText ' => 'Signé 📝 par José 👤 ' ,
447+ 'expectedSubstrings ' => ['Signé ' , 'José ' ],
448+ 'forbiddenSubstrings ' => ['é ' , '& ' , '&# ' ],
449+ ],
450+ ];
451+ }
328452}
0 commit comments