Skip to content

Commit bb3d158

Browse files
committed
#35706 Remote storage issue #35706
- Changed ImageFactory from static to generic class - Added test for ImageFactory
1 parent 4faa250 commit bb3d158

File tree

4 files changed

+203
-23
lines changed

4 files changed

+203
-23
lines changed

app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ abstract class AbstractPdf extends \Magento\Framework\DataObject
6262
*/
6363
private $rtlTextHandler;
6464

65+
/**
66+
* @var \Magento\Framework\File\Pdf\Image|mixed
67+
*/
68+
protected $image;
69+
6570
/**
6671
* Retrieve PDF
6772
*
@@ -150,6 +155,7 @@ abstract public function getPdf();
150155
* @param array $data
151156
* @param Database $fileStorageDatabase
152157
* @param RtlTextHandler|null $rtlTextHandler
158+
* @param Image $image
153159
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
154160
*/
155161
public function __construct(
@@ -165,7 +171,8 @@ public function __construct(
165171
\Magento\Sales\Model\Order\Address\Renderer $addressRenderer,
166172
array $data = [],
167173
Database $fileStorageDatabase = null,
168-
?RtlTextHandler $rtlTextHandler = null
174+
?RtlTextHandler $rtlTextHandler = null,
175+
?Image $image = null
169176
) {
170177
$this->addressRenderer = $addressRenderer;
171178
$this->_paymentData = $paymentData;
@@ -180,6 +187,7 @@ public function __construct(
180187
$this->inlineTranslation = $inlineTranslation;
181188
$this->fileStorageDatabase = $fileStorageDatabase ?: ObjectManager::getInstance()->get(Database::class);
182189
$this->rtlTextHandler = $rtlTextHandler ?: ObjectManager::getInstance()->get(RtlTextHandler::class);
190+
$this->image = $image ?: ObjectManager::getInstance()->get(Image::class);
183191
parent::__construct($data);
184192
}
185193

@@ -280,7 +288,7 @@ protected function insertLogo(&$page, $store = null)
280288
$this->fileStorageDatabase->saveFileToFilesystem($imagePath);
281289
}
282290
if ($this->_mediaDirectory->isFile($imagePath)) {
283-
$image = Image::imageWithPath($this->_mediaDirectory->getAbsolutePath($imagePath));
291+
$image = $this->image->imageWithPathAdvanced($this->_mediaDirectory->getAbsolutePath($imagePath));
284292
$top = 830;
285293
//top border of the page
286294
$widthLimit = 270;

lib/internal/Magento/Framework/File/Pdf/Image.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,21 @@
55
use Magento\Framework\File\Pdf\ImageResource\ImageFactory;
66
use Zend_Pdf_Image;
77

8-
abstract class Image extends Zend_Pdf_Image
8+
class Image extends Zend_Pdf_Image
99
{
10+
/**
11+
* @var \Magento\Framework\File\Pdf\ImageResource\ImageFactory
12+
*/
13+
private ImageFactory $imageFactory;
14+
15+
/**
16+
* @param \Magento\Framework\File\Pdf\ImageResource\ImageFactory $imageFactory
17+
*/
18+
public function __construct(ImageFactory $imageFactory)
19+
{
20+
$this->imageFactory = $imageFactory;
21+
}
22+
1023
/**
1124
* Filepath of image file
1225
*
@@ -15,8 +28,8 @@ abstract class Image extends Zend_Pdf_Image
1528
* @throws \Magento\Framework\Exception\FileSystemException
1629
* @throws \Zend_Pdf_Exception
1730
*/
18-
public static function imageWithPath($filePath)
31+
public function imageWithPathAdvanced($filePath)
1932
{
20-
return ImageFactory::factory($filePath);
33+
return $this->imageFactory->factory($filePath);
2134
}
2235
}

lib/internal/Magento/Framework/File/Pdf/ImageResource/ImageFactory.php

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,25 @@
1111
use Exception;
1212
use finfo;
1313
use Magento\Framework\App\Filesystem\DirectoryList;
14-
use Magento\Framework\App\ObjectManager;
1514
use Magento\Framework\Filesystem;
1615
use Magento\Framework\Filesystem\Directory\ReadInterface;
1716
use Zend_Pdf_Exception;
18-
use Zend_Pdf_Resource_ImageFactory;
1917

20-
class ImageFactory extends Zend_Pdf_Resource_ImageFactory
18+
class ImageFactory
2119
{
20+
/**
21+
* @var \Magento\Framework\Filesystem
22+
*/
23+
private Filesystem $filesystem;
24+
25+
/**
26+
* @param \Magento\Framework\Filesystem $filesystem
27+
*/
28+
public function __construct(Filesystem $filesystem)
29+
{
30+
$this->filesystem = $filesystem;
31+
}
32+
2233
/**
2334
* New zend image factory instance
2435
*
@@ -28,19 +39,18 @@ class ImageFactory extends Zend_Pdf_Resource_ImageFactory
2839
* @throws \Zend_Pdf_Exception
2940
* @SuppressWarnings(PHPMD.LongVariable)
3041
*/
31-
public static function factory($filename)
42+
public function factory($filename)
3243
{
33-
$filesystem = ObjectManager::getInstance()->get(Filesystem::class);
34-
$mediaReader = $filesystem->getDirectoryRead(DirectoryList::MEDIA);
44+
$mediaReader = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA);
3545
if (!$mediaReader->isFile($filename)) {
3646
#require_once 'Zend/Pdf/Exception.php';
3747
throw new Zend_Pdf_Exception("Cannot create image resource. File not found.");
3848
}
39-
$tempFilenameFromBucketOrDisk = self::createTemporaryFileAndPutContent($mediaReader, $filename);
40-
$tempResourceFilePath = self::getFilePathOfTemporaryFile($tempFilenameFromBucketOrDisk);
41-
$typeOfImage = self::getTypeOfImage($tempResourceFilePath, $filename);
42-
$zendPdfImage = self::getZendPdfImage($typeOfImage, $tempResourceFilePath);
43-
self::removeTemoraryFile($tempFilenameFromBucketOrDisk);
49+
$tempFilenameFromBucketOrDisk = $this->createTemporaryFileAndPutContent($mediaReader, $filename);
50+
$tempResourceFilePath = $this->getFilePathOfTemporaryFile($tempFilenameFromBucketOrDisk);
51+
$typeOfImage = $this->getTypeOfImage($tempResourceFilePath, $filename);
52+
$zendPdfImage = $this->getZendPdfImage($typeOfImage, $tempResourceFilePath);
53+
$this->removeTemoraryFile($tempFilenameFromBucketOrDisk);
4454
return $zendPdfImage;
4555
}
4656

@@ -54,7 +64,7 @@ public static function factory($filename)
5464
* @throws \Zend_Pdf_Exception
5565
* @SuppressWarnings(PHPMD.LongVariable)
5666
*/
57-
protected static function createTemporaryFileAndPutContent(ReadInterface $mediaReader, string $filename)
67+
protected function createTemporaryFileAndPutContent(ReadInterface $mediaReader, string $filename)
5868
{
5969
$tempFilenameFromBucketOrDisk = tmpfile();
6070
if ($tempFilenameFromBucketOrDisk === false) {
@@ -72,7 +82,7 @@ protected static function createTemporaryFileAndPutContent(ReadInterface $mediaR
7282
* @return string
7383
* @SuppressWarnings(PHPMD.LongVariable)
7484
*/
75-
protected static function getFilePathOfTemporaryFile($tempFilenameFromBucketOrDisk): string
85+
protected function getFilePathOfTemporaryFile($tempFilenameFromBucketOrDisk): string
7686
{
7787
try {
7888
return stream_get_meta_data($tempFilenameFromBucketOrDisk)['uri'];
@@ -89,15 +99,15 @@ protected static function getFilePathOfTemporaryFile($tempFilenameFromBucketOrDi
8999
* @return mixed|string
90100
* @throws \Zend_Pdf_Exception
91101
*/
92-
protected static function getTypeOfImage(string $filepath, string $baseFileName)
102+
protected function getTypeOfImage(string $filepath, string $baseFileName)
93103
{
94104
if (class_exists('finfo', false) && !empty($filepath)) {
95105
$finfo = new finfo(FILEINFO_MIME_TYPE);
96106
$classicMimeType = $finfo->file($filepath);
97107
} elseif (function_exists('mime_content_type') && !empty($filepath)) {
98108
$classicMimeType = mime_content_type($filepath);
99109
} else {
100-
$classicMimeType = self::fetchFallbackMimeType($baseFileName);
110+
$classicMimeType = $this->fetchFallbackMimeType($baseFileName);
101111
}
102112
if (!empty($classicMimeType)) {
103113
return explode("/", $classicMimeType)[1] ?? '';
@@ -113,7 +123,7 @@ protected static function getTypeOfImage(string $filepath, string $baseFileName)
113123
* @return string
114124
* @throws \Zend_Pdf_Exception
115125
*/
116-
protected static function fetchFallbackMimeType(string $baseFileName): string
126+
protected function fetchFallbackMimeType(string $baseFileName): string
117127
{
118128
$extension = pathinfo($baseFileName, PATHINFO_EXTENSION);
119129
switch (strtolower($extension)) {
@@ -148,7 +158,7 @@ protected static function fetchFallbackMimeType(string $baseFileName): string
148158
* @param string $tempResourceFilePath
149159
* @return \Zend_Pdf_Resource_Image_Jpeg|\Zend_Pdf_Resource_Image_Png|\Zend_Pdf_Resource_Image_Tiff|object
150160
*/
151-
protected static function getZendPdfImage($typeOfImage, $tempResourceFilePath)
161+
protected function getZendPdfImage($typeOfImage, $tempResourceFilePath)
152162
{
153163
$classToUseAsPdfImage = sprintf('Zend_Pdf_Resource_Image_%s', ucfirst($typeOfImage));
154164
return new $classToUseAsPdfImage($tempResourceFilePath);
@@ -161,7 +171,7 @@ protected static function getZendPdfImage($typeOfImage, $tempResourceFilePath)
161171
* @return void
162172
* @SuppressWarnings(PHPMD.LongVariable)
163173
*/
164-
protected static function removeTemoraryFile($tempFilenameFromBucketOrDisk): void
174+
protected function removeTemoraryFile($tempFilenameFromBucketOrDisk): void
165175
{
166176
fclose($tempFilenameFromBucketOrDisk);
167177
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\Test\Unit\File\Pdf\ImageResource;
9+
10+
use Exception;
11+
use Magento\Framework\File\Pdf\ImageResource\ImageFactory;
12+
use Magento\Framework\Filesystem;
13+
use Magento\Framework\Filesystem\Directory\Read;
14+
use PHPUnit\Framework\TestCase;
15+
use Zend_Pdf_Resource_Image_Jpeg;
16+
use Zend_Pdf_Resource_Image_Png;
17+
18+
/**
19+
* Class for testing Magento\Framework\File\Pdf\ImageResource\ImageFactory
20+
* @SuppressWarnings(PHPMD.LongVariable)
21+
*/
22+
class ImageFactoryTest extends TestCase
23+
{
24+
/**
25+
* Url of AWS main image
26+
*/
27+
private const REMOTE_IMAGE_PATH = 'https://a0.awsstatic.com/libra-css/' .
28+
'images/logos/aws_smile-header-desktop-en-white_59x35.png';
29+
30+
/**
31+
* @var \Magento\Framework\File\Pdf\ImageResource\ImageFactory
32+
*/
33+
private ImageFactory $factory;
34+
35+
/**
36+
* @return void
37+
* @throws \Magento\Framework\Exception\FileSystemException
38+
* @throws \Zend_Pdf_Exception
39+
*/
40+
public function testFactoryWithLocalImage(): void
41+
{
42+
$filesystemMock = $this->createMock(Filesystem::class);
43+
44+
$tempFileResouceFromBucketOrDisk = tmpfile();
45+
$tempFilenameFromBucketOrDisk = stream_get_meta_data($tempFileResouceFromBucketOrDisk)['uri'];
46+
$readerMock = $this->createMock(Read::class);
47+
$readerMock->method('isFile')
48+
->with($tempFilenameFromBucketOrDisk)
49+
->willReturn(true);
50+
$imagePath = $this->generateImageByConfig(
51+
[
52+
'image-width' => 36,
53+
'image-height' => 69,
54+
'image-name' => $tempFilenameFromBucketOrDisk
55+
]
56+
);
57+
58+
$readerMock->method('readFile')
59+
->with($tempFilenameFromBucketOrDisk)
60+
->willReturn(file_get_contents($tempFilenameFromBucketOrDisk));
61+
62+
$filesystemMock->method('getDirectoryRead')
63+
->willReturn($readerMock);
64+
65+
$this->factory = new ImageFactory($filesystemMock);
66+
67+
/** @var \Zend_Pdf_Resource_Image_Jpeg|\Zend_Pdf_Resource_Image_Png|\Zend_Pdf_Resource_Image_Tiff $result */
68+
$result = $this->factory->factory($tempFilenameFromBucketOrDisk);
69+
unlink($imagePath);
70+
$this->assertEquals(69, $result->getPixelHeight());
71+
$this->assertEquals(36, $result->getPixelWidth());
72+
$this->assertInstanceOf(Zend_Pdf_Resource_Image_Jpeg::class, $result);
73+
}
74+
75+
/**
76+
* @param array<mixed> $config
77+
* @return array|string|string[]|null
78+
* @throws \Exception
79+
*/
80+
private function generateImageByConfig(array $config)
81+
{
82+
// phpcs:disable Magento2.Functions.DiscouragedFunction
83+
$binaryData = '';
84+
$data = str_split(sha1($config['image-name']), 2);
85+
foreach ($data as $item) {
86+
$binaryData .= base_convert($item, 16, 2);
87+
}
88+
$binaryData = str_split($binaryData, 1);
89+
90+
$image = imagecreate($config['image-width'], $config['image-height']);
91+
$bgColor = imagecolorallocate($image, 240, 240, 240);
92+
// mt_rand() here is not for cryptographic use.
93+
// phpcs:ignore Magento2.Security.InsecureFunction
94+
$fgColor = imagecolorallocate($image, mt_rand(0, 230), mt_rand(0, 230), mt_rand(0, 230));
95+
$colors = [$fgColor, $bgColor];
96+
imagefilledrectangle($image, 0, 0, $config['image-width'], $config['image-height'], $bgColor);
97+
98+
for ($row = 10; $row < ($config['image-height'] - 10); $row += 10) {
99+
for ($col = 10; $col < ($config['image-width'] - 10); $col += 10) {
100+
if (next($binaryData) === false) {
101+
reset($binaryData);
102+
}
103+
imagefilledrectangle($image, $row, $col, $row + 10, $col + 10, $colors[current($binaryData)]);
104+
}
105+
}
106+
107+
$imagePath = $config['image-name'];
108+
$imagePath = preg_replace('|/{2,}|', '/', $imagePath);
109+
$memory = fopen('php://memory', 'r+');
110+
if (!imagejpeg($image, $memory)) {
111+
throw new Exception('Could not create picture ' . $imagePath);
112+
}
113+
file_put_contents($imagePath, stream_get_contents($memory, -1, 0));
114+
fclose($memory);
115+
imagedestroy($image);
116+
// phpcs:enable
117+
118+
return $imagePath;
119+
}
120+
121+
/**
122+
* @return void
123+
* @throws \Magento\Framework\Exception\FileSystemException
124+
* @throws \Zend_Pdf_Exception
125+
*/
126+
public function testFactoryWithRemoteImage(): void
127+
{
128+
$filesystemMock = $this->createMock(Filesystem::class);
129+
130+
$readerMock = $this->createMock(Read::class);
131+
$readerMock->method('isFile')
132+
->with(self::REMOTE_IMAGE_PATH)
133+
->willReturn(true);
134+
$readerMock->method('readFile')
135+
->with(self::REMOTE_IMAGE_PATH)
136+
->willReturn(file_get_contents(self::REMOTE_IMAGE_PATH));
137+
138+
$filesystemMock->method('getDirectoryRead')
139+
->willReturn($readerMock);
140+
141+
$this->factory = new ImageFactory($filesystemMock);
142+
143+
/** @var \Zend_Pdf_Resource_Image_Jpeg|\Zend_Pdf_Resource_Image_Png|\Zend_Pdf_Resource_Image_Tiff $result */
144+
$result = $this->factory->factory(self::REMOTE_IMAGE_PATH);
145+
$this->assertEquals(35, $result->getPixelHeight());
146+
$this->assertEquals(59, $result->getPixelWidth());
147+
$this->assertInstanceOf(Zend_Pdf_Resource_Image_Png::class, $result);
148+
}
149+
}

0 commit comments

Comments
 (0)