PageRenderTime 144ms CodeModel.GetById 53ms app.highlight 57ms RepoModel.GetById 23ms app.codeStats 1ms

/library/Zend/Text/Figlet.php

https://bitbucket.org/fabiancarlos/feature_seguimentos
PHP | 1232 lines | 668 code | 171 blank | 393 comment | 247 complexity | 76b110f2fa58b653dc93005d595ae02e MD5 | raw file
   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-2011 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 23775 2011-03-01 17:25:24Z ralph $
  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-2011 Zend Technologies USA Inc. (http://www.zend.com)
  28 * @license   http://framework.zend.com/license/new-bsd     New BSD License
  29 */
  30class 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}