PluginProbe
E2Pdf – Export Pdf Tool for WordPress / 1.32.49
E2Pdf – Export Pdf Tool for WordPress v1.32.49
1.32.49 1.32.48 1.32.43 1.32.40 1.32.34 1.32.32 1.32.31 1.32.26 1.32.22 1.32.23 1.32.18 1.32.17 1.32.15 trunk 1.00.00 1.00.13 1.01.01 1.02.02 1.03.07 1.04.07 1.05.03 1.06.02 1.07.11 1.08.00 1.08.06 All 72 releases
e2pdf / vendors / svggraph / Text.php

Text.php in E2Pdf – Export Pdf Tool for WordPress 1.32.49, at vendors/svggraph/Text.php

778 lines 20.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Copyright (C) 2018-2023 Graham Breach
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19 * For more information, please contact <graham@goat1000.com>
20 */
21
22 namespace Goat1000\SVGGraph;
23
24 /**
25 * Text functions using calculated font metrics
26 */
27 class Text {
28
29 private $graph;
30 private $font;
31 private $metrics_file;
32 private $adjust;
33 private $encoding;
34 private $no_tspan;
35 private $no_metrics;
36 private static $metrics;
37 private static $measure_cache;
38 private static $use_iconv = null;
39 private static $use_mbstring = null;
40
41 public function __construct(&$graph, $font = null, $adjust = 0)
42 {
43 // iconv can be troublesome
44 if(Text::$use_iconv === null) {
45 Text::$use_iconv = false;
46 if($graph->getOption('use_iconv', true) && extension_loaded('iconv')) {
47 // test the iconv function
48 $test_euro = "Test:\u{20ac}";
49 $out = iconv('UTF-8', 'ASCII//TRANSLIT', $test_euro);
50 Text::$use_iconv = (strlen($out) > 0);
51 }
52 }
53
54 // mbstring should be OK, but allow disabling it
55 if(Text::$use_mbstring === null) {
56 Text::$use_mbstring = $graph->getOption('use_mbstring', true) &&
57 extension_loaded('mbstring');
58 }
59
60 $this->graph =& $graph;
61 $this->font = $font;
62 if($font !== null)
63 $this->metrics_file = $this->metricsFilename($font);
64 $this->encoding = strtoupper($graph->encoding);
65 $this->no_tspan = $graph->getOption('no_tspan');
66 $this->no_metrics = $graph->getOption('no_font_metrics');
67 $this->adjust = $adjust ? $adjust : 0.6;
68 }
69
70 /**
71 * Measures a block of text
72 * returns array($width, $height)
73 */
74 public function measure($text, $font_size, $angle = 0, $line_spacing = 0)
75 {
76 if(!is_numeric($font_size))
77 throw new \InvalidArgumentException("Font size is not numeric");
78
79 if(!is_string($text)) {
80 if(is_numeric($text)) {
81 $num = new Number($text);
82 $text = $num->format();
83 } else {
84 $text = (string)$text;
85 }
86 }
87
88 // convert to UTF-8
89 $text = $this->convert($text);
90 $cached = $this->measureCached($text, $font_size, $angle, $line_spacing);
91 if($cached !== null)
92 return $cached;
93
94 if($line_spacing !== 0 && !is_numeric($line_spacing))
95 throw new \InvalidArgumentException("Line spacing is not numeric");
96
97 $lines = $line_spacing > 0 ? $this->splitLines($text) : [$text];
98 $width = $height = 0;
99 foreach($lines as $l) {
100 list($lw, $lh) = $this->measureLine($l, $font_size);
101 if($lw > $width)
102 $width = $lw;
103 $height = $lh;
104 }
105 // height is height of first line + (line spacing) x (other lines)
106 $height += $line_spacing * (count($lines) - 1);
107
108 if($angle % 180 != 0) {
109 $w = $height;
110 $h = $width;
111 if($angle % 90 != 0) {
112 $a = deg2rad($angle);
113 $sa = abs(sin($a));
114 $ca = abs(cos($a));
115 $w = $ca * $width + $sa * $height;
116 $h = $sa * $width + $ca * $height;
117 }
118 $width = $w;
119 $height = $h;
120 }
121
122 $this->cacheMeasurement($text, $font_size, $angle, $line_spacing, $width,
123 $height);
124 return [$width, $height];
125 }
126
127 /**
128 * Measures a block of text and finds its position
129 * returns array($x, $y, $width, $height)
130 */
131 public function measurePosition($text, $font_size, $line_spacing, $x, $y,
132 $anchor, $angle = 0, $rcx = null, $rcy = null)
133 {
134 $baseline = $this->baseline($font_size);
135 $y -= $baseline;
136 list($width, $height) = $this->measure($text, $font_size, 0,
137 $line_spacing);
138 if($anchor == 'end')
139 $x -= $width;
140 elseif($anchor == 'middle')
141 $x -= $width / 2;
142
143 if($angle) {
144 // the four corners of the bounding box
145 $points = [
146 [$x, $y], [$x + $width, $y],
147 [$x, $y + $height], [$x + $width, $y + $height]
148 ];
149 $arad = deg2rad($angle);
150 $s = sin($arad);
151 $c = cos($arad);
152 $xp = [];
153 $yp = [];
154 foreach($points as $point) {
155 // translate to origin
156 $point[0] -= $rcx;
157 $point[1] -= $rcy;
158 // rotate
159 $x1 = $point[0] * $c - $point[1] * $s;
160 $y1 = $point[0] * $s + $point[1] * $c;
161 // translate back
162 $xp[] = $x1 + $rcx;
163 $yp[] = $y1 + $rcy;
164 }
165 // find extents
166 $x = min($xp);
167 $max_x = max($xp);
168 $y = min($yp);
169 $max_y = max($yp);
170 $width = $max_x - $x;
171 $height = $max_y - $y;
172 }
173 return [$x, $y, $width, $height];
174 }
175
176 /**
177 * Measures a single line of UTF-8 text
178 */
179 private function measureLine($text, $font_size)
180 {
181 $metrics = $this->no_metrics ? false : $this->loadMetrics($this->font);
182 $width = $height = 0;
183
184 // want to measure characters, not entities
185 $text = $this->replaceEntities($text);
186
187 $chars = $this->processLineArray($text);
188 if(!$metrics) {
189 // no metrics, use adjust value based on length of string
190 $length = 0;
191 foreach($chars as $char) {
192 $code = Text::charCode($char);
193 $length += Text::charWidth($code);
194 }
195 $width = $length * $font_size * $this->adjust;
196 // height is font size
197 return [$width, $font_size];
198 }
199
200 // metrics file found, so use char widths and kerning
201 $metrics =& Text::$metrics[$this->metrics_file];
202
203 $char_widths = [];
204 foreach($chars as $char) {
205 $code = Text::charCode($char);
206 if(isset($metrics['widths'][$code])) {
207 $char_widths[] = $metrics['widths'][$code];
208 continue;
209 }
210
211 // use mean * relative width
212 $char_size = Text::charWidth($code);
213 $char_width = $metrics['mean'] * $char_size;
214 $char_widths[] = $char_width;
215 }
216 $len = count($chars);
217 for($i = 0; $i < $len - 1; ++$i) {
218 $j = $i + 1;
219 $sub = $chars[$i] . $chars[$j];
220 if(isset($metrics['kern'][$sub])) {
221 $kerning = $char_widths[$i] + $char_widths[$j] - $metrics['kern'][$sub];
222 $char_widths[$j] -= $kerning;
223 }
224 }
225 for($i = 0; $i < $len; ++$i) {
226 $width += $char_widths[$i];
227 }
228 $width = $width * $font_size / $metrics['size'];
229 $height = $metrics['height'] * $font_size / $metrics['size'];
230 return [$width, $height];
231 }
232
233 /**
234 * Returns the cached measurement, or NULL if not in cache
235 */
236 private function measureCached($text, $font_size, $angle, $line_spacing)
237 {
238 $key = $this->getCacheKey($text, $font_size, $angle, $line_spacing);
239 return isset(Text::$measure_cache[$key]) ?
240 Text::$measure_cache[$key] : null;
241 }
242
243 /**
244 * Caches measurement
245 */
246 private function cacheMeasurement($text, $font_size, $angle, $line_spacing,
247 $width, $height)
248 {
249 $key = $this->getCacheKey($text, $font_size, $angle, $line_spacing);
250 Text::$measure_cache[$key] = [$width, $height];
251 }
252
253 /**
254 * Returns the cache key for a string
255 */
256 private function getCacheKey($text, $font_size, $angle, $line_spacing)
257 {
258 $key = $text . '-' . $this->font . '-' . $this->adjust . '-' . $font_size .
259 '-' . $angle . '-' . $line_spacing;
260 return $key;
261 }
262
263 /**
264 * Returns the baseline offset for the current font
265 */
266 public function baseline($font_size)
267 {
268 if(!is_numeric($font_size))
269 throw new \InvalidArgumentException("Font size is not numeric");
270
271 $metrics = $this->no_metrics ? false : $this->loadMetrics($this->font);
272
273 if($metrics) {
274 $metrics =& Text::$metrics[$this->metrics_file];
275 return $metrics['baseline'] * $font_size / $metrics['size'];
276 }
277
278 // this approximation has been good enough until now
279 return $font_size * 0.85;
280 }
281
282 /**
283 * Displays text
284 */
285 public function text($text, $line_spacing, $attribs, $styles = null)
286 {
287 // convert to UTF-8 for processing
288 $text = $this->convert($text);
289
290 // strip special characters
291 $text = htmlspecialchars($text, ENT_COMPAT, 'UTF-8');
292
293 // put entities back in (XML entity names only)
294 $text = preg_replace('/&amp;(amp|apos|gt|lt|quot|#x[a-fA-F0-9]+|#\d+);/u',
295 '&$1;', $text);
296
297 // empty string
298 if($this->strlen($text, 'UTF-8') == 0) {
299 $content = $this->unconvert(' ');
300 return $this->graph->element('text', $attribs, $styles, $content);
301 }
302
303 // single line
304 if($this->strpos($text, "\n", 0, 'UTF-8') === false) {
305 $content = $this->processLine($text);
306 $content = $this->unconvert($content);
307 return $this->graph->element('text', $attribs, $styles, $content);
308 }
309
310 if($line_spacing !== 0 && !is_numeric($line_spacing))
311 throw new \InvalidArgumentException("Line spacing is not numeric");
312
313 $lines = $this->splitLines($text);
314 $content = '';
315 $group = 'text';
316 $line_element = 'tspan';
317 $line_attr = ['x' => $attribs['x']];
318 if($this->no_tspan) {
319 $line_attr['y'] = $attribs['y'];
320 $line_element = 'text';
321 $group = 'g';
322 } else {
323 $line_attr['dy'] = 0;
324 }
325 $count = 1;
326 foreach($lines as $line) {
327 // blank tspan elements collapse to nothing, so insert a space
328 if($this->strlen($line, 'UTF-8') == 0)
329 $line = ' ';
330 else
331 $line = $this->processLine($line);
332 $line = $this->unconvert($line);
333
334 // the TRUE is for no whitespace
335 $content .= $this->graph->element($line_element, $line_attr, null,
336 $line, true);
337 if($this->no_tspan) {
338 $line_attr['y'] = $attribs['y'] + $line_spacing * $count;
339 } else {
340 $line_attr['dy'] = $line_spacing;
341 }
342 ++$count;
343 }
344 if($this->no_tspan)
345 unset($attribs['x'], $attribs['y']);
346
347 return $this->graph->element($group, $attribs, $styles, $content);
348 }
349
350 /**
351 * Trim spaces at ends, collapse multiple spaces, return chars as array
352 */
353 public function processLineArray($str)
354 {
355 $chars = $this->splitChars($str);
356 $start = 0;
357 $end = count($chars) - 1;
358
359 if($end >= $start) {
360 // find first non-space
361 do {
362 $code = $this->charCode($chars[$start]);
363 } while($this->isSpace($code) && ++$start <= $end);
364 }
365
366 if($start > $end) {
367 // all spaces
368 return [' '];
369 }
370
371 // find last non-space
372 do {
373 $code = $this->charCode($chars[$end]);
374 } while($this->isSpace($code) && $start < --$end);
375
376 $last_space = false;
377 $processed = [];
378 for($i = $start; $i <= $end; ++$i) {
379
380 // reduce multiple normal spaces to one
381 if($chars[$i] == ' ') {
382 $last_space = true;
383 continue;
384 }
385
386 if($last_space)
387 $processed[] = ' ';
388 $processed[] = $chars[$i];
389 $last_space = false;
390 }
391 return $processed;
392 }
393
394 /**
395 * Trim spaces at ends, collapse multiple spaces, return string
396 */
397 public function processLine($str)
398 {
399 $processed = $this->processLineArray($str);
400 return implode($processed);
401 }
402
403 /**
404 * Returns string length
405 */
406 public function strlen($text, $enc = null)
407 {
408 if($text === null)
409 return 0;
410 if($enc === null)
411 $enc = $this->encoding;
412 if(Text::$use_iconv)
413 return iconv_strlen($text, $enc);
414 if(Text::$use_mbstring)
415 return mb_strlen($text, $enc);
416 return strlen($text);
417 }
418
419 /**
420 * Returns position of needle in haystack
421 */
422 public function strpos($text, $needle, $offset = 0, $enc = null)
423 {
424 if($enc === null)
425 $enc = $this->encoding;
426 if(Text::$use_iconv)
427 return iconv_strpos($text, $needle, $offset, $enc);
428 if(Text::$use_mbstring)
429 return mb_strpos($text, $needle, $offset, $enc);
430 return strpos($text, $needle, $offset);
431 }
432
433 /**
434 * Splits UTF-8 string into lines
435 */
436 public function splitLines($text)
437 {
438 return preg_split("/\\n/u", $text);
439 }
440
441 /**
442 * Splits UTF-8 string into chars
443 */
444 private function splitChars($text)
445 {
446 return preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY);
447 }
448
449 /**
450 * Returns the code for a UTF-8 character
451 */
452 public static function charCode($char)
453 {
454 $code = ord(substr($char, 0, 1));
455 if($code < 0x80) {
456 return $code;
457 } elseif(($code & 0xe0) == 0xc0) {
458 $n = 1;
459 $code = $code & 0x1f;
460 } elseif(($code & 0xf0) == 0xe0) {
461 $n = 2;
462 $code = $code & 0x0f;
463 } elseif(($code & 0xf8) == 0xf0) {
464 $n = 3;
465 $code = $code & 0x07;
466 } else {
467 // replacement character for unknown code point
468 return 0xfffd;
469 }
470
471 for($i = 1; $i <= $n; ++$i) {
472 $byte = ord(substr($char, $i, 1));
473 $code = ($code * 0x40) + ($byte & 0x3f);
474 }
475 // should be some validation here
476
477 return $code;
478 }
479
480 /**
481 * Returns the UTF-8 character for a code point
482 */
483 public static function charFromCode($code)
484 {
485 if($code < 0x80)
486 return chr($code);
487
488 $u = ($code >> 16) & 0x1f;
489 $z = ($code >> 12) & 0x0f;
490 $y = ($code >> 6) & 0x3f;
491 $x = ($code & 0x3f);
492
493 if($u) {
494 $b1 = 0xf0 + ($u >> 2);
495 $b2 = 0x80 + (($u & 0x03) << 4) + $z;
496 $b3 = 0x80 + $y;
497 $b4 = 0x80 + $x;
498 return chr($b1) . chr($b2) . chr($b3) . chr($b4);
499 }
500
501 if($z) {
502 $b1 = 0xe0 + $z;
503 $b2 = 0x80 + $y;
504 $b3 = 0x80 + $x;
505 return chr($b1) . chr($b2) . chr($b3);
506 }
507
508 $b1 = 0xc0 + $y;
509 $b2 = 0x80 + $x;
510 return chr($b1) . chr($b2);
511 }
512
513 /**
514 * Returns the width of a character, relative to a plain letter
515 */
516 public static function charWidth($char)
517 {
518 if(Text::isCombiner($char))
519 return 0;
520
521 if(Text::isDoubleWidth($char))
522 return 2;
523 return 1;
524 }
525
526 /**
527 * Returns TRUE if the character is some kind of space
528 */
529 public static function isSpace($char, $type = false)
530 {
531 $spaces = [
532 0x20 => 'space',
533 0xa0 => 'no-break space',
534 // 0x1680 => 'ogham space mark', // more dash than space
535 0x2000 => 'en quad',
536 0x2001 => 'em quad',
537 0x2002 => 'en space',
538 0x2003 => 'em space',
539 0x2004 => 'three-per-em space',
540 0x2005 => 'four-per-em space',
541 0x2006 => 'six-per-em space',
542 0x2007 => 'figure space',
543 0x2008 => 'punctuation space',
544 0x2009 => 'thin space',
545 0x200a => 'hair space',
546 0x202f => 'narrow no-break space',
547 0x205f => 'medium mathematical space',
548 0x3000 => 'ideographic space',
549 ];
550 if(isset($spaces[$char]))
551 return $type ? $spaces[$char] : true;
552 return false;
553 }
554
555 /**
556 * Returns TRUE if the character is a combining mark
557 */
558 public static function isCombiner($char)
559 {
560 $ranges = [
561 [0x300,0x36f], // combining diacritical marks
562 [0x1ab0,0x1aff], // combining diacritical marks extended
563 [0x1dc0,0x1dff], // combining diacritical marks supplement
564 [0x20d0,0x20ff], // combining diacritical marks for symbols
565 [0xfe20,0xfe2f], // combining half marks
566 ];
567 foreach($ranges as $range) {
568 if($char >= $range[0] && $char <= $range[1])
569 return true;
570 }
571 return false;
572 }
573
574 /**
575 * Returns TRUE for a double-width character
576 */
577 public static function isDoubleWidth($char)
578 {
579 $ranges = [
580 [0x1100,0x115F], // hangul jamo
581 [0x11A3,0x11A7],
582 [0x11FA,0x11FF],
583 [0x2329,0x232A], // tech
584 [0x2E80,0x2E99], // cjk radicals
585 [0x2E9B,0x2EF3],
586 [0x2F00,0x2FD5], // kangxi radicals
587 [0x2FF0,0x2FFB], // ideographic description characters
588 [0x3000,0x303E], // cjk symbols
589 [0x3041,0x3096], // hiragana
590 [0x3099,0x30FF], // katakana
591 [0x3105,0x312D], // bopomofo
592 [0x3131,0x318E], // hangul compat jamo
593 [0x3190,0x31BA], // kanbun
594 [0x31C0,0x31E3], // cjk strokes
595 [0x31F0,0x321E], // katakana phonetic extensions
596 [0x3220,0x3247], // enclosed cjk
597 [0x3250,0x32FE],
598 [0x3300,0x4DBF], // cjk
599 [0x4E00,0xA48C],
600 [0xA490,0xA4C6], // yi radicals
601 [0xA960,0xA97C], // hangul jamo extended a
602 [0xAC00,0xD7A3], // hangul syllables
603 [0xD7B0,0xD7C6], // hangul jamo extended b
604 [0xD7CB,0xD7FB],
605 [0xF900,0xFAFF], // cjk compat ideographs
606 [0xFE10,0xFE19], // vertical forms
607 [0xFE30,0xFE52], // cjk compat forms
608 [0xFE54,0xFE66], // small form variants
609 [0xFE68,0xFE6B],
610 [0xFF01,0xFF60], // halfwidth and fullwidth forms
611 [0xFFE0,0xFFE6],
612 [0x1B000,0x1B001], // kana supplement
613 [0x1F200,0x1F202], // enclosed ideographic supplement
614 [0x1F210,0x1F23A],
615 [0x1F240,0x1F248],
616 [0x1F250,0x1F251],
617 [0x1F600,0x1F64F], // emoticons
618 [0x1F900,0x1F9FF], // emoticons
619 [0x20000,0x2FFFD], // cjk ideographs
620 ];
621 foreach($ranges as $range) {
622 if($char >= $range[0] && $char <= $range[1])
623 return true;
624 }
625 return false;
626 }
627
628 /**
629 * Returns a substring
630 */
631 public function substr($text, $begin, $length = null)
632 {
633 if(Text::$use_iconv) {
634 if($length === null)
635 $length = iconv_strlen($text, $this->encoding);
636 return iconv_substr($text, $begin, $length, $this->encoding);
637 }
638
639 if(Text::$use_mbstring)
640 return mb_substr($text, $begin, $length, $this->encoding);
641
642 return $length === null ? substr($text, $begin) :
643 substr($text, $begin, $length);
644 }
645
646 /**
647 * Returns the number of lines in a string
648 */
649 public function lines($text)
650 {
651 $c = 1;
652 $pos = 0;
653 while(($pos = $this->strpos($text, "\n", $pos)) !== false) {
654 ++$c;
655 ++$pos;
656 }
657 return $c;
658 }
659
660 /**
661 * Replaces the entities in a string
662 */
663 public function replaceEntities($text)
664 {
665 $patterns = ['&amp;', '&apos;', '&lt;', '&gt;', '&quot;'];
666 $replacements = ['&', "'", '<', '>', '"'];
667 $text = str_replace($patterns, $replacements, $text);
668
669 // numeric entities a bit trickier
670 $text = preg_replace_callback('/&#(x)?([a-fA-F0-9]+);/ui', function($ent) {
671 $code = ($ent[1] == 'x' ? base_convert($ent[2], 16, 10) : $ent[2]);
672 return Text::charFromCode($code);
673 }, $text);
674 return $text;
675 }
676
677 /**
678 * Converts a string to UTF-8, if possible
679 */
680 private function convert($text)
681 {
682 if($this->encoding != 'UTF-8') {
683 $converted = false;
684 if(Text::$use_iconv) {
685 $converted = @iconv($this->encoding, 'UTF-8', $text);
686 if($converted !== false) {
687 $text = $converted;
688 $converted = true;
689 }
690 }
691 if(!$converted && Text::$use_mbstring) {
692 $order = mb_detect_order();
693 array_unshift($order, $this->encoding);
694 $enc = mb_detect_encoding($text, $order);
695 if($enc !== false)
696 $text = @mb_convert_encoding($text, 'UTF-8', $enc);
697 }
698 }
699 return $text;
700 }
701
702 /**
703 * Converts back from UTF-8 to chosen encoding
704 */
705 private function unconvert($text)
706 {
707 if($this->encoding != 'UTF-8') {
708 $converted = false;
709 if(Text::$use_iconv) {
710 $converted = @iconv('UTF-8', $this->encoding, $text);
711 if($converted !== false) {
712 $text = $converted;
713 $converted = true;
714 }
715 }
716 if(!$converted && Text::$use_mbstring) {
717 $text = @mb_convert_encoding($text, $this->encoding, 'UTF-8');
718 }
719 }
720 return $text;
721 }
722
723 /**
724 * Returns the filename for a font metrics file
725 */
726 private static function metricsFilename($font)
727 {
728 return preg_replace('/[^a-z0-9]+/ui', '_', strtolower($font));
729 }
730
731 /**
732 * Loads metrics for a font
733 */
734 private static function loadMetrics($font)
735 {
736 // metrics use JSON, so we need support
737 if(!extension_loaded('json'))
738 return false;
739
740 $filename = Text::metricsFilename($font);
741 if(isset(Text::$metrics[$filename])) {
742 return (Text::$metrics[$filename] !== false);
743 }
744
745 $metrics_path = __DIR__ . '/fonts/' . $filename . '.json';
746 if(file_exists($metrics_path)) {
747 $metrics = file_get_contents($metrics_path);
748 $metrics = @json_decode($metrics, true);
749 // validate the metrics in case the file is corrupt
750 if(is_array($metrics)) {
751 if(isset($metrics['map'])) {
752 $map_to = $metrics['map'];
753 if(isset(Text::$metrics[$map_to])) {
754 $metrics = Text::$metrics[$map_to];
755 } else {
756 $metrics_path = __DIR__ . '/fonts/' . $map_to . '.json';
757 if(file_exists($metrics_path)) {
758 $metrics = file_get_contents($metrics_path);
759 $metrics = @json_decode($metrics, true);
760 }
761 }
762 }
763
764 if(isset($metrics['mean']) && $metrics['mean'] > 1
765 && isset($metrics['size']) && $metrics['size'] > 1
766 && isset($metrics['height']) && $metrics['height'] > 1
767 && isset($metrics['baseline']) && $metrics['baseline'] > 1) {
768 Text::$metrics[$filename] = $metrics;
769 return true;
770 }
771 }
772 }
773 Text::$metrics[$filename] = false;
774 return false;
775 }
776 }
777
778