PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk 1.2.0 All 47 releases
fluent-cart / app / Services / PDF / PdfGeneratorService.php

PdfGeneratorService.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Services/PDF/PdfGeneratorService.php

195 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\PDF;
4
5 use FluentPdf\Classes\Controller\AvailableOptions;
6
7 class PdfGeneratorService
8 {
9 private $workingDir = '';
10 private $tempDir = '';
11 private $pdfCacheDir = '';
12 private $fontDir = '';
13
14 public function __construct()
15 {
16 if (!defined('FLUENT_PDF')) {
17 return;
18 }
19
20 $dirStructure = AvailableOptions::getDirStructure();
21
22 $this->workingDir = $dirStructure['workingDir'];
23 $this->tempDir = $dirStructure['tempDir'];
24 $this->pdfCacheDir = $dirStructure['pdfCacheDir'];
25 $this->fontDir = $dirStructure['fontDir'];
26 }
27
28 public function getGenerator(array $mpdfConfig = []): \FluentPdf\Vendor\Mpdf\Mpdf
29 {
30 $uploadDir = wp_upload_dir();
31 $fluentPdfFontDir = $uploadDir['basedir'] . '/FLUENT_PDF_TEMPLATES/fonts';
32
33 $fontDirs = [$this->fontDir];
34 if (is_dir($fluentPdfFontDir)) {
35 $fontDirs[] = $fluentPdfFontDir;
36 }
37
38 $defaults = [
39 'fontDir' => $fontDirs,
40 'tempDir' => $this->tempDir,
41 'curlCaCertificate' => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
42 'curlFollowLocation' => true,
43 'allow_output_buffering' => true,
44 'autoLangToFont' => true,
45 'autoScriptToLang' => true,
46 'useSubstitutions' => true,
47 'ignore_invalid_utf8' => true,
48 'setAutoTopMargin' => 'stretch',
49 'setAutoBottomMargin' => 'stretch',
50 'enableImports' => true,
51 'use_kwt' => true,
52 'keepColumns' => true,
53 'biDirectional' => true,
54 'showWatermarkText' => true,
55 'showWatermarkImage' => true,
56 'default_font' => 'dejavusans',
57 ];
58
59 $mpdfConfig = wp_parse_args($mpdfConfig, $defaults);
60
61 $mpdfConfig = apply_filters('fluent_cart/pdf_templates/mpdf_config', $mpdfConfig);
62
63 return new \FluentPdf\Vendor\Mpdf\Mpdf($mpdfConfig);
64 }
65
66 public function getTempDirectory(): string
67 {
68 return $this->tempDir;
69 }
70
71 /**
72 * Resolve same-site image URLs in HTML to local filesystem paths for mPDF.
73 *
74 * @param string $html
75 * @return string
76 */
77 public function prepareHtmlForPdf(string $html): string
78 {
79 if ($html === '') {
80 return $html;
81 }
82
83 // Remove <img> tags with empty src to prevent mPDF errors
84 $html = preg_replace('/<img\s[^>]*\bsrc\s*=\s*["\']["\'][^>]*>/i', '', $html) ?? $html;
85
86 $upload_dir = wp_upload_dir();
87 $content_url = content_url();
88 $content_dir = WP_CONTENT_DIR;
89 $home_host = wp_parse_url(home_url(), PHP_URL_HOST);
90
91 $resolveUrl = function ($url) use ($upload_dir, $content_url, $content_dir, $home_host) {
92 $url = trim($url);
93 if ($url === '' || strpos($url, 'data:') === 0) {
94 return null;
95 }
96 $url_host = wp_parse_url($url, PHP_URL_HOST);
97 if ($url_host !== null && $url_host !== $home_host) {
98 return null;
99 }
100 $path_part = wp_parse_url($url, PHP_URL_PATH);
101 if ($path_part === null || $path_part === '') {
102 return null;
103 }
104 if (!empty($upload_dir['baseurl']) && strpos($url, $upload_dir['baseurl']) === 0) {
105 $local_path = $upload_dir['basedir'] . substr($path_part, strlen(wp_parse_url($upload_dir['baseurl'], PHP_URL_PATH)));
106 $local_path = wp_normalize_path($local_path);
107 if (file_exists($local_path) && is_readable($local_path)) {
108 return $local_path;
109 }
110 return null;
111 }
112 if (!empty($content_url) && strpos($url, $content_url) === 0) {
113 $content_path = wp_parse_url($content_url, PHP_URL_PATH);
114 $local_path = $content_dir . substr($path_part, strlen($content_path));
115 $local_path = wp_normalize_path($local_path);
116 if (file_exists($local_path) && is_readable($local_path)) {
117 return $local_path;
118 }
119 return null;
120 }
121 return null;
122 };
123
124 $html = preg_replace_callback(
125 '/(<img\s[^>]*\bsrc\s*=\s*["\'])([^"\']+)(["\'][^>]*>)|\b(url\s*\(\s*["\']?)([^"\')\s]+)(["\']?\s*\))/i',
126 function ($m) use ($resolveUrl) {
127 if ($m[1] !== '') {
128 $resolved = $resolveUrl($m[2]);
129 if ($resolved === null) {
130 return $m[0];
131 }
132 $dataUri = $this->flattenPngTransparency($resolved);
133 return $dataUri !== null ? $m[1] . $dataUri . $m[3] : $m[1] . $resolved . $m[3];
134 }
135 $resolved = $resolveUrl($m[5]);
136 return $resolved !== null
137 ? 'url(\'' . str_replace(["\\", "'"], ["\\\\", "\\'"], $resolved) . '\')'
138 : $m[0];
139 },
140 $html
141 );
142
143 return $html;
144 }
145
146 /**
147 * If $path is a PNG with an alpha channel, composite it onto a white
148 * canvas and return a base64 JPEG data URI.
149 *
150 * @param string $path
151 * @return string|null
152 */
153 private function flattenPngTransparency(string $path): ?string
154 {
155 if (!function_exists('imagecreatefrompng') || strtolower(pathinfo($path, PATHINFO_EXTENSION)) !== 'png') {
156 return null;
157 }
158
159 $src = @imagecreatefrompng($path);
160 if (!$src) {
161 return null;
162 }
163
164 $w = imagesx($src);
165 $h = imagesy($src);
166
167 $hasAlpha = false;
168 for ($x = 0; $x < $w && !$hasAlpha; $x++) {
169 for ($y = 0; $y < $h && !$hasAlpha; $y++) {
170 if ((imagecolorat($src, $x, $y) >> 24) & 0x7F) {
171 $hasAlpha = true;
172 }
173 }
174 }
175
176 if (!$hasAlpha) {
177 imagedestroy($src);
178 return null;
179 }
180
181 $canvas = imagecreatetruecolor($w, $h);
182 $white = imagecolorallocate($canvas, 255, 255, 255);
183 imagefill($canvas, 0, 0, $white);
184 imagecopy($canvas, $src, 0, 0, 0, 0, $w, $h);
185 imagedestroy($src);
186
187 ob_start();
188 imagejpeg($canvas, null, 92);
189 $jpeg = ob_get_clean();
190 imagedestroy($canvas);
191
192 return 'data:image/jpeg;base64,' . base64_encode($jpeg);
193 }
194 }
195