Skip to content

Commit

Permalink
ENGCOM-7465: Fix Arabic and Hebrew in invoices #27887
Browse files Browse the repository at this point in the history
  • Loading branch information
slavvka authored Apr 24, 2020
2 parents 9480de1 + d35ce43 commit 6ceff9c
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 10 deletions.
19 changes: 14 additions & 5 deletions app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
namespace Magento\Sales\Model\Order\Pdf;

use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\ObjectManager;
use Magento\MediaStorage\Helper\File\Storage\Database;
use Magento\Sales\Model\RtlTextHandler;

/**
* Sales Order PDF abstract model
Expand Down Expand Up @@ -53,6 +55,11 @@ abstract class AbstractPdf extends \Magento\Framework\DataObject
*/
protected $_pdf;

/**
* @var RtlTextHandler
*/
private $rtlTextHandler;

/**
* Retrieve PDF
*
Expand Down Expand Up @@ -142,6 +149,7 @@ abstract public function getPdf();
* @param \Magento\Sales\Model\Order\Address\Renderer $addressRenderer
* @param array $data
* @param Database $fileStorageDatabase
* @param RtlTextHandler|null $rtlTextHandler
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
Expand All @@ -156,7 +164,8 @@ public function __construct(
\Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
\Magento\Sales\Model\Order\Address\Renderer $addressRenderer,
array $data = [],
Database $fileStorageDatabase = null
Database $fileStorageDatabase = null,
?RtlTextHandler $rtlTextHandler = null
) {
$this->addressRenderer = $addressRenderer;
$this->_paymentData = $paymentData;
Expand All @@ -169,8 +178,8 @@ public function __construct(
$this->_pdfTotalFactory = $pdfTotalFactory;
$this->_pdfItemsFactory = $pdfItemsFactory;
$this->inlineTranslation = $inlineTranslation;
$this->fileStorageDatabase = $fileStorageDatabase ?:
\Magento\Framework\App\ObjectManager::getInstance()->get(Database::class);
$this->fileStorageDatabase = $fileStorageDatabase ?: ObjectManager::getInstance()->get(Database::class);
$this->rtlTextHandler = $rtlTextHandler ?: ObjectManager::getInstance()->get(RtlTextHandler::class);
parent::__construct($data);
}

Expand Down Expand Up @@ -501,7 +510,7 @@ protected function insertOrder(&$page, $obj, $putOrderId = true)
if ($value !== '') {
$text = [];
foreach ($this->string->split($value, 45, true, true) as $_value) {
$text[] = $_value;
$text[] = $this->rtlTextHandler->reverseRtlText($_value);
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 35, $this->y, 'UTF-8');
Expand All @@ -518,7 +527,7 @@ protected function insertOrder(&$page, $obj, $putOrderId = true)
if ($value !== '') {
$text = [];
foreach ($this->string->split($value, 45, true, true) as $_value) {
$text[] = $_value;
$text[] = $this->rtlTextHandler->reverseRtlText($_value);
}
foreach ($text as $part) {
$page->drawText(strip_tags(ltrim($part)), 285, $this->y, 'UTF-8');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

namespace Magento\Sales\Model\Order\Pdf\Items\Invoice;

use Magento\Framework\App\ObjectManager;
use Magento\Sales\Model\RtlTextHandler;

/**
* Sales Order Invoice Pdf default items renderer
*/
Expand All @@ -19,6 +22,11 @@ class DefaultInvoice extends \Magento\Sales\Model\Order\Pdf\Items\AbstractItems
*/
protected $string;

/**
* @var RtlTextHandler
*/
private $rtlTextHandler;

