File indexing completed on 2024-05-12 06:03:09

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category  Zend
0016  * @package   Zend_Text_Figlet
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license   http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version   $Id$
0020  */
0021 
0022 /**
0023  * Zend_Text_Figlet is a PHP implementation of FIGlet
0024  *
0025  * @category  Zend
0026  * @package   Zend_Text_Figlet
0027  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0028  * @license   http://framework.zend.com/license/new-bsd     New BSD License
0029  */
0030 class Zend_Text_Figlet
0031 {
0032     /**
0033      * Smush2 layout modes
0034      */
0035     const SM_EQUAL     = 0x01;
0036     const SM_LOWLINE   = 0x02;
0037     const SM_HIERARCHY = 0x04;
0038     const SM_PAIR      = 0x08;
0039     const SM_BIGX      = 0x10;
0040     const SM_HARDBLANK = 0x20;
0041     const SM_KERN      = 0x40;
0042     const SM_SMUSH     = 0x80;
0043 
0044     /**
0045      * Smush mode override modes
0046      */
0047     const SMO_NO    = 0;
0048     const SMO_YES   = 1;
0049     const SMO_FORCE = 2;
0050 
0051     /**
0052      * Justifications
0053      */
0054     const JUSTIFICATION_LEFT   = 0;
0055     const JUSTIFICATION_CENTER = 1;
0056     const JUSTIFICATION_RIGHT  = 2;
0057 
0058     /**
0059      * Write directions
0060      */
0061     const DIRECTION_LEFT_TO_RIGHT = 0;
0062     const DIRECTION_RIGHT_TO_LEFT = 1;
0063 
0064     /**
0065      * Magic fontfile number
0066      */
0067     const FONTFILE_MAGIC_NUMBER = 'flf2';
0068 
0069     /**
0070      * Array containing all characters of the current font
0071      *
0072      * @var array
0073      */
0074     protected $_charList = array();
0075 
0076     /**
0077      * Indicates if a font was loaded yet
0078      *
0079      * @var boolean
0080      */
0081     protected $_fontLoaded = false;
0082 
0083     /**
0084      * Latin-1 codes for German letters, respectively:
0085      *
0086      * LATIN CAPITAL LETTER A WITH DIAERESIS = A-umlaut
0087      * LATIN CAPITAL LETTER O WITH DIAERESIS = O-umlaut
0088      * LATIN CAPITAL LETTER U WITH DIAERESIS = U-umlaut
0089      * LATIN SMALL LETTER A WITH DIAERESIS = a-umlaut
0090      * LATIN SMALL LETTER O WITH DIAERESIS = o-umlaut
0091      * LATIN SMALL LETTER U WITH DIAERESIS = u-umlaut
0092      * LATIN SMALL LETTER SHARP S = ess-zed
0093      *
0094      * @var array
0095      */
0096     protected $_germanChars = array(196, 214, 220, 228, 246, 252, 223);
0097 
0098     /**
0099      * Output width, defaults to 80.
0100      *
0101      * @var integer
0102      */
0103     protected $_outputWidth = 80;
0104 
0105     /**
0106      * Hard blank character
0107      *
0108      * @var string
0109      */
0110     protected $_hardBlank;
0111 
0112     /**
0113      * Height of the characters
0114      *
0115      * @var integer
0116      */
0117     protected $_charHeight;
0118 
0119     /**
0120      * Max length of any character
0121      *
0122      * @var integer
0123      */
0124     protected $_maxLength;
0125 
0126     /**
0127      * Smush mode
0128      *
0129      * @var integer
0130      */
0131     protected $_smushMode = 0;
0132 
0133     /**
0134      * Smush defined by the font
0135      *
0136      * @var integer
0137      */
0138     protected $_fontSmush = 0;
0139 
0140     /**
0141      * Smush defined by the user
0142      *
0143      * @var integer
0144      */
0145     protected $_userSmush = 0;
0146 
0147     /**
0148      * Wether to handle paragraphs || not
0149      *
0150      * @var boolean
0151      */
0152     protected $_handleParagraphs = false;
0153 
0154     /**
0155      * Justification for the text, according to $_outputWidth
0156      *
0157      * For using font default, this parameter should be null, else one of
0158      * the values of Zend_Text_Figlet::JUSTIFICATION_*
0159      *
0160      * @var integer
0161      */
0162     protected $_justification = null;
0163 
0164     /**
0165      * Direction of text-writing, namely right to left
0166      *
0167      * For using font default, this parameter should be null, else one of
0168      * the values of Zend_Text_Figlet::DIRECTION_*
0169      *
0170      * @var integer
0171      */
0172     protected $_rightToLeft = null;
0173 
0174     /**
0175      * Override font file smush layout
0176      *
0177      * @var integer
0178      */
0179     protected $_smushOverride = 0;
0180 
0181     /**
0182      * Options of the current font
0183      *
0184      * @var array
0185      */
0186     protected $_fontOptions = array();
0187 
0188     /**
0189      * Previous character width
0190      *
0191      * @var integer
0192      */
0193     protected $_previousCharWidth = 0;
0194 
0195     /**
0196      * Current character width
0197      *
0198      * @var integer
0199      */
0200     protected $_currentCharWidth = 0;
0201 
0202     /**
0203      * Current outline length
0204      *
0205      * @var integer
0206      */
0207     protected $_outlineLength = 0;
0208 
0209     /**
0210      * Maxmimum outline length
0211      *
0212      * @var integer
0213      */
0214     protected $_outlineLengthLimit = 0;
0215 
0216     /**
0217      * In character line
0218      *
0219      * @var string
0220      */
0221     protected $_inCharLine;
0222 
0223     /**
0224      * In character line length
0225      *
0226      * @var integer
0227      */
0228     protected $_inCharLineLength = 0;
0229 
0230     /**
0231      * Maximum in character line length
0232      *
0233      * @var integer
0234      */
0235     protected $_inCharLineLengthLimit = 0;
0236 
0237     /**
0238      * Current char
0239      *
0240      * @var array
0241      */
0242     protected $_currentChar = null;
0243 
0244     /**
0245      * Current output line
0246      *
0247      * @var array
0248      */
0249     protected $_outputLine;
0250 
0251     /**
0252      * Current output
0253      *
0254      * @var string
0255      */
0256     protected $_output;
0257 
0258     /**
0259      * Option keys to skip when calling setOptions()
0260      *
0261      * @var array
0262      */
0263     protected $_skipOptions = array(
0264         'options',
0265         'config',
0266     );
0267 
0268     /**
0269      * Instantiate the FIGlet with a specific font. If no font is given, the
0270      * standard font is used. You can also supply multiple options via
0271      * the $options variable, which can either be an array or an instance of
0272      * Zend_Config.
0273      *
0274      * @param array|Zend_Config $options Options for the output
0275      */
0276     public function __construct($options = null)
0277     {
0278         // Set options
0279         if (is_array($options)) {
0280             $this->setOptions($options);
0281         } else if ($options instanceof Zend_Config) {
0282             $this->setConfig($options);
0283         }
0284 
0285         // If no font was defined, load default font
0286         if (!$this->_fontLoaded) {
0287             $this->_loadFont(dirname(__FILE__) . '/Figlet/zend-framework.flf');
0288         }
0289     }
0290 
0291     /**
0292      * Set options from array
0293      *
0294      * @param  array $options Configuration for Zend_Text_Figlet
0295      * @return Zend_Text_Figlet
0296      */
0297     public function setOptions(array $options)
0298     {
0299         foreach ($options as $key => $value) {
0300             if (in_array(strtolower($key), $this->_skipOptions)) {
0301                 continue;
0302             }
0303 
0304             $method = 'set' . ucfirst($key);
0305             if (method_exists($this, $method)) {
0306                 $this->$method($value);
0307             }
0308         }
0309         return $this;
0310     }
0311 
0312     /**
0313      * Set options from config object
0314      *
0315      * @param  Zend_Config $config Configuration for Zend_Text_Figlet
0316      * @return Zend_Text_Figlet
0317      */
0318     public function setConfig(Zend_Config $config)
0319     {
0320         return $this->setOptions($config->toArray());
0321     }
0322 
0323     /**
0324      * Set a font to use
0325      *
0326      * @param  string $font Path to the font
0327      * @return Zend_Text_Figlet
0328      */
0329     public function setFont($font)
0330     {
0331         $this->_loadFont($font);
0332         return $this;
0333     }
0334 
0335     /**
0336      * Set handling of paragraphs
0337      *
0338      * @param  boolean $handleParagraphs Wether to handle paragraphs or not
0339      * @return Zend_Text_Figlet
0340      */
0341     public function setHandleParagraphs($handleParagraphs)
0342     {
0343         $this->_handleParagraphs = (bool) $handleParagraphs;
0344         return $this;
0345     }
0346 
0347     /**
0348      * Set the justification. 0 stands for left aligned, 1 for centered and 2
0349      * for right aligned.
0350      *
0351      * @param  integer $justification Justification of the output text
0352      * @return Zend_Text_Figlet
0353      */
0354     public function setJustification($justification)
0355     {
0356         $this->_justification = min(3, max(0, (int) $justification));
0357         return $this;
0358     }
0359 
0360     /**
0361      * Set the output width
0362      *
0363      * @param  integer $outputWidth Output with which should be used for word
0364      *                              wrapping and justification
0365      * @return Zend_Text_Figlet
0366      */
0367     public function setOutputWidth($outputWidth)
0368     {
0369         $this->_outputWidth = max(1, (int) $outputWidth);
0370         return $this;
0371     }
0372 
0373     /**
0374      * Set right to left mode. For writing from left to right, use
0375      * Zend_Text_Figlet::DIRECTION_LEFT_TO_RIGHT. For writing from right to left,
0376      * use Zend_Text_Figlet::DIRECTION_RIGHT_TO_LEFT.
0377      *
0378      * @param  integer $rightToLeft Right-to-left mode
0379      * @return Zend_Text_Figlet
0380      */
0381     public function setRightToLeft($rightToLeft)
0382     {
0383         $this->_rightToLeft = min(1, max(0, (int) $rightToLeft));
0384         return $this;
0385     }
0386 
0387     /**
0388      * Set the smush mode.
0389      *
0390      * Use one of the constants of Zend_Text_Figlet::SM_*, you may combine them.
0391      *
0392      * @param  integer $smushMode Smush mode to use for generating text
0393      * @return Zend_Text_Figlet
0394      */
0395     public function setSmushMode($smushMode)
0396     {
0397         $smushMode = (int) $smushMode;
0398 
0399         if ($smushMode < -1) {
0400             $this->_smushOverride = self::SMO_NO;
0401         } else {
0402             if ($smushMode === 0) {
0403                 $this->_userSmush = self::SM_KERN;
0404             } else if ($smushMode === -1) {
0405                 $this->_userSmush = 0;
0406             } else {
0407                 $this->_userSmush = (($smushMode & 63) | self::SM_SMUSH);
0408             }
0409 
0410             $this->_smushOverride = self::SMO_YES;
0411         }
0412 
0413         $this->_setUsedSmush();
0414 
0415         return $this;
0416     }
0417 
0418     /**
0419      * Render a FIGlet text
0420      *
0421      * @param  string $text     Text to convert to a figlet text
0422      * @param  string $encoding Encoding of the input string
0423      * @throws InvalidArgumentException When $text is not a string
0424      * @throws Zend_Text_Figlet_Exception    When $text it not properly encoded
0425      * @return string
0426      */
0427     public function render($text, $encoding = 'UTF-8')
0428     {
0429         if (!is_string($text)) {
0430             throw new InvalidArgumentException('$text must be a string');
0431         }
0432 
0433         if ($encoding !== 'UTF-8') {
0434             $text = iconv($encoding, 'UTF-8', $text);
0435         }
0436 
0437         $this->_output     = '';
0438         $this->_outputLine = array();
0439 
0440         $this->_clearLine();
0441 
0442         $this->_outlineLengthLimit    = ($this->_outputWidth - 1);
0443         $this->_inCharLineLengthLimit = ($this->_outputWidth * 4 + 100);
0444 
0445         $wordBreakMode  = 0;
0446         $lastCharWasEol = false;
0447         $textLength     = @iconv_strlen($text, 'UTF-8');
0448 
0449         if ($textLength === false) {
0450             // require_once 'Zend/Text/Figlet/Exception.php';
0451             throw new Zend_Text_Figlet_Exception('$text is not encoded with ' . $encoding);
0452         }
0453 
0454         for ($charNum = 0; $charNum < $textLength; $charNum++) {
0455             // Handle paragraphs
0456             $char = iconv_substr($text, $charNum, 1, 'UTF-8');
0457 
0458             if ($char === "\n" && $this->_handleParagraphs && !$lastCharWasEol) {
0459                 $nextChar = iconv_substr($text, ($charNum + 1), 1, 'UTF-8');
0460                 if (!$nextChar) {
0461                     $nextChar = null;
0462                 }
0463 
0464                 $char = (ctype_space($nextChar)) ? "\n" : ' ';
0465             }
0466 
0467             $lastCharWasEol = (ctype_space($char) && $char !== "\t" && $char !== ' ');
0468 
0469             if (ctype_space($char)) {
0470                 $char = ($char === "\t" || $char === ' ') ? ' ': "\n";
0471             }
0472 
0473             // Skip unprintable characters
0474             $ordChar = $this->_uniOrd($char);
0475             if (($ordChar > 0 && $ordChar < 32 && $char !== "\n") || $ordChar === 127) {
0476                 continue;
0477             }
0478 
0479             // Build the character
0480             // Note: The following code is complex and thoroughly tested.
0481             // Be careful when modifying!
0482             do {
0483                 $charNotAdded = false;
0484 
0485                 if ($wordBreakMode === -1) {
0486                     if ($char === ' ') {
0487                         break;
0488                     } else if ($char === "\n") {
0489                         $wordBreakMode = 0;
0490                         break;
0491                     }
0492 
0493                     $wordBreakMode = 0;
0494                 }
0495 
0496                 if ($char === "\n") {
0497                     $this->_appendLine();
0498                     $wordBreakMode = false;
0499                 } else if ($this->_addChar($char)) {
0500                     if ($char !== ' ') {
0501                         $wordBreakMode = ($wordBreakMode >= 2) ? 3: 1;
0502                     } else {
0503                         $wordBreakMode = ($wordBreakMode > 0) ? 2: 0;
0504                     }
0505                 } else if ($this->_outlineLength === 0) {
0506                     for ($i = 0; $i < $this->_charHeight; $i++) {
0507                         if ($this->_rightToLeft === 1 && $this->_outputWidth > 1) {
0508                             $offset = (strlen($this->_currentChar[$i]) - $this->_outlineLengthLimit);
0509                             $this->_putString(substr($this->_currentChar[$i], $offset));
0510                         } else {
0511                             $this->_putString($this->_currentChar[$i]);
0512                         }
0513                     }
0514 
0515                     $wordBreakMode = -1;
0516                 } else if ($char === ' ') {
0517                     if ($wordBreakMode === 2) {
0518                         $this->_splitLine();
0519                     } else {
0520                         $this->_appendLine();
0521                     }
0522 
0523                     $wordBreakMode = -1;
0524                 } else {
0525                     if ($wordBreakMode >= 2) {
0526                         $this->_splitLine();
0527                     } else {
0528                         $this->_appendLine();
0529                     }
0530 
0531                     $wordBreakMode = ($wordBreakMode === 3) ? 1 : 0;
0532                     $charNotAdded  = true;
0533                 }
0534             } while ($charNotAdded);
0535         }
0536 
0537         if ($this->_outlineLength !== 0) {
0538             $this->_appendLine();
0539         }
0540 
0541         return $this->_output;
0542     }
0543 
0544     /**
0545      * Puts the given string, substituting blanks for hardblanks. If outputWidth
0546      * is 1, puts the entire string; otherwise puts at most outputWidth - 1
0547      * characters. Puts a newline at the end of the string. The string is left-
0548      * justified, centered or right-justified (taking outputWidth as the screen
0549      * width) if justification is 0, 1 or 2 respectively.
0550      *
0551      * @param  string $string The string to add to the output
0552      * @return void
0553      */
0554     protected function _putString($string)
0555     {
0556         $length = strlen($string);
0557 
0558         if ($this->_outputWidth > 1) {
0559             if ($length > ($this->_outputWidth - 1)) {
0560                 $length = ($this->_outputWidth - 1);
0561             }
0562 
0563             if ($this->_justification > 0) {
0564                 for ($i = 1;
0565                      ((3 - $this->_justification) * $i + $length + $this->_justification - 2) < $this->_outputWidth;
0566                      $i++) {
0567                     $this->_output .= ' ';
0568                 }
0569             }
0570         }
0571 
0572         $this->_output .= str_replace($this->_hardBlank, ' ', $string) . "\n";
0573     }
0574 
0575     /**
0576      * Appends the current line to the output
0577      *
0578      * @return void
0579      */
0580     protected function _appendLine()
0581     {
0582         for ($i = 0; $i < $this->_charHeight; $i++) {
0583             $this->_putString($this->_outputLine[$i]);
0584         }
0585 
0586         $this->_clearLine();
0587     }
0588 
0589     /**
0590      * Splits inCharLine at the last word break (bunch of consecutive blanks).
0591      * Makes a new line out of the first part and appends it using appendLine().
0592      * Makes a new line out of the second part and returns.
0593      *
0594      * @return void
0595      */
0596     protected function _splitLine()
0597     {
0598         $gotSpace = false;
0599         for ($i = ($this->_inCharLineLength - 1); $i >= 0; $i--) {
0600             if (!$gotSpace && $this->_inCharLine[$i] === ' ') {
0601                 $gotSpace  = true;
0602                 $lastSpace = $i;
0603             }
0604 
0605             if ($gotSpace && $this->_inCharLine[$i] !== ' ') {
0606                 break;
0607             }
0608         }
0609 
0610         $firstLength = ($i + 1);
0611         $lastLength  = ($this->_inCharLineLength - $lastSpace - 1);
0612 
0613         $firstPart = '';
0614         for ($i = 0; $i < $firstLength; $i++) {
0615             $firstPart[$i] = $this->_inCharLine[$i];
0616         }
0617 
0618         $lastPart = '';
0619         for ($i = 0; $i < $lastLength; $i++) {
0620             $lastPart[$i] = $this->_inCharLine[($lastSpace + 1 + $i)];
0621         }
0622 
0623         $this->_clearLine();
0624 
0625         for ($i = 0; $i < $firstLength; $i++) {
0626             $this->_addChar($firstPart[$i]);
0627         }
0628 
0629         $this->_appendLine();
0630 
0631         for ($i = 0; $i < $lastLength; $i++) {
0632             $this->_addChar($lastPart[$i]);
0633         }
0634     }
0635 
0636     /**
0637      * Clears the current line
0638      *
0639      * @return void
0640      */
0641     protected function _clearLine()
0642     {
0643         for ($i = 0; $i < $this->_charHeight; $i++) {
0644             $this->_outputLine[$i] = '';
0645         }
0646 
0647         $this->_outlineLength    = 0;
0648         $this->_inCharLineLength = 0;
0649     }
0650 
0651     /**
0652      * Attempts to add the given character onto the end of the current line.
0653      * Returns true if this can be done, false otherwise.
0654      *
0655      * @param  string $char Character which to add to the output
0656      * @return boolean
0657      */
0658     protected function _addChar($char)
0659     {
0660         $this->_getLetter($char);
0661 
0662         if ($this->_currentChar === null) {
0663             return true;
0664         }
0665 
0666         $smushAmount = $this->_smushAmount();
0667 
0668         if (($this->_outlineLength + $this->_currentCharWidth - $smushAmount) > $this->_outlineLengthLimit
0669             || ($this->_inCharLineLength + 1) > $this->_inCharLineLengthLimit) {
0670             return false;
0671         }
0672 
0673         $tempLine = '';
0674         for ($row = 0; $row < $this->_charHeight; $row++) {
0675             if ($this->_rightToLeft === 1) {
0676                 $tempLine = $this->_currentChar[$row];
0677 
0678                 for ($k = 0; $k < $smushAmount; $k++) {
0679                     $position            = ($this->_currentCharWidth - $smushAmount + $k);
0680                     $tempLine[$position] = $this->_smushem($tempLine[$position], $this->_outputLine[$row][$k]);
0681                 }
0682 
0683                 $this->_outputLine[$row] = $tempLine . substr($this->_outputLine[$row], $smushAmount);
0684             } else {
0685                 for ($k = 0; $k < $smushAmount; $k++) {
0686                     if (($this->_outlineLength - $smushAmount + $k) < 0) {
0687                         continue;
0688                     }
0689 
0690                     $position = ($this->_outlineLength - $smushAmount + $k);
0691                     if (isset($this->_outputLine[$row][$position])) {
0692                         $leftChar = $this->_outputLine[$row][$position];
0693                     } else {
0694                         $leftChar = null;
0695                     }
0696 
0697                     $this->_outputLine[$row][$position] = $this->_smushem($leftChar, $this->_currentChar[$row][$k]);
0698                 }
0699 
0700                 $this->_outputLine[$row] .= substr($this->_currentChar[$row], $smushAmount);
0701             }
0702         }
0703 
0704         $this->_outlineLength                          = strlen($this->_outputLine[0]);
0705         $this->_inCharLine[$this->_inCharLineLength++] = $char;
0706 
0707         return true;
0708     }
0709 
0710     /**
0711      * Gets the requested character and sets current and previous char width.
0712      *
0713      * @param  string $char The character from which to get the letter of
0714      * @return void
0715      */
0716     protected function _getLetter($char)
0717     {
0718         if (array_key_exists($this->_uniOrd($char), $this->_charList)) {
0719             $this->_currentChar       = $this->_charList[$this->_uniOrd($char)];
0720             $this->_previousCharWidth = $this->_currentCharWidth;
0721             $this->_currentCharWidth  = strlen($this->_currentChar[0]);
0722         } else {
0723             $this->_currentChar = null;
0724         }
0725     }
0726 
0727     /**
0728      * Returns the maximum amount that the current character can be smushed into
0729      * the current line.
0730      *
0731      * @return integer
0732      */
0733     protected function _smushAmount()
0734     {
0735         if (($this->_smushMode & (self::SM_SMUSH | self::SM_KERN)) === 0) {
0736             return 0;
0737         }
0738 
0739         $maxSmush = $this->_currentCharWidth;
0740         $amount   = $maxSmush;
0741 
0742         for ($row = 0; $row < $this->_charHeight; $row++) {
0743             if ($this->_rightToLeft === 1) {
0744                 $charbd = strlen($this->_currentChar[$row]);
0745                 while (true) {
0746                     if (!isset($this->_currentChar[$row][$charbd])) {
0747                         $leftChar = null;
0748                     } else {
0749                         $leftChar = $this->_currentChar[$row][$charbd];
0750                     }
0751 
0752                     if ($charbd > 0 && ($leftChar === null || $leftChar == ' ')) {
0753                         $charbd--;
0754                     } else {
0755                         break;
0756                     }
0757                 }
0758 
0759                 $linebd = 0;
0760                 while (true) {
0761                     if (!isset($this->_outputLine[$row][$linebd])) {
0762                         $rightChar = null;
0763                     } else {
0764                         $rightChar = $this->_outputLine[$row][$linebd];
0765                     }
0766 
0767                     if ($rightChar === ' ') {
0768                         $linebd++;
0769                     } else {
0770                         break;
0771                     }
0772                 }
0773 
0774                 $amount = ($linebd + $this->_currentCharWidth - 1 - $charbd);
0775             } else {
0776                 $linebd = strlen($this->_outputLine[$row]);
0777                 while (true) {
0778                     if (!isset($this->_outputLine[$row][$linebd])) {
0779                         $leftChar = null;
0780                     } else {
0781                         $leftChar = $this->_outputLine[$row][$linebd];
0782                     }
0783 
0784                     if ($linebd > 0 && ($leftChar === null || $leftChar == ' ')) {
0785                         $linebd--;
0786                     } else {
0787                         break;
0788                     }
0789                 }
0790 
0791                 $charbd = 0;
0792                 while (true) {
0793                     if (!isset($this->_currentChar[$row][$charbd])) {
0794                         $rightChar = null;
0795                     } else {
0796                         $rightChar = $this->_currentChar[$row][$charbd];
0797                     }
0798 
0799                     if ($rightChar === ' ') {
0800                         $charbd++;
0801                     } else {
0802                         break;
0803                     }
0804                 }
0805 
0806                 $amount = ($charbd + $this->_outlineLength - 1 - $linebd);
0807             }
0808 
0809             if (empty($leftChar) || $leftChar === ' ') {
0810                 $amount++;
0811             } else if (!empty($rightChar)) {
0812                 if ($this->_smushem($leftChar, $rightChar) !== null) {
0813                     $amount++;
0814                 }
0815             }
0816 
0817             $maxSmush = min($amount, $maxSmush);
0818         }
0819 
0820         return $maxSmush;
0821     }
0822 
0823     /**
0824      * Given two characters, attempts to smush them into one, according to the
0825      * current smushmode. Returns smushed character or false if no smushing can
0826      * be done.
0827      *
0828      * Smushmode values are sum of following (all values smush blanks):
0829      *
0830      *  1: Smush equal chars (not hardblanks)
0831      *  2: Smush '_' with any char in hierarchy below
0832      *  4: hierarchy: "|", "/\", "[]", "{}", "()", "<>"
0833      *     Each class in hier. can be replaced by later class.
0834      *  8: [ + ] -> |, { + } -> |, ( + ) -> |
0835      * 16: / + \ -> X, > + < -> X (only in that order)
0836      * 32: hardblank + hardblank -> hardblank
0837      *
0838      * @param  string $leftChar  Left character to smush
0839      * @param  string $rightChar Right character to smush
0840      * @return string
0841      */
0842     protected function _smushem($leftChar, $rightChar)
0843     {
0844         if ($leftChar === ' ') {
0845             return $rightChar;
0846         }
0847 
0848         if ($rightChar === ' ') {
0849             return $leftChar;
0850         }
0851 
0852         if ($this->_previousCharWidth < 2 || $this->_currentCharWidth < 2) {
0853             // Disallows overlapping if the previous character or the current
0854             // character has a width of one or zero.
0855             return null;
0856         }
0857 
0858         if (($this->_smushMode & self::SM_SMUSH) === 0) {
0859             // Kerning
0860             return null;
0861         }
0862 
0863         if (($this->_smushMode & 63) === 0) {
0864             // This is smushing by universal overlapping
0865             if ($leftChar === ' ') {
0866                 return $rightChar;
0867             } else if ($rightChar === ' ') {
0868                 return $leftChar;
0869             } else if ($leftChar === $this->_hardBlank) {
0870                 return $rightChar;
0871             } else if ($rightChar === $this->_hardBlank) {
0872                 return $rightChar;
0873             } else if ($this->_rightToLeft === 1) {
0874                 return $leftChar;
0875             } else {
0876                 // Occurs in the absence of above exceptions
0877                 return $rightChar;
0878             }
0879         }
0880 
0881         if (($this->_smushMode & self::SM_HARDBLANK) > 0) {
0882             if ($leftChar === $this->_hardBlank && $rightChar === $this->_hardBlank) {
0883                 return $leftChar;
0884             }
0885         }
0886 
0887         if ($leftChar === $this->_hardBlank && $rightChar === $this->_hardBlank) {
0888             return null;
0889         }
0890 
0891         if (($this->_smushMode & self::SM_EQUAL) > 0) {
0892             if ($leftChar === $rightChar) {
0893                 return $leftChar;
0894             }
0895         }
0896 
0897         if (($this->_smushMode & self::SM_LOWLINE) > 0) {
0898             if ($leftChar === '_' && strchr('|/\\[]{}()<>', $rightChar) !== false) {
0899                 return $rightChar;
0900             } else if ($rightChar === '_' && strchr('|/\\[]{}()<>', $leftChar) !== false) {
0901                 return $leftChar;
0902             }
0903         }
0904 
0905         if (($this->_smushMode & self::SM_HIERARCHY) > 0) {
0906             if ($leftChar === '|' && strchr('/\\[]{}()<>', $rightChar) !== false) {
0907                 return $rightChar;
0908             } else if ($rightChar === '|' && strchr('/\\[]{}()<>', $leftChar) !== false) {
0909                 return $leftChar;
0910             } else if (strchr('/\\', $leftChar) && strchr('[]{}()<>', $rightChar) !== false) {
0911                 return $rightChar;
0912             } else if (strchr('/\\', $rightChar) && strchr('[]{}()<>', $leftChar) !== false) {
0913                 return $leftChar;
0914             } else if (strchr('[]', $leftChar) && strchr('{}()<>', $rightChar) !== false) {
0915                 return $rightChar;
0916             } else if (strchr('[]', $rightChar) && strchr('{}()<>', $leftChar) !== false) {
0917                 return $leftChar;
0918             } else if (strchr('{}', $leftChar) && strchr('()<>', $rightChar) !== false) {
0919                 return $rightChar;
0920             } else if (strchr('{}', $rightChar) && strchr('()<>', $leftChar) !== false) {
0921                 return $leftChar;
0922             } else if (strchr('()', $leftChar) && strchr('<>', $rightChar) !== false) {
0923                 return $rightChar;
0924             } else if (strchr('()', $rightChar) && strchr('<>', $leftChar) !== false) {
0925                 return $leftChar;
0926             }
0927         }
0928 
0929         if (($this->_smushMode & self::SM_PAIR) > 0) {
0930             if ($leftChar === '[' && $rightChar === ']') {
0931                 return '|';
0932             } else if ($rightChar === '[' && $leftChar === ']') {
0933                 return '|';
0934             } else if ($leftChar === '{' && $rightChar === '}') {
0935                 return '|';
0936             } else if ($rightChar === '{' && $leftChar === '}') {
0937                 return '|';
0938             } else if ($leftChar === '(' && $rightChar === ')') {
0939                 return '|';
0940             } else if ($rightChar === '(' && $leftChar === ')') {
0941                 return '|';
0942             }
0943         }
0944 
0945         if (($this->_smushMode & self::SM_BIGX) > 0) {
0946             if ($leftChar === '/' && $rightChar === '\\') {
0947                 return '|';
0948             } else if ($rightChar === '/' && $leftChar === '\\') {
0949                 return 'Y';
0950             } else if ($leftChar === '>' && $rightChar === '<') {
0951                 return 'X';
0952             }
0953         }
0954 
0955         return null;
0956     }
0957 
0958     /**
0959      * Load the specified font
0960      *
0961      * @param  string $fontFile Font file to load
0962      * @throws Zend_Text_Figlet_Exception When font file was not found
0963      * @throws Zend_Text_Figlet_Exception When GZIP library is required but not found
0964      * @throws Zend_Text_Figlet_Exception When font file is not readable
0965      * @return void
0966      */
0967     protected function _loadFont($fontFile)
0968     {
0969         // Check if the font file exists
0970         if (!file_exists($fontFile)) {
0971             // require_once 'Zend/Text/Figlet/Exception.php';
0972             throw new Zend_Text_Figlet_Exception($fontFile . ': Font file not found');
0973         }
0974 
0975         // Check if gzip support is required
0976         if (substr($fontFile, -3) === '.gz') {
0977             if (!function_exists('gzcompress')) {
0978                 // require_once 'Zend/Text/Figlet/Exception.php';
0979                 throw new Zend_Text_Figlet_Exception('GZIP library is required for '
0980                                                      . 'gzip compressed font files');
0981             }
0982 
0983             $fontFile   = 'compress.zlib://' . $fontFile;
0984             $compressed = true;
0985         } else {
0986             $compressed = false;
0987         }
0988 
0989         // Try to open the file
0990         $fp = fopen($fontFile, 'rb');
0991         if ($fp === false) {
0992             // require_once 'Zend/Text/Figlet/Exception.php';
0993             throw new Zend_Text_Figlet_Exception($fontFile . ': Could not open file');
0994         }
0995 
0996         // If the file is not compressed, lock the stream
0997         if (!$compressed) {
0998             flock($fp, LOCK_SH);
0999         }
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 }