PluginProbe
MBE eShip / trunk
MBE eShip vtrunk
2.8.1 trunk 1.0.0 1.1.0 1.1.3 1.2.1 1.2.2 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.6.0 1.7.0 1.7.1 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1.0 2.1.1 2.1.2 2.2.1 All 37 releases
mail-boxes-etc / lib / dompdf / src / Image / Cache.php

Cache.php in MBE eShip trunk, at lib/dompdf/src/Image/Cache.php

255 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package dompdf
4 * @link https://github.com/dompdf/dompdf
5 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
6 */
7 namespace Dompdf\Image;
8
9 use Dompdf\Options;
10 use Dompdf\Helpers;
11 use Dompdf\Exception\ImageException;
12
13 /**
14 * Static class that resolves image urls and downloads and caches
15 * remote images if required.
16 *
17 * @package dompdf
18 */
19 class Cache
20 {
21 /**
22 * Array of downloaded images. Cached so that identical images are
23 * not needlessly downloaded.
24 *
25 * @var array
26 */
27 protected static $_cache = [];
28
29 /**
30 * @var array
31 */
32 protected static $tempImages = [];
33
34 /**
35 * The url to the "broken image" used when images can't be loaded
36 *
37 * @var string
38 */
39 public static $broken_image = "data:image/svg+xml;charset=utf8,%3C?xml version='1.0'?%3E%3Csvg width='64' height='64' xmlns='http://www.w3.org/2000/svg'%3E%3Cg%3E%3Crect stroke='%23666666' id='svg_1' height='60.499994' width='60.166667' y='1.666669' x='1.999998' stroke-width='1.5' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_3' y2='59.333253' x2='59.749916' y1='4.333415' x1='4.250079' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_4' y2='59.999665' x2='4.062838' y1='3.750342' x1='60.062164' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3C/g%3E%3C/svg%3E";
40
41 public static $error_message = "Image not found or type unknown";
42
43 /**
44 * Resolve and fetch an image for use.
45 *
46 * @param string $url The url of the image
47 * @param string $protocol Default protocol if none specified in $url
48 * @param string $host Default host if none specified in $url
49 * @param string $base_path Default path if none specified in $url
50 * @param Options $options An instance of Dompdf\Options
51 *
52 * @return array An array with three elements: The local path to the image, the image
53 * extension, and an error message if the image could not be cached
54 */
55 static function resolve_url($url, $protocol, $host, $base_path, Options $options)
56 {
57 $tempfile = null;
58 $resolved_url = null;
59 $type = null;
60 $message = null;
61
62 try {
63 $full_url = Helpers::build_url($protocol, $host, $base_path, $url);
64
65 if ($full_url === null) {
66 throw new ImageException("Unable to parse image URL $url.", E_WARNING);
67 }
68
69 $parsed_url = Helpers::explode_url($full_url);
70 $protocol = strtolower($parsed_url["protocol"]);
71 $is_data_uri = strpos($protocol, "data:") === 0;
72
73 if (!$is_data_uri) {
74 $allowed_protocols = $options->getAllowedProtocols();
75 if (!array_key_exists($protocol, $allowed_protocols)) {
76 throw new ImageException("Permission denied on $url. The communication protocol is not supported.", E_WARNING);
77 }
78 foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
79 [$result, $message] = $rule($full_url);
80 if (!$result) {
81 throw new ImageException("Error loading $url: $message", E_WARNING);
82 }
83 }
84 }
85
86 if ($protocol === "file://") {
87 $resolved_url = $full_url;
88 } elseif (isset(self::$_cache[$full_url])) {
89 $resolved_url = self::$_cache[$full_url];
90 } else {
91 $tmp_dir = $options->getTempDir();
92 if (($resolved_url = @tempnam($tmp_dir, "ca_dompdf_img_")) === false) {
93 throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
94 }
95 $tempfile = $resolved_url;
96
97 $image = null;
98 if ($is_data_uri) {
99 if (($parsed_data_uri = Helpers::parse_data_uri($url)) !== false) {
100 $image = $parsed_data_uri["data"];
101 }
102 } else {
103 list($image, $http_response_header) = Helpers::getFileContent($full_url, $options->getHttpContext());
104 }
105
106 // Image not found or invalid
107 if ($image === null) {
108 $msg = ($is_data_uri ? "Data-URI could not be parsed" : "Image not found");
109 throw new ImageException($msg, E_WARNING);
110 }
111
112 if (@file_put_contents($resolved_url, $image) === false) {
113 throw new ImageException("Unable to create temporary image in " . $tmp_dir, E_WARNING);
114 }
115
116 self::$_cache[$full_url] = $resolved_url;
117 }
118
119 // Check if the local file is readable
120 if (!is_readable($resolved_url) || !filesize($resolved_url)) {
121 throw new ImageException("Image not readable or empty", E_WARNING);
122 }
123
124 list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
125
126 if (($width && $height && in_array($type, ["gif", "png", "jpeg", "bmp", "svg","webp"], true)) === false) {
127 throw new ImageException("Image type unknown", E_WARNING);
128 }
129
130 if ($type === "svg") {
131 $parser = xml_parser_create("utf-8");
132 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
133 xml_set_element_handler(
134 $parser,
135 function ($parser, $name, $attributes) use ($options, $parsed_url, $full_url) {
136 if ($name === "image") {
137 $attributes = array_change_key_case($attributes, CASE_LOWER);
138 $url = $attributes["xlink:href"] ?? $attributes["href"];
139 if (!empty($url)) {
140 $inner_full_url = Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url);
141 if ($inner_full_url === $full_url) {
142 throw new ImageException("SVG self-reference is not allowed", E_WARNING);
143 }
144 [$resolved_url, $type, $message] = self::resolve_url($url, $parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options);
145 if (!empty($message)) {
146 throw new ImageException("This SVG document references a restricted resource. $message", E_WARNING);
147 }
148 }
149 }
150 },
151 false
152 );
153
154 if (($fp = fopen($resolved_url, "r")) !== false) {
155 while ($line = fread($fp, 8192)) {
156 xml_parse($parser, $line, false);
157 }
158 fclose($fp);
159 }
160 xml_parser_free($parser);
161 }
162 } catch (ImageException $e) {
163 if ($tempfile) {
164 unlink($tempfile);
165 }
166 $resolved_url = self::$broken_image;
167 list($width, $height, $type) = Helpers::dompdf_getimagesize($resolved_url, $options->getHttpContext());
168 $message = self::$error_message;
169 Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url", $e->getFile(), $e->getLine());
170 self::$_cache[$full_url] = $resolved_url;
171 }
172
173 return [$resolved_url, $type, $message];
174 }
175
176 /**
177 * Register a temp file for the given original image file.
178 *
179 * @param string $filePath The path of the original image.
180 * @param string $tempPath The path of the temp file to register.
181 * @param string $key An optional key to register the temp file at.
182 */
183 static function addTempImage(string $filePath, string $tempPath, string $key = "default"): void
184 {
185 if (!isset(self::$tempImages[$filePath])) {
186 self::$tempImages[$filePath] = [];
187 }
188
189 self::$tempImages[$filePath][$key] = $tempPath;
190 }
191
192 /**
193 * Get the path of a temp file registered for the given original image file.
194 *
195 * @param string $filePath The path of the original image.
196 * @param string $key The key the temp file is registered at.
197 */
198 static function getTempImage(string $filePath, string $key = "default"): ?string
199 {
200 return self::$tempImages[$filePath][$key] ?? null;
201 }
202
203 /**
204 * Unlink all cached images (i.e. temporary images either downloaded
205 * or converted) except for the bundled "broken image"
206 */
207 static function clear(bool $debugPng = false)
208 {
209 foreach (self::$_cache as $file) {
210 if ($file === self::$broken_image) {
211 continue;
212 }
213 if ($debugPng) {
214 print "[clear unlink $file]";
215 }
216 if (file_exists($file)) {
217 unlink($file);
218 }
219 }
220
221 foreach (self::$tempImages as $versions) {
222 foreach ($versions as $file) {
223 if ($file === self::$broken_image) {
224 continue;
225 }
226 if ($debugPng) {
227 print "[unlink temp image $file]";
228 }
229 if (file_exists($file)) {
230 unlink($file);
231 }
232 }
233 }
234
235 self::$_cache = [];
236 self::$tempImages = [];
237 }
238
239 static function detect_type($file, $context = null)
240 {
241 list(, , $type) = Helpers::dompdf_getimagesize($file, $context);
242
243 return $type;
244 }
245
246 static function is_broken($url)
247 {
248 return $url === self::$broken_image;
249 }
250 }
251
252 if (file_exists(realpath(__DIR__ . "/../../lib/res/broken_image.svg"))) {
253 Cache::$broken_image = realpath(__DIR__ . "/../../lib/res/broken_image.svg");
254 }
255