PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.21
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.21
1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 All 166 releases
woocommerce-pos / vendor_prefixed / dompdf / dompdf / src / FontMetrics.php

FontMetrics.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.21, at vendor_prefixed/dompdf/dompdf/src/FontMetrics.php

617 lines 20.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @package dompdf
5 * @link https://github.com/dompdf/dompdf
6 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7 */
8 namespace WCPOS\Vendor\Dompdf;
9
10 use WCPOS\Vendor\Dompdf\Css\Style;
11 use WCPOS\Vendor\FontLib\Font;
12 /**
13 * The font metrics class
14 *
15 * This class provides information about fonts and text. It can resolve
16 * font names into actual installed font files, as well as determine the
17 * size of text in a particular font and size.
18 *
19 * @static
20 * @package dompdf
21 */
22 class FontMetrics
23 {
24 /**
25 * Name of the user font families lookup cache file
26 *
27 * This file must be readable and writable by the executing (webserver)
28 * process in order to cache user installed font information.
29 */
30 const USER_FONTS_FILE = "installed-fonts.json";
31 /**
32 * Underlying {@link Canvas} object to perform text size calculations
33 *
34 * @var Canvas
35 */
36 protected $canvas;
37 /**
38 * Array of bundled font family names to variants
39 *
40 * @var array
41 */
42 protected $bundledFonts = [];
43 /**
44 * Array of user defined font family names to variants
45 *
46 * @var array
47 */
48 protected $userFonts = [];
49 /**
50 * combined list of all font families with absolute paths
51 *
52 * @var array
53 */
54 protected $fontFamilies;
55 /**
56 * @var Options
57 */
58 private $options;
59 /**
60 * Class initialization
61 */
62 public function __construct(Canvas $canvas, Options $options)
63 {
64 $this->setCanvas($canvas);
65 $this->setOptions($options);
66 $this->loadFontFamilies();
67 }
68 /**
69 * @deprecated
70 */
71 public function save_font_families()
72 {
73 $this->saveFontFamilies();
74 }
75 /**
76 * Saves the stored font family cache
77 *
78 * The name and location of the cache file are determined by {@link
79 * FontMetrics::USER_FONTS_FILE}. This file should be writable by the
80 * webserver process.
81 *
82 * @see FontMetrics::loadFontFamilies()
83 */
84 public function saveFontFamilies()
85 {
86 \file_put_contents($this->getUserFontsFilePath(), \json_encode($this->userFonts, \JSON_PRETTY_PRINT));
87 }
88 /**
89 * @deprecated
90 */
91 public function load_font_families()
92 {
93 $this->loadFontFamilies();
94 }
95 /**
96 * Loads the stored font family cache
97 *
98 * @see FontMetrics::saveFontFamilies()
99 */
100 public function loadFontFamilies()
101 {
102 $file = $this->options->getRootDir() . "/lib/fonts/installed-fonts.dist.json";
103 $this->bundledFonts = \json_decode(\file_get_contents($file), \true);
104 if (\is_readable($this->getUserFontsFilePath())) {
105 $this->userFonts = \json_decode(\file_get_contents($this->getUserFontsFilePath()), \true);
106 } else {
107 $this->loadFontFamiliesLegacy();
108 }
109 }
110 private function loadFontFamiliesLegacy()
111 {
112 $legacyCacheFile = $this->options->getFontDir() . '/dompdf_font_family_cache.php';
113 if (\is_readable($legacyCacheFile)) {
114 $fontDir = $this->options->getFontDir();
115 $rootDir = $this->options->getRootDir();
116 $cacheDataClosure = (require $legacyCacheFile);
117 $cacheData = \is_array($cacheDataClosure) ? $cacheDataClosure : $cacheDataClosure($fontDir, $rootDir);
118 if (\is_array($cacheData)) {
119 foreach ($cacheData as $family => $variants) {
120 if (!isset($this->bundledFonts[$family]) && \is_array($variants)) {
121 foreach ($variants as $variant => $variantPath) {
122 $variantName = \basename($variantPath);
123 $variantDir = \dirname($variantPath);
124 if ($variantDir == $fontDir) {
125 $this->userFonts[$family][$variant] = $variantName;
126 } else {
127 $this->userFonts[$family][$variant] = $variantPath;
128 }
129 }
130 }
131 }
132 $this->saveFontFamilies();
133 }
134 }
135 }
136 /**
137 * @param array $style
138 * @param string $remote_file
139 * @param resource $context
140 * @return bool
141 * @deprecated
142 */
143 public function register_font($style, $remote_file, $context = null)
144 {
145 return $this->registerFont($style, $remote_file);
146 }
147 /**
148 * @param array $style
149 * @param string $remoteFile
150 * @param resource $context
151 * @return bool
152 */
153 public function registerFont($style, $remoteFile, $context = null)
154 {
155 $fontname = \mb_strtolower($style["family"], "UTF-8");
156 $families = $this->getFontFamilies();
157 $entry = [];
158 if (isset($families[$fontname])) {
159 $entry = $families[$fontname];
160 }
161 $styleString = $this->getType("{$style['weight']} {$style['style']}");
162 $remoteHash = \md5($remoteFile);
163 $prefix = $fontname . "_" . $styleString;
164 $prefix = \trim($prefix, "-");
165 if (\function_exists('iconv')) {
166 $prefix = @\iconv('utf-8', 'us-ascii//TRANSLIT', $prefix);
167 }
168 $prefix_encoding = \mb_detect_encoding($prefix, \mb_detect_order(), \true);
169 $substchar = \mb_substitute_character();
170 \mb_substitute_character(0x5f);
171 $prefix = \mb_convert_encoding($prefix, "ISO-8859-1", $prefix_encoding);
172 \mb_substitute_character($substchar);
173 $prefix = \preg_replace("[\\W]", "_", $prefix);
174 $prefix = \preg_replace("/[^-_\\w]+/", "", $prefix);
175 $localFile = $prefix . "_" . $remoteHash;
176 $localFilePath = $this->getOptions()->getFontDir() . "/" . $localFile;
177 if (isset($entry[$styleString]) && $localFilePath == $entry[$styleString]) {
178 return \true;
179 }
180 $entry[$styleString] = $localFile;
181 // Download the remote file
182 [$protocol] = Helpers::explode_url($remoteFile);
183 $allowed_protocols = $this->options->getAllowedProtocols();
184 if (!\array_key_exists($protocol, $allowed_protocols)) {
185 Helpers::record_warnings(\E_USER_WARNING, "Permission denied on {$remoteFile}. The communication protocol is not supported.", __FILE__, __LINE__);
186 return \false;
187 }
188 foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
189 [$result, $message] = $rule($remoteFile);
190 if ($result !== \true) {
191 Helpers::record_warnings(\E_USER_WARNING, "Error loading {$remoteFile}: {$message}", __FILE__, __LINE__);
192 return \false;
193 }
194 }
195 [$remoteFileContent, $http_response_header] = @Helpers::getFileContent($remoteFile, $context);
196 if ($remoteFileContent === null) {
197 return \false;
198 }
199 $localTempFile = @\tempnam($this->options->get("tempDir"), "dompdf-font-");
200 \file_put_contents($localTempFile, $remoteFileContent);
201 $font = Font::load($localTempFile);
202 if (!$font) {
203 \unlink($localTempFile);
204 return \false;
205 }
206 $font->parse();
207 $font->saveAdobeFontMetrics("{$localFilePath}.ufm");
208 $font->close();
209 \unlink($localTempFile);
210 if (!\file_exists("{$localFilePath}.ufm")) {
211 return \false;
212 }
213 $fontExtension = ".ttf";
214 switch ($font->getFontType()) {
215 case "TrueType":
216 default:
217 $fontExtension = ".ttf";
218 break;
219 }
220 // Save the changes
221 \file_put_contents($localFilePath . $fontExtension, $remoteFileContent);
222 if (!\file_exists($localFilePath . $fontExtension)) {
223 \unlink("{$localFilePath}.ufm");
224 return \false;
225 }
226 $this->setFontFamily($fontname, $entry);
227 return \true;
228 }
229 /**
230 * @param $text
231 * @param $font
232 * @param $size
233 * @param float $word_spacing
234 * @param float $char_spacing
235 * @return float
236 * @deprecated
237 */
238 public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
239 {
240 //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
241 return $this->getTextWidth($text, $font, $size, $word_spacing, $char_spacing);
242 }
243 /**
244 * Calculates text size, in points
245 *
246 * @param string $text The text to be sized
247 * @param string $font The font file to use
248 * @param float $size The font size, in points
249 * @param float $wordSpacing Word spacing, if any
250 * @param float $charSpacing Char spacing, if any
251 *
252 * @return float
253 */
254 public function getTextWidth(string $text, $font, float $size, float $wordSpacing = 0.0, float $charSpacing = 0.0) : float
255 {
256 // @todo Make sure this cache is efficient before enabling it
257 static $cache = [];
258 if ($text === "") {
259 return 0;
260 }
261 // Don't cache long strings
262 $useCache = !isset($text[50]);
263 // Faster than strlen
264 // Text-size calculations depend on the canvas used. Make sure to not
265 // return wrong values when switching canvas backends
266 $canvasClass = \get_class($this->canvas);
267 $key = "{$canvasClass}/{$font}/{$size}/{$wordSpacing}/{$charSpacing}";
268 if ($useCache && isset($cache[$key][$text])) {
269 return $cache[$key][$text];
270 }
271 $width = $this->canvas->get_text_width($text, $font, $size, $wordSpacing, $charSpacing);
272 if ($useCache) {
273 $cache[$key][$text] = $width;
274 }
275 return $width;
276 }
277 /**
278 * Maps substrings of text against the provided font list. This is achieved by
279 * parsing each character of the string against the supported glyphs for each
280 * font. Fonts preference is based on the order of the font list.
281 *
282 * Returns an array containing substring information that indicates the
283 * matched font (if any), start index, substring length, and (optionally)
284 * the actual text of the substring.
285 *
286 * @param string $text The text to map
287 * @param array $fontFamilies List of font families to map against
288 * @param string $subtype The font subtype (italic, bold, etc.)
289 * @param int $count The number of matches to return
290 * @param bool $returnSubstring Should the actual matched text be returned
291 * @return array
292 */
293 public function mapTextToFonts(string $text, array $fontFamilies, string $subtype = "normal", int $count = -1, bool $returnSubstring = \false) : array
294 {
295 $char_mapping = [];
296 $fonts = [];
297 foreach ($fontFamilies as $family) {
298 $font = $this->getFont($family, $subtype);
299 if ($font !== null) {
300 $fonts[] = $font;
301 }
302 }
303 if (\function_exists("mb_str_split")) {
304 $char_array = \mb_str_split($text, 1, "UTF-8");
305 } else {
306 $char_array = \preg_split("//u", $text, -1, \PREG_SPLIT_NO_EMPTY);
307 }
308 $start_index = 0;
309 $char_index = -1;
310 while (isset($char_array[++$char_index])) {
311 $char = $char_array[$char_index];
312 if (\preg_match('/[\\x00-\\x1F\\x7F]/u', $char)) {
313 //non-printable, moving on
314 continue;
315 }
316 $mapped_font = null;
317 foreach ($fonts as $font) {
318 if ($this->canvas->font_supports_char($font, $char)) {
319 $mapped_font = $font;
320 break;
321 }
322 }
323 if (!isset($char_mapping[$start_index])) {
324 $char_mapping[$start_index] = ["font" => $mapped_font, "length" => 0, "text" => null];
325 }
326 if ($mapped_font !== $char_mapping[$start_index]["font"]) {
327 $char_mapping[$start_index]["length"] = $char_index - $start_index;
328 if ($count > 0 && \count($char_mapping) === $count) {
329 break;
330 }
331 $start_index = $char_index;
332 $char_mapping[$start_index] = ["font" => $mapped_font, "length" => 0, "text" => null];
333 }
334 }
335 if ($returnSubstring) {
336 // build the string for each mapping
337 foreach ($char_mapping as $start_index => &$info) {
338 $info["text"] = \mb_substr($text, $start_index, $info["length"], "UTF-8");
339 }
340 }
341 return $char_mapping;
342 }
343 /**
344 * @param $font
345 * @param $size
346 * @return float
347 * @deprecated
348 */
349 public function get_font_height($font, $size)
350 {
351 return $this->getFontHeight($font, $size);
352 }
353 /**
354 * Calculates font height, in points
355 *
356 * @param string $font The font file to use
357 * @param float $size The font size, in points
358 *
359 * @return float
360 */
361 public function getFontHeight($font, float $size) : float
362 {
363 return $this->canvas->get_font_height($font, $size);
364 }
365 /**
366 * Calculates font baseline, in points
367 *
368 * @param string $font The font file to use
369 * @param float $size The font size, in points
370 *
371 * @return float
372 */
373 public function getFontBaseline($font, float $size) : float
374 {
375 return $this->canvas->get_font_baseline($font, $size);
376 }
377 /**
378 * @param $family_raw
379 * @param string $subtype_raw
380 * @return string
381 * @deprecated
382 */
383 public function get_font($family_raw, $subtype_raw = "normal")
384 {
385 return $this->getFont($family_raw, $subtype_raw);
386 }
387 /**
388 * Resolves a font family & subtype into an actual font file
389 * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
390 * the particular font family has no suitable font file, the default font
391 * ({@link Options::defaultFont}) is used. The font file returned
392 * is the absolute pathname to the font file on the system.
393 *
394 * @param string|null $familyRaw
395 * @param string $subtypeRaw
396 *
397 * @return string|null
398 */
399 public function getFont($familyRaw, $subtypeRaw = "normal")
400 {
401 static $cache = [];
402 if (!$familyRaw) {
403 $familyRaw = $familyRaw === null ? 0 : $this->options->getDefaultFont();
404 }
405 if (!$subtypeRaw) {
406 $subtypeRaw = "normal";
407 }
408 if (isset($cache[$familyRaw][$subtypeRaw])) {
409 return $cache[$familyRaw][$subtypeRaw];
410 }
411 /* Allow calling for various fonts in search path. Therefore not immediately
412 * return replacement on non match.
413 * Only when called with NULL try replacement.
414 * When this is also missing there is really trouble.
415 * If only the subtype fails, nevertheless return failure.
416 * Only on checking the fallback font, check various subtypes on same font.
417 */
418 $subtype = \strtolower($subtypeRaw);
419 $families = $this->getFontFamilies();
420 if ($familyRaw) {
421 $family = \str_replace(["'", '"'], "", \strtolower($familyRaw));
422 if (isset($families[$family][$subtype])) {
423 return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
424 }
425 return null;
426 }
427 $fallback_families = [\strtolower($this->options->getDefaultFont()), "serif"];
428 foreach ($fallback_families as $family) {
429 if (isset($families[$family][$subtype])) {
430 return $cache[$familyRaw][$subtypeRaw] = $families[$family][$subtype];
431 }
432 if (!isset($families[$family])) {
433 continue;
434 }
435 $family = $families[$family];
436 foreach ($family as $sub => $font) {
437 if (\strpos($subtype, $sub) !== \false) {
438 return $cache[$familyRaw][$subtypeRaw] = $font;
439 }
440 }
441 if ($subtype !== "normal") {
442 foreach ($family as $sub => $font) {
443 if ($sub !== "normal") {
444 return $cache[$familyRaw][$subtypeRaw] = $font;
445 }
446 }
447 }
448 $subtype = "normal";
449 if (isset($family[$subtype])) {
450 return $cache[$familyRaw][$subtypeRaw] = $family[$subtype];
451 }
452 }
453 return null;
454 }
455 /**
456 * @param $family
457 * @return null|string
458 * @deprecated
459 */
460 public function get_family($family)
461 {
462 return $this->getFamily($family);
463 }
464 /**
465 * @param string $family
466 * @return null|string
467 */
468 public function getFamily($family)
469 {
470 $family = \str_replace(["'", '"'], "", \mb_strtolower($family, "UTF-8"));
471 $families = $this->getFontFamilies();
472 if (isset($families[$family])) {
473 return $families[$family];
474 }
475 return null;
476 }
477 /**
478 * @param $type
479 * @return string
480 * @deprecated
481 */
482 public function get_type($type)
483 {
484 return $this->getType($type);
485 }
486 /**
487 * @param string $type
488 * @return string
489 */
490 public function getType($type)
491 {
492 if (\preg_match('/bold/i', $type)) {
493 $weight = 700;
494 } elseif (\preg_match('/([1-9]00)/', $type, $match)) {
495 $weight = (int) $match[0];
496 } else {
497 $weight = 400;
498 }
499 $weight = $weight === 400 ? 'normal' : $weight;
500 $weight = $weight === 700 ? 'bold' : $weight;
501 $style = \preg_match('/italic|oblique/i', $type) ? 'italic' : null;
502 if ($weight === 'normal' && $style !== null) {
503 return $style;
504 }
505 return $style === null ? $weight : $weight . '_' . $style;
506 }
507 /**
508 * @return array
509 * @deprecated
510 */
511 public function get_font_families()
512 {
513 return $this->getFontFamilies();
514 }
515 /**
516 * Returns the current font lookup table
517 *
518 * @return array
519 */
520 public function getFontFamilies()
521 {
522 if (!isset($this->fontFamilies)) {
523 $this->setFontFamilies();
524 }
525 return $this->fontFamilies;
526 }
527 /**
528 * Convert loaded fonts to font lookup table
529 *
530 * @return array
531 */
532 public function setFontFamilies()
533 {
534 $fontFamilies = [];
535 if (isset($this->bundledFonts) && \is_array($this->bundledFonts)) {
536 foreach ($this->bundledFonts as $family => $variants) {
537 if (!isset($fontFamilies[$family])) {
538 $fontFamilies[$family] = \array_map(function ($variant) {
539 return $this->getOptions()->getRootDir() . '/lib/fonts/' . $variant;
540 }, $variants);
541 }
542 }
543 }
544 if (isset($this->userFonts) && \is_array($this->userFonts)) {
545 foreach ($this->userFonts as $family => $variants) {
546 $fontFamilies[$family] = \array_map(function ($variant) {
547 $variantName = \basename($variant);
548 if ($variantName === $variant) {
549 return $this->getOptions()->getFontDir() . '/' . $variant;
550 }
551 return $variant;
552 }, $variants);
553 }
554 }
555 $this->fontFamilies = $fontFamilies;
556 }
557 /**
558 * @param string $fontname
559 * @param mixed $entry
560 * @deprecated
561 */
562 public function set_font_family($fontname, $entry)
563 {
564 $this->setFontFamily($fontname, $entry);
565 }
566 /**
567 * @param string $fontname
568 * @param mixed $entry
569 */
570 public function setFontFamily($fontname, $entry)
571 {
572 $this->userFonts[\mb_strtolower($fontname, "UTF-8")] = $entry;
573 $this->saveFontFamilies();
574 unset($this->fontFamilies);
575 }
576 /**
577 * @return string
578 */
579 public function getUserFontsFilePath()
580 {
581 return $this->options->getFontDir() . '/' . self::USER_FONTS_FILE;
582 }
583 /**
584 * @param Options $options
585 * @return $this
586 */
587 public function setOptions(Options $options)
588 {
589 $this->options = $options;
590 unset($this->fontFamilies);
591 return $this;
592 }
593 /**
594 * @return Options
595 */
596 public function getOptions()
597 {
598 return $this->options;
599 }
600 /**
601 * @param Canvas $canvas
602 * @return $this
603 */
604 public function setCanvas(Canvas $canvas)
605 {
606 $this->canvas = $canvas;
607 return $this;
608 }
609 /**
610 * @return Canvas
611 */
612 public function getCanvas()
613 {
614 return $this->canvas;
615 }
616 }
617