setOptions($options); } if (!$this->_fontLoaded) { throw new Exception("You have to set a font option in options parameter"); } } /** * Set options from array * * @param array $options Configuration for Zend_Text_Figlet * @return Zend_Text_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 Zend_Text_Figlet */ public function setFont($font) { $this->_loadFont($font); return $this; } /** * Set handling of paragraphs * * @param boolean $handleParagraphs Wether to handle paragraphs or not * @return Zend_Text_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 integer $justification Justification of the output text * @return Zend_Text_Figlet */ public function setJustification($justification) { $this->_justification = min(3, max(0, (int) $justification)); return $this; } /** * Set the output width * * @param integer $outputWidth Output with which should be used for word * wrapping and justification * @return Zend_Text_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 integer $rightToLeft Right-to-left mode * @return Zend_Text_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 integer $smushMode Smush mode to use for generating text * @return Zend_Text_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; } else if ($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 InvalidArgumentException When $text is not a string * @throws Zend_Text_Figlet_Exception When $text it not properly encoded * @return string */ public function render($text, $encoding = 'UTF-8') { if (!is_string($text)) { throw new InvalidArgumentException('$text must be a string'); } if ($encoding !== 'UTF-8') { $text = iconv($encoding, 'UTF-8', $text); } $this->_output = ''; $this->_outputLine = array(); $this->_clearLine(); $this->_outlineLengthLimit = ($this->_outputWidth - 1); $this->_inCharLineLengthLimit = ($this->_outputWidth * 4 + 100); $wordBreakMode = 0; $lastCharWasEol = false; $textLength = @iconv_strlen($text, 'UTF-8'); if ($textLength === false) { throw new Exception('$text is not encoded with ' . $encoding); } for ($charNum = 0; $charNum < $textLength; $charNum++) { // Handle paragraphs $char = iconv_substr($text, $charNum, 1, 'UTF-8'); if ($char === "\n" && $this->_handleParagraphs && !$lastCharWasEol) { $nextChar = iconv_substr($text, ($charNum + 1), 1, 'UTF-8'); 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; } else if ($char === "\n") { $wordBreakMode = 0; break; } $wordBreakMode = 0; } if ($char === "\n") { $this->_appendLine(); $wordBreakMode = false; } else if ($this->_addChar($char)) { if ($char !== ' ') { $wordBreakMode = ($wordBreakMode >= 2) ? 3: 1; } else { $wordBreakMode = ($wordBreakMode > 0) ? 2: 0; } } else if ($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; } else if ($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 boolean */ 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; } $tempLine = ''; 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 integer */ protected function _smushAmount() { if (($this->_smushMode & (self::SM_SMUSH | self::SM_KERN)) === 0) { return 0; } $maxSmush = $this->_currentCharWidth; $amount = $maxSmush; 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++; } else if (!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; } else if ($rightChar === ' ') { return $leftChar; } else if ($leftChar === $this->_hardBlank) { return $rightChar; } else if ($rightChar === $this->_hardBlank) { return $rightChar; } else if ($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; } else if ($rightChar === '_' && strchr('|/\\[]{}()<>', $leftChar) !== false) { return $leftChar; } } if (($this->_smushMode & self::SM_HIERARCHY) > 0) { if ($leftChar === '|' && strchr('/\\[]{}()<>', $rightChar) !== false) { return $rightChar; } else if ($rightChar === '|' && strchr('/\\[]{}()<>', $leftChar) !== false) { return $leftChar; } else if (strchr('/\\', $leftChar) && strchr('[]{}()<>', $rightChar) !== false) { return $rightChar; } else if (strchr('/\\', $rightChar) && strchr('[]{}()<>', $leftChar) !== false) { return $leftChar; } else if (strchr('[]', $leftChar) && strchr('{}()<>', $rightChar) !== false) { return $rightChar; } else if (strchr('[]', $rightChar) && strchr('{}()<>', $leftChar) !== false) { return $leftChar; } else if (strchr('{}', $leftChar) && strchr('()<>', $rightChar) !== false) { return $rightChar; } else if (strchr('{}', $rightChar) && strchr('()<>', $leftChar) !== false) { return $leftChar; } else if (strchr('()', $leftChar) && strchr('<>', $rightChar) !== false) { return $rightChar; } else if (strchr('()', $rightChar) && strchr('<>', $leftChar) !== false) { return $leftChar; } } if (($this->_smushMode & self::SM_PAIR) > 0) { if ($leftChar === '[' && $rightChar === ']') { return '|'; } else if ($rightChar === '[' && $leftChar === ']') { return '|'; } else if ($leftChar === '{' && $rightChar === '}') { return '|'; } else if ($rightChar === '{' && $leftChar === '}') { return '|'; } else if ($leftChar === '(' && $rightChar === ')') { return '|'; } else if ($rightChar === '(' && $leftChar === ')') { return '|'; } } if (($this->_smushMode & self::SM_BIGX) > 0) { if ($leftChar === '/' && $rightChar === '\\') { return '|'; } else if ($rightChar === '/' && $leftChar === '\\') { return 'Y'; } else if ($leftChar === '>' && $rightChar === '<') { return 'X'; } } return null; } /** * Load the specified font * * @param string $fontFile Font file to load * @throws Zend_Text_Figlet_Exception When font file was not found * @throws Zend_Text_Figlet_Exception When GZIP library is required but not found * @throws Zend_Text_Figlet_Exception When font file is not readable * @return void */ protected function _loadFont($fontFile) { // Check if the font file exists if (!file_exists($fontFile)) { throw new Exception($fontFile . ': Font file not found'); } // Check if gzip support is required if (substr($fontFile, -3) === '.gz') { if (!function_exists('gzcompress')) { throw new Exception('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($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($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; } else if ($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)); } else if (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 smsush and * font smush. * * @return void */ protected function _setUsedSmush() { if ($this->_smushOverride === self::SMO_NO) { $this->_smushMode = $this->_fontSmush; } else if ($this->_smushOverride === self::SMO_YES) { $this->_smushMode = $this->_userSmush; } else if ($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 integer */ protected function _uniOrd($c) { $h = ord($c[0]); if ($h <= 0x7F) { $ord = $h; } else if ($h < 0xC2) { $ord = 0; } else if ($h <= 0xDF) { $ord = (($h & 0x1F) << 6 | (ord($c[1]) & 0x3F)); } else if ($h <= 0xEF) { $ord = (($h & 0x0F) << 12 | (ord($c[1]) & 0x3F) << 6 | (ord($c[2]) & 0x3F)); } else if ($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; } } ?>