|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Text_Figlet |
|
17 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
18 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
19 * @version $Id: Figlet.php 20096 2010-01-06 02:05:09Z bkarwin $ |
|
20 */ |
|
21 |
|
22 /** |
|
23 * Zend_Text_Figlet is a PHP implementation of FIGlet |
|
24 * |
|
25 * @category Zend |
|
26 * @package Zend_Text_Figlet |
|
27 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
28 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
29 */ |
|
30 class Zend_Text_Figlet |
|
31 { |
|
32 /** |
|
33 * Smush2 layout modes |
|
34 */ |
|
35 const SM_EQUAL = 0x01; |
|
36 const SM_LOWLINE = 0x02; |
|
37 const SM_HIERARCHY = 0x04; |
|
38 const SM_PAIR = 0x08; |
|
39 const SM_BIGX = 0x10; |
|
40 const SM_HARDBLANK = 0x20; |
|
41 const SM_KERN = 0x40; |
|
42 const SM_SMUSH = 0x80; |
|
43 |
|
44 /** |
|
45 * Smush mode override modes |
|
46 */ |
|
47 const SMO_NO = 0; |
|
48 const SMO_YES = 1; |
|
49 const SMO_FORCE = 2; |
|
50 |
|
51 /** |
|
52 * Justifications |
|
53 */ |
|
54 const JUSTIFICATION_LEFT = 0; |
|
55 const JUSTIFICATION_CENTER = 1; |
|
56 const JUSTIFICATION_RIGHT = 2; |
|
57 |
|
58 /** |
|
59 * Write directions |
|
60 */ |
|
61 const DIRECTION_LEFT_TO_RIGHT = 0; |
|
62 const DIRECTION_RIGHT_TO_LEFT = 1; |
|
63 |
|
64 /** |
|
65 * Magic fontfile number |
|
66 */ |
|
67 const FONTFILE_MAGIC_NUMBER = 'flf2'; |
|
68 |
|
69 /** |
|
70 * Array containing all characters of the current font |
|
71 * |
|
72 * @var array |
|
73 */ |
|
74 protected $_charList = array(); |
|
75 |
|
76 /** |
|
77 * Indicates if a font was loaded yet |
|
78 * |
|
79 * @var boolean |
|
80 */ |
|
81 protected $_fontLoaded = false; |
|
82 |
|
83 /** |
|
84 * Latin-1 codes for German letters, respectively: |
|
85 * |
|
86 * LATIN CAPITAL LETTER A WITH DIAERESIS = A-umlaut |
|
87 * LATIN CAPITAL LETTER O WITH DIAERESIS = O-umlaut |
|
88 * LATIN CAPITAL LETTER U WITH DIAERESIS = U-umlaut |
|
89 * LATIN SMALL LETTER A WITH DIAERESIS = a-umlaut |
|
90 * LATIN SMALL LETTER O WITH DIAERESIS = o-umlaut |
|
91 * LATIN SMALL LETTER U WITH DIAERESIS = u-umlaut |
|
92 * LATIN SMALL LETTER SHARP S = ess-zed |
|
93 * |
|
94 * @var array |
|
95 */ |
|
96 protected $_germanChars = array(196, 214, 220, 228, 246, 252, 223); |
|
97 |
|
98 /** |
|
99 * Output width, defaults to 80. |
|
100 * |
|
101 * @var integer |
|
102 */ |
|
103 protected $_outputWidth = 80; |
|
104 |
|
105 /** |
|
106 * Hard blank character |
|
107 * |
|
108 * @var string |
|
109 */ |
|
110 protected $_hardBlank; |
|
111 |
|
112 /** |
|
113 * Height of the characters |
|
114 * |
|
115 * @var integer |
|
116 */ |
|
117 protected $_charHeight; |
|
118 |
|
119 /** |
|
120 * Max length of any character |
|
121 * |
|
122 * @var integer |
|
123 */ |
|
124 protected $_maxLength; |
|
125 |
|
126 /** |
|
127 * Smush mode |
|
128 * |
|
129 * @var integer |
|
130 */ |
|
131 protected $_smushMode = 0; |
|
132 |
|
133 /** |
|
134 * Smush defined by the font |
|
135 * |
|
136 * @var integer |
|
137 */ |
|
138 protected $_fontSmush = 0; |
|
139 |
|
140 /** |
|
141 * Smush defined by the user |
|
142 * |
|
143 * @var integer |
|
144 */ |
|
145 protected $_userSmush = 0; |
|
146 |
|
147 /** |
|
148 * Wether to handle paragraphs || not |
|
149 * |
|
150 * @var boolean |
|
151 */ |
|
152 protected $_handleParagraphs = false; |
|
153 |
|
154 /** |
|
155 * Justification for the text, according to $_outputWidth |
|
156 * |
|
157 * For using font default, this parameter should be null, else one of |
|
158 * the values of Zend_Text_Figlet::JUSTIFICATION_* |
|
159 * |
|
160 * @var integer |
|
161 */ |
|
162 protected $_justification = null; |
|
163 |
|
164 /** |
|
165 * Direction of text-writing, namely right to left |
|
166 * |
|
167 * For using font default, this parameter should be null, else one of |
|
168 * the values of Zend_Text_Figlet::DIRECTION_* |
|
169 * |
|
170 * @var integer |
|
171 */ |
|
172 protected $_rightToLeft = null; |
|
173 |
|
174 /** |
|
175 * Override font file smush layout |
|
176 * |
|
177 * @var integer |
|
178 */ |
|
179 protected $_smushOverride = 0; |
|
180 |
|
181 /** |
|
182 * Options of the current font |
|
183 * |
|
184 * @var array |
|
185 */ |
|
186 protected $_fontOptions = array(); |
|
187 |
|
188 /** |
|
189 * Previous character width |
|
190 * |
|
191 * @var integer |
|
192 */ |
|
193 protected $_previousCharWidth = 0; |
|
194 |
|
195 /** |
|
196 * Current character width |
|
197 * |
|
198 * @var integer |
|
199 */ |
|
200 protected $_currentCharWidth = 0; |
|
201 |
|
202 /** |
|
203 * Current outline length |
|
204 * |
|
205 * @var integer |
|
206 */ |
|
207 protected $_outlineLength = 0; |
|
208 |
|
209 /** |
|
210 * Maxmimum outline length |
|
211 * |
|
212 * @var integer |
|
213 */ |
|
214 protected $_outlineLengthLimit = 0; |
|
215 |
|
216 /** |
|
217 * In character line |
|
218 * |
|
219 * @var string |
|
220 */ |
|
221 protected $_inCharLine; |
|
222 |
|
223 /** |
|
224 * In character line length |
|
225 * |
|
226 * @var integer |
|
227 */ |
|
228 protected $_inCharLineLength = 0; |
|
229 |
|
230 /** |
|
231 * Maximum in character line length |
|
232 * |
|
233 * @var integer |
|
234 */ |
|
235 protected $_inCharLineLengthLimit = 0; |
|
236 |
|
237 /** |
|
238 * Current char |
|
239 * |
|
240 * @var array |
|
241 */ |
|
242 protected $_currentChar = null; |
|
243 |
|
244 /** |
|
245 * Current output line |
|
246 * |
|
247 * @var array |
|
248 */ |
|
249 protected $_outputLine; |
|
250 |
|
251 /** |
|
252 * Current output |
|
253 * |
|
254 * @var string |
|
255 */ |
|
256 protected $_output; |
|
257 |
|
258 /** |
|
259 * Option keys to skip when calling setOptions() |
|
260 * |
|
261 * @var array |
|
262 */ |
|
263 protected $_skipOptions = array( |
|
264 'options', |
|
265 'config', |
|
266 ); |
|
267 |
|
268 /** |
|
269 * Instantiate the FIGlet with a specific font. If no font is given, the |
|
270 * standard font is used. You can also supply multiple options via |
|
271 * the $options variable, which can either be an array or an instance of |
|
272 * Zend_Config. |
|
273 * |
|
274 * @param array|Zend_Config $options Options for the output |
|
275 */ |
|
276 public function __construct($options = null) |
|
277 { |
|
278 // Set options |
|
279 if (is_array($options)) { |
|
280 $this->setOptions($options); |
|
281 } else if ($options instanceof Zend_Config) { |
|
282 $this->setConfig($options); |
|
283 } |
|
284 |
|
285 // If no font was defined, load default font |
|
286 if (!$this->_fontLoaded) { |
|
287 $this->_loadFont(dirname(__FILE__) . '/Figlet/zend-framework.flf'); |
|
288 } |
|
289 } |
|
290 |
|
291 /** |
|
292 * Set options from array |
|
293 * |
|
294 * @param array $options Configuration for Zend_Text_Figlet |
|
295 * @return Zend_Text_Figlet |
|
296 */ |
|
297 public function setOptions(array $options) |
|
298 { |
|
299 foreach ($options as $key => $value) { |
|
300 if (in_array(strtolower($key), $this->_skipOptions)) { |
|
301 continue; |
|
302 } |
|
303 |
|
304 $method = 'set' . ucfirst($key); |
|
305 if (method_exists($this, $method)) { |
|
306 $this->$method($value); |
|
307 } |
|
308 } |
|
309 return $this; |
|
310 } |
|
311 |
|
312 /** |
|
313 * Set options from config object |
|
314 * |
|
315 * @param Zend_Config $config Configuration for Zend_Text_Figlet |
|
316 * @return Zend_Text_Figlet |
|
317 */ |
|
318 public function setConfig(Zend_Config $config) |
|
319 { |
|
320 return $this->setOptions($config->toArray()); |
|
321 } |
|
322 |
|
323 /** |
|
324 * Set a font to use |
|
325 * |
|
326 * @param string $font Path to the font |
|
327 * @return Zend_Text_Figlet |
|
328 */ |
|
329 public function setFont($font) |
|
330 { |
|
331 $this->_loadFont($font); |
|
332 return $this; |
|
333 } |
|
334 |
|
335 /** |
|
336 * Set handling of paragraphs |
|
337 * |
|
338 * @param boolean $handleParagraphs Wether to handle paragraphs or not |
|
339 * @return Zend_Text_Figlet |
|
340 */ |
|
341 public function setHandleParagraphs($handleParagraphs) |
|
342 { |
|
343 $this->_handleParagraphs = (bool) $handleParagraphs; |
|
344 return $this; |
|
345 } |
|
346 |
|
347 /** |
|
348 * Set the justification. 0 stands for left aligned, 1 for centered and 2 |
|
349 * for right aligned. |
|
350 * |
|
351 * @param integer $justification Justification of the output text |
|
352 * @return Zend_Text_Figlet |
|
353 */ |
|
354 public function setJustification($justification) |
|
355 { |
|
356 $this->_justification = min(3, max(0, (int) $justification)); |
|
357 return $this; |
|
358 } |
|
359 |
|
360 /** |
|
361 * Set the output width |
|
362 * |
|
363 * @param integer $outputWidth Output with which should be used for word |
|
364 * wrapping and justification |
|
365 * @return Zend_Text_Figlet |
|
366 */ |
|
367 public function setOutputWidth($outputWidth) |
|
368 { |
|
369 $this->_outputWidth = max(1, (int) $outputWidth); |
|
370 return $this; |
|
371 } |
|
372 |
|
373 /** |
|
374 * Set right to left mode. For writing from left to right, use |
|
375 * Zend_Text_Figlet::DIRECTION_LEFT_TO_RIGHT. For writing from right to left, |
|
376 * use Zend_Text_Figlet::DIRECTION_RIGHT_TO_LEFT. |
|
377 * |
|
378 * @param integer $rightToLeft Right-to-left mode |
|
379 * @return Zend_Text_Figlet |
|
380 */ |
|
381 public function setRightToLeft($rightToLeft) |
|
382 { |
|
383 $this->_rightToLeft = min(1, max(0, (int) $rightToLeft)); |
|
384 return $this; |
|
385 } |
|
386 |
|
387 /** |
|
388 * Set the smush mode. |
|
389 * |
|
390 * Use one of the constants of Zend_Text_Figlet::SM_*, you may combine them. |
|
391 * |
|
392 * @param integer $smushMode Smush mode to use for generating text |
|
393 * @return Zend_Text_Figlet |
|
394 */ |
|
395 public function setSmushMode($smushMode) |
|
396 { |
|
397 $smushMode = (int) $smushMode; |
|
398 |
|
399 if ($smushMode < -1) { |
|
400 $this->_smushOverride = self::SMO_NO; |
|
401 } else { |
|
402 if ($smushMode === 0) { |
|
403 $this->_userSmush = self::SM_KERN; |
|
404 } else if ($smushMode === -1) { |
|
405 $this->_userSmush = 0; |
|
406 } else { |
|
407 $this->_userSmush = (($smushMode & 63) | self::SM_SMUSH); |
|
408 } |
|
409 |
|
410 $this->_smushOverride = self::SMO_YES; |
|
411 } |
|
412 |
|
413 $this->_setUsedSmush(); |
|
414 |
|
415 return $this; |
|
416 } |
|
417 |
|
418 /** |
|
419 * Render a FIGlet text |
|
420 * |
|
421 * @param string $text Text to convert to a figlet text |
|
422 * @param string $encoding Encoding of the input string |
|
423 * @throws InvalidArgumentException When $text is not a string |
|
424 * @throws Zend_Text_Figlet_Exception When $text it not properly encoded |
|
425 * @return string |
|
426 */ |
|
427 public function render($text, $encoding = 'UTF-8') |
|
428 { |
|
429 if (!is_string($text)) { |
|
430 throw new InvalidArgumentException('$text must be a string'); |
|
431 } |
|
432 |
|
433 if ($encoding !== 'UTF-8') { |
|
434 $text = iconv($encoding, 'UTF-8', $text); |
|
435 } |
|
436 |
|
437 $this->_output = ''; |
|
438 $this->_outputLine = array(); |
|
439 |
|
440 $this->_clearLine(); |
|
441 |
|
442 $this->_outlineLengthLimit = ($this->_outputWidth - 1); |
|
443 $this->_inCharLineLengthLimit = ($this->_outputWidth * 4 + 100); |
|
444 |
|
445 $wordBreakMode = 0; |
|
446 $lastCharWasEol = false; |
|
447 $textLength = @iconv_strlen($text, 'UTF-8'); |
|
448 |
|
449 if ($textLength === false) { |
|
450 require_once 'Zend/Text/Figlet/Exception.php'; |
|
451 throw new Zend_Text_Figlet_Exception('$text is not encoded with ' . $encoding); |
|
452 } |
|
453 |
|
454 for ($charNum = 0; $charNum < $textLength; $charNum++) { |
|
455 // Handle paragraphs |
|
456 $char = iconv_substr($text, $charNum, 1, 'UTF-8'); |
|
457 |
|
458 if ($char === "\n" && $this->_handleParagraphs && !$lastCharWasEol) { |
|
459 $nextChar = iconv_substr($text, ($charNum + 1), 1, 'UTF-8'); |
|
460 if (!$nextChar) { |
|
461 $nextChar = null; |
|
462 } |
|
463 |
|
464 $char = (ctype_space($nextChar)) ? "\n" : ' '; |
|
465 } |
|
466 |
|
467 $lastCharWasEol = (ctype_space($char) && $char !== "\t" && $char !== ' '); |
|
468 |
|
469 if (ctype_space($char)) { |
|
470 $char = ($char === "\t" || $char === ' ') ? ' ': "\n"; |
|
471 } |
|
472 |
|
473 // Skip unprintable characters |
|
474 $ordChar = $this->_uniOrd($char); |
|
475 if (($ordChar > 0 && $ordChar < 32 && $char !== "\n") || $ordChar === 127) { |
|
476 continue; |
|
477 } |
|
478 |
|
479 // Build the character |
|
480 // Note: The following code is complex and thoroughly tested. |
|
481 // Be careful when modifying! |
|
482 do { |
|
483 $charNotAdded = false; |
|
484 |
|
485 if ($wordBreakMode === -1) { |
|
486 if ($char === ' ') { |
|
487 break; |
|
488 } else if ($char === "\n") { |
|
489 $wordBreakMode = 0; |
|
490 break; |
|
491 } |
|
492 |
|
493 $wordBreakMode = 0; |
|
494 } |
|
495 |
|
496 if ($char === "\n") { |
|
497 $this->_appendLine(); |
|
498 $wordBreakMode = false; |
|
499 } else if ($this->_addChar($char)) { |
|
500 if ($char !== ' ') { |
|
501 $wordBreakMode = ($wordBreakMode >= 2) ? 3: 1; |
|
502 } else { |
|
503 $wordBreakMode = ($wordBreakMode > 0) ? 2: 0; |
|
504 } |
|
505 } else if ($this->_outlineLength === 0) { |
|
506 for ($i = 0; $i < $this->_charHeight; $i++) { |
|
507 if ($this->_rightToLeft === 1 && $this->_outputWidth > 1) { |
|
508 $offset = (strlen($this->_currentChar[$i]) - $this->_outlineLengthLimit); |
|
509 $this->_putString(substr($this->_currentChar[$i], $offset)); |
|
510 } else { |
|
511 $this->_putString($this->_currentChar[$i]); |
|
512 } |
|
513 } |
|
514 |
|
515 $wordBreakMode = -1; |
|
516 } else if ($char === ' ') { |
|
517 if ($wordBreakMode === 2) { |
|
518 $this->_splitLine(); |
|
519 } else { |
|
520 $this->_appendLine(); |
|
521 } |
|
522 |
|
523 $wordBreakMode = -1; |
|
524 } else { |
|
525 if ($wordBreakMode >= 2) { |
|
526 $this->_splitLine(); |
|
527 } else { |
|
528 $this->_appendLine(); |
|
529 } |
|
530 |
|
531 $wordBreakMode = ($wordBreakMode === 3) ? 1 : 0; |
|
532 $charNotAdded = true; |
|
533 } |
|
534 } while ($charNotAdded); |
|
535 } |
|
536 |
|
537 if ($this->_outlineLength !== 0) { |
|
538 $this->_appendLine(); |
|
539 } |
|
540 |
|
541 return $this->_output; |
|
542 } |
|
543 |
|
544 /** |
|
545 * Puts the given string, substituting blanks for hardblanks. If outputWidth |
|
546 * is 1, puts the entire string; otherwise puts at most outputWidth - 1 |
|
547 * characters. Puts a newline at the end of the string. The string is left- |
|
548 * justified, centered or right-justified (taking outputWidth as the screen |
|
549 * width) if justification is 0, 1 or 2 respectively. |
|
550 * |
|
551 * @param string $string The string to add to the output |
|
552 * @return void |
|
553 */ |
|
554 protected function _putString($string) |
|
555 { |
|
556 $length = strlen($string); |
|
557 |
|
558 if ($this->_outputWidth > 1) { |
|
559 if ($length > ($this->_outputWidth - 1)) { |
|
560 $length = ($this->_outputWidth - 1); |
|
561 } |
|
562 |
|
563 if ($this->_justification > 0) { |
|
564 for ($i = 1; |
|
565 ((3 - $this->_justification) * $i + $length + $this->_justification - 2) < $this->_outputWidth; |
|
566 $i++) { |
|
567 $this->_output .= ' '; |
|
568 } |
|
569 } |
|
570 } |
|
571 |
|
572 $this->_output .= str_replace($this->_hardBlank, ' ', $string) . "\n"; |
|
573 } |
|
574 |
|
575 /** |
|
576 * Appends the current line to the output |
|
577 * |
|
578 * @return void |
|
579 */ |
|
580 protected function _appendLine() |
|
581 { |
|
582 for ($i = 0; $i < $this->_charHeight; $i++) { |
|
583 $this->_putString($this->_outputLine[$i]); |
|
584 } |
|
585 |
|
586 $this->_clearLine(); |
|
587 } |
|
588 |
|
589 /** |
|
590 * Splits inCharLine at the last word break (bunch of consecutive blanks). |
|
591 * Makes a new line out of the first part and appends it using appendLine(). |
|
592 * Makes a new line out of the second part and returns. |
|
593 * |
|
594 * @return void |
|
595 */ |
|
596 protected function _splitLine() |
|
597 { |
|
598 $gotSpace = false; |
|
599 for ($i = ($this->_inCharLineLength - 1); $i >= 0; $i--) { |
|
600 if (!$gotSpace && $this->_inCharLine[$i] === ' ') { |
|
601 $gotSpace = true; |
|
602 $lastSpace = $i; |
|
603 } |
|
604 |
|
605 if ($gotSpace && $this->_inCharLine[$i] !== ' ') { |
|
606 break; |
|
607 } |
|
608 } |
|
609 |
|
610 $firstLength = ($i + 1); |
|
611 $lastLength = ($this->_inCharLineLength - $lastSpace - 1); |
|
612 |
|
613 $firstPart = ''; |
|
614 for ($i = 0; $i < $firstLength; $i++) { |
|
615 $firstPart[$i] = $this->_inCharLine[$i]; |
|
616 } |
|
617 |
|
618 $lastPart = ''; |
|
619 for ($i = 0; $i < $lastLength; $i++) { |
|
620 $lastPart[$i] = $this->_inCharLine[($lastSpace + 1 + $i)]; |
|
621 } |
|
622 |
|
623 $this->_clearLine(); |
|
624 |
|
625 for ($i = 0; $i < $firstLength; $i++) { |
|
626 $this->_addChar($firstPart[$i]); |
|
627 } |
|
628 |
|
629 $this->_appendLine(); |
|
630 |
|
631 for ($i = 0; $i < $lastLength; $i++) { |
|
632 $this->_addChar($lastPart[$i]); |
|
633 } |
|
634 } |
|
635 |
|
636 /** |
|
637 * Clears the current line |
|
638 * |
|
639 * @return void |
|
640 */ |
|
641 protected function _clearLine() |
|
642 { |
|
643 for ($i = 0; $i < $this->_charHeight; $i++) { |
|
644 $this->_outputLine[$i] = ''; |
|
645 } |
|
646 |
|
647 $this->_outlineLength = 0; |
|
648 $this->_inCharLineLength = 0; |
|
649 } |
|
650 |
|
651 /** |
|
652 * Attempts to add the given character onto the end of the current line. |
|
653 * Returns true if this can be done, false otherwise. |
|
654 * |
|
655 * @param string $char Character which to add to the output |
|
656 * @return boolean |
|
657 */ |
|
658 protected function _addChar($char) |
|
659 { |
|
660 $this->_getLetter($char); |
|
661 |
|
662 if ($this->_currentChar === null) { |
|
663 return true; |
|
664 } |
|
665 |
|
666 $smushAmount = $this->_smushAmount(); |
|
667 |
|
668 if (($this->_outlineLength + $this->_currentCharWidth - $smushAmount) > $this->_outlineLengthLimit |
|
669 || ($this->_inCharLineLength + 1) > $this->_inCharLineLengthLimit) { |
|
670 return false; |
|
671 } |
|
672 |
|
673 $tempLine = ''; |
|
674 for ($row = 0; $row < $this->_charHeight; $row++) { |
|
675 if ($this->_rightToLeft === 1) { |
|
676 $tempLine = $this->_currentChar[$row]; |
|
677 |
|
678 for ($k = 0; $k < $smushAmount; $k++) { |
|
679 $position = ($this->_currentCharWidth - $smushAmount + $k); |
|
680 $tempLine[$position] = $this->_smushem($tempLine[$position], $this->_outputLine[$row][$k]); |
|
681 } |
|
682 |
|
683 $this->_outputLine[$row] = $tempLine . substr($this->_outputLine[$row], $smushAmount); |
|
684 } else { |
|
685 for ($k = 0; $k < $smushAmount; $k++) { |
|
686 if (($this->_outlineLength - $smushAmount + $k) < 0) { |
|
687 continue; |
|
688 } |
|
689 |
|
690 $position = ($this->_outlineLength - $smushAmount + $k); |
|
691 if (isset($this->_outputLine[$row][$position])) { |
|
692 $leftChar = $this->_outputLine[$row][$position]; |
|
693 } else { |
|
694 $leftChar = null; |
|
695 } |
|
696 |
|
697 $this->_outputLine[$row][$position] = $this->_smushem($leftChar, $this->_currentChar[$row][$k]); |
|
698 } |
|
699 |
|
700 $this->_outputLine[$row] .= substr($this->_currentChar[$row], $smushAmount); |
|
701 } |
|
702 } |
|
703 |
|
704 $this->_outlineLength = strlen($this->_outputLine[0]); |
|
705 $this->_inCharLine[$this->_inCharLineLength++] = $char; |
|
706 |
|
707 return true; |
|
708 } |
|
709 |
|
710 /** |
|
711 * Gets the requested character and sets current and previous char width. |
|
712 * |
|
713 * @param string $char The character from which to get the letter of |
|
714 * @return void |
|
715 */ |
|
716 protected function _getLetter($char) |
|
717 { |
|
718 if (array_key_exists($this->_uniOrd($char), $this->_charList)) { |
|
719 $this->_currentChar = $this->_charList[$this->_uniOrd($char)]; |
|
720 $this->_previousCharWidth = $this->_currentCharWidth; |
|
721 $this->_currentCharWidth = strlen($this->_currentChar[0]); |
|
722 } else { |
|
723 $this->_currentChar = null; |
|
724 } |
|
725 } |
|
726 |
|
727 /** |
|
728 * Returns the maximum amount that the current character can be smushed into |
|
729 * the current line. |
|
730 * |
|
731 * @return integer |
|
732 */ |
|
733 protected function _smushAmount() |
|
734 { |
|
735 if (($this->_smushMode & (self::SM_SMUSH | self::SM_KERN)) === 0) { |
|
736 return 0; |
|
737 } |
|
738 |
|
739 $maxSmush = $this->_currentCharWidth; |
|
740 $amount = $maxSmush; |
|
741 |
|
742 for ($row = 0; $row < $this->_charHeight; $row++) { |
|
743 if ($this->_rightToLeft === 1) { |
|
744 $charbd = strlen($this->_currentChar[$row]); |
|
745 while (true) { |
|
746 if (!isset($this->_currentChar[$row][$charbd])) { |
|
747 $leftChar = null; |
|
748 } else { |
|
749 $leftChar = $this->_currentChar[$row][$charbd]; |
|
750 } |
|
751 |
|
752 if ($charbd > 0 && ($leftChar === null || $leftChar == ' ')) { |
|
753 $charbd--; |
|
754 } else { |
|
755 break; |
|
756 } |
|
757 } |
|
758 |
|
759 $linebd = 0; |
|
760 while (true) { |
|
761 if (!isset($this->_outputLine[$row][$linebd])) { |
|
762 $rightChar = null; |
|
763 } else { |
|
764 $rightChar = $this->_outputLine[$row][$linebd]; |
|
765 } |
|
766 |
|
767 if ($rightChar === ' ') { |
|
768 $linebd++; |
|
769 } else { |
|
770 break; |
|
771 } |
|
772 } |
|
773 |
|
774 $amount = ($linebd + $this->_currentCharWidth - 1 - $charbd); |
|
775 } else { |
|
776 $linebd = strlen($this->_outputLine[$row]); |
|
777 while (true) { |
|
778 if (!isset($this->_outputLine[$row][$linebd])) { |
|
779 $leftChar = null; |
|
780 } else { |
|
781 $leftChar = $this->_outputLine[$row][$linebd]; |
|
782 } |
|
783 |
|
784 if ($linebd > 0 && ($leftChar === null || $leftChar == ' ')) { |
|
785 $linebd--; |
|
786 } else { |
|
787 break; |
|
788 } |
|
789 } |
|
790 |
|
791 $charbd = 0; |
|
792 while (true) { |
|
793 if (!isset($this->_currentChar[$row][$charbd])) { |
|
794 $rightChar = null; |
|
795 } else { |
|
796 $rightChar = $this->_currentChar[$row][$charbd]; |
|
797 } |
|
798 |
|
799 if ($rightChar === ' ') { |
|
800 $charbd++; |
|
801 } else { |
|
802 break; |
|
803 } |
|
804 } |
|
805 |
|
806 $amount = ($charbd + $this->_outlineLength - 1 - $linebd); |
|
807 } |
|
808 |
|
809 if (empty($leftChar) || $leftChar === ' ') { |
|
810 $amount++; |
|
811 } else if (!empty($rightChar)) { |
|
812 if ($this->_smushem($leftChar, $rightChar) !== null) { |
|
813 $amount++; |
|
814 } |
|
815 } |
|
816 |
|
817 $maxSmush = min($amount, $maxSmush); |
|
818 } |
|
819 |
|
820 return $maxSmush; |
|
821 } |
|
822 |
|
823 /** |
|
824 * Given two characters, attempts to smush them into one, according to the |
|
825 * current smushmode. Returns smushed character or false if no smushing can |
|
826 * be done. |
|
827 * |
|
828 * Smushmode values are sum of following (all values smush blanks): |
|
829 * |
|
830 * 1: Smush equal chars (not hardblanks) |
|
831 * 2: Smush '_' with any char in hierarchy below |
|
832 * 4: hierarchy: "|", "/\", "[]", "{}", "()", "<>" |
|
833 * Each class in hier. can be replaced by later class. |
|
834 * 8: [ + ] -> |, { + } -> |, ( + ) -> | |
|
835 * 16: / + \ -> X, > + < -> X (only in that order) |
|
836 * 32: hardblank + hardblank -> hardblank |
|
837 * |
|
838 * @param string $leftChar Left character to smush |
|
839 * @param string $rightChar Right character to smush |
|
840 * @return string |
|
841 */ |
|
842 protected function _smushem($leftChar, $rightChar) |
|
843 { |
|
844 if ($leftChar === ' ') { |
|
845 return $rightChar; |
|
846 } |
|
847 |
|
848 if ($rightChar === ' ') { |
|
849 return $leftChar; |
|
850 } |
|
851 |
|
852 if ($this->_previousCharWidth < 2 || $this->_currentCharWidth < 2) { |
|
853 // Disallows overlapping if the previous character or the current |
|
854 // character has a width of one or zero. |
|
855 return null; |
|
856 } |
|
857 |
|
858 if (($this->_smushMode & self::SM_SMUSH) === 0) { |
|
859 // Kerning |
|
860 return null; |
|
861 } |
|
862 |
|
863 if (($this->_smushMode & 63) === 0) { |
|
864 // This is smushing by universal overlapping |
|
865 if ($leftChar === ' ') { |
|
866 return $rightChar; |
|
867 } else if ($rightChar === ' ') { |
|
868 return $leftChar; |
|
869 } else if ($leftChar === $this->_hardBlank) { |
|
870 return $rightChar; |
|
871 } else if ($rightChar === $this->_hardBlank) { |
|
872 return $rightChar; |
|
873 } else if ($this->_rightToLeft === 1) { |
|
874 return $leftChar; |
|
875 } else { |
|
876 // Occurs in the absence of above exceptions |
|
877 return $rightChar; |
|
878 } |
|
879 } |
|
880 |
|
881 if (($this->_smushMode & self::SM_HARDBLANK) > 0) { |
|
882 if ($leftChar === $this->_hardBlank && $rightChar === $this->_hardBlank) { |
|
883 return $leftChar; |
|
884 } |
|
885 } |
|
886 |
|
887 if ($leftChar === $this->_hardBlank && $rightChar === $this->_hardBlank) { |
|
888 return null; |
|
889 } |
|
890 |
|
891 if (($this->_smushMode & self::SM_EQUAL) > 0) { |
|
892 if ($leftChar === $rightChar) { |
|
893 return $leftChar; |
|
894 } |
|
895 } |
|
896 |
|
897 if (($this->_smushMode & self::SM_LOWLINE) > 0) { |
|
898 if ($leftChar === '_' && strchr('|/\\[]{}()<>', $rightChar) !== false) { |
|
899 return $rightChar; |
|
900 } else if ($rightChar === '_' && strchr('|/\\[]{}()<>', $leftChar) !== false) { |
|
901 return $leftChar; |
|
902 } |
|
903 } |
|
904 |
|
905 if (($this->_smushMode & self::SM_HIERARCHY) > 0) { |
|
906 if ($leftChar === '|' && strchr('/\\[]{}()<>', $rightChar) !== false) { |
|
907 return $rightChar; |
|
908 } else if ($rightChar === '|' && strchr('/\\[]{}()<>', $leftChar) !== false) { |
|
909 return $leftChar; |
|
910 } else if (strchr('/\\', $leftChar) && strchr('[]{}()<>', $rightChar) !== false) { |
|
911 return $rightChar; |
|
912 } else if (strchr('/\\', $rightChar) && strchr('[]{}()<>', $leftChar) !== false) { |
|
913 return $leftChar; |
|
914 } else if (strchr('[]', $leftChar) && strchr('{}()<>', $rightChar) !== false) { |
|
915 return $rightChar; |
|
916 } else if (strchr('[]', $rightChar) && strchr('{}()<>', $leftChar) !== false) { |
|
917 return $leftChar; |
|
918 } else if (strchr('{}', $leftChar) && strchr('()<>', $rightChar) !== false) { |
|
919 return $rightChar; |
|
920 } else if (strchr('{}', $rightChar) && strchr('()<>', $leftChar) !== false) { |
|
921 return $leftChar; |
|
922 } else if (strchr('()', $leftChar) && strchr('<>', $rightChar) !== false) { |
|
923 return $rightChar; |
|
924 } else if (strchr('()', $rightChar) && strchr('<>', $leftChar) !== false) { |
|
925 return $leftChar; |
|
926 } |
|
927 } |
|
928 |
|
929 if (($this->_smushMode & self::SM_PAIR) > 0) { |
|
930 if ($leftChar === '[' && $rightChar === ']') { |
|
931 return '|'; |
|
932 } else if ($rightChar === '[' && $leftChar === ']') { |
|
933 return '|'; |
|
934 } else if ($leftChar === '{' && $rightChar === '}') { |
|
935 return '|'; |
|
936 } else if ($rightChar === '{' && $leftChar === '}') { |
|
937 return '|'; |
|
938 } else if ($leftChar === '(' && $rightChar === ')') { |
|
939 return '|'; |
|
940 } else if ($rightChar === '(' && $leftChar === ')') { |
|
941 return '|'; |
|
942 } |
|
943 } |
|
944 |
|
945 if (($this->_smushMode & self::SM_BIGX) > 0) { |
|
946 if ($leftChar === '/' && $rightChar === '\\') { |
|
947 return '|'; |
|
948 } else if ($rightChar === '/' && $leftChar === '\\') { |
|
949 return 'Y'; |
|
950 } else if ($leftChar === '>' && $rightChar === '<') { |
|
951 return 'X'; |
|
952 } |
|
953 } |
|
954 |
|
955 return null; |
|
956 } |
|
957 |
|
958 /** |
|
959 * Load the specified font |
|
960 * |
|
961 * @param string $fontFile Font file to load |
|
962 * @throws Zend_Text_Figlet_Exception When font file was not found |
|
963 * @throws Zend_Text_Figlet_Exception When GZIP library is required but not found |
|
964 * @throws Zend_Text_Figlet_Exception When font file is not readable |
|
965 * @return void |
|
966 */ |
|
967 protected function _loadFont($fontFile) |
|
968 { |
|
969 // Check if the font file exists |
|
970 if (!file_exists($fontFile)) { |
|
971 require_once 'Zend/Text/Figlet/Exception.php'; |
|
972 throw new Zend_Text_Figlet_Exception($fontFile . ': Font file not found'); |
|
973 } |
|
974 |
|
975 // Check if gzip support is required |
|
976 if (substr($fontFile, -3) === '.gz') { |
|
977 if (!function_exists('gzcompress')) { |
|
978 require_once 'Zend/Text/Figlet/Exception.php'; |
|
979 throw new Zend_Text_Figlet_Exception('GZIP library is required for ' |
|
980 . 'gzip compressed font files'); |
|
981 } |
|
982 |
|
983 $fontFile = 'compress.zlib://' . $fontFile; |
|
984 $compressed = true; |
|
985 } else { |
|
986 $compressed = false; |
|
987 } |
|
988 |
|
989 // Try to open the file |
|
990 $fp = fopen($fontFile, 'rb'); |
|
991 if ($fp === false) { |
|
992 require_once 'Zend/Text/Figlet/Exception.php'; |
|
993 throw new Zend_Text_Figlet_Exception($fontFile . ': Could not open file'); |
|
994 } |
|
995 |
|
996 // If the file is not compressed, lock the stream |
|
997 if (!$compressed) { |
|
998 flock($fp, LOCK_SH); |
|
999 } |
|
1000 |
|
1001 // Get magic |
|
1002 $magic = $this->_readMagic($fp); |
|
1003 |
|
1004 // Get the header |
|
1005 $numsRead = sscanf(fgets($fp, 1000), |
|
1006 '%*c%c %d %*d %d %d %d %d %d', |
|
1007 $this->_hardBlank, |
|
1008 $this->_charHeight, |
|
1009 $this->_maxLength, |
|
1010 $smush, |
|
1011 $cmtLines, |
|
1012 $rightToLeft, |
|
1013 $this->_fontSmush); |
|
1014 |
|
1015 if ($magic !== self::FONTFILE_MAGIC_NUMBER || $numsRead < 5) { |
|
1016 require_once 'Zend/Text/Figlet/Exception.php'; |
|
1017 throw new Zend_Text_Figlet_Exception($fontFile . ': Not a FIGlet 2 font file'); |
|
1018 } |
|
1019 |
|
1020 // Set default right to left |
|
1021 if ($numsRead < 6) { |
|
1022 $rightToLeft = 0; |
|
1023 } |
|
1024 |
|
1025 // If no smush2, decode smush into smush2 |
|
1026 if ($numsRead < 7) { |
|
1027 if ($smush === 2) { |
|
1028 $this->_fontSmush = self::SM_KERN; |
|
1029 } else if ($smush < 0) { |
|
1030 $this->_fontSmush = 0; |
|
1031 } else { |
|
1032 $this->_fontSmush = (($smush & 31) | self::SM_SMUSH); |
|
1033 } |
|
1034 } |
|
1035 |
|
1036 // Correct char height && maxlength |
|
1037 $this->_charHeight = max(1, $this->_charHeight); |
|
1038 $this->_maxLength = max(1, $this->_maxLength); |
|
1039 |
|
1040 // Give ourselves some extra room |
|
1041 $this->_maxLength += 100; |
|
1042 |
|
1043 // See if we have to override smush settings |
|
1044 $this->_setUsedSmush(); |
|
1045 |
|
1046 // Get left to right value |
|
1047 if ($this->_rightToLeft === null) { |
|
1048 $this->_rightToLeft = $rightToLeft; |
|
1049 } |
|
1050 |
|
1051 // Get justification value |
|
1052 if ($this->_justification === null) { |
|
1053 $this->_justification = (2 * $this->_rightToLeft); |
|
1054 } |
|
1055 |
|
1056 // Skip all comment lines |
|
1057 for ($line = 1; $line <= $cmtLines; $line++) { |
|
1058 $this->_skipToEol($fp); |
|
1059 } |
|
1060 |
|
1061 // Fetch all ASCII characters |
|
1062 for ($asciiCode = 32; $asciiCode < 127; $asciiCode++) { |
|
1063 $this->_charList[$asciiCode] = $this->_loadChar($fp); |
|
1064 } |
|
1065 |
|
1066 // Fetch all german characters |
|
1067 foreach ($this->_germanChars as $uniCode) { |
|
1068 $char = $this->_loadChar($fp); |
|
1069 |
|
1070 if ($char === false) { |
|
1071 fclose($fp); |
|
1072 return; |
|
1073 } |
|
1074 |
|
1075 if (trim(implode('', $char)) !== '') { |
|
1076 $this->_charList[$uniCode] = $char; |
|
1077 } |
|
1078 } |
|
1079 |
|
1080 // At the end fetch all extended characters |
|
1081 while (!feof($fp)) { |
|
1082 // Get the Unicode |
|
1083 list($uniCode) = explode(' ', fgets($fp, 2048)); |
|
1084 |
|
1085 if (empty($uniCode)) { |
|
1086 continue; |
|
1087 } |
|
1088 |
|
1089 // Convert it if required |
|
1090 if (substr($uniCode, 0, 2) === '0x') { |
|
1091 $uniCode = hexdec(substr($uniCode, 2)); |
|
1092 } else if (substr($uniCode, 0, 1) === '0' and |
|
1093 $uniCode !== '0' or |
|
1094 substr($uniCode, 0, 2) === '-0') { |
|
1095 $uniCode = octdec($uniCode); |
|
1096 } else { |
|
1097 $uniCode = (int) $uniCode; |
|
1098 } |
|
1099 |
|
1100 // Now fetch the character |
|
1101 $char = $this->_loadChar($fp); |
|
1102 |
|
1103 if ($char === false) { |
|
1104 fclose($fp); |
|
1105 return; |
|
1106 } |
|
1107 |
|
1108 $this->_charList[$uniCode] = $char; |
|
1109 } |
|
1110 |
|
1111 fclose($fp); |
|
1112 |
|
1113 $this->_fontLoaded = true; |
|
1114 } |
|
1115 |
|
1116 /** |
|
1117 * Set the used smush mode, according to smush override, user smsush and |
|
1118 * font smush. |
|
1119 * |
|
1120 * @return void |
|
1121 */ |
|
1122 protected function _setUsedSmush() |
|
1123 { |
|
1124 if ($this->_smushOverride === self::SMO_NO) { |
|
1125 $this->_smushMode = $this->_fontSmush; |
|
1126 } else if ($this->_smushOverride === self::SMO_YES) { |
|
1127 $this->_smushMode = $this->_userSmush; |
|
1128 } else if ($this->_smushOverride === self::SMO_FORCE) { |
|
1129 $this->_smushMode = ($this->_fontSmush | $this->_userSmush); |
|
1130 } |
|
1131 } |
|
1132 |
|
1133 /** |
|
1134 * Reads a four-character magic string from a stream |
|
1135 * |
|
1136 * @param resource $fp File pointer to the font file |
|
1137 * @return string |
|
1138 */ |
|
1139 protected function _readMagic($fp) |
|
1140 { |
|
1141 $magic = ''; |
|
1142 |
|
1143 for ($i = 0; $i < 4; $i++) { |
|
1144 $magic .= fgetc($fp); |
|
1145 } |
|
1146 |
|
1147 return $magic; |
|
1148 } |
|
1149 |
|
1150 /** |
|
1151 * Skip a stream to the end of line |
|
1152 * |
|
1153 * @param resource $fp File pointer to the font file |
|
1154 * @return void |
|
1155 */ |
|
1156 protected function _skipToEol($fp) |
|
1157 { |
|
1158 $dummy = fgetc($fp); |
|
1159 while ($dummy !== false && !feof($fp)) { |
|
1160 if ($dummy === "\n") { |
|
1161 return; |
|
1162 } |
|
1163 |
|
1164 if ($dummy === "\r") { |
|
1165 $dummy = fgetc($fp); |
|
1166 |
|
1167 if (!feof($fp) && $dummy !== "\n") { |
|
1168 fseek($fp, -1, SEEK_SET); |
|
1169 } |
|
1170 |
|
1171 return; |
|
1172 } |
|
1173 |
|
1174 $dummy = fgetc($fp); |
|
1175 } |
|
1176 } |
|
1177 |
|
1178 /** |
|
1179 * Load a single character from the font file |
|
1180 * |
|
1181 * @param resource $fp File pointer to the font file |
|
1182 * @return array |
|
1183 */ |
|
1184 protected function _loadChar($fp) |
|
1185 { |
|
1186 $char = array(); |
|
1187 |
|
1188 for ($i = 0; $i < $this->_charHeight; $i++) { |
|
1189 if (feof($fp)) { |
|
1190 return false; |
|
1191 } |
|
1192 |
|
1193 $line = rtrim(fgets($fp, 2048), "\r\n"); |
|
1194 |
|
1195 if (preg_match('#(.)\\1?$#', $line, $result) === 1) { |
|
1196 $line = str_replace($result[1], '', $line); |
|
1197 } |
|
1198 |
|
1199 $char[] = $line; |
|
1200 } |
|
1201 |
|
1202 return $char; |
|
1203 } |
|
1204 |
|
1205 /** |
|
1206 * Unicode compatible ord() method |
|
1207 * |
|
1208 * @param string $c The char to get the value from |
|
1209 * @return integer |
|
1210 */ |
|
1211 protected function _uniOrd($c) |
|
1212 { |
|
1213 $h = ord($c[0]); |
|
1214 |
|
1215 if ($h <= 0x7F) { |
|
1216 $ord = $h; |
|
1217 } else if ($h < 0xC2) { |
|
1218 $ord = 0; |
|
1219 } else if ($h <= 0xDF) { |
|
1220 $ord = (($h & 0x1F) << 6 | (ord($c[1]) & 0x3F)); |
|
1221 } else if ($h <= 0xEF) { |
|
1222 $ord = (($h & 0x0F) << 12 | (ord($c[1]) & 0x3F) << 6 | (ord($c[2]) & 0x3F)); |
|
1223 } else if ($h <= 0xF4) { |
|
1224 $ord = (($h & 0x0F) << 18 | (ord($c[1]) & 0x3F) << 12 | |
|
1225 (ord($c[2]) & 0x3F) << 6 | (ord($c[3]) & 0x3F)); |
|
1226 } else { |
|
1227 $ord = 0; |
|
1228 } |
|
1229 |
|
1230 return $ord; |
|
1231 } |
|
1232 } |