web/enmi/Zend/Text/Figlet.php
changeset 19 1c2f13fd785c
parent 0 4eba9c11703f
equal deleted inserted replaced
18:bd595ad770fc 19:1c2f13fd785c
       
     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 }