Skip to content

Commit 93c172b

Browse files
committed
Mpdf Large Images
See PR PHPOffice#2879 Issue PHPOffice#2876.
1 parent dda72e7 commit 93c172b

2 files changed

Lines changed: 123 additions & 2 deletions

File tree

src/PhpWord/Writer/PDF/MPDF.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,23 @@ public function save(string $filename): void
8888
$pdf->WriteHTML(substr($html, 0, $bodyLocation));
8989
$html = substr($html, $bodyLocation);
9090
}
91-
foreach (explode("\n", $html) as $line) {
92-
$pdf->WriteHTML("$line\n");
91+
$pcreBacktrackLimitString = ini_get('pcre.backtrack_limit');
92+
$pcreBacktrackLimit = (int) $pcreBacktrackLimitString;
93+
$origLimit = $pcreBacktrackLimit;
94+
95+
try {
96+
foreach (explode("\n", $html) as $line) {
97+
$lineLen = strlen($line);
98+
if ($lineLen > $pcreBacktrackLimit) {
99+
$pcreBacktrackLimit = $lineLen + 1;
100+
ini_set('pcre.backtrack_limit', (string) $pcreBacktrackLimit);
101+
}
102+
$pdf->WriteHTML("$line\n");
103+
}
104+
} finally {
105+
if ($pcreBacktrackLimit !== $origLimit) {
106+
ini_set('pcre.backtrack_limit', $pcreBacktrackLimitString);
107+
}
93108
}
94109

95110
// Write to file
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
/**
4+
* This file is part of PHPWord - A pure PHP library for reading and writing
5+
* word processing documents.
6+
*
7+
* PHPWord is free software distributed under the terms of the GNU Lesser
8+
* General Public License version 3 as published by the Free Software Foundation.
9+
*
10+
* For the full copyright and license information, please read the LICENSE
11+
* file that was distributed with this source code. For the full list of
12+
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
13+
*
14+
* @see https://github.com/PHPOffice/PHPWord
15+
*
16+
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
17+
*/
18+
19+
namespace PhpOffice\PhpWordTests\Writer\PDF;
20+
21+
use PhpOffice\PhpWord\PhpWord;
22+
use PhpOffice\PhpWord\Writer\PDF\MPDF;
23+
24+
/**
25+
* Test class for PhpOffice\PhpWord\Writer\PDF\MPDF.
26+
*
27+
* @runTestsInSeparateProcesses
28+
*/
29+
class MpdfLargeImageTest extends \PHPUnit\Framework\TestCase
30+
{
31+
/** @var string */
32+
private $jpegName = '';
33+
34+
/** @var string */
35+
private $pdfName = '';
36+
37+
protected function tearDown(): void
38+
{
39+
// Nothing sensible to do if unlink fails.
40+
if ($this->jpegName !== '') {
41+
@unlink($this->jpegName);
42+
}
43+
if ($this->pdfName !== '') {
44+
@unlink($this->pdfName);
45+
}
46+
}
47+
48+
/**
49+
* Test that a large embedded image does not trigger the pcre.backtrack_limit error (issue #2876).
50+
*
51+
* PHPWord embeds images as base64 data URIs on a single HTML line. When the image is large
52+
* the line exceeds pcre.backtrack_limit and Mpdf refuses the WriteHTML call. The fix must
53+
* handle this gracefully without requiring the caller to raise pcre.backtrack_limit.
54+
*/
55+
public function testLargeImageDoesNotExceedBacktrackLimit(): void
56+
{
57+
// Generate a random-noise JPEG (~700-900 KB) that resists JPEG compression.
58+
// The resulting base64 data URI will be a single HTML line > 900 KB,
59+
// which exceeds the default pcre.backtrack_limit of 1 000 000.
60+
$size1 = 1600;
61+
$size2 = 1200;
62+
$img = imagecreatetruecolor($size1, $size2);
63+
if ($img === false) {
64+
self::fail('imagecreatetruecolor() failed.');
65+
}
66+
for ($y = 0; $y < $size2; ++$y) {
67+
for ($x = 0; $x < $size1; ++$x) {
68+
imagesetpixel($img, $x, $y, (int) imagecolorallocate($img, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255)));
69+
}
70+
}
71+
ob_start();
72+
self::assertTrue(imagejpeg($img, null, 95));
73+
$imageData = ob_get_clean();
74+
imagedestroy($img);
75+
76+
$tmpImage = $this->jpegName = tempnam(sys_get_temp_dir(), 'tmp_');
77+
file_put_contents($tmpImage, $imageData);
78+
79+
$pcreBacktrackLimitOld = ini_get('pcre.backtrack_limit');
80+
$phpWord = new PhpWord();
81+
$section = $phpWord->addSection();
82+
$section->addText('Large-image PDF — issue #2876 regression test');
83+
$section->addImage($tmpImage, ['width' => 400, 'height' => 267]);
84+
85+
$writer = new MPDF($phpWord);
86+
$file = $this->pdfName = tempnam(sys_get_temp_dir(), 'tmp_');
87+
$writer->save($file);
88+
89+
$pcreBacktrackLimitNew = ini_get('pcre.backtrack_limit');
90+
self::assertSame($pcreBacktrackLimitOld, $pcreBacktrackLimitNew);
91+
self::assertFileExists($file);
92+
self::assertGreaterThan(0, filesize($file));
93+
94+
$longLine = false;
95+
$maxLen = (int) $pcreBacktrackLimitOld;
96+
$html = $writer->getContent();
97+
foreach (explode("\n", $html) as $line) {
98+
if (strlen($line) > $maxLen) {
99+
$longLine = true;
100+
101+
break;
102+
}
103+
}
104+
self::assertTrue($longLine);
105+
}
106+
}

0 commit comments

Comments
 (0)