Skip to content

Instantly share code, notes, and snippets.

@ninsuo
Last active January 9, 2019 19:55
Show Gist options
  • Save ninsuo/5a5e1adc6114593f8f62db41d710f6c2 to your computer and use it in GitHub Desktop.
Save ninsuo/5a5e1adc6114593f8f62db41d710f6c2 to your computer and use it in GitHub Desktop.
<?php
require('Monospace.php');
$postedSize = 80;
if (isset($_REQUEST['size']))
{
$postedSize = $_REQUEST['size'];
if (!is_numeric($postedSize) || ($postedSize < 10) || ($postedSize > 1000))
{
$postedSize = 80;
}
}
echo '<pre>';
function csv2array($file)
{
$df = fopen($file, 'r');
$array = array ();
while (($row = fgetcsv($df)))
{
$array[] = $row;
}
fclose($df);
return $array;
}
$table1 = csv2array("Monospace-table1.csv");
$doc = new Monospace($postedSize, " ", "\n");
echo $doc
->setBorderTop("-")
->setBorderBottom('-')
->setBorderLeft('|')
->setBorderRight('|')
->setMarginTop(1)
->setMarginBottom(1)
->setMarginLeft(2)
->setMarginRight(2)
->text("This is an email", Monospace::ALIGN_CENTER)
->jump()
->line('*', 0, Monospace::ALIGN_CENTER)
->jump()
->text("Hello, world!")
->jump()
->text("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Typi non habent claritatem insitam; est usus legentis in iis qui facit eorum claritatem. Investigationes demonstraverunt lectores legere me lius quod ii legunt saepius. Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.")
->jump()
->indent("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.")
->jump()
->indent("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.",
4, 'o ')
->jump()
->indent("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.",
4, '=>')
->jump()
->indent("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.",
4, '4. ')
->jump()
->text("Thank you!", Monospace::ALIGN_RIGHT)
->jump()
->line('*', 10, Monospace::ALIGN_CENTER)
->jump()
->keyValue(array ('success' => 42, 'warning' => 127, 'error' => 11))
->jump()
->table($table1, Monospace::ALIGN_LEFT, Monospace::ALIGN_LEFT)
->jump()
->draw();
;
echo '</pre>';
<?php
/**
* Cette classe permet d'écrire un document de type plain/text
* formatté en monospace.
*
* Cette classe supporte :
* - les lignes
* - l'alignement
* - les sauts de ligne
* - les bordures de document
* - les marges de document
* - le texte indente et les puces
* - les clefs/valeurs bien alignees
* - les tableaux
*
* @todo rendre multibyte-safe
*
* @copyright fuz.org
* @author alain tiemblo <alain@fuz.org>
*/
class Monospace
{
const TYPE_LINE = 'line';
const TYPE_TEXT = 'text';
const TYPE_INDENT = 'indent';
const TYPE_KEY_VALUE = 'key-value';
const TYPE_TABLE = 'table';
const ALIGN_DEFAULT = 'default';
const ALIGN_LEFT = 'left';
const ALIGN_CENTER = 'center';
const ALIGN_RIGHT = 'right';
const POSITION_TOP = 'top';
const POSITION_BOTTOM = 'bottom';
/**
* Le nombre de colonnes contenues dans le document
*
* @access private
* @var int
*/
private $_width;
/**
* Caractère d'espacement
*
* @access private
* @var string
*/
private $_space;
/**
* Caractère de saut de ligne
*
* @access private
* @var string
*/
private $_eol;
/**
* Le contenu du document
*
* @access private
* @var stdClass[]
*/
private $_objects;
/**
* Alignement du texte par défaut
*
* @access private
* @var string
*/
private $_align;
/**
* Bordure en haut du document
*
* @access public
* @var string
*/
private $_borderTop;
/**
* Bordure en bas du document
*
* @access public
* @var string
*/
private $_borderBottom;
/**
* Angles des bordures de la page
*
* @access private
* @var string
*/
private $_borderCorners;
/**
* Bordure à gauche du document
*
* @access public
* @var string
*/
private $_borderLeft;
/**
* Bordure à droite du document
*
* @access public
* @var string
*/
private $_borderRight;
/**
* Marge en haut du document
*
* @access public
* @var int
*/
private $_marginTop;
/**
* Marge en bas du document
*
* @access public
* @var int
*/
private $_marginBottom;
/**
* Marge à gauche du document
*
* @access public
* @var int
*/
private $_marginLeft;
/**
* Marge à droite du document
*
* @access public
* @var int
*/
private $_marginRight;
public function __construct($width = 80, $space = ' ', $eol = "\n")
{
$this->_width = 80;
$this->_space = " ";
$this->_eol = "\n";
$this->_objects = array ();
$this->_align = self::ALIGN_LEFT;
$this->_borderTop = '-';
$this->_borderBottom = '-';
$this->_borderLeft = '|';
$this->_borderRight = '|';
$this->_borderCorners = '+';
$this->_marginTop = 1;
$this->_marginBottom = 1;
$this->_marginLeft = 1;
$this->_marginRight = 1;
$this->setWidth($width);
$this->setSpace($space);
$this->setEol($eol);
}
/* * ************************************************************************************
* Setters / getters
* ************************************************************************************ */
public function setWidth($width)
{
if ((is_numeric($width)) && ($width > 0))
{
$this->_width = $width;
}
return $this;
}
public function getWidth()
{
return $this->_width;
}
public function setSpace($space)
{
if ((is_string($space)) && (strlen($space) == 1))
{
$this->_space = $space;
}
return $this;
}
public function getSpace()
{
return $this->_space;
}
public function setEol($eol)
{
if ((is_string($eol)) && (strlen($eol) > 0))
{
$this->_eol = $eol;
}
return $this;
}
public function getEol()
{
return $this->_eol;
}
public function setBorderTop($borderTop)
{
if (is_string($borderTop))
{
$this->_borderTop = $borderTop;
}
return $this;
}
public function getBorderTop()
{
return $this->_borderTop;
}
public function setBorderBottom($borderBottom)
{
if (is_string($borderBottom))
{
$this->_borderBottom = $borderBottom;
}
return $this;
}
public function getBorderBottom()
{
return $this->_borderBottom;
}
public function setBorderLeft($borderLeft)
{
if (is_string($borderLeft))
{
$this->_borderLeft = $borderLeft;
}
return $this;
}
public function getBorderLeft()
{
return $this->_borderLeft;
}
public function setBorderRight($borderRight)
{
if (is_string($borderRight))
{
$this->_borderRight = $borderRight;
}
return $this;
}
public function getBorderRight()
{
return $this->_borderRight;
}
public function setBorderCorners($borderCorners)
{
if (is_string($borderCorners))
{
$this->_borderCorners = $borderCorners;
}
return $this;
}
public function getBorderCorners()
{
return $this->_borderCorners;
}
public function setMarginTop($marginTop)
{
if ((is_numeric($marginTop)) && ($marginTop >= 0))
{
$this->_marginTop = $marginTop;
}
return $this;
}
public function getMarginTop()
{
return $this->_marginTop;
}
public function setMarginBottom($marginBottom)
{
if ((is_numeric($marginBottom)) && ($marginBottom >= 0))
{
$this->_marginBottom = $marginBottom;
}
return $this;
}
public function getMarginBottom()
{
return $this->_marginBottom;
}
public function setMarginLeft($marginLeft)
{
if ((is_numeric($marginLeft)) && ($marginLeft >= 0))
{
$this->_marginLeft = $marginLeft;
}
return $this;
}
public function getMarginLeft()
{
return $this->_marginLeft;
}
public function setMarginRight($marginRight)
{
if ((is_numeric($marginRight)) && ($marginRight >= 0))
{
$this->_marginRight = $marginRight;
}
return $this;
}
public function getMarginRight()
{
return $this->_marginRight;
}
public function setDefaultAlign($align)
{
if ($this->_isAlign($align))
{
$this->_align = $align;
}
return $this;
}
public function getDefaultAlign()
{
return $this->_align;
}
private function _isAlign($align)
{
return in_array($align,
array (
self::ALIGN_LEFT,
self::ALIGN_CENTER,
self::ALIGN_RIGHT,
));
}
/* * ************************************************************************************
* Methodes de remplissage du document
* ************************************************************************************ */
public function line($text, $size = 0, $align = self::ALIGN_DEFAULT)
{
$obj = new stdClass();
$obj->type = self::TYPE_LINE;
$obj->text = $this->_space;
$obj->size = 0;
$obj->align = $this->_align;
if (strlen($text) > 0)
{
$obj->text = $text;
}
if ((is_numeric($size)) && ($size >= 0))
{
$obj->size = floor($size);
}
if ($this->_isAlign($align))
{
$obj->align = $align;
}
$this->_objects[] = $obj;
return $this;
}
public function jump()
{
return $this->line($this->_space);
}
public function text($text, $align = self::ALIGN_DEFAULT)
{
$obj = new stdClass();
$obj->type = self::TYPE_TEXT;
$obj->text = $this->str_remove_accents($text);
$obj->align = $this->_align;
if ($this->_isAlign($align))
{
$obj->align = $align;
}
$this->_objects[] = $obj;
return $this;
}
public function indent($text, $nbSpaces = 4, $bullet = null, $align = self::ALIGN_DEFAULT)
{
$obj = new stdClass();
$obj->type = self::TYPE_INDENT;
$obj->nbSpaces = 4;
$obj->bullet = $this->_space;
$obj->align = $this->_align;
$obj->text = $this->str_remove_accents($text);
if ((is_numeric($nbSpaces)) && ($nbSpaces >= 0))
{
$obj->nbSpaces = $nbSpaces;
}
if ((!is_null($bullet)) && (strlen($bullet) > 0))
{
$obj->bullet = $bullet;
}
if ($this->_isAlign($align))
{
$obj->align = $align;
}
$this->_objects[] = $obj;
return $this;
}
public function keyValue(array $list, $separator = ' : ')
{
$obj = new stdClass();
$obj->type = self::TYPE_KEY_VALUE;
$obj->list = $list;
$obj->separator = ' : ';
if (is_string($separator))
{
$obj->separator = $separator;
}
$this->_objects[] = $obj;
return $this;
}
public function table(array $table, $tableAlign = self::ALIGN_DEFAULT, $cellAlign = self::ALIGN_DEFAULT,
$verticalBar = ' | ', $horizontalBar = '-', $corner = '+')
{
$obj = new stdClass();
$obj->type = self::TYPE_TABLE;
$obj->table = $table;
$obj->tableAlign = $this->_align;
$obj->cellAlign = $this->_align;
$obj->verticalBar = '|';
$obj->horizontalBar = '-';
$obj->corner = '+';
if ($this->_isAlign($tableAlign))
{
$obj->tableAlign = $tableAlign;
}
if ($this->_isAlign($cellAlign))
{
$obj->cellAlign = $cellAlign;
}
if ((is_string($verticalBar)) && (strlen($verticalBar) == 1))
{
$obj->verticalBar = $verticalBar;
}
if ((is_string($horizontalBar)) && (strlen($horizontalBar) == 1))
{
$obj->horizontalBar = $horizontalBar;
}
if ((is_string($corner)) && (strlen($corner) == 1))
{
$obj->corner = $corner;
}
$this->_objects[] = $obj;
return $this;
}
public function reset()
{
$this->_objects[] = array();
$this->_align = self::ALIGN_LEFT;
}
/* * ************************************************************************************
* Methodes bas niveau
* ************************************************************************************ */
private function _getContentWidth()
{
$width = $this->_width;
$width -= $this->_marginLeft;
$width -= strlen($this->_borderLeft);
$width -= strlen($this->_borderRight);
$width -= $this->_marginRight;
if ($width <= 0)
{
throw new Exception("Not enough width to put content.");
}
return $width;
}
private function _putContent($text)
{
$width = $this->_getContentWidth();
if (strlen($text) > $width)
{
$text = substr($text, 0, $width);
}
if (strlen($text) < $width)
{
$text = $text . str_repeat($this->_space, $width - strlen($text));
}
$draw = '';
$draw .= $this->_borderLeft;
$draw .= str_repeat($this->_space, $this->_marginLeft);
$draw .= $text;
$draw .= str_repeat($this->_space, $this->_marginRight);
$draw .= $this->_borderRight;
$draw .= $this->_eol;
return $draw;
}
/* * ************************************************************************************
* Methodes d'affichage du document
* ************************************************************************************ */
public function draw()
{
$draw = '';
$draw .= $this->_drawBorder(self::POSITION_TOP);
$draw .= $this->_drawMargin(self::POSITION_TOP);
foreach ($this->_objects as $obj)
{
switch ($obj->type)
{
case self::TYPE_LINE:
$draw .= $this->_drawLine($obj->text, $obj->size, $obj->align);
break;
case self::TYPE_TEXT:
$draw .= $this->_drawText($obj->text, $obj->align);
break;
case self::TYPE_INDENT:
$draw .= $this->_drawIndent($obj->text, $obj->align, $obj->nbSpaces, $obj->bullet);
break;
case self::TYPE_KEY_VALUE:
$draw .= $this->_drawKeyValue($obj->list, $obj->separator);
break;
case self::TYPE_TABLE:
$draw .= $this->_drawTable($obj->table, $obj->tableAlign, $obj->cellAlign, $obj->verticalBar,
$obj->horizontalBar, $obj->corner);
break;
}
}
$draw .= $this->_drawMargin(self::POSITION_BOTTOM);
$draw .= $this->_drawBorder(self::POSITION_BOTTOM);
return $draw;
}
private function _drawBorder($position)
{
$draw = '';
if ((strlen($this->_borderLeft) == 0) || (strlen($this->_borderRight) == 0)
|| (($position == self::POSITION_TOP) && (strlen($this->_borderTop) == 0))
|| (($position == self::POSITION_BOTTOM) && (strlen($this->_borderBottom) == 0))
|| (!in_array($position, array (self::POSITION_TOP, self::POSITION_BOTTOM))))
{
return $draw;
}
if ($position == self::POSITION_TOP)
{
$char = $this->_borderTop;
}
else
{
$char = $this->_borderBottom;
}
$draw .= $this->_borderCorners;
$draw .= str_repeat($char, ($this->_width / strlen($char)) - (2 * strlen($this->_borderCorners)));
$draw .= $this->_borderCorners;
$draw .= $this->_eol;
return $draw;
}
private function _drawMargin($position)
{
$draw = '';
if ((($position == self::POSITION_TOP) && ($this->_marginTop == 0))
|| (($position == self::POSITION_BOTTOM) && ($this->_marginBottom == 0))
|| (!in_array($position, array (self::POSITION_TOP, self::POSITION_BOTTOM))))
{
return $draw;
}
if ($position == self::POSITION_TOP)
{
$size = $this->_marginTop;
}
else
{
$size = $this->_marginBottom;
}
for ($i = 0; ($i < $size); $i++)
{
$draw .= $this->_drawLine($this->_space);
}
return $draw;
}
private function _drawLine($text, $size, $align)
{
$draw = '';
if (strlen($text) == 0)
{
return $draw;
}
if ($size == 0)
{
$size = $this->_getContentWidth();
}
return $this->_drawText(str_repeat($text, $size / strlen($text)), $align);
}
private function _drawText($text, $align)
{
$draw = '';
$width = $this->_getContentWidth();
$strings = explode($this->_eol, wordwrap($text, $width, $this->_eol, true));
foreach ($strings as $string)
{
$line = $this->_drawAlignedText($string, $align);
$draw .= $this->_putContent($line);
}
return $draw;
}
private function _drawAlignedText($text, $align, $width = null)
{
if (is_null($width))
{
$width = $this->_getContentWidth();
}
$draw = '';
switch ($align)
{
case self::ALIGN_LEFT:
$space = str_repeat($this->_space, $width - strlen($text));
$draw .= $text . $space;
break;
case self::ALIGN_CENTER:
$space = str_repeat($this->_space, floor($width - strlen($text)) / 2);
$draw .= $this->_drawAlignedText($space . $text, self::ALIGN_LEFT, $width);
break;
case self::ALIGN_RIGHT:
$space = str_repeat($this->_space, $width - strlen($text));
$draw .= $space . $text;
break;
}
return $draw;
}
private function _drawIndent($text, $align, $nbSpaces, $bullet)
{
$draw = '';
if ($align == self::ALIGN_CENTER)
{
return $this->_drawText($text, $align);
}
$indent = str_repeat($this->_space, $nbSpaces);
$width = $this->_getContentWidth() - strlen($indent);
if ($width <= 0)
{
throw new Exception("Not enough width to put content.");
}
$strings = explode($this->_eol, wordwrap($text, $width, $this->_eol, true));
$bulletedIndent = $indent;
if (strlen($indent) >= (strlen($bullet) + 1))
{
$bulletedIndent = substr($indent, 0, strlen($indent) - 1 - strlen($bullet)) . $bullet . $this->_space;
}
foreach ($strings as $key => $string)
{
switch ($align)
{
case self::ALIGN_LEFT:
if ($key == 0)
{
$draw .= $this->_drawText($bulletedIndent . $string, $align);
}
else
{
$draw .= $this->_drawText($indent . $string, $align);
}
break;
case self::ALIGN_RIGHT:
$draw .= $this->_drawText($string . $indent, $align);
break;
}
}
return $draw;
}
private function _drawKeyValue(array $list, $separator)
{
$draw = '';
$size = 0;
foreach (array_keys($list) as $key)
{
$len = strlen($key);
if ($len > $size)
{
$size = $len;
}
}
foreach ($list as $key => $value)
{
$text = $key . str_repeat($this->_space, $size - strlen($key)) . $separator . $value;
$draw .= $this->_drawText($text, $this->_align);
}
return $draw;
}
private function _drawTable(array $table, $tableAlign, $cellAlign, $verticalBar, $horizontalBar, $corner)
{
$draw = '';
if (count($table) == 0)
{
return $draw;
}
$colWidth = array ();
$titles = reset($table);
foreach ($titles as $cellNb => $title)
{
$colWidth[$cellNb] = strlen($title);
}
$newTable = array ();
$documentWidth = $this->_getContentWidth();
do
{
$colMaxHeight = array ();
$newTable = $this->_drawTableFitToTitleWidth($table, $colWidth, $colMaxHeight);
$toIncrease = array_keys($colMaxHeight, max($colMaxHeight));
$count = count($toIncrease);
$currentRowWidth = (array_sum($colWidth) + 3 * count($colWidth) + 1);
for ($i = 0; (($i < $count) && ($currentRowWidth < $documentWidth)); $i++)
{
$nbLines = count($newTable);
for ($j = 0; ($j < $nbLines); $j++)
{
if (count($newTable[$j][$toIncrease[$i]]) > 1)
{
$colWidth[$toIncrease[$i]]++;
$currentRowWidth++;
break;
}
}
}
}
while ((max($colMaxHeight) > 1) && ((array_sum($colWidth) + 3 * count($colWidth) + 1) < $documentWidth));
$draw = $this->_drawTableView($newTable, $tableAlign, $cellAlign, $verticalBar, $horizontalBar, $corner, $colWidth);
return $draw;
}
private function _drawTableFitToTitleWidth(array $table, array $colWidth, array &$colMaxHeight)
{
$colMaxHeight = array_combine(array_keys($colWidth), array_values(array_fill(0, count($colWidth), 0)));
$newTable = array ();
foreach ($table as $rowNb => $row)
{
$newTable[$rowNb] = array ();
foreach ($row as $cellNb => $cell)
{
$cell = str_replace("\n", " ", $cell);
$newTable[$rowNb][$cellNb] = explode("\n", wordwrap($cell, $colWidth[$cellNb], "\n", true));
$count = count($newTable[$rowNb][$cellNb]);
if ($count > $colMaxHeight[$cellNb])
{
$colMaxHeight[$cellNb] = $count;
}
}
}
return $newTable;
}
private function _drawTableView($table, $tableAlign, $cellAlign, $verticalBar, $horizontalBar, $corner, $colWidth)
{
$draw = '';
$line = $this->_drawTableLine($colWidth, $horizontalBar, $corner);
$draw .= $this->_drawText($line, $tableAlign);
foreach ($table as $row)
{
$maxLines = 1;
foreach ($row as $lines)
{
$count = count($lines);
if ($count > $maxLines)
{
$maxLines = $count;
}
}
for ($i = 0; ($i < $maxLines); $i++)
{
$line = '';
foreach ($row as $cellNb => $lines)
{
if ($cellNb == 0)
{
$line .= $verticalBar;
}
$line .= $this->_space;
if (array_key_exists($i, $lines))
{
$content = $this->_drawAlignedText($lines[$i], $cellAlign, $colWidth[$cellNb]);
$line .= $content;
$line .= str_repeat($this->_space, $colWidth[$cellNb] - strlen($content));
}
else
{
$line .= str_repeat($this->_space, $colWidth[$cellNb]);
}
$line .= $this->_space;
$line .= $verticalBar;
}
$draw .= $this->_drawText($line, $tableAlign);
}
$line = $this->_drawTableLine($colWidth, $horizontalBar, $corner);
$draw .= $this->_drawText($line, $tableAlign);
}
return $draw;
}
private function _drawTableLine($sizes, $horizontalBar, $corner)
{
$draw = '';
$count = count($sizes);
foreach ($sizes as $size)
{
$draw .= $corner;
$draw .= str_repeat($horizontalBar, $size + 2);
}
$draw .= $corner;
return $draw;
}
/* * ************************************************************************************
* Methodes multibytes
* ************************************************************************************ */
public function str_remove_accents($string)
{
$search = preg_split('//u', 'àáâãäçèéêëìíîïñòóôõöùúûüýÿÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ', -1, PREG_SPLIT_NO_EMPTY);
$replace = str_split('aaaaaceeeeiiiinooooouuuuyyAAAAACEEEEIIIINOOOOOUUUUY');
$chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
$final = '';
foreach ($chars as $char)
{
if (in_array($char, $search))
{
$final .= $replace[array_search($char, $search)];
}
else
{
$final .= $char;
}
}
return $final;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment