Spade
Mini Shell
| Directory:~$ /home/lmsyaran/public_html/administrator/components/com_phocacart/libraries/Escpos/ |
| [Home] [System Details] [Kill Me] |
<?php
/**
* This file is part of escpos-php: PHP receipt printer library for use
with
* ESC/POS-compatible thermal and impact printers.
*
* Copyright (c) 2014-16 Michael Billington <
michael.billington@gmail.com >,
* incorporating modifications by others. See CONTRIBUTORS.md for a full
list.
*
* This software is distributed under the terms of the MIT license. See
LICENSE.md
* for details.
*/
namespace Mike42\Escpos;
use Exception;
use InvalidArgumentException;
/**
* This class deals with images in raster formats, and converts them into
formats
* which are suitable for use on thermal receipt printers. Currently, only
PNG
* images (in) and ESC/POS raster format (out) are implemeted.
*
* Input formats:
* - Currently, only PNG is supported.
* - Other easily read raster formats (jpg, gif) will be added at a later
date, as this is not complex.
* - The BMP format can be directly read by some commands, but this has
not yet been implemented.
*
* Output formats:
* - Currently, only ESC/POS raster format is supported
* - ESC/POS 'column format' support is partially implemented,
but is not yet used by Escpos.php library.
* - Output as multiple rows of column format image is not yet in the
works.
*
* Libraries:
* - Currently, php-gd is used to read the input. Support for imagemagick
where gd is not installed is
* also not complex to add, and is a likely future feature.
* - Support for native use of the BMP format is a goal, for maximum
compatibility with target environments.
*/
abstract class EscposImage
{
/**
* @var int $imgHeight
* height of the image.
*/
protected $imgHeight = 0;
/**
* @var int $imgWidth
* width of the image
*/
protected $imgWidth = 0;
/**
* @var string $imgData
* Image data in rows: 1 for black, 0 for white.
*/
private $imgData = null;
/**
* @var string $imgColumnData
* Cached column-format data to avoid re-computation
*/
private $imgColumnData = array();
/**
* @var array:string $imgRasterData
* Cached raster format data to avoid re-computation
*/
private $imgRasterData = null;
/**
* @var string $filename
* Filename of image on disk - null if not loaded from disk.
*/
private $filename = null;
/**
* @var boolean $allowOptimisations
* True to allow faster library-specific rendering shortcuts, false to
always just use
* image libraries to read pixels (more reproducible between systems).
*/
private $allowOptimisations = true;
/**
* Construct a new EscposImage.
*
* @param string $filename Path to image filename, or null to create an
empty image.
* @param boolean $allowOptimisations True (default) to use any
library-specific tricks
* to speed up rendering, false to force the image to be read in
pixel-by-pixel,
* which is easier to unit test and more reproducible between systems,
but slower.
*/
public function __construct($filename = null, $allowOptimisations =
true)
{
$this -> filename = $filename;
$this -> allowOptimisations = $allowOptimisations;
}
/**
* @return int height of the image in pixels
*/
public function getHeight()
{
return $this -> imgHeight;
}
/**
* @return int Number of bytes to represent a row of this image
*/
public function getHeightBytes()
{
return (int)(($this -> imgHeight + 7) / 8);
}
/**
* @return int Width of the image
*/
public function getWidth()
{
return $this -> imgWidth;
}
/**
* @return int Number of bytes to represent a row of this image
*/
public function getWidthBytes()
{
return (int)(($this -> imgWidth + 7) / 8);
}
/**
* Output the image in raster (row) format. This can result in padding
on the
* right of the image, if its width is not divisible by 8.
*
* @throws Exception Where the generated data is unsuitable for the
printer
* (indicates a bug or oversized image).
* @return string The image in raster format.
*/
public function toRasterFormat()
{
// Just wraps implementations for caching & lazy loading
if ($this -> imgRasterData !== null) {
/* Return cached value */
return $this -> imgRasterData;
}
if ($this -> allowOptimisations) {
/* Use optimised code if allowed */
$this -> imgRasterData = $this ->
getRasterFormatFromFile($this -> filename);
}
if ($this -> imgRasterData === null) {
/* Load in full image and render the slow way if no faster
implementation
is available, or if we've been asked not to use it */
if ($this -> imgData === null) {
$this -> loadImageData($this -> filename);
}
$this -> imgRasterData = $this -> getRasterFormat();
}
return $this -> imgRasterData;
}
/**
* Output the image in column format.
*
* @param boolean $doubleDensity True for double density (24px) lines,
false for single-density (8px) lines.
* @return string[] an array, one item per line of output. All lines
will be of equal size.
*/
public function toColumnFormat($doubleDensity = false)
{
// Just wraps implementations for caching and lazy loading
if (isset($this -> imgColumnData[$doubleDensity])) {
/* Return cached value */
return $this -> imgColumnData[$doubleDensity];
}
$this -> imgColumnData[$doubleDensity] = null;
if ($this -> allowOptimisations) {
/* Use optimised code if allowed */
$data = $this -> getColumnFormatFromFile($this ->
filename, $doubleDensity);
$this -> imgColumnData[$doubleDensity] = $data;
}
if ($this -> imgColumnData[$doubleDensity] === null) {
/* Load in full image and render the slow way if no faster
implementation
is available, or if we've been asked not to use it */
if ($this -> imgData === null) {
$this -> loadImageData($this -> filename);
}
$this -> imgColumnData[$doubleDensity] = $this ->
getColumnFormat($doubleDensity);
}
return $this -> imgColumnData[$doubleDensity];
}
/**
* Load an image from disk. This default implementation always gives a
zero-sized image.
*
* @param string $filename Filename to load from.
*/
protected function loadImageData($filename = null)
{
// Load image in to string of 1's and 0's, also set width
& height
$this -> setImgWidth(0);
$this -> setImgHeight(0);
$this -> setImgData("");
}
/**
* Set image data.
*
* @param string $data Image data to use, string of 1's (black)
and 0's (white) in row-major order.
*/
protected function setImgData($data)
{
$this -> imgData = $data;
}
/**
* Set image width.
*
* @param int $width width of the image
*/
protected function setImgWidth($width)
{
$this -> imgWidth = $width;
}
/**
* Set image height.
*
* @param int $height height of the image.
*/
protected function setImgHeight($height)
{
$this -> imgHeight = $height;
}
/**
* @param string $filename
* Filename to load from
* @return string|NULL
* Raster format data, or NULL if no optimised renderer is available
in
* this implementation.
*/
protected function getRasterFormatFromFile($filename = null)
{
// No optimised implementation to provide
return null;
}
/**
* @param string $filename
* Filename to load from
* @param boolean $highDensityVertical
* True for high density output (24px lines), false for regular
density (8px)
* @return string[]|NULL
* Column format data as array, or NULL if optimised renderer
isn't
* available in this implementation.
*/
protected function getColumnFormatFromFile($filename = null,
$highDensityVertical = true)
{
// No optimised implementation to provide
return null;
}
/**
* Get column fromat from loaded image pixels, line by line.
*
* @throws Exception
* Where wrong number of bytes has been generated.
* @return string
* Raster format data
*/
private function getRasterFormat()
{
/* Loop through and convert format */
$widthPixels = $this -> getWidth();
$heightPixels = $this -> getHeight();
$widthBytes = $this -> getWidthBytes();
$heightBytes = $this -> getHeightBytes();
$x = $y = $bit = $byte = $byteVal = 0;
$data = str_repeat("\0", $widthBytes * $heightPixels);
if (strlen($data) == 0) {
return $data;
}
do {
$byteVal |= (int)$this -> imgData[$y * $widthPixels + $x]
<< (7 - $bit);
$x++;
$bit++;
if ($x >= $widthPixels) {
$x = 0;
$y++;
$bit = 8;
if ($y >= $heightPixels) {
$data[$byte] = chr($byteVal);
break;
}
}
if ($bit >= 8) {
$data[$byte] = chr($byteVal);
$byteVal = 0;
$bit = 0;
$byte++;
}
} while (true);
if (strlen($data) != ($this -> getWidthBytes() * $this ->
getHeight())) {
throw new Exception("Bug in " . __FUNCTION__ .
", wrong number of bytes.");
}
return $data;
}
/**
* Get column fromat from loaded image pixels, line by line.
*
* @param boolean $highDensity
* True for high density output (24px lines), false for regular
density (8px)
* @return string[]
* Array of column format data, one item per row.
*/
private function getColumnFormat($highDensity)
{
$out = array();
$i = 0;
while (($line = $this -> getColumnFormatLine($i, $highDensity))
!== null) {
$out[] = $line;
$i++;
}
return $out;
}
/**
* Output image in column format. Must be called once for each line of
output.
*
* @param string $lineNo
* Line number to retrieve
* @param string $highDensity
* True for high density output (24px lines), false for regular
density (8px)
* @throws Exception
* Where wrong number of bytes has been generated.
* @return NULL|string
* Column format data, or null if there is no more data (when
iterating)
*/
private function getColumnFormatLine($lineNo, $highDensity)
{
// Currently double density in both directions, very experimental
$widthPixels = $this -> getWidth();
$heightPixels = $this -> getHeight();
$widthBytes = $this -> getWidthBytes();
$heightBytes = $this -> getHeightBytes();
$lineHeight = $highDensity ? 3 : 1; // Vertical density. 1 or 3
(for 8 and 24 pixel lines)
// Initialise to zero
$x = $y = $bit = $byte = $byteVal = 0;
$data = str_repeat("\x00", $widthPixels * $lineHeight);
$yStart = $lineHeight * 8 * $lineNo;
if ($yStart >= $heightPixels) {
return null;
}
if (strlen($data) == 0) {
return $data;
}
do {
$yReal = $y + $yStart;
if ($yReal < $heightPixels) {
$byteVal |= (int)$this -> imgData[$yReal * $widthPixels
+ $x] << (7 - $bit);
}
$y++;
$bit++;
if ($y >= $lineHeight * 8) {
$y = 0;
$x++;
$bit = 8;
if ($x >= $widthPixels) {
$data[$byte] = chr($byteVal);
break;
}
}
if ($bit >= 8) {
$data[$byte] = chr($byteVal);
$byteVal = 0;
$bit = 0;
$byte++;
}
} while (true);
if (strlen($data) != $widthPixels * $lineHeight) {
throw new Exception("Bug in " . __FUNCTION__ .
", wrong number of bytes.");
}
return $data;
}
/**
* @return boolean True if GD is loaded, false otherwise
*/
public static function isGdLoaded()
{
return extension_loaded('gd');
}
/**
* @return boolean True if Imagick is loaded, false otherwise
*/
public static function isImagickLoaded()
{
return extension_loaded('imagick');
}
/**
* This is a convinience method to load an image from file,
auto-selecting
* an EscposImage implementation which uses an available library.
*
* The sub-classes can be constructed directly if you know that you
will
* have Imagick or GD on the print server.
*
* @param string $filename
* File to load from
* @param string $allow_optimisations
* True to allow the fastest rendering shortcuts, false to force the
library
* to read the image into an internal raster format and use PHP to
render
* the image (slower but less fragile).
* @param array $preferred
* Order to try to load libraries in- escpos-php supports pluggable
image
* libraries. Items can be 'imagick', 'gd',
'native'.
* @throws Exception
* Where no suitable library could be found for the type of file being
loaded.
* @return EscposImage
*
*/
public static function load(
$filename,
$allow_optimisations = true,
array $preferred = array('imagick', 'gd',
'native')
) {
/* Fail early if file is not readble */
if (!file_exists($filename) || !is_readable($filename)) {
throw new Exception("File '$filename' does not
exist, or is not readable.");
}
$ext = pathinfo($filename, PATHINFO_EXTENSION);
/* Choose the first implementation which can handle this format */
foreach ($preferred as $implemetnation) {
if ($implemetnation === 'imagick') {
if (!self::isImagickLoaded()) {
// Skip option if Imagick is not loaded
continue;
}
return new \Mike42\Escpos\ImagickEscposImage($filename,
$allow_optimisations);
} elseif ($implemetnation === 'gd') {
if (!self::isGdLoaded()) {
// Skip option if GD not loaded
continue;
}
return new \Mike42\Escpos\GdEscposImage($filename,
$allow_optimisations);
} elseif ($implemetnation === 'native') {
if (!in_array($ext, array('wbmp',
'pbm', 'bmp'))) {
// Pure PHP is fastest way to generate raster output
from wbmp and pbm formats.
continue;
}
return new \Mike42\Escpos\NativeEscposImage($filename,
$allow_optimisations);
} else {
// Something else on the 'preferred' list.
throw new
InvalidArgumentException("'$implemetnation' is not a known
EscposImage implementation");
}
}
throw new InvalidArgumentException("No suitable EscposImage
implementation found for '$filename'.");
}
}