mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-03-01 15:21:02 +03:00
1209 lines
34 KiB
PHP
1209 lines
34 KiB
PHP
<?php
|
|
/**
|
|
* Zend Framework (http://framework.zend.com/)
|
|
*
|
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
|
*/
|
|
|
|
namespace Zend\Text\Figlet;
|
|
|
|
use Traversable;
|
|
use Zend\Stdlib\ArrayUtils;
|
|
use Zend\Stdlib\StringUtils;
|
|
|
|
/**
|
|
* Zend\Text\Figlet is a PHP implementation of FIGlet
|
|
*/
|
|
class Figlet
|
|
{
|
|
/**
|
|
* Smush2 layout modes
|
|
*/
|
|
const SM_EQUAL = 0x01;
|
|
const SM_LOWLINE = 0x02;
|
|
const SM_HIERARCHY = 0x04;
|
|
const SM_PAIR = 0x08;
|
|
const SM_BIGX = 0x10;
|
|
const SM_HARDBLANK = 0x20;
|
|
const SM_KERN = 0x40;
|
|
const SM_SMUSH = 0x80;
|
|
|
|
/**
|
|
* Smush mode override modes
|
|
*/
|
|
const SMO_NO = 0;
|
|
const SMO_YES = 1;
|
|
const SMO_FORCE = 2;
|
|
|
|
/**
|
|
* Justifications
|
|
*/
|
|
const JUSTIFICATION_LEFT = 0;
|
|
const JUSTIFICATION_CENTER = 1;
|
|
const JUSTIFICATION_RIGHT = 2;
|
|
|
|
/**
|
|
* Write directions
|
|
*/
|
|
const DIRECTION_LEFT_TO_RIGHT = 0;
|
|
const DIRECTION_RIGHT_TO_LEFT = 1;
|
|
|
|
/**
|
|
* Magic fontfile number
|
|
*/
|
|
const FONTFILE_MAGIC_NUMBER = 'flf2';
|
|
|
|
/**
|
|
* Array containing all characters of the current font
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $charList = array();
|
|
|
|
/**
|
|
* Indicates if a font was loaded yet
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $fontLoaded = false;
|
|
|
|
/**
|
|
* Latin-1 codes for German letters, respectively:
|
|
*
|
|
* LATIN CAPITAL LETTER A WITH DIAERESIS = A-umlaut
|
|
* LATIN CAPITAL LETTER O WITH DIAERESIS = O-umlaut
|
|
* LATIN CAPITAL LETTER U WITH DIAERESIS = U-umlaut
|
|
* LATIN SMALL LETTER A WITH DIAERESIS = a-umlaut
|
|
* LATIN SMALL LETTER O WITH DIAERESIS = o-umlaut
|
|
* LATIN SMALL LETTER U WITH DIAERESIS = u-umlaut
|
|
* LATIN SMALL LETTER SHARP S = ess-zed
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $germanChars = array(196, 214, 220, 228, 246, 252, 223);
|
|
|
|
/**
|
|
* Output width, defaults to 80.
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $outputWidth = 80;
|
|
|
|
/**
|
|
* Hard blank character
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $hardBlank;
|
|
|
|
/**
|
|
* Height of the characters
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $charHeight;
|
|
|
|
/**
|
|
* Max length of any character
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $maxLength;
|
|
|
|
/**
|
|
* Smush mode
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $smushMode = 0;
|
|
|
|
/**
|
|
* Smush defined by the font
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $fontSmush = 0;
|
|
|
|
/**
|
|
* Smush defined by the user
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $userSmush = 0;
|
|
|
|
/**
|
|
* Whether to handle paragraphs || not
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $handleParagraphs = false;
|
|
|
|
/**
|
|
* Justification for the text, according to $outputWidth
|
|
*
|
|
* For using font default, this parameter should be null, else one of
|
|
* the values of Zend\Text\Figlet::JUSTIFICATION_*
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $justification = null;
|
|
|
|
/**
|
|
* Direction of text-writing, namely right to left
|
|
*
|
|
* For using font default, this parameter should be null, else one of
|
|
* the values of Zend\Text\Figlet::DIRECTION_*
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $rightToLeft = null;
|
|
|
|
/**
|
|
* Override font file smush layout
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $smushOverride = 0;
|
|
|
|
/**
|
|
* Options of the current font
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $fontOptions = array();
|
|
|
|
/**
|
|
* Previous character width
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $previousCharWidth = 0;
|
|
|
|
/**
|
|
* Current character width
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $currentCharWidth = 0;
|
|
|
|
/**
|
|
* Current outline length
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $outlineLength = 0;
|
|
|
|
/**
|
|
* Maximum outline length
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $outlineLengthLimit = 0;
|
|
|
|
/**
|
|
* In character line
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $inCharLine;
|
|
|
|
/**
|
|
* In character line length
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $inCharLineLength = 0;
|
|
|
|
/**
|
|
* Maximum in character line length
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $inCharLineLengthLimit = 0;
|
|
|
|
/**
|
|
* Current char
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $currentChar = null;
|
|
|
|
/**
|
|
* Current output line
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $outputLine;
|
|
|
|
/**
|
|
* Current output
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $output;
|
|
|
|
/**
|
|
* Option keys to skip when calling setOptions()
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $skipOptions = array(
|
|
'options',
|
|
'config',
|
|
);
|
|
|
|
/**
|
|
* Instantiate the FIGlet with a specific font. If no font is given, the
|
|
* standard font is used. You can also supply multiple options via
|
|
* the $options variable, which can either be an array or an instance of
|
|
* Zend\Config\Config.
|
|
*
|
|
* @param array|Traversable $options Options for the output
|
|
*/
|
|
public function __construct($options = null)
|
|
{
|
|
// Set options
|
|
if ($options instanceof Traversable) {
|
|
$options = ArrayUtils::iteratorToArray($options);
|
|
}
|
|
if (is_array($options)) {
|
|
$this->setOptions($options);
|
|
}
|
|
|
|
// If no font was defined, load default font
|
|
if (!$this->fontLoaded) {
|
|
$this->_loadFont(__DIR__ . '/zend-framework.flf');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set options from array
|
|
*
|
|
* @param array $options Configuration for Figlet
|
|
* @return Figlet
|
|
*/
|
|
public function setOptions(array $options)
|
|
{
|
|
foreach ($options as $key => $value) {
|
|
if (in_array(strtolower($key), $this->skipOptions)) {
|
|
continue;
|
|
}
|
|
|
|
$method = 'set' . ucfirst($key);
|
|
if (method_exists($this, $method)) {
|
|
$this->$method($value);
|
|
}
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set a font to use
|
|
*
|
|
* @param string $font Path to the font
|
|
* @return Figlet
|
|
*/
|
|
public function setFont($font)
|
|
{
|
|
$this->_loadFont($font);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set handling of paragraphs
|
|
*
|
|
* @param bool $handleParagraphs Whether to handle paragraphs or not
|
|
* @return Figlet
|
|
*/
|
|
public function setHandleParagraphs($handleParagraphs)
|
|
{
|
|
$this->handleParagraphs = (bool) $handleParagraphs;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the justification. 0 stands for left aligned, 1 for centered and 2
|
|
* for right aligned.
|
|
*
|
|
* @param int $justification Justification of the output text
|
|
* @return Figlet
|
|
*/
|
|
public function setJustification($justification)
|
|
{
|
|
$this->justification = min(3, max(0, (int) $justification));
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the output width
|
|
*
|
|
* @param int $outputWidth Output with which should be used for word
|
|
* wrapping and justification
|
|
* @return Figlet
|
|
*/
|
|
public function setOutputWidth($outputWidth)
|
|
{
|
|
$this->outputWidth = max(1, (int) $outputWidth);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set right to left mode. For writing from left to right, use
|
|
* Zend\Text\Figlet::DIRECTION_LEFT_TO_RIGHT. For writing from right to left,
|
|
* use Zend\Text\Figlet::DIRECTION_RIGHT_TO_LEFT.
|
|
*
|
|
* @param int $rightToLeft Right-to-left mode
|
|
* @return Figlet
|
|
*/
|
|
public function setRightToLeft($rightToLeft)
|
|
{
|
|
$this->rightToLeft = min(1, max(0, (int) $rightToLeft));
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Set the smush mode.
|
|
*
|
|
* Use one of the constants of Zend\Text\Figlet::SM_*, you may combine them.
|
|
*
|
|
* @param int $smushMode Smush mode to use for generating text
|
|
* @return Figlet
|
|
*/
|
|
public function setSmushMode($smushMode)
|
|
{
|
|
$smushMode = (int) $smushMode;
|
|
|
|
if ($smushMode < -1) {
|
|
$this->smushOverride = self::SMO_NO;
|
|
} else {
|
|
if ($smushMode === 0) {
|
|
$this->userSmush = self::SM_KERN;
|
|
} elseif ($smushMode === -1) {
|
|
$this->userSmush = 0;
|
|
} else {
|
|
$this->userSmush = (($smushMode & 63) | self::SM_SMUSH);
|
|
}
|
|
|
|
$this->smushOverride = self::SMO_YES;
|
|
}
|
|
|
|
$this->_setUsedSmush();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Render a FIGlet text
|
|
*
|
|
* @param string $text Text to convert to a figlet text
|
|
* @param string $encoding Encoding of the input string
|
|
* @throws Exception\InvalidArgumentException When $text is not a string
|
|
* @throws Exception\UnexpectedValueException When $text it not properly encoded
|
|
* @return string
|
|
*/
|
|
public function render($text, $encoding = 'UTF-8')
|
|
{
|
|
if (!is_string($text)) {
|
|
throw new Exception\InvalidArgumentException('$text must be a string');
|
|
}
|
|
|
|
// Get the string wrapper supporting UTF-8 character encoding and the input encoding
|
|
$strWrapper = StringUtils::getWrapper($encoding, 'UTF-8');
|
|
|
|
// Convert $text to UTF-8 and check encoding
|
|
$text = $strWrapper->convert($text);
|
|
if (!StringUtils::isValidUtf8($text)) {
|
|
throw new Exception\UnexpectedValueException('$text is not encoded with ' . $encoding);
|
|
}
|
|
|
|
$strWrapper = StringUtils::getWrapper('UTF-8');
|
|
|
|
$this->output = '';
|
|
$this->outputLine = array();
|
|
|
|
$this->_clearLine();
|
|
|
|
$this->outlineLengthLimit = ($this->outputWidth - 1);
|
|
$this->inCharLineLengthLimit = ($this->outputWidth * 4 + 100);
|
|
|
|
$wordBreakMode = 0;
|
|
$lastCharWasEol = false;
|
|
$textLength = $strWrapper->strlen($text);
|
|
|
|
for ($charNum = 0; $charNum < $textLength; $charNum++) {
|
|
// Handle paragraphs
|
|
$char = $strWrapper->substr($text, $charNum, 1);
|
|
|
|
if ($char === "\n" && $this->handleParagraphs && !$lastCharWasEol) {
|
|
$nextChar = $strWrapper->substr($text, ($charNum + 1), 1);
|
|
if (!$nextChar) {
|
|
$nextChar = null;
|
|
}
|
|
|
|
$char = (ctype_space($nextChar)) ? "\n" : ' ';
|
|
}
|
|
|
|
$lastCharWasEol = (ctype_space($char) && $char !== "\t" && $char !== ' ');
|
|
|
|
if (ctype_space($char)) {
|
|
$char = ($char === "\t" || $char === ' ') ? ' ': "\n";
|
|
}
|
|
|
|
// Skip unprintable characters
|
|
$ordChar = $this->_uniOrd($char);
|
|
if (($ordChar > 0 && $ordChar < 32 && $char !== "\n") || $ordChar === 127) {
|
|
continue;
|
|
}
|
|
|
|
// Build the character
|
|
// Note: The following code is complex and thoroughly tested.
|
|
// Be careful when modifying!
|
|
do {
|
|
$charNotAdded = false;
|
|
|
|
if ($wordBreakMode === -1) {
|
|
if ($char === ' ') {
|
|
break;
|
|
} elseif ($char === "\n") {
|
|
$wordBreakMode = 0;
|
|
break;
|
|
}
|
|
|
|
$wordBreakMode = 0;
|
|
}
|
|
|
|
if ($char === "\n") {
|
|
$this->_appendLine();
|
|
$wordBreakMode = false;
|
|
} elseif ($this->_addChar($char)) {
|
|
if ($char !== ' ') {
|
|
$wordBreakMode = ($wordBreakMode >= 2) ? 3: 1;
|
|
} else {
|
|
$wordBreakMode = ($wordBreakMode > 0) ? 2: 0;
|
|
}
|
|
} elseif ($this->outlineLength === 0) {
|
|
for ($i = 0; $i < $this->charHeight; $i++) {
|
|
if ($this->rightToLeft === 1 && $this->outputWidth > 1) {
|
|
$offset = (strlen($this->currentChar[$i]) - $this->outlineLengthLimit);
|
|
$this->_putString(substr($this->currentChar[$i], $offset));
|
|
} else {
|
|
$this->_putString($this->currentChar[$i]);
|
|
}
|
|
}
|
|
|
|
$wordBreakMode = -1;
|
|
} elseif ($char === ' ') {
|
|
if ($wordBreakMode === 2) {
|
|
$this->_splitLine();
|
|
} else {
|
|
$this->_appendLine();
|
|
}
|
|
|
|
$wordBreakMode = -1;
|
|
} else {
|
|
if ($wordBreakMode >= 2) {
|
|
$this->_splitLine();
|
|
} else {
|
|
$this->_appendLine();
|
|
}
|
|
|
|
$wordBreakMode = ($wordBreakMode === 3) ? 1 : 0;
|
|
$charNotAdded = true;
|
|
}
|
|
} while ($charNotAdded);
|
|
}
|
|
|
|
if ($this->outlineLength !== 0) {
|
|
$this->_appendLine();
|
|
}
|
|
|
|
return $this->output;
|
|
}
|
|
|
|
/**
|
|
* Puts the given string, substituting blanks for hardblanks. If outputWidth
|
|
* is 1, puts the entire string; otherwise puts at most outputWidth - 1
|
|
* characters. Puts a newline at the end of the string. The string is left-
|
|
* justified, centered or right-justified (taking outputWidth as the screen
|
|
* width) if justification is 0, 1 or 2 respectively.
|
|
*
|
|
* @param string $string The string to add to the output
|
|
* @return void
|
|
*/
|
|
protected function _putString($string)
|
|
{
|
|
$length = strlen($string);
|
|
|
|
if ($this->outputWidth > 1) {
|
|
if ($length > ($this->outputWidth - 1)) {
|
|
$length = ($this->outputWidth - 1);
|
|
}
|
|
|
|
if ($this->justification > 0) {
|
|
for ($i = 1;
|
|
((3 - $this->justification) * $i + $length + $this->justification - 2) < $this->outputWidth;
|
|
$i++) {
|
|
$this->output .= ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->output .= str_replace($this->hardBlank, ' ', $string) . "\n";
|
|
}
|
|
|
|
/**
|
|
* Appends the current line to the output
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function _appendLine()
|
|
{
|
|
for ($i = 0; $i < $this->charHeight; $i++) {
|
|
$this->_putString($this->outputLine[$i]);
|
|
}
|
|
|
|
$this->_clearLine();
|
|
}
|
|
|
|
/**
|
|
* Splits inCharLine at the last word break (bunch of consecutive blanks).
|
|
* Makes a new line out of the first part and appends it using appendLine().
|
|
* Makes a new line out of the second part and returns.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function _splitLine()
|
|
{
|
|
$gotSpace = false;
|
|
for ($i = ($this->inCharLineLength - 1); $i >= 0; $i--) {
|
|
if (!$gotSpace && $this->inCharLine[$i] === ' ') {
|
|
$gotSpace = true;
|
|
$lastSpace = $i;
|
|
}
|
|
|
|
if ($gotSpace && $this->inCharLine[$i] !== ' ') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$firstLength = ($i + 1);
|
|
$lastLength = ($this->inCharLineLength - $lastSpace - 1);
|
|
|
|
$firstPart = '';
|
|
for ($i = 0; $i < $firstLength; $i++) {
|
|
$firstPart[$i] = $this->inCharLine[$i];
|
|
}
|
|
|
|
$lastPart = '';
|
|
for ($i = 0; $i < $lastLength; $i++) {
|
|
$lastPart[$i] = $this->inCharLine[($lastSpace + 1 + $i)];
|
|
}
|
|
|
|
$this->_clearLine();
|
|
|
|
for ($i = 0; $i < $firstLength; $i++) {
|
|
$this->_addChar($firstPart[$i]);
|
|
}
|
|
|
|
$this->_appendLine();
|
|
|
|
for ($i = 0; $i < $lastLength; $i++) {
|
|
$this->_addChar($lastPart[$i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears the current line
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function _clearLine()
|
|
{
|
|
for ($i = 0; $i < $this->charHeight; $i++) {
|
|
$this->outputLine[$i] = '';
|
|
}
|
|
|
|
$this->outlineLength = 0;
|
|
$this->inCharLineLength = 0;
|
|
}
|
|
|
|
/**
|
|
* Attempts to add the given character onto the end of the current line.
|
|
* Returns true if this can be done, false otherwise.
|
|
*
|
|
* @param string $char Character which to add to the output
|
|
* @return bool
|
|
*/
|
|
protected function _addChar($char)
|
|
{
|
|
$this->_getLetter($char);
|
|
|
|
if ($this->currentChar === null) {
|
|
return true;
|
|
}
|
|
|
|
$smushAmount = $this->_smushAmount();
|
|
|
|
if (($this->outlineLength + $this->currentCharWidth - $smushAmount) > $this->outlineLengthLimit
|
|
|| ($this->inCharLineLength + 1) > $this->inCharLineLengthLimit) {
|
|
return false;
|
|
}
|
|
|
|
for ($row = 0; $row < $this->charHeight; $row++) {
|
|
if ($this->rightToLeft === 1) {
|
|
$tempLine = $this->currentChar[$row];
|
|
|
|
for ($k = 0; $k < $smushAmount; $k++) {
|
|
$position = ($this->currentCharWidth - $smushAmount + $k);
|
|
$tempLine[$position] = $this->_smushem($tempLine[$position], $this->outputLine[$row][$k]);
|
|
}
|
|
|
|
$this->outputLine[$row] = $tempLine . substr($this->outputLine[$row], $smushAmount);
|
|
} else {
|
|
for ($k = 0; $k < $smushAmount; $k++) {
|
|
if (($this->outlineLength - $smushAmount + $k) < 0) {
|
|
continue;
|
|
}
|
|
|
|
$position = ($this->outlineLength - $smushAmount + $k);
|
|
if (isset($this->outputLine[$row][$position])) {
|
|
$leftChar = $this->outputLine[$row][$position];
|
|
} else {
|
|
$leftChar = null;
|
|
}
|
|
|
|
$this->outputLine[$row][$position] = $this->_smushem($leftChar, $this->currentChar[$row][$k]);
|
|
}
|
|
|
|
$this->outputLine[$row] .= substr($this->currentChar[$row], $smushAmount);
|
|
}
|
|
}
|
|
|
|
$this->outlineLength = strlen($this->outputLine[0]);
|
|
$this->inCharLine[$this->inCharLineLength++] = $char;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Gets the requested character and sets current and previous char width.
|
|
*
|
|
* @param string $char The character from which to get the letter of
|
|
* @return void
|
|
*/
|
|
protected function _getLetter($char)
|
|
{
|
|
if (array_key_exists($this->_uniOrd($char), $this->charList)) {
|
|
$this->currentChar = $this->charList[$this->_uniOrd($char)];
|
|
$this->previousCharWidth = $this->currentCharWidth;
|
|
$this->currentCharWidth = strlen($this->currentChar[0]);
|
|
} else {
|
|
$this->currentChar = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum amount that the current character can be smushed into
|
|
* the current line.
|
|
*
|
|
* @return int
|
|
*/
|
|
protected function _smushAmount()
|
|
{
|
|
if (($this->smushMode & (self::SM_SMUSH | self::SM_KERN)) === 0) {
|
|
return 0;
|
|
}
|
|
|
|
$maxSmush = $this->currentCharWidth;
|
|
|
|
for ($row = 0; $row < $this->charHeight; $row++) {
|
|
if ($this->rightToLeft === 1) {
|
|
$charbd = strlen($this->currentChar[$row]);
|
|
while (true) {
|
|
if (!isset($this->currentChar[$row][$charbd])) {
|
|
$leftChar = null;
|
|
} else {
|
|
$leftChar = $this->currentChar[$row][$charbd];
|
|
}
|
|
|
|
if ($charbd > 0 && ($leftChar === null || $leftChar == ' ')) {
|
|
$charbd--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$linebd = 0;
|
|
while (true) {
|
|
if (!isset($this->outputLine[$row][$linebd])) {
|
|
$rightChar = null;
|
|
} else {
|
|
$rightChar = $this->outputLine[$row][$linebd];
|
|
}
|
|
|
|
if ($rightChar === ' ') {
|
|
$linebd++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$amount = ($linebd + $this->currentCharWidth - 1 - $charbd);
|
|
} else {
|
|
$linebd = strlen($this->outputLine[$row]);
|
|
while (true) {
|
|
if (!isset($this->outputLine[$row][$linebd])) {
|
|
$leftChar = null;
|
|
} else {
|
|
$leftChar = $this->outputLine[$row][$linebd];
|
|
}
|
|
|
|
if ($linebd > 0 && ($leftChar === null || $leftChar == ' ')) {
|
|
$linebd--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$charbd = 0;
|
|
while (true) {
|
|
if (!isset($this->currentChar[$row][$charbd])) {
|
|
$rightChar = null;
|
|
} else {
|
|
$rightChar = $this->currentChar[$row][$charbd];
|
|
}
|
|
|
|
if ($rightChar === ' ') {
|
|
$charbd++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
$amount = ($charbd + $this->outlineLength - 1 - $linebd);
|
|
}
|
|
|
|
if (empty($leftChar) || $leftChar === ' ') {
|
|
$amount++;
|
|
} elseif (!empty($rightChar)) {
|
|
if ($this->_smushem($leftChar, $rightChar) !== null) {
|
|
$amount++;
|
|
}
|
|
}
|
|
|
|
$maxSmush = min($amount, $maxSmush);
|
|
}
|
|
|
|
return $maxSmush;
|
|
}
|
|
|
|
/**
|
|
* Given two characters, attempts to smush them into one, according to the
|
|
* current smushmode. Returns smushed character or false if no smushing can
|
|
* be done.
|
|
*
|
|
* Smushmode values are sum of following (all values smush blanks):
|
|
*
|
|
* 1: Smush equal chars (not hardblanks)
|
|
* 2: Smush '_' with any char in hierarchy below
|
|
* 4: hierarchy: "|", "/\", "[]", "{}", "()", "<>"
|
|
* Each class in hier. can be replaced by later class.
|
|
* 8: [ + ] -> |, { + } -> |, ( + ) -> |
|
|
* 16: / + \ -> X, > + < -> X (only in that order)
|
|
* 32: hardblank + hardblank -> hardblank
|
|
*
|
|
* @param string $leftChar Left character to smush
|
|
* @param string $rightChar Right character to smush
|
|
* @return string
|
|
*/
|
|
protected function _smushem($leftChar, $rightChar)
|
|
{
|
|
if ($leftChar === ' ') {
|
|
return $rightChar;
|
|
}
|
|
|
|
if ($rightChar === ' ') {
|
|
return $leftChar;
|
|
}
|
|
|
|
if ($this->previousCharWidth < 2 || $this->currentCharWidth < 2) {
|
|
// Disallows overlapping if the previous character or the current
|
|
// character has a width of one or zero.
|
|
return null;
|
|
}
|
|
|
|
if (($this->smushMode & self::SM_SMUSH) === 0) {
|
|
// Kerning
|
|
return null;
|
|
}
|
|
|
|
if (($this->smushMode & 63) === 0) {
|
|
// This is smushing by universal overlapping
|
|
if ($leftChar === ' ') {
|
|
return $rightChar;
|
|
} elseif ($rightChar === ' ') {
|
|
return $leftChar;
|
|
} elseif ($leftChar === $this->hardBlank) {
|
|
return $rightChar;
|
|
} elseif ($rightChar === $this->hardBlank) {
|
|
return $rightChar;
|
|
} elseif ($this->rightToLeft === 1) {
|
|
return $leftChar;
|
|
} else {
|
|
// Occurs in the absence of above exceptions
|
|
return $rightChar;
|
|
}
|
|
}
|
|
|
|
if (($this->smushMode & self::SM_HARDBLANK) > 0) {
|
|
if ($leftChar === $this->hardBlank && $rightChar === $this->hardBlank) {
|
|
return $leftChar;
|
|
}
|
|
}
|
|
|
|
if ($leftChar === $this->hardBlank && $rightChar === $this->hardBlank) {
|
|
return null;
|
|
}
|
|
|
|
if (($this->smushMode & self::SM_EQUAL) > 0) {
|
|
if ($leftChar === $rightChar) {
|
|
return $leftChar;
|
|
}
|
|
}
|
|
|
|
if (($this->smushMode & self::SM_LOWLINE) > 0) {
|
|
if ($leftChar === '_' && strchr('|/\\[]{}()<>', $rightChar) !== false) {
|
|
return $rightChar;
|
|
} elseif ($rightChar === '_' && strchr('|/\\[]{}()<>', $leftChar) !== false) {
|
|
return $leftChar;
|
|
}
|
|
}
|
|
|
|
if (($this->smushMode & self::SM_HIERARCHY) > 0) {
|
|
if ($leftChar === '|' && strchr('/\\[]{}()<>', $rightChar) !== false) {
|
|
return $rightChar;
|
|
} elseif ($rightChar === '|' && strchr('/\\[]{}()<>', $leftChar) !== false) {
|
|
return $leftChar;
|
|
} elseif (strchr('/\\', $leftChar) && strchr('[]{}()<>', $rightChar) !== false) {
|
|
return $rightChar;
|
|
} elseif (strchr('/\\', $rightChar) && strchr('[]{}()<>', $leftChar) !== false) {
|
|
return $leftChar;
|
|
} elseif (strchr('[]', $leftChar) && strchr('{}()<>', $rightChar) !== false) {
|
|
return $rightChar;
|
|
} elseif (strchr('[]', $rightChar) && strchr('{}()<>', $leftChar) !== false) {
|
|
return $leftChar;
|
|
} elseif (strchr('{}', $leftChar) && strchr('()<>', $rightChar) !== false) {
|
|
return $rightChar;
|
|
} elseif (strchr('{}', $rightChar) && strchr('()<>', $leftChar) !== false) {
|
|
return $leftChar;
|
|
} elseif (strchr('()', $leftChar) && strchr('<>', $rightChar) !== false) {
|
|
return $rightChar;
|
|
} elseif (strchr('()', $rightChar) && strchr('<>', $leftChar) !== false) {
|
|
return $leftChar;
|
|
}
|
|
}
|
|
|
|
if (($this->smushMode & self::SM_PAIR) > 0) {
|
|
if ($leftChar === '[' && $rightChar === ']') {
|
|
return '|';
|
|
} elseif ($rightChar === '[' && $leftChar === ']') {
|
|
return '|';
|
|
} elseif ($leftChar === '{' && $rightChar === '}') {
|
|
return '|';
|
|
} elseif ($rightChar === '{' && $leftChar === '}') {
|
|
return '|';
|
|
} elseif ($leftChar === '(' && $rightChar === ')') {
|
|
return '|';
|
|
} elseif ($rightChar === '(' && $leftChar === ')') {
|
|
return '|';
|
|
}
|
|
}
|
|
|
|
if (($this->smushMode & self::SM_BIGX) > 0) {
|
|
if ($leftChar === '/' && $rightChar === '\\') {
|
|
return '|';
|
|
} elseif ($rightChar === '/' && $leftChar === '\\') {
|
|
return 'Y';
|
|
} elseif ($leftChar === '>' && $rightChar === '<') {
|
|
return 'X';
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Load the specified font
|
|
*
|
|
* @param string $fontFile Font file to load
|
|
* @throws Exception\RuntimeException When font file was not found
|
|
* @throws Exception\RuntimeException When GZIP library is required but not found
|
|
* @throws Exception\RuntimeException When font file is not readable
|
|
* @throws Exception\UnexpectedValueException When font file is not a FIGlet 2 font file
|
|
* @return void
|
|
*/
|
|
protected function _loadFont($fontFile)
|
|
{
|
|
// Check if the font file exists
|
|
if (!file_exists($fontFile)) {
|
|
throw new Exception\RuntimeException($fontFile . ': Font file not found');
|
|
}
|
|
|
|
// Check if gzip support is required
|
|
if (substr($fontFile, -3) === '.gz') {
|
|
if (!function_exists('gzcompress')) {
|
|
throw new Exception\RuntimeException('GZIP library is required for '
|
|
. 'gzip compressed font files');
|
|
}
|
|
|
|
$fontFile = 'compress.zlib://' . $fontFile;
|
|
$compressed = true;
|
|
} else {
|
|
$compressed = false;
|
|
}
|
|
|
|
// Try to open the file
|
|
$fp = fopen($fontFile, 'rb');
|
|
if ($fp === false) {
|
|
throw new Exception\RuntimeException($fontFile . ': Could not open file');
|
|
}
|
|
|
|
// If the file is not compressed, lock the stream
|
|
if (!$compressed) {
|
|
flock($fp, LOCK_SH);
|
|
}
|
|
|
|
// Get magic
|
|
$magic = $this->_readMagic($fp);
|
|
|
|
// Get the header
|
|
$numsRead = sscanf(fgets($fp, 1000),
|
|
'%*c%c %d %*d %d %d %d %d %d',
|
|
$this->hardBlank,
|
|
$this->charHeight,
|
|
$this->maxLength,
|
|
$smush,
|
|
$cmtLines,
|
|
$rightToLeft,
|
|
$this->fontSmush);
|
|
|
|
if ($magic !== self::FONTFILE_MAGIC_NUMBER || $numsRead < 5) {
|
|
throw new Exception\UnexpectedValueException($fontFile . ': Not a FIGlet 2 font file');
|
|
}
|
|
|
|
// Set default right to left
|
|
if ($numsRead < 6) {
|
|
$rightToLeft = 0;
|
|
}
|
|
|
|
// If no smush2, decode smush into smush2
|
|
if ($numsRead < 7) {
|
|
if ($smush === 2) {
|
|
$this->fontSmush = self::SM_KERN;
|
|
} elseif ($smush < 0) {
|
|
$this->fontSmush = 0;
|
|
} else {
|
|
$this->fontSmush = (($smush & 31) | self::SM_SMUSH);
|
|
}
|
|
}
|
|
|
|
// Correct char height && maxlength
|
|
$this->charHeight = max(1, $this->charHeight);
|
|
$this->maxLength = max(1, $this->maxLength);
|
|
|
|
// Give ourselves some extra room
|
|
$this->maxLength += 100;
|
|
|
|
// See if we have to override smush settings
|
|
$this->_setUsedSmush();
|
|
|
|
// Get left to right value
|
|
if ($this->rightToLeft === null) {
|
|
$this->rightToLeft = $rightToLeft;
|
|
}
|
|
|
|
// Get justification value
|
|
if ($this->justification === null) {
|
|
$this->justification = (2 * $this->rightToLeft);
|
|
}
|
|
|
|
// Skip all comment lines
|
|
for ($line = 1; $line <= $cmtLines; $line++) {
|
|
$this->_skipToEol($fp);
|
|
}
|
|
|
|
// Fetch all ASCII characters
|
|
for ($asciiCode = 32; $asciiCode < 127; $asciiCode++) {
|
|
$this->charList[$asciiCode] = $this->_loadChar($fp);
|
|
}
|
|
|
|
// Fetch all german characters
|
|
foreach ($this->germanChars as $uniCode) {
|
|
$char = $this->_loadChar($fp);
|
|
|
|
if ($char === false) {
|
|
fclose($fp);
|
|
return;
|
|
}
|
|
|
|
if (trim(implode('', $char)) !== '') {
|
|
$this->charList[$uniCode] = $char;
|
|
}
|
|
}
|
|
|
|
// At the end fetch all extended characters
|
|
while (!feof($fp)) {
|
|
// Get the Unicode
|
|
list($uniCode) = explode(' ', fgets($fp, 2048));
|
|
|
|
if (empty($uniCode)) {
|
|
continue;
|
|
}
|
|
|
|
// Convert it if required
|
|
if (substr($uniCode, 0, 2) === '0x') {
|
|
$uniCode = hexdec(substr($uniCode, 2));
|
|
} elseif (substr($uniCode, 0, 1) === '0' and
|
|
$uniCode !== '0' or
|
|
substr($uniCode, 0, 2) === '-0') {
|
|
$uniCode = octdec($uniCode);
|
|
} else {
|
|
$uniCode = (int) $uniCode;
|
|
}
|
|
|
|
// Now fetch the character
|
|
$char = $this->_loadChar($fp);
|
|
|
|
if ($char === false) {
|
|
fclose($fp);
|
|
return;
|
|
}
|
|
|
|
$this->charList[$uniCode] = $char;
|
|
}
|
|
|
|
fclose($fp);
|
|
|
|
$this->fontLoaded = true;
|
|
}
|
|
|
|
/**
|
|
* Set the used smush mode, according to smush override, user smush and
|
|
* font smush.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function _setUsedSmush()
|
|
{
|
|
if ($this->smushOverride === self::SMO_NO) {
|
|
$this->smushMode = $this->fontSmush;
|
|
} elseif ($this->smushOverride === self::SMO_YES) {
|
|
$this->smushMode = $this->userSmush;
|
|
} elseif ($this->smushOverride === self::SMO_FORCE) {
|
|
$this->smushMode = ($this->fontSmush | $this->userSmush);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads a four-character magic string from a stream
|
|
*
|
|
* @param resource $fp File pointer to the font file
|
|
* @return string
|
|
*/
|
|
protected function _readMagic($fp)
|
|
{
|
|
$magic = '';
|
|
|
|
for ($i = 0; $i < 4; $i++) {
|
|
$magic .= fgetc($fp);
|
|
}
|
|
|
|
return $magic;
|
|
}
|
|
|
|
/**
|
|
* Skip a stream to the end of line
|
|
*
|
|
* @param resource $fp File pointer to the font file
|
|
* @return void
|
|
*/
|
|
protected function _skipToEol($fp)
|
|
{
|
|
$dummy = fgetc($fp);
|
|
while ($dummy !== false && !feof($fp)) {
|
|
if ($dummy === "\n") {
|
|
return;
|
|
}
|
|
|
|
if ($dummy === "\r") {
|
|
$dummy = fgetc($fp);
|
|
|
|
if (!feof($fp) && $dummy !== "\n") {
|
|
fseek($fp, -1, SEEK_SET);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
$dummy = fgetc($fp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load a single character from the font file
|
|
*
|
|
* @param resource $fp File pointer to the font file
|
|
* @return array
|
|
*/
|
|
protected function _loadChar($fp)
|
|
{
|
|
$char = array();
|
|
|
|
for ($i = 0; $i < $this->charHeight; $i++) {
|
|
if (feof($fp)) {
|
|
return false;
|
|
}
|
|
|
|
$line = rtrim(fgets($fp, 2048), "\r\n");
|
|
|
|
if (preg_match('#(.)\\1?$#', $line, $result) === 1) {
|
|
$line = str_replace($result[1], '', $line);
|
|
}
|
|
|
|
$char[] = $line;
|
|
}
|
|
|
|
return $char;
|
|
}
|
|
|
|
/**
|
|
* Unicode compatible ord() method
|
|
*
|
|
* @param string $c The char to get the value from
|
|
* @return int
|
|
*/
|
|
protected function _uniOrd($c)
|
|
{
|
|
$h = ord($c[0]);
|
|
|
|
if ($h <= 0x7F) {
|
|
$ord = $h;
|
|
} elseif ($h < 0xC2) {
|
|
$ord = 0;
|
|
} elseif ($h <= 0xDF) {
|
|
$ord = (($h & 0x1F) << 6 | (ord($c[1]) & 0x3F));
|
|
} elseif ($h <= 0xEF) {
|
|
$ord = (($h & 0x0F) << 12 | (ord($c[1]) & 0x3F) << 6 | (ord($c[2]) & 0x3F));
|
|
} elseif ($h <= 0xF4) {
|
|
$ord = (($h & 0x0F) << 18 | (ord($c[1]) & 0x3F) << 12 |
|
|
(ord($c[2]) & 0x3F) << 6 | (ord($c[3]) & 0x3F));
|
|
} else {
|
|
$ord = 0;
|
|
}
|
|
|
|
return $ord;
|
|
}
|
|
}
|