/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
Expand All @@ -29,6 +37,8 @@ class DefaultInvoice extends \Magento\Sales\Model\Order\Pdf\Items\AbstractItems
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
* @param RtlTextHandler|null $rtlTextHandler
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Framework\Model\Context $context,
Expand All @@ -39,7 +49,8 @@ public function __construct(
\Magento\Framework\Stdlib\StringUtils $string,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = []
array $data = [],
?RtlTextHandler $rtlTextHandler = null
) {
$this->string = $string;
parent::__construct(
Expand All @@ -52,6 +63,7 @@ public function __construct(
$resourceCollection,
$data
);
$this->rtlTextHandler = $rtlTextHandler ?: ObjectManager::getInstance()->get(RtlTextHandler::class);
}

/**
Expand All @@ -70,16 +82,14 @@ public function draw()
// draw Product name
$lines[0] = [
[
// phpcs:ignore Magento2.Functions.DiscouragedFunction
'text' => $this->string->split(html_entity_decode($item->getName()), 35, true, true),
'text' => $this->string->split($this->prepareText((string)$item->getName()), 35, true, true),
'feed' => 35
]
];

// draw SKU
$lines[0][] = [
// phpcs:ignore Magento2.Functions.DiscouragedFunction
'text' => $this->string->split(html_entity_decode($this->getSku($item)), 17),
'text' => $this->string->split($this->prepareText((string)$this->getSku($item)), 17),
'feed' => 290,
'align' => 'right',
];
Expand Down Expand Up @@ -156,4 +166,16 @@ public function draw()
$page = $pdf->drawLineBlocks($page, [$lineBlock], ['table_header' => true]);
$this->setPage($page);
}

/**
* Returns prepared for PDF text, reversed in case of RTL text
*
* @param string $string
* @return string
*/
private function prepareText(string $string): string
{
// phpcs:ignore Magento2.Functions.DiscouragedFunction
return $this->rtlTextHandler->reverseRtlText(html_entity_decode($string));
}
}
63 changes: 63 additions & 0 deletions app/code/Magento/Sales/Model/RtlTextHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Sales\Model;

use Magento\Framework\Stdlib\StringUtils;

class RtlTextHandler
{
/**
* @var StringUtils
*/
private $stringUtils;

/**
* @param StringUtils $stringUtils
*/
public function __construct(StringUtils $stringUtils)
{
$this->stringUtils = $stringUtils;
}

/**
* Detect an input string is Arabic
*
* @param string $subject
* @return bool
*/
public function isRtlText(string $subject): bool
{
return (preg_match('/[\p{Arabic}\p{Hebrew}]/u', $subject) > 0);
}

/**
* Reverse text with Arabic characters
*
* @param string $string
* @return string
*/
public function reverseRtlText(string $string): string
{
$splitText = explode(' ', $string);
$splitTextAmount = count($splitText);

for ($i = 0; $i < $splitTextAmount; $i++) {
if ($this->isRtlText($splitText[$i])) {
for ($j = $i + 1; $j < $splitTextAmount; $j++) {
$tmp = $this->isRtlText($splitText[$j])
? $this->stringUtils->strrev($splitText[$j]) : $splitText[$j];
$splitText[$j] = $this->isRtlText($splitText[$i])
? $this->stringUtils->strrev($splitText[$i]) : $splitText[$i];
$splitText[$i] = $tmp;
}
}
}

return implode(' ', $splitText);
}
}
71 changes: 71 additions & 0 deletions app/code/Magento/Sales/Test/Unit/Model/RtlTextHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Sales\Test\Unit\Model;

use Magento\Framework\Stdlib\StringUtils;
use Magento\Sales\Model\RtlTextHandler;
use PHPUnit\Framework\TestCase;

class RtlTextHandlerTest extends TestCase
{
/**
* @var RtlTextHandler
*/
private $rtlTextHandler;

/**
* @var StringUtils
*/
private $stringUtils;

protected function setUp(): void
{
$this->stringUtils = new StringUtils();
$this->rtlTextHandler = new RtlTextHandler($this->stringUtils);
}

/**
* @param string $str
* @param bool $isRtl
* @dataProvider provideRtlTexts
*/
public function testIsRtlText(string $str, bool $isRtl): void
{
$this->assertEquals($isRtl, $this->rtlTextHandler->isRtlText($str));
}

/**
* @param string $str
* @param bool $isRtl
* @dataProvider provideRtlTexts
*/
public function testReverseRtlText(string $str, bool $isRtl): void
{
$expectedStr = $isRtl ? $this->stringUtils->strrev($str) : $str;

$this->assertEquals($expectedStr, $this->rtlTextHandler->reverseRtlText($str));
}

public function provideRtlTexts(): array
{
return [
['Adeline Jacobson', false],//English
['Odell Fisher', false],//English
['Панов Аркадий Львович', false],//Russian
['Вероника Сергеевна Игнатьева', false],//Russian
['Mehmet Arnold-Döring', false],//German
['Herr Prof. Dr. Gerald Schüler B.A.', false],//German
['نديم مقداد نعمان القحطاني', true],//Arabic
['شهاب الفرحان', true],//Arabic
['צבר קרליבך', true],//Hebrew
['גורי מייזליש', true],//Hebrew
['اتابک بهشتی', true],//Persian
['مهداد محمدی', true],//Persian
];
}
}

0 comments on commit 6ceff9c

Please sign in to comment.