diff options
| -rw-r--r-- | app/Report/ReportPdfCell.php | 4 | ||||
| -rw-r--r-- | app/Report/ReportPdfText.php | 4 | ||||
| -rw-r--r-- | app/Reports/RightToLeftSupport.php (renamed from app/Functions/FunctionsRtl.php) | 27 | ||||
| -rw-r--r-- | tests/app/Reports/RightToLeftSupportTest.php | 257 |
4 files changed, 277 insertions, 15 deletions
diff --git a/app/Report/ReportPdfCell.php b/app/Report/ReportPdfCell.php index 67330b9ddd..417dd12fc2 100644 --- a/app/Report/ReportPdfCell.php +++ b/app/Report/ReportPdfCell.php @@ -19,7 +19,7 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Report; -use Fisharebest\Webtrees\Functions\FunctionsRtl; +use Fisharebest\Webtrees\Reports\RightToLeftSupport; use function hexdec; use function is_array; @@ -130,7 +130,7 @@ class ReportPdfCell extends ReportBaseCell if ($renderer->checkPageBreakPDF($cHT)) { $this->top = $renderer->tcpdf->GetY(); } - $temptext = FunctionsRtl::spanLtrRtl($temptext); + $temptext = RightToLeftSupport::spanLtrRtl($temptext); } // HTML ready - last value is true $renderer->tcpdf->MultiCell( diff --git a/app/Report/ReportPdfText.php b/app/Report/ReportPdfText.php index 40b28306d2..6c7188d2d3 100644 --- a/app/Report/ReportPdfText.php +++ b/app/Report/ReportPdfText.php @@ -19,7 +19,7 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Report; -use Fisharebest\Webtrees\Functions\FunctionsRtl; +use Fisharebest\Webtrees\Reports\RightToLeftSupport; use function count; use function explode; @@ -66,7 +66,7 @@ class ReportPdfText extends ReportBaseText } else { $renderer->tcpdf->SetTextColor(0, 0, 0); } - $temptext = FunctionsRtl::spanLtrRtl($temptext); + $temptext = RightToLeftSupport::spanLtrRtl($temptext); $temptext = str_replace( [ '<br><span dir="rtl">', diff --git a/app/Functions/FunctionsRtl.php b/app/Reports/RightToLeftSupport.php index 936fae9833..a991c3b72e 100644 --- a/app/Functions/FunctionsRtl.php +++ b/app/Reports/RightToLeftSupport.php @@ -17,16 +17,16 @@ declare(strict_types=1); -namespace Fisharebest\Webtrees\Functions; +namespace Fisharebest\Webtrees\Reports; use Fisharebest\Webtrees\I18N; use function str_contains; /** - * RTL Functions for use in the PDF/HTML reports + * RTL Functions for use in the PDF reports */ -class FunctionsRtl +class RightToLeftSupport { private const UTF8_LRM = "\xE2\x80\x8E"; // U+200E (Left to Right mark: zero-width character with LTR directionality) private const UTF8_RLM = "\xE2\x80\x8F"; // U+200F (Right to Left mark: zero-width character with RTL directionality) @@ -77,7 +77,7 @@ class FunctionsRtl * * @return string The input string, with ‎ and ‏ stripped */ - public static function stripLrmRlm(string $inputText): string + private static function stripLrmRlm(string $inputText): string { return str_replace([ self::UTF8_LRM, @@ -167,7 +167,7 @@ class FunctionsRtl $currentLen += $endPos; $entity = substr($workingText, 0, $currentLen); if (strtolower($entity) === ' ') { - $entity .= ' '; // Ensure consistent case for this entity + $entity = ' '; // Ensure consistent case for this entity } if (self::$waitingText === '') { $result .= $entity; @@ -474,12 +474,17 @@ class FunctionsRtl // Include solitary "-" and "+" in surrounding RTL text $result = str_replace([ self::END_RTL . self::START_LTR . '-' . self::END_LTR . self::START_RTL, - self::END_RTL . self::START_LTR . '-' . self::END_LTR . self::START_RTL, + self::END_RTL . self::START_LTR . '+' . self::END_LTR . self::START_RTL, ], [ '-', '+', ], $result); + //$result = strtr($result, [ + // self::END_RTL . self::START_LTR . '-' . self::END_LTR . self::START_RTL => '-', + // self::END_RTL . self::START_LTR . '+' . self::END_LTR . self::START_RTL => '+', + //]); + // Remove empty spans $result = str_replace([ self::START_LTR . self::END_LTR, @@ -514,7 +519,7 @@ class FunctionsRtl * * @return string */ - public static function starredName(string $textSpan, string $direction): string + private static function starredName(string $textSpan, string $direction): string { // To avoid a TCPDF bug that mixes up the word order, insert those <u> and </u> tags // only when page and span directions are identical. @@ -562,7 +567,7 @@ class FunctionsRtl * * @return array{'letter':string,'length':int} */ - public static function getChar(string $text, int $offset): array + private static function getChar(string $text, int $offset): array { if ($text === '') { return [ @@ -597,7 +602,7 @@ class FunctionsRtl * * @return void */ - public static function breakCurrentSpan(string &$result): void + private static function breakCurrentSpan(string &$result): void { // Interrupt the current span, insert that <br>, and then continue the current span $result .= self::$waitingText; @@ -614,7 +619,7 @@ class FunctionsRtl * * @return void */ - public static function beginCurrentSpan(string &$result): void + private static function beginCurrentSpan(string &$result): void { if (self::$currentState === 'LTR') { $result .= self::START_LTR; @@ -634,7 +639,7 @@ class FunctionsRtl * * @return void */ - public static function finishCurrentSpan(string &$result, bool $theEnd = false): void + private static function finishCurrentSpan(string &$result, bool $theEnd = false): void { $textSpan = substr($result, self::$posSpanStart); $result = substr($result, 0, self::$posSpanStart); diff --git a/tests/app/Reports/RightToLeftSupportTest.php b/tests/app/Reports/RightToLeftSupportTest.php new file mode 100644 index 0000000000..5b0de222a7 --- /dev/null +++ b/tests/app/Reports/RightToLeftSupportTest.php @@ -0,0 +1,257 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2021 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Reports; + +use Fisharebest\Webtrees\I18N; +use Fisharebest\Webtrees\TestCase; + +/** + * Test the RTL functions. This is very old code, and poorly understood. + * These tests exist to capture the existing functionality, and prevent regression during refactoring. + */ +class RightToLeftSupportTest extends TestCase +{ + /** + * @covers \Fisharebest\Webtrees\Reports\RightToLeftSupport + * + * @return void + */ + public function testEmptyString(): void + { + I18N::init('en-US', true); + $this->assertSame( + + '', + RightToLeftSupport::spanLtrRtl('') + ); + + I18N::init('he', true); + $this->assertSame( + '', + RightToLeftSupport::spanLtrRtl('') + ); + } + + /** + * @covers \Fisharebest\Webtrees\Reports\RightToLeftSupport + * + * @return void + */ + public function testStripControlCharacters(): void + { + I18N::init('en-US', true); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl('foo‎bar') + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl('foo‏bar') + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\x8Ebar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\x8Fbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xADbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xAEbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xAAbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xABbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xACbar") + ); + + I18N::init('he', true); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl('foo‎bar') + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl('foo‏bar') + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\x8Ebar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\x8Fbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xADbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xAEbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xAAbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xABbar") + ); + $this->assertSame( + '<span dir="ltr">foobar</span>', + RightToLeftSupport::spanLtrRtl("foo\xE2\x80\xACbar") + ); + } + + /** + * @covers \Fisharebest\Webtrees\Reports\RightToLeftSupport + * + * @return void + */ + public function testNewLinesBecomeHTMLBreaks(): void + { + I18N::init('en-US', true); + $this->assertSame( + '<span dir="ltr">foo</span><br><span dir="ltr">bar</span>', + RightToLeftSupport::spanLtrRtl("foo\nbar") + ); + $this->assertSame( + '<span dir="rtl">אבג</span><br><span dir="rtl">דהו</span>', + RightToLeftSupport::spanLtrRtl("אבג\nדהו") + ); + + I18N::init('he', true); + $this->assertSame( + '<span dir="ltr">foo</span><br><span dir="ltr">bar</span>', + RightToLeftSupport::spanLtrRtl("foo\nbar") + ); + $this->assertSame( + '<span dir="rtl">אבג</span><br><span dir="rtl">דהו</span>', + RightToLeftSupport::spanLtrRtl("אבג\nדהו") + ); + } + + /** + * @covers \Fisharebest\Webtrees\Reports\RightToLeftSupport + * + * @return void + */ + public function testLineBreaks(): void + { + I18N::init('en-US', true); + $this->assertSame( + '<span dir="ltr">foo</span><br><span dir="ltr">bar</span>', + RightToLeftSupport::spanLtrRtl("foo<br>bar") + ); + $this->assertSame( + '<span dir="rtl">אבג</span><br><span dir="rtl">דהו</span>', + RightToLeftSupport::spanLtrRtl("אבג<br>דהו") + ); + + I18N::init('he', true); + $this->assertSame( + '<span dir="ltr">foo</span><br><span dir="ltr">bar</span>', + RightToLeftSupport::spanLtrRtl("foo<br>bar") + ); + $this->assertSame( + '<span dir="rtl">אבג</span><br><span dir="rtl">דהו</span>', + RightToLeftSupport::spanLtrRtl("אבג<br>דהו") + ); + } + + /** + * @covers \Fisharebest\Webtrees\Reports\RightToLeftSupport + * + * @return void + */ + public function testHtmlEntities(): void + { + I18N::init('en-US', true); + $this->assertSame( + '<span dir="ltr">foo bar</span>', + RightToLeftSupport::spanLtrRtl("foo bar") + ); + $this->assertSame( + '<span dir="rtl">אבג דהו</span>', + RightToLeftSupport::spanLtrRtl("אבג דהו") + ); + + I18N::init('he', true); + $this->assertSame( + '<span dir="ltr">foo bar</span>', + RightToLeftSupport::spanLtrRtl("foo bar") + ); + $this->assertSame( + '<span dir="rtl">אבג דהו</span>', + RightToLeftSupport::spanLtrRtl("אבג דהו") + ); + } + + /** + * @covers \Fisharebest\Webtrees\Reports\RightToLeftSupport + * + * @return void + */ + public function xtestLeftToRight(): void + { + $this->assertSame('<span dir="ltr">fooébar</span>', RightToLeftSupport::spanLtrRtl('fooébar')); + // Number + $this->assertSame('<span dir="ltr">foo 123,456.78 bar</span>', RightToLeftSupport::spanLtrRtl('foo 123,456.78 bar')); + $this->assertSame('<span dir="ltr">foo -123,456.78 bar</span>', RightToLeftSupport::spanLtrRtl('foo -123,456.78 bar')); + $this->assertSame('<span dir="ltr">foo +123,456.78 bar</span>', RightToLeftSupport::spanLtrRtl('foo +123,456.78 bar')); + $this->assertSame('<span dir="rtl">אבג</span><span dir="ltr"> 123,456.78 bar</span>', RightToLeftSupport::spanLtrRtl('אבג 123,456.78 bar')); + $this->assertSame('<span dir="rtl">אבג</span><span dir="ltr"> -123,456.78 bar</span>', RightToLeftSupport::spanLtrRtl('אבג -123,456.78 bar')); + $this->assertSame('<span dir="rtl">אבג</span><span dir="ltr"> +123,456.78 bar</span>', RightToLeftSupport::spanLtrRtl('אבג +123,456.78 bar')); + // TCPDF directive + $this->assertSame('<span dir="ltr">{{FOO BAR}}</span>', RightToLeftSupport::spanLtrRtl('{{FOO BAR}}')); + // Broken TCPDF directive + $this->assertSame('<span dir="ltr">{{FOO BAR</span>', RightToLeftSupport::spanLtrRtl('{{FOO BAR')); + // Starred name. + $this->assertSame('<span dir="ltr">John <u>Paul</u> Sartre</span>', RightToLeftSupport::spanLtrRtl('John <span class="starredname">Paul</span> Sartre')); + // Unclosed HTML tag + $this->assertSame('<span dir="ltr"><foo</span>', RightToLeftSupport::spanLtrRtl('<foo')); + // All LTR/RTL + $this->assertSame('<span dir="ltr">foo</span>', RightToLeftSupport::spanLtrRtl('foo')); + $this->assertSame('<span dir="rtl">אבג</span>', RightToLeftSupport::spanLtrRtl('אבג')); + // Leading/trailing spaces + $this->assertSame('<span dir="ltr"> foo </span>', RightToLeftSupport::spanLtrRtl(' foo ')); + $this->assertSame('<span dir="ltr"> </span><span dir="rtl">אבג</span><span dir="ltr"> </span>', RightToLeftSupport::spanLtrRtl(' אבג ')); + $this->assertSame('<span dir="ltr"> foo </span>', RightToLeftSupport::spanLtrRtl(' foo ')); + $this->assertSame('<span dir="ltr"> </span><span dir="rtl">אבג</span><span dir="ltr"> </span>', RightToLeftSupport::spanLtrRtl(' אבג ')); + // Spaces stick to the LTR text + $this->assertSame('<span dir="ltr">foo </span><span dir="rtl">אבג</span>', RightToLeftSupport::spanLtrRtl('foo אבג')); + $this->assertSame('<span dir="rtl">אבג</span><span dir="ltr"> foo</span>', RightToLeftSupport::spanLtrRtl('אבג foo')); + // Line breaks + $this->assertSame('<span dir="ltr">foo</span><br><span dir="rtl">אבג</span><br><span dir="ltr">bar</span>', RightToLeftSupport::spanLtrRtl('foo<br>אבג<br>bar')); + } +} |
