Page rendering using ANSI support
This commit is contained in:
parent
a24bba0481
commit
b35655a163
@ -38,22 +38,24 @@ class ANSI
|
|||||||
|
|
||||||
/* MAGIC METHODS */
|
/* MAGIC METHODS */
|
||||||
|
|
||||||
public function __construct(string $file)
|
public function __construct(string $file='')
|
||||||
{
|
{
|
||||||
$this->width = collect();
|
$this->width = collect();
|
||||||
$this->ansi = collect();
|
$this->ansi = collect();
|
||||||
|
|
||||||
$f = fopen($file,'r');
|
if ($file) {
|
||||||
while (! feof($f)) {
|
$f = fopen($file,'r');
|
||||||
$line = stream_get_line($f,self::BUFREAD,"\r");
|
while (! feof($f)) {
|
||||||
|
$line = stream_get_line($f,self::BUFREAD,"\r");
|
||||||
|
|
||||||
// If the last line is blank, we'll ignore it
|
// If the last line is blank, we'll ignore it
|
||||||
if ((! feof($f)) || $line) {
|
if ((! feof($f)) || $line) {
|
||||||
$this->width->push(self::line_width($line,FALSE));
|
$this->width->push(self::line_width($line,FALSE));
|
||||||
$this->ansi->push(array_map(function($item) { return ord($item); },str_split($line,1)));
|
$this->ansi->push(array_map(function($item) { return ord($item); },str_split($line,1)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
fclose($f);
|
||||||
}
|
}
|
||||||
fclose($f);
|
|
||||||
|
|
||||||
return $this->ansi;
|
return $this->ansi;
|
||||||
}
|
}
|
||||||
@ -85,6 +87,33 @@ class ANSI
|
|||||||
return static::bin_to_ansi((new self($file))->ansi->toArray());
|
return static::bin_to_ansi((new self($file))->ansi->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an array of ANSI codes into a binary equivalent
|
||||||
|
*
|
||||||
|
* @param array $code
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function ansi_code(array $code): string
|
||||||
|
{
|
||||||
|
if (!$code)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
return "\x1b".chr(self::code($code));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render an ANSI binary code into an ANSI string
|
||||||
|
*
|
||||||
|
* @param int $code
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function ansi_color(int $code): string
|
||||||
|
{
|
||||||
|
static $current = [];
|
||||||
|
|
||||||
|
return "\x1b[".self::color($code,$current);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a binary ANS file to ANSI text
|
* Convert a binary ANS file to ANSI text
|
||||||
*
|
*
|
||||||
@ -230,7 +259,10 @@ class ANSI
|
|||||||
foreach ($code as $item) {
|
foreach ($code as $item) {
|
||||||
switch ($item) {
|
switch ($item) {
|
||||||
// Color Reset
|
// Color Reset
|
||||||
case 0: $result = 0; break;
|
case 0:
|
||||||
|
// Low Intensity
|
||||||
|
case 2: $result = 0; break;
|
||||||
|
|
||||||
// High Intensity
|
// High Intensity
|
||||||
case 1: $result |= self::COLOR_HIGH; break;
|
case 1: $result |= self::COLOR_HIGH; break;
|
||||||
|
|
||||||
@ -262,18 +294,8 @@ class ANSI
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function color_array(int $code): array
|
||||||
* Convert binary code to ANSI escape code
|
|
||||||
*
|
|
||||||
* @param int $code
|
|
||||||
* @param array $current
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private static function color(int $code,array &$current): string
|
|
||||||
{
|
{
|
||||||
if (! $current)
|
|
||||||
$current = static::reset();
|
|
||||||
|
|
||||||
$h = ($code&0x01);
|
$h = ($code&0x01);
|
||||||
|
|
||||||
switch ($x=(($code>>1)&0x07)) {
|
switch ($x=(($code>>1)&0x07)) {
|
||||||
@ -301,28 +323,45 @@ class ANSI
|
|||||||
default:
|
default:
|
||||||
dump(['unknown color'=>$x]);
|
dump(['unknown color'=>$x]);
|
||||||
}
|
}
|
||||||
$return = '';
|
|
||||||
$highlight_changed = false;
|
|
||||||
|
|
||||||
if ($h !== $current['h']) {
|
return ['h'=>$h,'f'=>$f,'b'=>$b];
|
||||||
$return .= $h;
|
}
|
||||||
$current['h'] = $h;
|
|
||||||
|
/**
|
||||||
|
* Convert binary code to ANSI escape code
|
||||||
|
*
|
||||||
|
* @param int $code
|
||||||
|
* @param array $current
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function color(int $code,array &$current): string
|
||||||
|
{
|
||||||
|
if (! $current)
|
||||||
|
$current = static::reset();
|
||||||
|
|
||||||
|
$return = '';
|
||||||
|
$color = self::color_array($code);
|
||||||
|
$highlight_changed = FALSE;
|
||||||
|
|
||||||
|
if ($color['h'] !== $current['h']) {
|
||||||
|
$return .= $color['h'] ?: ($code != 0x0e ? 2 : 0);
|
||||||
|
$current['h'] = $color['h'];
|
||||||
$highlight_changed = TRUE;
|
$highlight_changed = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($f !== $current['f']) {
|
if ($color['f'] !== $current['f']) {
|
||||||
if (! $highlight_changed || $h || (($f != self::DEFAULT_FORE) || ($b != self::DEFAULT_BACK)))
|
if (! $highlight_changed || $color['h'] || (($color['f'] != self::DEFAULT_FORE) || ($color['b'] != self::DEFAULT_BACK)))
|
||||||
$return .= (strlen($return) ? ';' : '').$f;
|
$return .= (strlen($return) ? ';' : '').$color['f'];
|
||||||
|
|
||||||
$x = $f;
|
$x = $color['f'];
|
||||||
$current['f'] = $f;
|
$current['f'] = $color['f'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($b !== $current['b']) {
|
if ($color['b'] !== $current['b']) {
|
||||||
if (! $highlight_changed || $h || (($x != self::DEFAULT_FORE) || ($b != self::DEFAULT_BACK)))
|
if (! $highlight_changed || $color['h'] || (($x != self::DEFAULT_FORE) || ($color['b'] != self::DEFAULT_BACK)))
|
||||||
$return .= (strlen($return) ? ';' : '').$b;
|
$return .= (strlen($return) ? ';' : '').$color['b'];
|
||||||
|
|
||||||
$current['b'] = $b;
|
$current['b'] = $color['b'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ($return !== '') ? $return.'m' : '';
|
return ($return !== '') ? $return.'m' : '';
|
||||||
@ -354,6 +393,19 @@ class ANSI
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a line with embedded color codes in ANSI
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function text_to_ansi(string $text): string
|
||||||
|
{
|
||||||
|
$ansi = preg_match('/\x1b./',$text);
|
||||||
|
|
||||||
|
return self::bin_to_ansi([array_map(function($item) {return ord($item); },str_split($text))],FALSE).((strlen($text) && $ansi) ? "\x1b[0m" : '');
|
||||||
|
}
|
||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,6 +11,127 @@ class Font
|
|||||||
|
|
||||||
protected const MSG_WIDTH = 79;
|
protected const MSG_WIDTH = 79;
|
||||||
|
|
||||||
|
private string $text = '';
|
||||||
|
private int $width = 0;
|
||||||
|
private int $height = 0;
|
||||||
|
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'height':
|
||||||
|
return $this->height;
|
||||||
|
|
||||||
|
case 'width':
|
||||||
|
return $this->width;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception(sprintf('Unknown key %s',$key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message text, goes after header, and if a logo, to the right of it
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
*/
|
||||||
|
public function addText(string $text)
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
$this->dimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Characters used in the font
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
private function chars(): Collection
|
||||||
|
{
|
||||||
|
static $chars = NULL;
|
||||||
|
|
||||||
|
if (is_null($chars) && $this->text) {
|
||||||
|
// Trim any leading/trailing spaces
|
||||||
|
$text = trim(strtolower($this->text));
|
||||||
|
$chars = collect();
|
||||||
|
|
||||||
|
// Work out the characters we need
|
||||||
|
foreach (array_unique(str_split($text)) as $c) {
|
||||||
|
if (! $x=Arr::get(static::FONT,$c))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$chars->put($c,$x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $chars ?: collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full width of the rendered text
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function dimensions(): void
|
||||||
|
{
|
||||||
|
$chars = $this->chars();
|
||||||
|
$escape = FALSE;
|
||||||
|
|
||||||
|
foreach (str_split(strtolower($this->text)) as $c) {
|
||||||
|
if ($c == "\x1b") {
|
||||||
|
$escape = TRUE;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
} elseif ($escape) {
|
||||||
|
$escape = FALSE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->width += ($x=Arr::get($chars->get($c),0)) ? count($x) : 1;
|
||||||
|
|
||||||
|
if ($x)
|
||||||
|
$this->height = (($y=count($chars->get($c))) > $this->height) ? $y : $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last character is a space, we'll reduce the width
|
||||||
|
$space = TRUE;
|
||||||
|
foreach ($chars->get($c) as $line => $x)
|
||||||
|
if (array_pop($x) != 32) {
|
||||||
|
$space = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($space)
|
||||||
|
$this->width--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_line(int $line): string
|
||||||
|
{
|
||||||
|
$chars = $this->chars();
|
||||||
|
$result = '';
|
||||||
|
$escape = FALSE;
|
||||||
|
$ansi = FALSE;
|
||||||
|
|
||||||
|
foreach (str_split(strtolower($this->text)) as $c) {
|
||||||
|
if (ord($c) == 0x1b) {
|
||||||
|
$escape = TRUE;
|
||||||
|
|
||||||
|
} elseif ($escape && $c) {
|
||||||
|
$result .= ANSI::ansi_color(ord($c));
|
||||||
|
$escape = FALSE;
|
||||||
|
$ansi = TRUE;
|
||||||
|
|
||||||
|
} elseif (($c == ' ') || (! $font_chars=$chars->get($c))) {
|
||||||
|
$result .= $c;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
foreach (Arr::get($font_chars,$line) as $char)
|
||||||
|
$result .= chr($char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result.($ansi ? ANSI::ansi_color(0x0e) : '');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will format text to static::MSG_WIDTH, as well as adding the logo.
|
* This function will format text to static::MSG_WIDTH, as well as adding the logo.
|
||||||
* It is up to the text to be spaced appropriately to wrap around the icon.
|
* It is up to the text to be spaced appropriately to wrap around the icon.
|
||||||
@ -146,9 +267,9 @@ class Font
|
|||||||
for ($line=0;$line<$font_height;$line++) {
|
for ($line=0;$line<$font_height;$line++) {
|
||||||
if ($line == 0) {
|
if ($line == 0) {
|
||||||
$line_icon_width = $icon_width
|
$line_icon_width = $icon_width
|
||||||
->skip(intdiv($result_height,$step)*$step)
|
->skip(intdiv($result_height,$step)*$step)
|
||||||
->take($step)
|
->take($step)
|
||||||
->max();
|
->max();
|
||||||
|
|
||||||
if ($line_icon_width)
|
if ($line_icon_width)
|
||||||
$line_icon_width += ANSI::LOGO_OFFSET_WIDTH+ANSI::LOGO_BUFFER_WIDTH;
|
$line_icon_width += ANSI::LOGO_OFFSET_WIDTH+ANSI::LOGO_BUFFER_WIDTH;
|
||||||
|
@ -4,9 +4,8 @@ namespace App\Classes\Fonts;
|
|||||||
|
|
||||||
use App\Classes\Font;
|
use App\Classes\Font;
|
||||||
|
|
||||||
class Thick extends Font
|
final class Thick extends Font
|
||||||
{
|
{
|
||||||
|
|
||||||
protected const FONT = [
|
protected const FONT = [
|
||||||
'a' => [
|
'a' => [
|
||||||
[0xdc,0xdc,0xdc,0x20],
|
[0xdc,0xdc,0xdc,0x20],
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Classes\Fonts;
|
namespace App\Classes\Fonts;
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
use App\Classes\Font;
|
use App\Classes\Font;
|
||||||
|
|
||||||
final class Thin extends Font
|
final class Thin extends Font
|
||||||
@ -104,11 +102,18 @@ final class Thin extends Font
|
|||||||
[0xc0,0xc4,0xbf],
|
[0xc0,0xc4,0xbf],
|
||||||
[0xc0,0xc4,0xd9],
|
[0xc0,0xc4,0xd9],
|
||||||
],
|
],
|
||||||
|
/*
|
||||||
't' => [
|
't' => [
|
||||||
[0xc2,0x20,0x20],
|
[0xc2,0x20,0x20],
|
||||||
[0xc5,0xc4,0x20],
|
[0xc5,0xc4,0x20],
|
||||||
[0xc1,0xc4,0xd9],
|
[0xc1,0xc4,0xd9],
|
||||||
],
|
],
|
||||||
|
*/
|
||||||
|
't' => [
|
||||||
|
[0xda,0xc2,0xbf],
|
||||||
|
[0x20,0xb3,0x20],
|
||||||
|
[0x20,0xc1,0x20],
|
||||||
|
],
|
||||||
'u' => [
|
'u' => [
|
||||||
[0xda,0x20,0xbf],
|
[0xda,0x20,0xbf],
|
||||||
[0xb3,0x20,0xb3],
|
[0xb3,0x20,0xb3],
|
||||||
|
292
app/Classes/Page.php
Normal file
292
app/Classes/Page.php
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page layout is as follows
|
||||||
|
*
|
||||||
|
* |----------|--------------|
|
||||||
|
* | LOGO | HEADER |
|
||||||
|
* | LEFT BOX | TEXT |
|
||||||
|
* |----------|--------------|
|
||||||
|
*/
|
||||||
|
class Page
|
||||||
|
{
|
||||||
|
protected const MSG_WIDTH = 78;
|
||||||
|
private const LOGO_OFFSET_WIDTH = 1;
|
||||||
|
|
||||||
|
private Font $header;
|
||||||
|
private string $header_foot = '';
|
||||||
|
private bool $header_right;
|
||||||
|
private int $header_underline = 0;
|
||||||
|
|
||||||
|
private Font $left_box;
|
||||||
|
|
||||||
|
private ANSI $logo;
|
||||||
|
private int $step = 0;
|
||||||
|
|
||||||
|
private string $text = '';
|
||||||
|
private bool $text_right = FALSE;
|
||||||
|
|
||||||
|
public function __construct(bool $crlf=FALSE)
|
||||||
|
{
|
||||||
|
$this->header = new Font;
|
||||||
|
$this->logo = new ANSI;
|
||||||
|
$this->left_box = new Font;
|
||||||
|
$this->crlf = $crlf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
// Height of the header
|
||||||
|
case 'header_height':
|
||||||
|
return $this->header->height+($this->header_foot ? 1 : 0)+($this->header_underline ? 1 : 0);
|
||||||
|
|
||||||
|
// The width of the left column
|
||||||
|
case 'left_width':
|
||||||
|
return $this->logo->width->max() ?: $this->left_box->width;
|
||||||
|
|
||||||
|
// The height of the left column
|
||||||
|
case 'left_height':
|
||||||
|
return $this->logo->height+$this->left_box->height+self::LOGO_OFFSET_WIDTH;
|
||||||
|
|
||||||
|
// The right width
|
||||||
|
case 'right_width':
|
||||||
|
return self::MSG_WIDTH-$this->left_width;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception(sprintf('Unknown key %s',$key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message header - goes at top, right of logo
|
||||||
|
*
|
||||||
|
* @param Font $text
|
||||||
|
* @param string $foot
|
||||||
|
* @param bool $right
|
||||||
|
* @param int $underline
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function addHeader(Font $text,string $foot='',bool $right=FALSE,int $underline=0): void
|
||||||
|
{
|
||||||
|
if (($text->width > $this->right_width) || (strlen($foot) > $this->right_width))
|
||||||
|
throw new \Exception(sprintf('Header or Header Footer greater than available width'));
|
||||||
|
|
||||||
|
$this->header = $text;
|
||||||
|
$this->header_foot = $foot;
|
||||||
|
$this->header_right = $right;
|
||||||
|
$this->header_underline = $underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content that can go below logo, to the left of the text, if text $logo_left_border is TRUE
|
||||||
|
*
|
||||||
|
* @param Font $text
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function addLeftBoxContent(Font $text): void
|
||||||
|
{
|
||||||
|
if ($this->left_width && ($text->width > $this->left_width))
|
||||||
|
throw new \Exception(sprintf('Leftbox content greater than icon width'));
|
||||||
|
|
||||||
|
$this->left_box = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message logo - goes at top left
|
||||||
|
*
|
||||||
|
* @param ANSI $ansi
|
||||||
|
*/
|
||||||
|
public function addLogo(ANSI $ansi): void
|
||||||
|
{
|
||||||
|
$this->logo = $ansi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message text, goes after header, and if a logo, to the right of it
|
||||||
|
*
|
||||||
|
* @param string $text Main Body Text
|
||||||
|
* @param bool $right Right Aligned
|
||||||
|
*/
|
||||||
|
public function addText(string $text,bool $right=FALSE)
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
$this->text_right = $right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the page.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function render(): string
|
||||||
|
{
|
||||||
|
$result = '';
|
||||||
|
$result_height = 0;
|
||||||
|
$current_pos = 0;
|
||||||
|
$text_length = strlen($this->text);
|
||||||
|
$this->step = 0; // @todo temp
|
||||||
|
$text_current_color = NULL;
|
||||||
|
|
||||||
|
while (TRUE) {
|
||||||
|
$result_line = ''; // Line being created
|
||||||
|
$lc = 0; // Line length count (without ANSI control codes)
|
||||||
|
|
||||||
|
// The buffer represents how many spaces need to pad between the left_width and whatever is drawn on the left
|
||||||
|
if (! $this->step) {
|
||||||
|
if (($this->left_height > $this->header_height) || ($result_height < $this->header_height-1))
|
||||||
|
$buffer = $this->left_width+($this->left_width ? self::LOGO_OFFSET_WIDTH*2 : 0);
|
||||||
|
else
|
||||||
|
$buffer = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$buffer = 0;
|
||||||
|
/*
|
||||||
|
// @todo
|
||||||
|
$buffer = $this->step
|
||||||
|
? $this->logo->width->skip(intdiv($result_height,$this->step)*$this->step)->take($this->step)->max()
|
||||||
|
: 1;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->left_width) {
|
||||||
|
// Add our logo
|
||||||
|
if ($result_height < $this->logo->height) {
|
||||||
|
$line = ANSI::bin_to_ansi([$this->logo->line($result_height)],FALSE);
|
||||||
|
$lc = $this->logo->line_width($this->logo->line_raw($result_height),FALSE);
|
||||||
|
|
||||||
|
$result_line = str_repeat(' ',self::LOGO_OFFSET_WIDTH)
|
||||||
|
.$line
|
||||||
|
.str_repeat(' ',$buffer-$lc-($this->left_width ? self::LOGO_OFFSET_WIDTH : 0));
|
||||||
|
|
||||||
|
} elseif (self::LOGO_OFFSET_WIDTH && $this->logo->height && ($result_height == $this->logo->height) && $this->left_box->height) {
|
||||||
|
$result_line = str_repeat(' ',$buffer);
|
||||||
|
|
||||||
|
} elseif ($result_height < $this->left_height-($this->logo->height ? 0 : self::LOGO_OFFSET_WIDTH)) {
|
||||||
|
$line = $this->left_box->render_line($result_height-($this->logo->height ? self::LOGO_OFFSET_WIDTH : 0)-$this->logo->height);
|
||||||
|
$lc = $this->left_box->width;
|
||||||
|
|
||||||
|
$result_line = str_repeat(' ',($this->left_box->width ? self::LOGO_OFFSET_WIDTH : 0))
|
||||||
|
.$line
|
||||||
|
.str_repeat(' ',($this->left_box->width ? $buffer-$lc-($this->left_width ? self::LOGO_OFFSET_WIDTH : 0) : (($current_pos < $text_length) ? $buffer : 0)));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ($current_pos < $text_length)
|
||||||
|
$result_line = str_repeat(' ',$buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our header
|
||||||
|
if ($result_height <= $this->header->height-1) {
|
||||||
|
$result_line .= str_repeat(' ',$this->header_right ? self::MSG_WIDTH-($this->left_width ? self::LOGO_OFFSET_WIDTH*2 : 0)-$this->left_width-$this->header->width : 0)
|
||||||
|
.$this->header->render_line($result_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our header footer
|
||||||
|
if ($x=ANSI::line_width($this->header_foot,FALSE)) {
|
||||||
|
if ($result_height == $this->header->height) {
|
||||||
|
$result_line .= str_repeat(' ',($this->header_right ? self::MSG_WIDTH-($this->left_width ? self::LOGO_OFFSET_WIDTH*2 : 0)-$this->left_width-$x : 0))
|
||||||
|
.ANSI::text_to_ansi($this->header_foot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our header underline
|
||||||
|
if ($this->header_underline) {
|
||||||
|
if ($result_height == $this->header->height+($this->header_foot ? 1 : 0)) {
|
||||||
|
$result_line .= str_repeat(chr($this->header_underline),self::MSG_WIDTH-$buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$subtext = '';
|
||||||
|
$subtext_ansi = '';
|
||||||
|
$subtext_length = 0;
|
||||||
|
|
||||||
|
if ($current_pos < $text_length) {
|
||||||
|
if ($result_height >= $this->header_height) {
|
||||||
|
// Look for a return
|
||||||
|
$return_pos = strpos($this->text,"\r",$current_pos);
|
||||||
|
|
||||||
|
// We have a return
|
||||||
|
if ($return_pos !== FALSE) {
|
||||||
|
// If the remaining text is within our width, we'll use it all.
|
||||||
|
if ($return_pos-$current_pos < self::MSG_WIDTH-$buffer) {
|
||||||
|
$subtext = substr($this->text,$current_pos,$return_pos-$current_pos);
|
||||||
|
|
||||||
|
// Look for the space.
|
||||||
|
} else {
|
||||||
|
$space_pos = strrpos(substr($this->text,$current_pos,($return_pos-$current_pos > static::MSG_WIDTH-$this->left_width ? static::MSG_WIDTH-$this->left_width : $return_pos-$current_pos)),' ');
|
||||||
|
$subtext = substr($this->text,$current_pos,$space_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the reset of the string will fit on the current line
|
||||||
|
} elseif ($text_length-$current_pos < static::MSG_WIDTH-$buffer) {
|
||||||
|
$subtext = substr($this->text,$current_pos);
|
||||||
|
|
||||||
|
// Get the next lines worth of chars
|
||||||
|
} else {
|
||||||
|
$subtext = $this->text_substr(substr($this->text,$current_pos),static::MSG_WIDTH-$buffer);
|
||||||
|
|
||||||
|
// Include the text up to the last space
|
||||||
|
if (substr($this->text,$current_pos+strlen($subtext),1) !== ' ')
|
||||||
|
$subtext = substr($subtext,0,strrpos($subtext,' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_pos += strlen($subtext)+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($subtext) {
|
||||||
|
$subtext_length = ANSI::line_width($subtext,FALSE);
|
||||||
|
$subtext_ansi = ANSI::text_to_ansi(($text_current_color ? "\x1b".$text_current_color : '').$subtext);
|
||||||
|
|
||||||
|
// Get our last color used, for the next line.
|
||||||
|
$m = [];
|
||||||
|
preg_match('/^.*(\x1b(.))+(.*?)$/s',$subtext,$m);
|
||||||
|
if (Arr::get($m,2))
|
||||||
|
$text_current_color = $m[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result_line || $subtext) {
|
||||||
|
$result .= $result_line.
|
||||||
|
($subtext_ansi
|
||||||
|
? str_repeat(' ',
|
||||||
|
($this->text_right && ($result_height > $this->header_height-1) ? static::MSG_WIDTH-$subtext_length-$buffer : 0)).$subtext_ansi
|
||||||
|
: '');
|
||||||
|
|
||||||
|
$result .= $this->crlf ? "\n\r" : "\r";
|
||||||
|
}
|
||||||
|
|
||||||
|
$result_height++;
|
||||||
|
|
||||||
|
if (($result_height > $this->logo->height) &&
|
||||||
|
($result_height > $this->left_height) &&
|
||||||
|
($current_pos >= $text_length))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function text_substr(string $text,int $goal): string
|
||||||
|
{
|
||||||
|
$chars = $goal;
|
||||||
|
|
||||||
|
while (($x=ANSI::line_width($subtext=substr($text,0,$chars),FALSE)) < $goal) {
|
||||||
|
$chars += ($chars-$x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last char is an escape, we need to more chars until the last char is no longer an escape.
|
||||||
|
while (preg_match('/\x1b$/',$subtext) && (strlen($subtext) < strlen($text))) {
|
||||||
|
$subtext .= substr($text,strlen($subtext),2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subtext;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user