diff --git a/htdocs/includes/mike42/escpos-php/.gitignore b/htdocs/includes/mike42/escpos-php/.gitignore index 3fe7ed0ea16..0e8fc4a31ee 100644 --- a/htdocs/includes/mike42/escpos-php/.gitignore +++ b/htdocs/includes/mike42/escpos-php/.gitignore @@ -10,6 +10,3 @@ doc/doxygen_sqlite3.db # composer files vendor/ - -# cache -src/Mike42/Escpos/PrintBuffers/cache/ \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/.travis.yml b/htdocs/includes/mike42/escpos-php/.travis.yml deleted file mode 100644 index 969cd6aad15..00000000000 --- a/htdocs/includes/mike42/escpos-php/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: php - -php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - hhvm - - nightly - -matrix: - allow_failures: - - php: nightly - -script: phpunit --configuration test/phpunit.xml --coverage-text - -install: - - composer install - diff --git a/htdocs/includes/mike42/escpos-php/Escpos.php b/htdocs/includes/mike42/escpos-php/Escpos.php new file mode 100644 index 00000000000..57e7eb2c8ac --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/Escpos.php @@ -0,0 +1,851 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This class generates ESC/POS printer control commands for compatible printers. + * See README.md for a summary of compatible printers and supported commands, and + * basic usage. + * + * See example/demo.php for a detailed print-out demonstrating the range of commands + * implemented in this project. + * + * Note that some functions have not been implemented: + * - Set paper sensors + * - Select print colour + * + * Please direct feature requests, bug reports and contributions to escpos-php + * on Github: + * - https://github.com/mike42/escpos-php + */ +require_once(dirname(__FILE__) . "/src/EscposImage.php"); +require_once(dirname(__FILE__) . "/src/PrintBuffer.php"); +require_once(dirname(__FILE__) . "/src/EscposPrintBuffer.php"); +require_once(dirname(__FILE__) . "/src/PrintConnector.php"); +require_once(dirname(__FILE__) . "/src/WindowsPrintConnector.php"); +require_once(dirname(__FILE__) . "/src/FilePrintConnector.php"); +require_once(dirname(__FILE__) . "/src/NetworkPrintConnector.php"); +require_once(dirname(__FILE__) . "/src/AbstractCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/DefaultCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/SimpleCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/EposTepCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/StarCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/P822DCapabilityProfile.php"); +require_once(dirname(__FILE__) . "/src/CodePage.php"); +require_once(dirname(__FILE__) . "/src/ImagePrintBuffer.php"); + +class Escpos { + /* ASCII codes */ + const NUL = "\x00"; + const LF = "\x0a"; + const ESC = "\x1b"; + const FS = "\x1c"; + const FF = "\x0c"; + const GS = "\x1d"; + const DLE = "\x10"; + const EOT = "\x04"; + + /* Barcode types */ + const BARCODE_UPCA = 65; + const BARCODE_UPCE = 66; + const BARCODE_JAN13 = 67; + const BARCODE_JAN8 = 68; + const BARCODE_CODE39 = 69; + const BARCODE_ITF = 70; + const BARCODE_CODABAR = 71; + const BARCODE_CODE93 = 72; + const BARCODE_CODE128 = 73; + + /* Barcode HRI (human-readable interpretation) text position */ + const BARCODE_TEXT_NONE = 0; + const BARCODE_TEXT_ABOVE = 1; + const BARCODE_TEXT_BELOW = 2; + + /* Cut types */ + const CUT_FULL = 65; + const CUT_PARTIAL = 66; + + /* Fonts */ + const FONT_A = 0; + const FONT_B = 1; + const FONT_C = 2; + + /* Image sizing options */ + const IMG_DEFAULT = 0; + const IMG_DOUBLE_WIDTH = 1; + const IMG_DOUBLE_HEIGHT = 2; + + /* Justifications */ + const JUSTIFY_LEFT = 0; + const JUSTIFY_CENTER = 1; + const JUSTIFY_RIGHT = 2; + + /* Print mode constants */ + const MODE_FONT_A = 0; + const MODE_FONT_B = 1; + const MODE_EMPHASIZED = 8; + const MODE_DOUBLE_HEIGHT = 16; + const MODE_DOUBLE_WIDTH = 32; + const MODE_UNDERLINE = 128; + + /* QR code error correction levels */ + const QR_ECLEVEL_L = 0; + const QR_ECLEVEL_M = 1; + const QR_ECLEVEL_Q = 2; + const QR_ECLEVEL_H = 3; + + /* QR code models */ + const QR_MODEL_1 = 1; + const QR_MODEL_2 = 2; + const QR_MICRO = 3; + + /* Printer statuses */ + const STATUS_PRINTER = 1; + const STATUS_OFFLINE_CAUSE = 2; + const STATUS_ERROR_CAUSE = 3; + const STATUS_PAPER_ROLL = 4; + const STATUS_INK_A = 7; + const STATUS_INK_B = 6; + const STATUS_PEELER = 8; + + /* Underline */ + const UNDERLINE_NONE = 0; + const UNDERLINE_SINGLE = 1; + const UNDERLINE_DOUBLE = 2; + + /** + * @var PrintBuffer The printer's output buffer. + */ + private $buffer; + + /** + * @var PrintConnector + */ + private $connector; + + /** + * @var AbstractCapabilityProfile + */ + private $profile; + + /** + * @var int Current character code table + */ + private $characterTable; + + /** + * Construct a new print object + * + * @param PrintConnector $connector The PrintConnector to send data to. If not set, output is sent to standard output. + * @param AbstractCapabilityProfile $profile Supported features of this printer. If not set, the DefaultCapabilityProfile will be used, which is suitable for Epson printers. + * @throws InvalidArgumentException + */ + function __construct(PrintConnector $connector = null, AbstractCapabilityProfile $profile = null) { + if(is_null($connector)) { + if(php_sapi_name() == 'cli') { + $connector = new FilePrintConnector("php://stdout"); + } else { + throw new InvalidArgumentException("Argument passed to Escpos::__construct() must implement interface PrintConnector, null given."); + } + } + /* Set connector */ + $this -> connector = $connector; + + /* Set capability profile */ + if($profile === null) { + $profile = DefaultCapabilityProfile::getInstance(); + } + $this -> profile = $profile; + /* Set buffer */ + $buffer = new EscposPrintBuffer(); + $this -> buffer = null; + $this -> setPrintBuffer($buffer); + $this -> initialize(); + } + + /** + * Print a barcode. + * + * @param string $content The information to encode. + * @param int $type The barcode standard to output. If not specified, `Escpos::BARCODE_CODE39` will be used. Note that some barcode formats only support specific lengths or sets of characters. + * @throws InvalidArgumentException Where the length or characters used in $content is invalid for the requested barcode format. + */ + function barcode($content, $type = self::BARCODE_CODE39) { + /* Validate input */ + self::validateInteger($type, 65, 73, __FUNCTION__, "Barcode type"); + $len = strlen($content); + switch($type) { + case self::BARCODE_UPCA: + self::validateInteger($len, 11, 12, __FUNCTION__, "UPCA barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{11,12}$/", "UPCA barcode content"); + break; + case self::BARCODE_UPCE: + self::validateIntegerMulti($len, array(array(6, 8), array(11, 12)), __FUNCTION__, "UPCE barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^([0-9]{6,8}|[0-9]{11,12})$/", "UPCE barcode content"); + break; + case self::BARCODE_JAN13: + self::validateInteger($len, 12, 13, __FUNCTION__, "JAN13 barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{12,13}$/", "JAN13 barcode content"); + break; + case self::BARCODE_JAN8: + self::validateInteger($len, 7, 8, __FUNCTION__, "JAN8 barcode content length"); + self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{7,8}$/", "JAN8 barcode content"); + break; + case self::BARCODE_CODE39: + self::validateInteger($len, 1, 255, __FUNCTION__, "CODE39 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$/", "CODE39 barcode content"); + break; + case self::BARCODE_ITF: + self::validateInteger($len, 2, 255, __FUNCTION__, "ITF barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^([0-9]{2})+$/", "ITF barcode content"); + break; + case self::BARCODE_CODABAR: + self::validateInteger($len, 1, 255, __FUNCTION__, "Codabar barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$/", "Codabar barcode content"); + break; + case self::BARCODE_CODE93: + self::validateInteger($len, 1, 255, __FUNCTION__, "Code93 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + self::validateStringRegex($content, __FUNCTION__, "/^[\\x00-\\x7F]+$/", "Code93 barcode content"); + break; + case self::BARCODE_CODE128: + self::validateInteger($len, 1, 255, __FUNCTION__, "Code128 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format. + // The CODE128 encoder is quite complex, so only a very basic header-check is applied here. + self::validateStringRegex($content, __FUNCTION__, "/^\{[A-C][\\x00-\\x7F]+$/", "Code128 barcode content"); + break; + } + if(!$this -> profile -> getSupportsBarcodeB()) { + // A simpler barcode command which supports fewer codes + self::validateInteger($type, 65, 71, __FUNCTION__); + $this -> connector -> write(self::GS . "k" . chr($type - 65) . $content . self::NUL); + return; + } + // More advanced function B, used in preference + $this -> connector -> write(self::GS . "k" . chr($type) . chr(strlen($content)) . $content); + } + + /** + * Print an image, using the older "bit image" command. This creates padding on the right of the image, + * if its width is not divisible by 8. + * + * Should only be used if your printer does not support the graphics() command. + * + * @param EscposImage $img The image to print + * @param EscposImage $size Size modifier for the image. + */ + function bitImage(EscposImage $img, $size = self::IMG_DEFAULT) { + self::validateInteger($size, 0, 3, __FUNCTION__); + $header = self::dataHeader(array($img -> getWidthBytes(), $img -> getHeight()), true); + $this -> connector -> write(self::GS . "v0" . chr($size) . $header); + $this -> connector -> write($img -> toRasterFormat()); + } + + /** + * Close the underlying buffer. With some connectors, the + * job will not actually be sent to the printer until this is called. + */ + function close() { + $this -> connector -> finalize(); + } + + /** + * Cut the paper. + * + * @param int $mode Cut mode, either Escpos::CUT_FULL or Escpos::CUT_PARTIAL. If not specified, `Escpos::CUT_FULL` will be used. + * @param int $lines Number of lines to feed + */ + function cut($mode = self::CUT_FULL, $lines = 3) { + // TODO validation on cut() inputs + $this -> connector -> write(self::GS . "V" . chr($mode) . chr($lines)); + } + + /** + * Print and feed line / Print and feed n lines. + * + * @param int $lines Number of lines to feed + */ + function feed($lines = 1) { + self::validateInteger($lines, 1, 255, __FUNCTION__); + if($lines <= 1) { + $this -> connector -> write(self::LF); + } else { + $this -> connector -> write(self::ESC . "d" . chr($lines)); + } + } + + /** + * Some printers require a form feed to release the paper. On most printers, this + * command is only useful in page mode, which is not implemented in this driver. + */ + function feedForm() { + $this -> connector -> write(self::FF); + } + + /** + * Print and reverse feed n lines. + * + * @param int $lines number of lines to feed. If not specified, 1 line will be fed. + */ + function feedReverse($lines = 1) { + self::validateInteger($lines, 1, 255, __FUNCTION__); + $this -> connector -> write(self::ESC . "e" . chr($lines)); + } + + /** + * @return number + */ + function getCharacterTable() { + return $this -> characterTable; + } + + /** + * @return PrintBuffer + */ + function getPrintBuffer() { + return $this -> buffer; + } + + /** + * @return PrintConnector + */ + function getPrintConnector() { + return $this -> connector; + } + + /** + * @return AbstractCapabilityProfile + */ + function getPrinterCapabilityProfile() { + return $this -> profile; + } + + /** + * @param int $type The type of status to request + * @return stdClass Class containing requested status, or null if either no status was received, or your print connector is unable to read from the printer. + */ + function getPrinterStatus($type = self::STATUS_PRINTER) { + self::validateIntegerMulti($type, array(array(1, 4), array(6, 8)), __FUNCTION__); + // Determine which flags we are looking for + $statusFlags = array( + self::STATUS_PRINTER => array( + 4 => "pulseHigh", // connector pin 3, see pulse(). + 8 => "offline", + 32 => "waitingForOnlineRecovery", + 64 => "feedButtonPressed" + ), + self::STATUS_OFFLINE_CAUSE => array( + 4 => "coverOpen", + 8 => "paperManualFeed", + 32 => "paperEnd", + 64 => "errorOccurred" + ), + self::STATUS_ERROR_CAUSE => array( + 4 => "recoverableError", + 8 => "autocutterError", + 32 => "unrecoverableError", + 64 => "autorecoverableError" + ), + self::STATUS_PAPER_ROLL => array( + 4 => "paperNearEnd", + 32 => "paperNotPresent" + ), + self::STATUS_INK_A => array( + 4 => "inkNearEnd", + 8 => "inkEnd", + 32 => "inkNotPresent", + 64 => "cleaning" + ), + self::STATUS_INK_B => array( + 4 => "inkNearEnd", + 8 => "inkEnd", + 32 => "inkNotPresent" + ), + self::STATUS_PEELER => array( + 4 => "labelWaitingForRemoval", + 32 => "labelPaperNotDetected" + ) + ); + $flags = $statusFlags[$type]; + // Clear any previous statuses which haven't been read yet + $f = $this -> connector -> read(1); + // Make request + $reqC = chr($type); + switch($type) { + // Special cases: These are two-character requests + case self::STATUS_INK_A: + $reqC = chr(7) . chr(1); + break; + case self::STATUS_INK_B: + $reqC = chr(7) . chr(2); + break; + case self::STATUS_PEELER: + $reqC = chr(8) . chr(3); + break; + } + $this -> connector -> write(self::DLE . self::EOT . $reqC); + // Wait for single-character response + $f = $this -> connector -> read(1); + $i = 0; + while($f === false && $i < 50000) { + usleep(100); + $f = $this -> connector -> read(1); + $i++; + } + if($f === false) { + // Timeout + return null; + } + $ret = new stdClass(); + foreach($flags as $num => $name) { + $ret -> $name = (ord($f) & $num) != 0; + } + return $ret; + } + + /** + * Print an image to the printer. + * + * Size modifiers are: + * - IMG_DEFAULT (leave image at original size) + * - IMG_DOUBLE_WIDTH + * - IMG_DOUBLE_HEIGHT + * + * See the example/ folder for detailed examples. + * + * The function bitImage() takes the same parameters, and can be used if + * your printer doesn't support the newer graphics commands. + * + * @param EscposImage $img The image to print. + * @param int $size Output size modifier for the image. + */ + function graphics(EscposImage $img, $size = self::IMG_DEFAULT) { + self::validateInteger($size, 0, 3, __FUNCTION__); + $imgHeader = self::dataHeader(array($img -> getWidth(), $img -> getHeight()), true); + $tone = '0'; + $colors = '1'; + $xm = (($size & self::IMG_DOUBLE_WIDTH) == self::IMG_DOUBLE_WIDTH) ? chr(2) : chr(1); + $ym = (($size & self::IMG_DOUBLE_HEIGHT) == self::IMG_DOUBLE_HEIGHT) ? chr(2) : chr(1); + $header = $tone . $xm . $ym . $colors . $imgHeader; + $this -> wrapperSendGraphicsData('0', 'p', $header . $img -> toRasterFormat()); + $this -> wrapperSendGraphicsData('0', '2'); + } + + /** + * Initialize printer. This resets formatting back to the defaults. + */ + function initialize() { + $this -> connector -> write(self::ESC . "@"); + $this -> characterTable = 0; + } + + /** + * Generate a pulse, for opening a cash drawer if one is connected. + * The default settings should open an Epson drawer. + * + * @param int $pin 0 or 1, for pin 2 or pin 5 kick-out connector respectively. + * @param int $on_ms pulse ON time, in milliseconds. + * @param int $off_ms pulse OFF time, in milliseconds. + */ + function pulse($pin = 0, $on_ms = 120, $off_ms = 240) { + self::validateInteger($pin, 0, 1, __FUNCTION__); + self::validateInteger($on_ms, 1, 511, __FUNCTION__); + self::validateInteger($off_ms, 1, 511, __FUNCTION__); + $this -> connector -> write(self::ESC . "p" . chr($pin + 48) . chr($on_ms / 2) . chr($off_ms / 2)); + } + + /** + * Print the given data as a QR code on the printer. + * + * @param string $content The content of the code. Numeric data will be more efficiently compacted. + * @param int $ec Error-correction level to use. One of Escpos::QR_ECLEVEL_L (default), Escpos::QR_ECLEVEL_M, Escpos::QR_ECLEVEL_Q or Escpos::QR_ECLEVEL_H. Higher error correction results in a less compact code. + * @param int $size Pixel size to use. Must be 1-16 (default 3) + * @param int $model QR code model to use. Must be one of Escpos::QR_MODEL_1, Escpos::QR_MODEL_2 (default) or Escpos::QR_MICRO (not supported by all printers). + */ + function qrCode($content, $ec = self::QR_ECLEVEL_L, $size = 3, $model = self::QR_MODEL_2) { + self::validateString($content, __FUNCTION__); + self::validateInteger($ec, 0, 3, __FUNCTION__); + self::validateInteger($size, 1, 16, __FUNCTION__); + self::validateInteger($model, 1, 3, __FUNCTION__); + if($content == "") { + return; + } + if(!$this -> profile -> getSupportsQrCode()) { + // TODO use software rendering via phpqrcode instead + throw new Exception("QR codes are not supported on your printer."); + } + $cn = '1'; // Code type for QR code + // Select model: 1, 2 or micro. + $this -> wrapperSend2dCodeData(chr(65), $cn, chr(48 + $model) . chr(0)); + // Set dot size. + $this -> wrapperSend2dCodeData(chr(67), $cn, chr($size)); + // Set error correction level: L, M, Q, or H + $this -> wrapperSend2dCodeData(chr(69), $cn, chr(48 + $ec)); + // Send content & print + $this -> wrapperSend2dCodeData(chr(80), $cn, $content, '0'); + $this -> wrapperSend2dCodeData(chr(81), $cn, '', '0'); + } + + /** + * Switch character table (code page) manually. Used in conjunction with textRaw() to + * print special characters which can't be encoded automatically. + * + * @param int $table The table to select. Available code tables are model-specific. + */ + function selectCharacterTable($table = 0) { + self::validateInteger($table, 0, 255, __FUNCTION__); + $supported = $this -> profile -> getSupportedCodePages(); + if(!isset($supported[$table])) { + throw new InvalidArgumentException("There is no code table $table allowed by this printer's capability profile."); + } + $this -> characterTable = $table; + if($this -> profile -> getSupportsStarCommands()) { + /* Not an ESC/POS command: STAR printers stash all the extra code pages under a different command. */ + $this -> connector -> write(self::ESC . self::GS . "t" . chr($table)); + return; + } + $this -> connector -> write(self::ESC . "t" . chr($table)); + } + + /** + * Select print mode(s). + * + * Several MODE_* constants can be OR'd together passed to this function's `$mode` argument. The valid modes are: + * - MODE_FONT_A + * - MODE_FONT_B + * - MODE_EMPHASIZED + * - MODE_DOUBLE_HEIGHT + * - MODE_DOUBLE_WIDTH + * - MODE_UNDERLINE + * + * @param int $mode The mode to use. Default is Escpos::MODE_FONT_A, with no special formatting. This has a similar effect to running initialize(). + */ + function selectPrintMode($mode = self::MODE_FONT_A) { + $allModes = self::MODE_FONT_B | self::MODE_EMPHASIZED | self::MODE_DOUBLE_HEIGHT | self::MODE_DOUBLE_WIDTH | self::MODE_UNDERLINE; + if(!is_integer($mode) || $mode < 0 || ($mode & $allModes) != $mode) { + throw new InvalidArgumentException("Invalid mode"); + } + + $this -> connector -> write(self::ESC . "!" . chr($mode)); + } + + /** + * Set barcode height. + * + * @param int $height Height in dots. If not specified, 8 will be used. + */ + function setBarcodeHeight($height = 8) { + self::validateInteger($height, 1, 255, __FUNCTION__); + $this -> connector -> write(self::GS . "h" . chr($height)); + } + + + /** + * Set the position for the Human Readable Interpretation (HRI) of barcode characters. + * + * @param position $position. Use Escpos::BARCODE_TEXT_NONE to hide the text (default), or any combination of Escpos::BARCODE_TEXT_TOP and Escpos::BARCODE_TEXT_BOTTOM flags to display the text. + */ + function setBarcodeTextPosition($position = self::BARCODE_TEXT_NONE) { + self::validateInteger($position, 0, 3, __FUNCTION__, "Barcode text position"); + $this -> connector -> write(self::GS . "H" . chr($position)); + } + + /** + * Turn double-strike mode on/off. + * + * @param boolean $on true for double strike, false for no double strike + */ + function setDoubleStrike($on = true) { + self::validateBoolean($on, __FUNCTION__); + $this -> connector -> write(self::ESC . "G". ($on ? chr(1) : chr(0))); + } + + /** + * Turn emphasized mode on/off. + * + * @param boolean $on true for emphasis, false for no emphasis + */ + function setEmphasis($on = true) { + self::validateBoolean($on, __FUNCTION__); + $this -> connector -> write(self::ESC . "E". ($on ? chr(1) : chr(0))); + } + + /** + * Select font. Most printers have two fonts (Fonts A and B), and some have a third (Font C). + * + * @param int $font The font to use. Must be either Escpos::FONT_A, Escpos::FONT_B, or Escpos::FONT_C. + */ + function setFont($font = self::FONT_A) { + self::validateInteger($font, 0, 2, __FUNCTION__); + $this -> connector -> write(self::ESC . "M" . chr($font)); + } + + /** + * Select justification. + * + * @param int $justification One of Escpos::JUSTIFY_LEFT, Escpos::JUSTIFY_CENTER, or Escpos::JUSTIFY_RIGHT. + */ + function setJustification($justification = self::JUSTIFY_LEFT) { + self::validateInteger($justification, 0, 2, __FUNCTION__); + $this -> connector -> write(self::ESC . "a" . chr($justification)); + } + + /** + * Attach a different print buffer to the printer. Buffers are responsible for handling text output to the printer. + * + * @param PrintBuffer $buffer The buffer to use. + * @throws InvalidArgumentException Where the buffer is already attached to a different printer. + */ + function setPrintBuffer(PrintBuffer $buffer) { + if($buffer === $this -> buffer) { + return; + } + if($buffer -> getPrinter() != null) { + throw new InvalidArgumentException("This buffer is already attached to a printer."); + } + if($this -> buffer !== null) { + $this -> buffer -> setPrinter(null); + } + $this -> buffer = $buffer; + $this -> buffer -> setPrinter($this); + } + + /** + * Set black/white reverse mode on or off. In this mode, text is printed white on a black background. + * + * @param boolean $on True to enable, false to disable. + */ + function setReverseColors($on = true) { + self::validateBoolean($on, __FUNCTION__); + $this -> connector -> write(self::GS . "B" . ($on ? chr(1) : chr(0))); + } + + /** + * Set the size of text, as a multiple of the normal size. + * + * @param int $widthMultiplier Multiple of the regular height to use (range 1 - 8) + * @param int $heightMultiplier Multiple of the regular height to use (range 1 - 8) + */ + function setTextSize($widthMultiplier, $heightMultiplier) { + self::validateInteger($widthMultiplier, 1, 8, __FUNCTION__); + self::validateInteger($heightMultiplier, 1, 8, __FUNCTION__); + $c = pow(2,4) * ($widthMultiplier - 1) + ($heightMultiplier - 1); + $this -> connector -> write(self::GS . "!" . chr($c)); + } + + /** + * Set underline for printed text. + * + * Argument can be true/false, or one of UNDERLINE_NONE, + * UNDERLINE_SINGLE or UNDERLINE_DOUBLE. + * + * @param int $underline Either true/false, or one of Escpos::UNDERLINE_NONE, Escpos::UNDERLINE_SINGLE or Escpos::UNDERLINE_DOUBLE. Defaults to Escpos::UNDERLINE_SINGLE. + */ + function setUnderline($underline = self::UNDERLINE_SINGLE) { + /* Map true/false to underline constants */ + if($underline === true) { + $underline = self::UNDERLINE_SINGLE; + } else if($underline === false) { + $underline = self::UNDERLINE_NONE; + } + /* Set the underline */ + self::validateInteger($underline, 0, 2, __FUNCTION__); + $this -> connector -> write(self::ESC . "-". chr($underline)); + } + + /** + * Add text to the buffer. + * + * Text should either be followed by a line-break, or feed() should be called + * after this to clear the print buffer. + * + * @param string $str Text to print + */ + function text($str = "") { + self::validateString($str, __FUNCTION__); + $this -> buffer -> writeText((string)$str); + } + + /** + * Add text to the buffer without attempting to interpret chararacter codes. + * + * Text should either be followed by a line-break, or feed() should be called + * after this to clear the print buffer. + * + * @param string $str Text to print + */ + function textRaw($str = "") { + self::validateString($str, __FUNCTION__); + $this -> buffer -> writeTextRaw((string)$str); + } + + /** + * Wrapper for GS ( k, to calculate and send correct data length. + * + * @param string $fn Function to use + * @param string $cn Output code type. Affects available data + * @param string $data Data to send. + * @param string $m Modifier/variant for function. Often '0' where used. + * @throws InvalidArgumentException Where the input lengths are bad. + */ + private function wrapperSend2dCodeData($fn, $cn, $data = '', $m = '') { + if(strlen($m) > 1 || strlen($cn) != 1 || strlen($fn) != 1) { + throw new InvalidArgumentException("wrapperSend2dCodeData: cn and fn must be one character each."); + } + $header = $this -> intLowHigh(strlen($data) + strlen($m) + 2, 2); + $this -> connector -> write(self::GS . "(k" . $header . $cn . $fn . $m . $data); + } + + /** + * Wrapper for GS ( L, to calculate and send correct data length. + * + * @param string $m Modifier/variant for function. Usually '0'. + * @param string $fn Function number to use, as character. + * @param string $data Data to send. + * @throws InvalidArgumentException Where the input lengths are bad. + */ + private function wrapperSendGraphicsData($m, $fn, $data = '') { + if(strlen($m) != 1 || strlen($fn) != 1) { + throw new InvalidArgumentException("wrapperSendGraphicsData: m and fn must be one character each."); + } + $header = $this -> intLowHigh(strlen($data) + 2, 2); + $this -> connector -> write(self::GS . "(L" . $header . $m . $fn . $data); + } + + /** + * Convert widths and heights to characters. Used before sending graphics to set the size. + * + * @param array $inputs + * @param boolean $long True to use 4 bytes, false to use 2 + * @return string + */ + private static function dataHeader(array $inputs, $long = true) { + $outp = array(); + foreach($inputs as $input) { + if($long) { + $outp[] = Escpos::intLowHigh($input, 2); + } else { + self::validateInteger($input, 0 , 255, __FUNCTION__); + $outp[] = chr($input); + } + } + return implode("", $outp); + } + + /** + * Generate two characters for a number: In lower and higher parts, or more parts as needed. + * @param int $int Input number + * @param int $length The number of bytes to output (1 - 4). + */ + private static function intLowHigh($input, $length) { + $maxInput = (256 << ($length * 8) - 1); + self::validateInteger($length, 1, 4, __FUNCTION__); + self::validateInteger($input, 0, $maxInput, __FUNCTION__); + $outp = ""; + for($i = 0; $i < $length; $i++) { + $outp .= chr($input % 256); + $input = (int)($input / 256); + } + return $outp; + } + + /** + * Throw an exception if the argument given is not a boolean + * + * @param boolean $test the input to test + * @param string $source the name of the function calling this + */ + protected static function validateBoolean($test, $source) { + if(!($test === true || $test === false)) { + throw new InvalidArgumentException("Argument to $source must be a boolean"); + } + } + + /** + * Throw an exception if the argument given is not an integer within the specified range + * + * @param int $test the input to test + * @param int $min the minimum allowable value (inclusive) + * @param int $max the maximum allowable value (inclusive) + * @param string $source the name of the function calling this + * @param string $argument the name of the invalid parameter + */ + protected static function validateInteger($test, $min, $max, $source, $argument = "Argument") { + self::validateIntegerMulti($test, array(array($min, $max)), $source, $argument); + } + + /** + * Throw an exception if the argument given is not an integer within one of the specified ranges + * + * @param int $test the input to test + * @param arrray $ranges array of two-item min/max ranges. + * @param string $source the name of the function calling this + * @param string $source the name of the function calling this + * @param string $argument the name of the invalid parameter + */ + protected static function validateIntegerMulti($test, array $ranges, $source, $argument = "Argument") { + if(!is_integer($test)) { + throw new InvalidArgumentException("$argument given to $source must be a number, but '$test' was given."); + } + $match = false; + foreach($ranges as $range) { + $match |= $test >= $range[0] && $test <= $range[1]; + } + if(!$match) { + // Put together a good error "range 1-2 or 4-6" + $rangeStr = "range "; + for($i = 0; $i < count($ranges); $i++) { + $rangeStr .= $ranges[$i][0] . "-" . $ranges[$i][1]; + if($i == count($ranges) - 1) { + continue; + } else if($i == count($ranges) - 2) { + $rangeStr .= " or "; + } else { + $rangeStr .= ", "; + } + } + throw new InvalidArgumentException("$argument given to $source must be in $rangeStr, but $test was given."); + } + } + + /** + * Throw an exception if the argument given can't be cast to a string + * + * @param string $test the input to test + * @param string $source the name of the function calling this + * @param string $argument the name of the invalid parameter + */ + protected static function validateString($test, $source, $argument = "Argument") { + if (is_object($test) && !method_exists($test, '__toString')) { + throw new InvalidArgumentException("$argument to $source must be a string"); + } + } + + protected static function validateStringRegex($test, $source, $regex, $argument = "Argument") { + if(preg_match($regex, $test) === 0) { + throw new InvalidArgumentException("$argument given to $source is invalid. It should match regex '$regex', but '$test' was given."); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/README.md b/htdocs/includes/mike42/escpos-php/README.md index 065fdea3768..43798bb0852 100644 --- a/htdocs/includes/mike42/escpos-php/README.md +++ b/htdocs/includes/mike42/escpos-php/README.md @@ -1,10 +1,73 @@ -# ESC/POS Print Driver for PHP - +ESC/POS Print Driver for PHP +============================ This project implements a subset of Epson's ESC/POS protocol for thermal receipt printers. It allows you to generate and print receipts with basic formatting, cutting, and barcodes on a compatible printer. The library was developed to add drop-in support for receipt printing to any PHP app, including web-based point-of-sale (POS) applications. -## Compatibility +Basic usage +----------- +A "hello world" receipt can be generated easily (Call this `hello-world.php`): +```php + text("Hello World!\n"); +$printer -> cut(); +$printer -> close(); +``` +This would be printed as: +``` +# Networked printer +php hello-world.php | nc 10.x.x.x. 9100 +# Local printer +php hello-world.php > /dev/... +# Windows local printer +php hello-world.php > foo.txt +net use LPT1 \\server\printer +copy foo.txt LPT1 +del foo.txt +``` + +From your web app, you could pass the output directly to a socket if your printer is networked: +```php + text("Hello World!\n"); +$printer -> cut(); +$printer -> close(); +``` + +Or to a local printer: +```php + text("Hello World!\n"); +$printer -> cut(); +$printer -> close(); +``` + +### Basic workflow +The library should be initialised with a PrintConnector, which will pass on the data to your printer. +Use the table under "Compatibility", or the examples below to choose the appropriate connector for your +platform & interface. If no connector is specified, then standard output is used. + +When you have finished using the print object, call `close()` to finalize any data transfers. + +### Tips & examples +On Linux, your printer device file will be somewhere like `/dev/lp0` (parallel), `/dev/usb/lp1` (USB), `/dev/ttyUSB0` (USB-Serial), `/dev/ttyS0` (serial). + +On Windows, the device files will be along the lines of `LPT1` (parallel) or `COM1` (serial). Use the `WindowsPrintConnector` to tap into system printing on Windows (eg. [Windows USB](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-usb.php), [SMB](https://github.com/mike42/escpos-php/tree/master/example/interface/smb.php) or [Windows LPT](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-lpt.php)) - this submits print jobs via a queue rather than communicating directly with the printer. + +A complete real-world receipt can be found in the code of [Auth](https://github.com/mike42/Auth) in [ReceiptPrinter.php](https://github.com/mike42/Auth/blob/master/lib/misc/ReceiptPrinter.php). It includes justification, boldness, and a barcode. + +Other examples are located in the [example/](https://github.com/mike42/escpos-php/blob/master/example/) directory. + +Compatibility +------------- ### Interfaces and operating systems This driver is known to work with the following OS/interface combinations: @@ -52,19 +115,11 @@ This driver is known to work with the following OS/interface combinations: No Yes - -CUPS hosted -Yes -Yes -No - ### Printers Many thermal receipt printers support ESC/POS to some degree. This driver has been known to work with: -- Bixolon SRP-350III -- Citizen CBM1000-II - EPOS TEP 220M - Epson TM-T88III - Epson TM-T88IV @@ -74,12 +129,9 @@ Many thermal receipt printers support ESC/POS to some degree. This driver has be - Epson TM-T70II - Epson TM-U220 - Epson FX-890 (requires `feedForm()` to release paper). -- Excelvan HOP-E58 (connect through powered hub) - Okipos 80 Plus III - P-822D -- P85A-401 (make unknown) - SEYPOS PRP-300 (Also marketed as TYSSO PRP-300) -- Silicon SP-201 / RP80USE - Star TSP-650 - Star TUP-592 - Xprinter XP-Q800 @@ -87,152 +139,16 @@ Many thermal receipt printers support ESC/POS to some degree. This driver has be - Zijang ZJ-5870 - Zijang ZJ-5890T (Marketed as POS 5890T) -If you use any other printer with this code, please [let us know](https://github.com/mike42/escpos-php/issues/new) so that it can be added to the list. +If you use any other printer with this code, please let me know so I can add it to the list. -## Basic usage - -### Include the library - -#### Composer -If you are using composer, then add `mike42/escpos-php` as a dependency: - -```` -composer require mike42/escpos-php -```` - -In this case, you would include composer's auto-loader at the top of your source files: - -```` - text("Hello World!\n"); -$printer -> cut(); -$printer -> close(); -``` - -Some examples are below for common interfaces. - -Communicate with a printer with an Ethernet interface using `netcat`: -```` -php hello-world.php | nc 10.x.x.x. 9100 -```` - -A USB local printer connected with `usblp` on Linux has a device file (Includes USB-parallel interfaces): -```` -php hello-world.php > /dev/usb/lp0 -```` - -A computer installed into the local `cups` server is accessed through `lp` or `lpr`: -```` -php hello-world.php > foo.txt -lpr -o raw -H localhost -P printer foo.txt -```` - -A local or networked printer on a Windows computer is mapped in to a file, and generally requires you to share the printer first: - -```` -php hello-world.php > foo.txt -net use LPT1 \\server\printer -copy foo.txt LPT1 -del foo.txt -``` - -If you have troubles at this point, then you should consult your OS and printer system documentation to try to find a working print command. - -### Using a PrintConnector - -To print receipts from PHP, use the most applicable [PrintConnector](https://github.com/mike42/escpos-php/tree/master/src/Mike42/Escpos/PrintConnectors) for your setup. The connector simply provides the plumbing to get data to the printer. - -For example, a `NetworkPrintConnector` accepts an IP address and port: - -````php -use Mike42\Escpos\PrintConnectors\NetworkPrintConnector; -use Mike42\Escpos\Printer; -$connector = new NetworkPrintConnector("10.x.x.x", 9100); -$printer = new Printer($connector); -try { - // ... Print stuff -} finally { - $printer -> close(); -} -```` - -While a serial printer might use: -```php -use Mike42\Escpos\PrintConnectors\FilePrintConnector; -use Mike42\Escpos\Printer; -$connector = new FilePrintConnector("/dev/ttyS0"); -$printer = new Printer($connector); -``` - -For each OS/interface combination that's supported, there are examples in the compatibility section of how a `PrintConnector` would be constructed. If you can't get a `PrintConnector` to work, then be sure to include the working print command in bug. - -### Using a CapabilityProfile - -Support for commands and code pages varies between printer vendors and models. By default, the driver will accept UTF-8, and output commands that are suitable for Epson TM-series printers. - -When trying out a new brand of printer, it's a good idea to use the `SimpleCapabilityProfile`, which instructs the driver to avoid the use of advanced features (generally simpler image handling, ASCII-only text). - -```php -use Mike42\Escpos\PrintConnectors\FilePrintConnector; -use Mike42\Escpos\CapabilityProfiles\SimpleCapabilityProfile; -$connector = new WindowsPrintConnector("smb://computer/printer"); -$printer = new Printer($connector, $profile); -``` - -As another example, Star-branded printers use different commands: - -```php -use Mike42\Escpos\PrintConnectors\FilePrintConnector; -use Mike42\Escpos\CapabilityProfiles\StarCapabilityProfile; -$connector = new WindowsPrintConnector("smb://computer/printer"); -$printer = new Printer($connector, $profile); -``` - -Further developing this mechanism is a priority for future releases. - -### Tips & examples -On Linux, your printer device file will be somewhere like `/dev/lp0` (parallel), `/dev/usb/lp1` (USB), `/dev/ttyUSB0` (USB-Serial), `/dev/ttyS0` (serial). - -On Windows, the device files will be along the lines of `LPT1` (parallel) or `COM1` (serial). Use the `WindowsPrintConnector` to tap into system printing on Windows (eg. [Windows USB](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-usb.php), [SMB](https://github.com/mike42/escpos-php/tree/master/example/interface/smb.php) or [Windows LPT](https://github.com/mike42/escpos-php/tree/master/example/interface/windows-lpt.php)) - this submits print jobs via a queue rather than communicating directly with the printer. - -A complete real-world receipt can be found in the code of [Auth](https://github.com/mike42/Auth) in [ReceiptPrinter.php](https://github.com/mike42/Auth/blob/master/lib/misc/ReceiptPrinter.php). It includes justification, boldness, and a barcode. - -Other examples are located in the [example/](https://github.com/mike42/escpos-php/blob/master/example/) directory. - -## Available methods +Available methods +----------------- ### __construct(PrintConnector $connector, AbstractCapabilityProfile $profile) Construct new print object. Parameters: -- `PrintConnector $connector`: The PrintConnector to send data to. +- `PrintConnector $connector`: The PrintConnector to send data to. If not set, output is sent to standard output. - `AbstractCapabilityProfile $profile` Supported features of this printer. If not set, the DefaultCapabilityProfile will be used, which is suitable for Epson printers. See [example/interface/]("https://github.com/mike42/escpos-php/tree/master/example/interface/) for ways to open connections for different platforms and interfaces. @@ -243,7 +159,7 @@ Print a barcode. Parameters: - `string $content`: The information to encode. -- `int $type`: The barcode standard to output. If not specified, `Printer::BARCODE_CODE39` will be used. +- `int $type`: The barcode standard to output. If not specified, `Escpos::BARCODE_CODE39` will be used. Currently supported barcode standards are (depending on your printer): @@ -265,7 +181,7 @@ Cut the paper. Parameters: -- `int $mode`: Cut mode, either `Printer::CUT_FULL` or `Printer::CUT_PARTIAL`. If not specified, `Printer::CUT_FULL` will be used. +- `int $mode`: Cut mode, either `Escpos::CUT_FULL` or `Escpos::CUT_PARTIAL`. If not specified, `Escpos::CUT_FULL` will be used. - `int $lines`: Number of lines to feed before cutting. If not specified, 3 will be used. ### feed($lines) @@ -309,7 +225,7 @@ $printer -> graphics($img); See the [example/](https://github.com/mike42/escpos-php/blob/master/example/) folder for detailed examples. -The function [bitImage()](#bitimageescposimage-image-size) takes the same parameters, and can be used if your printer doesn't support the newer graphics commands. As an additional fallback, the `bitImageColumnFormat()` function is also provided. +The function [bitImage()](#bitimageescposimage-image-size) takes the same parameters, and can be used if your printer doesn't support the newer graphics commands. ### initialize() Initialize printer. This resets formatting back to the defaults. @@ -327,16 +243,16 @@ Parameters: Print the given data as a QR code on the printer. - `string $content`: The content of the code. Numeric data will be more efficiently compacted. -- `int $ec` Error-correction level to use. One of `Printer::QR_ECLEVEL_L` (default), `Printer::QR_ECLEVEL_M`, `Printer::QR_ECLEVEL_Q` or `Printer::QR_ECLEVEL_H`. Higher error correction results in a less compact code. +- `int $ec` Error-correction level to use. One of `Escpos::QR_ECLEVEL_L` (default), `Escpos::QR_ECLEVEL_M`, `Escpos::QR_ECLEVEL_Q` or `Escpos::QR_ECLEVEL_H`. Higher error correction results in a less compact code. - `int $size`: Pixel size to use. Must be 1-16 (default 3) -- `int $model`: QR code model to use. Must be one of `Printer::QR_MODEL_1`, `Printer::QR_MODEL_2` (default) or `Printer::QR_MICRO` (not supported by all printers). +- `int $model`: QR code model to use. Must be one of `Escpos::QR_MODEL_1`, `Escpos::QR_MODEL_2` (default) or `Escpos::QR_MICRO` (not supported by all printers). ### selectPrintMode($mode) Select print mode(s). Parameters: -- `int $mode`: The mode to use. Default is `Printer::MODE_FONT_A`, with no special formatting. This has a similar effect to running `initialize()`. +- `int $mode`: The mode to use. Default is `Escpos::MODE_FONT_A`, with no special formatting. This has a similar effect to running `initialize()`. Several MODE_* constants can be OR'd together passed to this function's `$mode` argument. The valid modes are: @@ -354,13 +270,6 @@ Parameters: - `int $height`: Height in dots. If not specified, 8 will be used. -### setColor($color) -Select print color - on printers that support multiple colors. - -Parameters: - -- `int $color`: Color to use. Must be either `Printer::COLOR_1` (default), or `Printer::COLOR_2` - ### setDoubleStrike($on) Turn double-strike mode on/off. @@ -380,14 +289,14 @@ Select font. Most printers have two fonts (Fonts A and B), and some have a third Parameters: -- `int $font`: The font to use. Must be either `Printer::FONT_A`, `Printer::FONT_B`, or `Printer::FONT_C`. +- `int $font`: The font to use. Must be either `Escpos::FONT_A`, `Escpos::FONT_B`, or `Escpos::FONT_C`. ### setJustification($justification) Select justification. Parameters: -- `int $justification`: One of `Printer::JUSTIFY_LEFT`, `Printer::JUSTIFY_CENTER`, or `Printer::JUSTIFY_RIGHT`. +- `int $justification`: One of `Escpos::JUSTIFY_LEFT`, `Escpos::JUSTIFY_CENTER`, or `Escpos::JUSTIFY_RIGHT`. ### setReverseColors($on) Set black/white reverse mode on or off. In this mode, text is printed white on a black background. @@ -409,7 +318,7 @@ Set underline for printed text. Parameters: -- `int $underline`: Either `true`/`false`, or one of `Printer::UNDERLINE_NONE`, `Printer::UNDERLINE_SINGLE` or `Printer::UNDERLINE_DOUBLE`. Defaults to `Printer::UNDERLINE_SINGLE`. +- `int $underline`: Either `true`/`false`, or one of `Escpos::UNDERLINE_NONE`, `Escpos::UNDERLINE_SINGLE` or `Escpos::UNDERLINE_DOUBLE`. Defaults to `Escpos::UNDERLINE_SINGLE`. ### text($str) Add text to the buffer. Text should either be followed by a line-break, or `feed()` should be called after this. @@ -418,46 +327,26 @@ Parameters: - `string $str`: The string to print. -# Further notes +Further notes +------------- Posts I've written up for people who are learning how to use receipt printers: * [What is ESC/POS, and how do I use it?](http://mike.bitrevision.com/blog/what-is-escpos-and-how-do-i-use-it), which documents the output of test.php. * [Setting up an Epson receipt printer](http://mike.bitrevision.com/blog/2014-20-26-setting-up-an-epson-receipt-printer) * [Getting a USB receipt printer working on Linux](http://mike.bitrevision.com/blog/2015-03-getting-a-usb-receipt-printer-working-on-linux) -# Development - -This code is MIT licensed, and you are encouraged to contribute any modifications back to the project. - -For development, it's suggested that you load `imagick` and `gd` `Xdebug` PHP modules, and install `composer` and `phpunit`. - -The tests are executed on [Travis CI](https://travis-ci.org/mike42/escpos-php) over versions of PHP from 5.3 up to 5.6, 7, and HHVM. Earlier versions of PHP are not supported. - -Fetch a copy of this code and load idependencies with composer: - - git clone https://github.com/mike42/escpos-php - cd escpos-php/ - composer install - -Execute unit tests via `phpunit`: - - phpunit --configuration test/phpunit.xml --coverage-text - -Pull requests and bug reports welcome. - - +- [ronisaha/php-esc-pos](https://github.com/ronisaha/php-esc-pos) - + diff --git a/htdocs/includes/mike42/escpos-php/autoload.php b/htdocs/includes/mike42/escpos-php/autoload.php deleted file mode 100644 index 7779486b824..00000000000 --- a/htdocs/includes/mike42/escpos-php/autoload.php +++ /dev/null @@ -1,26 +0,0 @@ -setBarcodeHeight ( 40 ); + +/* Text position */ +$printer->selectPrintMode ( Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_DOUBLE_WIDTH ); +$printer->text ( "Text position\n" ); +$printer->selectPrintMode (); +$hri = array ( + Escpos::BARCODE_TEXT_NONE => "No text", + Escpos::BARCODE_TEXT_ABOVE => "Above", + Escpos::BARCODE_TEXT_BELOW => "Below", + Escpos::BARCODE_TEXT_ABOVE | Escpos::BARCODE_TEXT_BELOW => "Both" +); +foreach ( $hri as $position => $caption ) { + $printer->text ( $caption . "\n" ); + $printer->setBarcodeTextPosition ( $position ); + $printer->barcode ( "012345678901", Escpos::BARCODE_JAN13 ); + $printer->feed (); +} + +/* Barcode types */ +$standards = array ( + Escpos::BARCODE_UPCA => array ( + "title" => "UPC-A", + "caption" => "Fixed-length numeric product barcodes.", + "example" => array ( + array ( + "caption" => "12 char numeric including (wrong) check digit.", + "content" => "012345678901" + ), + array ( + "caption" => "Send 11 chars to add check digit automatically.", + "content" => "01234567890" + ) + ) + ), + Escpos::BARCODE_UPCE => array ( + "title" => "UPC-E", + "caption" => "Fixed-length numeric compact product barcodes.", + "example" => array ( + array ( + "caption" => "6 char numeric - auto check digit & NSC", + "content" => "123456" + ), + array ( + "caption" => "7 char numeric - auto check digit", + "content" => "0123456" + ), + array ( + "caption" => "8 char numeric", + "content" => "01234567" + ), + array ( + "caption" => "11 char numeric - auto check digit", + "content" => "01234567890" + ), + array ( + "caption" => "12 char numeric including (wrong) check digit", + "content" => "012345678901" + ) + ) + ), + Escpos::BARCODE_JAN13 => array ( + "title" => "JAN13/EAN13", + "caption" => "Fixed-length numeric barcodes.", + "example" => array ( + array ( + "caption" => "12 char numeric - auto check digit", + "content" => "012345678901" + ), + array ( + "caption" => "13 char numeric including (wrong) check digit", + "content" => "0123456789012" + ) + ) + ), + Escpos::BARCODE_JAN8 => array ( + "title" => "JAN8/EAN8", + "caption" => "Fixed-length numeric barcodes.", + "example" => array ( + array ( + "caption" => "7 char numeric - auto check digit", + "content" => "0123456" + ), + array ( + "caption" => "8 char numeric including (wrong) check digit", + "content" => "01234567" + ) + ) + ), + Escpos::BARCODE_CODE39 => array ( + "title" => "Code39", + "caption" => "Variable length alphanumeric w/ some special chars.", + "example" => array ( + array ( + "caption" => "Text, numbers, spaces", + "content" => "ABC 012" + ), + array ( + "caption" => "Special characters", + "content" => "$%+-./" + ), + array ( + "caption" => "Extra char (*) Used as start/stop", + "content" => "*TEXT*" + ) + ) + ), + Escpos::BARCODE_ITF => array ( + "title" => "ITF", + "caption" => "Variable length numeric w/even number of digits,\nas they are encoded in pairs.", + "example" => array ( + array ( + "caption" => "Numeric- even number of digits", + "content" => "0123456789" + ) + ) + ), + Escpos::BARCODE_CODABAR => array ( + "title" => "Codabar", + "caption" => "Varaible length numeric with some allowable\nextra characters. ABCD/abcd must be used as\nstart/stop characters (one at the start, one\nat the end) to distinguish between barcode\napplications.", + "example" => array ( + array ( + "caption" => "Numeric w/ A A start/stop. ", + "content" => "A012345A" + ), + array ( + "caption" => "Extra allowable characters", + "content" => "A012$+-./:A" + ) + ) + ), + Escpos::BARCODE_CODE93 => array ( + "title" => "Code93", + "caption" => "Variable length- any ASCII is available", + "example" => array ( + array ( + "caption" => "Text", + "content" => "012abcd" + ) + ) + ), + Escpos::BARCODE_CODE128 => array ( + "title" => "Code128", + "caption" => "Variable length- any ASCII is available", + "example" => array ( + array ( + "caption" => "Code set A uppercase & symbols", + "content" => "{A" . "012ABCD" + ), + array ( + "caption" => "Code set B general text", + "content" => "{B" . "012ABCDabcd" + ), + array ( + "caption" => "Code set C compact numbers\n Sending chr(21) chr(32) chr(43)", + "content" => "{C" . chr ( 21 ) . chr ( 32 ) . chr ( 43 ) + ) + ) + ) +); +$printer->setBarcodeTextPosition ( Escpos::BARCODE_TEXT_BELOW ); +foreach ( $standards as $type => $standard ) { + $printer->selectPrintMode ( Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_DOUBLE_WIDTH ); + $printer->text ( $standard ["title"] . "\n" ); + $printer->selectPrintMode (); + $printer->text ( $standard ["caption"] . "\n\n" ); + foreach ( $standard ["example"] as $id => $barcode ) { + $printer->setEmphasis ( true ); + $printer->text ( $barcode ["caption"] . "\n" ); + $printer->setEmphasis ( false ); + $printer->text ( "Content: " . $barcode ["content"] . "\n" ); + $printer->barcode ( $barcode ["content"], $type ); + $printer->feed (); + } +} +$printer->cut (); +$printer->close (); + diff --git a/htdocs/includes/mike42/escpos-php/example/bit-image.php b/htdocs/includes/mike42/escpos-php/example/bit-image.php new file mode 100644 index 00000000000..f3a4054d98f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/bit-image.php @@ -0,0 +1,32 @@ + text("These example images are printed with the older\nbit image print command. You should only use\n\$p -> bitImage() if \$p -> graphics() does not\nwork on your printer.\n\n"); + + $printer -> bitImage($tux); + $printer -> text("Regular Tux (bit image).\n"); + $printer -> feed(); + + $printer -> bitImage($tux, Escpos::IMG_DOUBLE_WIDTH); + $printer -> text("Wide Tux (bit image).\n"); + $printer -> feed(); + + $printer -> bitImage($tux, Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Tall Tux (bit image).\n"); + $printer -> feed(); + + $printer -> bitImage($tux, Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Large Tux in correct proportion (bit image).\n"); +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} + +$printer -> cut(); +$printer -> close(); +?> diff --git a/htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php b/htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php new file mode 100644 index 00000000000..a98bdbc96ae --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php @@ -0,0 +1,59 @@ + selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Implemented languages\n"); + $printer -> selectPrintMode(); + foreach($inputsOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + foreach($buffers as $buffer) { + $printer -> setPrintBuffer($buffer); + $printer -> text($str); + } + $printer -> setPrintBuffer($buffers[0]); + } + $printer -> feed(); + + $printer -> selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Works in progress\n"); + $printer -> selectPrintMode(); + foreach($inputsNotOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + foreach($buffers as $buffer) { + $printer -> setPrintBuffer($buffer); + $printer -> text($str); + } + $printer -> setPrintBuffer($buffers[0]); + } + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/character-encodings.php b/htdocs/includes/mike42/escpos-php/example/character-encodings.php new file mode 100644 index 00000000000..8a42ecd0847 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/character-encodings.php @@ -0,0 +1,58 @@ + selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Implemented languages\n"); + $printer -> selectPrintMode(); + foreach($inputsOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + $printer -> text($str); + } + $printer -> feed(); + + $printer -> selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_EMPHASIZED | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text("Works in progress\n"); + $printer -> selectPrintMode(); + foreach($inputsNotOk as $label => $str) { + $printer -> setEmphasis(true); + $printer -> text($label . ":\n"); + $printer -> setEmphasis(false); + $printer -> text($str); + } + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/character-tables.php b/htdocs/includes/mike42/escpos-php/example/character-tables.php new file mode 100644 index 00000000000..1ce2c5c9f1e --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/character-tables.php @@ -0,0 +1,71 @@ + getSupportedCodePages(); +$first = true; // Print larger table for first code-page. +foreach($codePages as $table => $name) { + /* Change printer code page */ + $printer -> selectCharacterTable(255); + $printer -> selectCharacterTable($table); + /* Select & print a label for it */ + $label = $name; + if($name === false) { + $label= " (not matched to iconv table)"; + } + $printer -> setEmphasis(true); + $printer -> textRaw("Table $table: $label\n"); + $printer -> setEmphasis(false); + if($name === false && !$verbose) { + continue; // Skip non-recognised + } + /* Print a table of available characters (first table is larger than subsequent ones */ + if($first) { + $first = false; + compactCharTable($printer, 1, true); + } else { + compactCharTable($printer); + } +} +$printer -> cut(); +$printer -> close(); + +function compactCharTable($printer, $start = 4, $header = false) { + /* Output a compact character table for the current encoding */ + $chars = str_repeat(' ', 256); + for($i = 0; $i < 255; $i++) { + $chars[$i] = ($i > 32 && $i != 127) ? chr($i) : ' '; + } + if($header) { + $printer -> setEmphasis(true); + $printer -> textRaw(" 0123456789ABCDEF0123456789ABCDEF\n"); + $printer -> setEmphasis(false); + } + for($y = $start; $y < 8; $y++) { + $printer -> setEmphasis(true); + $printer -> textRaw(strtoupper(dechex($y * 2)) . " "); + $printer -> setEmphasis(false); + $printer -> textRaw(substr($chars, $y * 32, 32) . "\n"); + } +} diff --git a/htdocs/includes/mike42/escpos-php/example/demo.php b/htdocs/includes/mike42/escpos-php/example/demo.php new file mode 100644 index 00000000000..1c99e5cc880 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/demo.php @@ -0,0 +1,167 @@ + + */ +require_once(dirname(__FILE__) . "/../Escpos.php"); +$printer = new Escpos(); + +/* Initialize */ +$printer -> initialize(); + +/* Text */ +$printer -> text("Hello world\n"); +$printer -> cut(); + +/* Line feeds */ +$printer -> text("ABC"); +$printer -> feed(7); +$printer -> text("DEF"); +$printer -> feedReverse(3); +$printer -> text("GHI"); +$printer -> feed(); +$printer -> cut(); + +/* Font modes */ +$modes = array( + Escpos::MODE_FONT_B, + Escpos::MODE_EMPHASIZED, + Escpos::MODE_DOUBLE_HEIGHT, + Escpos::MODE_DOUBLE_WIDTH, + Escpos::MODE_UNDERLINE); +for($i = 0; $i < pow(2, count($modes)); $i++) { + $bits = str_pad(decbin($i), count($modes), "0", STR_PAD_LEFT); + $mode = 0; + for($j = 0; $j < strlen($bits); $j++) { + if(substr($bits, $j, 1) == "1") { + $mode |= $modes[$j]; + } + } + $printer -> selectPrintMode($mode); + $printer -> text("ABCDEFGHIJabcdefghijk\n"); +} +$printer -> selectPrintMode(); // Reset +$printer -> cut(); + +/* Underline */ +for($i = 0; $i < 3; $i++) { + $printer -> setUnderline($i); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setUnderline(0); // Reset +$printer -> cut(); + +/* Cuts */ +$printer -> text("Partial cut\n(not available on all printers)\n"); +$printer -> cut(Escpos::CUT_PARTIAL); +$printer -> text("Full cut\n"); +$printer -> cut(Escpos::CUT_FULL); + +/* Emphasis */ +for($i = 0; $i < 2; $i++) { + $printer -> setEmphasis($i == 1); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setEmphasis(false); // Reset +$printer -> cut(); + +/* Double-strike (looks basically the same as emphasis) */ +for($i = 0; $i < 2; $i++) { + $printer -> setDoubleStrike($i == 1); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setDoubleStrike(false); +$printer -> cut(); + +/* Fonts (many printers do not have a 'Font C') */ +$fonts = array( + Escpos::FONT_A, + Escpos::FONT_B, + Escpos::FONT_C); +for($i = 0; $i < count($fonts); $i++) { + $printer -> setFont($fonts[$i]); + $printer -> text("The quick brown fox jumps over the lazy dog\n"); +} +$printer -> setFont(); // Reset +$printer -> cut(); + +/* Justification */ +$justification = array( + Escpos::JUSTIFY_LEFT, + Escpos::JUSTIFY_CENTER, + Escpos::JUSTIFY_RIGHT); +for($i = 0; $i < count($justification); $i++) { + $printer -> setJustification($justification[$i]); + $printer -> text("A man a plan a canal panama\n"); +} +$printer -> setJustification(); // Reset +$printer -> cut(); + +/* Barcodes - see barcode.php for more detail */ +$printer -> setBarcodeHeight(80); +$printer->setBarcodeTextPosition ( Escpos::BARCODE_TEXT_BELOW ); +$printer -> barcode("9876"); +$printer -> feed(); +$printer -> cut(); + +/* Graphics - this demo will not work on some non-Epson printers */ +try { + $logo = new EscposImage("resources/escpos-php.png"); + $imgModes = array( + Escpos::IMG_DEFAULT, + Escpos::IMG_DOUBLE_WIDTH, + Escpos::IMG_DOUBLE_HEIGHT, + Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT + ); + foreach($imgModes as $mode) { + $printer -> graphics($logo, $mode); + } +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} +$printer -> cut(); + +/* Bit image */ +try { + $logo = new EscposImage("resources/escpos-php.png"); + $imgModes = array( + Escpos::IMG_DEFAULT, + Escpos::IMG_DOUBLE_WIDTH, + Escpos::IMG_DOUBLE_HEIGHT, + Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT + ); + foreach($imgModes as $mode) { + $printer -> bitImage($logo, $mode); + } +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} +$printer -> cut(); + +/* QR Code - see also the more in-depth demo at qr-code.php */ +$testStr = "Testing 123"; +$models = array( + Escpos::QR_MODEL_1 => "QR Model 1", + Escpos::QR_MODEL_2 => "QR Model 2 (default)", + Escpos::QR_MICRO => "Micro QR code\n(not supported on all printers)"); +foreach($models as $model => $name) { + $printer -> qrCode($testStr, Escpos::QR_ECLEVEL_L, 3, $model); + $printer -> text("$name\n"); + $printer -> feed(); +} +$printer -> cut(); + +/* Pulse */ +$printer -> pulse(); + +/* Always close the printer! On some PrintConnectors, no actual + * data is sent until the printer is closed. */ +$printer -> close(); +?> diff --git a/htdocs/includes/mike42/escpos-php/example/graphics.php b/htdocs/includes/mike42/escpos-php/example/graphics.php new file mode 100644 index 00000000000..7650b6b9a08 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/graphics.php @@ -0,0 +1,32 @@ + graphics($tux); + $printer -> text("Regular Tux.\n"); + $printer -> feed(); + + $printer -> graphics($tux, Escpos::IMG_DOUBLE_WIDTH); + $printer -> text("Wide Tux.\n"); + $printer -> feed(); + + $printer -> graphics($tux, Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Tall Tux.\n"); + $printer -> feed(); + + $printer -> graphics($tux, Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT); + $printer -> text("Large Tux in correct proportion.\n"); + + $printer -> cut(); +} catch(Exception $e) { + /* Images not supported on your PHP, or image file not found */ + $printer -> text($e -> getMessage() . "\n"); +} + +$printer -> close(); +?> diff --git a/htdocs/includes/mike42/escpos-php/example/interface/README.md b/htdocs/includes/mike42/escpos-php/example/interface/README.md new file mode 100644 index 00000000000..96fe2eed2fb --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/README.md @@ -0,0 +1,8 @@ +Interfaces +---------- +This directory contains boilerpalte code to show you how to open a print connector +to printers which are connected in different ways. + +To get a list of supported interfaces and operating systems, see the main README.md file for the project. + +If you have a printer interface with no example, and you want to help put one together, then please lodge a request on the bug tracker: https://github.com/mike42/escpos-php/issues diff --git a/htdocs/includes/mike42/escpos-php/example/interface/ethernet.php b/htdocs/includes/mike42/escpos-php/example/interface/ethernet.php new file mode 100644 index 00000000000..bfea443903c --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/ethernet.php @@ -0,0 +1,22 @@ + text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php b/htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php new file mode 100644 index 00000000000..9e1d96b91c0 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php @@ -0,0 +1,33 @@ + text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/example/interface/smb.php b/htdocs/includes/mike42/escpos-php/example/interface/smb.php new file mode 100644 index 00000000000..40988f553ee --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/smb.php @@ -0,0 +1,51 @@ + testfile + * ## If you need authentication, use "net use" to hook up the printer: + * # net use "\\computername\Receipt Printer" /user:Guest + * # net use "\\computername\Receipt Printer" /user:Bob secret + * # net use "\\computername\Receipt Printer" /user:workgroup\Bob secret + * copy testfile "\\computername\Receipt Printer" + * del testfile + * + * GNU/Linux: + * # No authentication + * echo "Hello World" | smbclient "//computername/Receipt Printer" -c "print -" -N + * # Guest login + * echo "Hello World" | smbclient "//computername/Receipt Printer" -U Guest -c "print -" -N + * # Basic username/password + * echo "Hello World" | smbclient "//computername/Receipt Printer" secret -U "Bob" -c "print -" + * # Including domain name + * echo "Hello World" | smbclient "//computername/Receipt Printer" secret -U "workgroup\\Bob" -c "print -" + */ +try { + // Enter the share name for your printer here, as a smb:// url format + $connector = null; + //$connector = new WindowsPrintConnector("smb://computername/Receipt Printer"); + //$connector = new WindowsPrintConnector("smb://Guest@computername/Receipt Printer"); + //$connector = new WindowsPrintConnector("smb://FooUser:secret@computername/workgroup/Receipt Printer"); + //$connector = new WindowsPrintConnector("smb://User:secret@computername/Receipt Printer"); + + /* Print a "Hello world" receipt" */ + $printer = new Escpos($connector); + $printer -> text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} diff --git a/htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php b/htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php new file mode 100644 index 00000000000..76294425cdc --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php @@ -0,0 +1,30 @@ + LPT1 + */ +try { + $connector = null; + //$connector = new WindowsPrintConnector("LPT1"); + + // A FilePrintConnector will also work, but on non-Windows systems, writes + // to an actual file called 'LPT1' rather than giving a useful error. + // $connector = new FilePrintConnector("LPT1"); + + /* Print a "Hello world" receipt" */ + $printer = new Escpos($connector); + $printer -> text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} diff --git a/htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php b/htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php new file mode 100644 index 00000000000..d4b550e2f93 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php @@ -0,0 +1,32 @@ + testfile + * copy testfile "\\%COMPUTERNAME%\Receipt Printer" + * del testfile + */ +try { + // Enter the share name for your USB printer here + $connector = null; + //$connector = new WindowsPrintConnector("Receipt Printer"); + + /* Print a "Hello world" receipt" */ + $printer = new Escpos($connector); + $printer -> text("Hello World!\n"); + $printer -> cut(); + + /* Close printer */ + $printer -> close(); +} catch(Exception $e) { + echo "Couldn't print to this printer: " . $e -> getMessage() . "\n"; +} diff --git a/htdocs/includes/mike42/escpos-php/example/print-from-html.php b/htdocs/includes/mike42/escpos-php/example/print-from-html.php new file mode 100644 index 00000000000..f0751d7a4ae --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/print-from-html.php @@ -0,0 +1,53 @@ + 225 below) and printing w/ Escpos::IMG_DOUBLE_WIDTH | Escpos::IMG_DOUBLE_HEIGHT + */ +try { + /* Set up command */ + $source = "http://en.m.wikipedia.org/wiki/ESC/P"; + $width = 550; + $dest = tempnam(sys_get_temp_dir(), 'escpos') . ".png"; + $cmd = sprintf("wkhtmltoimage -n -q --width %s %s %s", + escapeshellarg($width), + escapeshellarg($source), + escapeshellarg($dest)); + + /* Run wkhtmltoimage */ + ob_start(); + system($cmd); // Can also use popen() for better control of process + $outp = ob_get_contents(); + ob_end_clean(); + if(!file_exists($dest)) { + throw new Exception("Command $cmd failed: $outp"); + } + + /* Load up the image */ + try { + $img = new EscposImage($dest); + } catch(Exception $e) { + unlink($dest); + throw $e; + } + unlink($dest); + + /* Print it */ + $printer = new Escpos(); // Add connector for your printer here. + $printer -> bitImage($img); // bitImage() seems to allow larger images than graphics() on the TM-T20. + $printer -> cut(); + $printer -> close(); +} catch(Exception $e) { + echo $e -> getMessage(); +} + diff --git a/htdocs/includes/mike42/escpos-php/example/print-from-pdf.php b/htdocs/includes/mike42/escpos-php/example/print-from-pdf.php new file mode 100644 index 00000000000..ba6a4c67a96 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/print-from-pdf.php @@ -0,0 +1,71 @@ + graphics($page); + } + $printer -> cut(); + $printer -> close(); +} catch(Exception $e) { + /* + * loadPdf() throws exceptions if files or not found, or you don't have the + * imagick extension to read PDF's + */ + echo $e -> getMessage() . "\n"; + exit(0); +} + + +/* + * 2: Speed up printing by roughly halving the resolution, and printing double-size. + * This gives a 75% speed increase at the expense of some quality. + * + * Reduce the page width further if necessary: if it extends past the printing area, your prints will be very slow. + */ +$printer = new Escpos(); +$pdf = 'resources/document.pdf'; +$pages = EscposImage::loadPdf($pdf, 260); +foreach($pages as $page) { + $printer -> graphics($page, Escpos::IMG_DOUBLE_HEIGHT | Escpos::IMG_DOUBLE_WIDTH); +} +$printer -> cut(); +$printer -> close(); + +/* + * 3: PDF printing still too slow? If you regularly print the same files, serialize & compress your + * EscposImage objects (after printing[1]), instead of throwing them away. + * + * (You can also do this to print logos on computers which don't have an + * image processing library, by preparing a serialized version of your logo on your PC) + * + * [1]After printing, the pixels are loaded and formatted for the print command you used, so even a raspberry pi can print complex PDF's quickly. + */ +$printer = new Escpos(); +$pdf = 'resources/document.pdf'; +$ser = 'resources/document.z'; +if(!file_exists($ser)) { + $pages = EscposImage::loadPdf($pdf); +} else { + $pages = unserialize(gzuncompress(file_get_contents($ser))); +} + +foreach($pages as $page) { + $printer -> graphics($page); +} +$printer -> cut(); +$printer -> close(); + +if(!file_exists($ser)) { + file_put_contents($ser, gzcompress(serialize($pages))); +} diff --git a/htdocs/includes/mike42/escpos-php/example/qr-code.php b/htdocs/includes/mike42/escpos-php/example/qr-code.php new file mode 100644 index 00000000000..dbf21aaedf4 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/qr-code.php @@ -0,0 +1,81 @@ + qrCode($testStr); +$printer -> text("Most simple example\n"); +$printer -> feed(); + +// Demo that alignment is the same as text +$printer -> setJustification(Escpos::JUSTIFY_CENTER); +$printer -> qrCode($testStr); +$printer -> text("Same example, centred\n"); +$printer -> setJustification(); +$printer -> feed(); + +// Demo of numeric data being packed more densly +title($printer, "Data encoding\n"); +$test = array( + "Numeric" => "0123456789012345678901234567890123456789", + "Alphanumeric" => "abcdefghijklmnopqrstuvwxyzabcdefghijklmn", + "Binary" => str_repeat("\0", 40)); +foreach($test as $type => $data) { + $printer -> qrCode($data); + $printer -> text("$type\n"); + $printer -> feed(); +} + +// Demo of error correction +title($printer, "Error correction\n"); +$ec = array( + Escpos::QR_ECLEVEL_L => "L", + Escpos::QR_ECLEVEL_M => "M", + Escpos::QR_ECLEVEL_Q => "Q", + Escpos::QR_ECLEVEL_H => "H"); +foreach($ec as $level => $name) { + $printer -> qrCode($testStr, $level); + $printer -> text("Error correction $name\n"); + $printer -> feed(); +} + +// Change size +title($printer, "Pixel size\n"); +$sizes = array( + 1 => "(minimum)", + 2 => "", + 3 => "(default)", + 4 => "", + 5 => "", + 10 => "", + 16 => "(maximum)"); +foreach($sizes as $size => $label) { + $printer -> qrCode($testStr, Escpos::QR_ECLEVEL_L, $size); + $printer -> text("Pixel size $size $label\n"); + $printer -> feed(); +} + +// Change model +title($printer, "QR model\n"); +$models = array( + Escpos::QR_MODEL_1 => "QR Model 1", + Escpos::QR_MODEL_2 => "QR Model 2 (default)", + Escpos::QR_MICRO => "Micro QR code\n(not supported on all printers)"); +foreach($models as $model => $name) { + $printer -> qrCode($testStr, Escpos::QR_ECLEVEL_L, 3, $model); + $printer -> text("$name\n"); + $printer -> feed(); +} + +// Cut & close +$printer -> cut(); +$printer -> close(); + +function title(Escpos $printer, $str) { + $printer -> selectPrintMode(Escpos::MODE_DOUBLE_HEIGHT | Escpos::MODE_DOUBLE_WIDTH); + $printer -> text($str); + $printer -> selectPrintMode(); +} diff --git a/htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php b/htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php new file mode 100644 index 00000000000..8322c09c750 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php @@ -0,0 +1,96 @@ + setJustification(Escpos::JUSTIFY_CENTER); +$printer -> graphics($logo); + +/* Name of shop */ +$printer -> selectPrintMode(Escpos::MODE_DOUBLE_WIDTH); +$printer -> text("ExampleMart Ltd.\n"); +$printer -> selectPrintMode(); +$printer -> text("Shop No. 42.\n"); +$printer -> feed(); + +/* Title of receipt */ +$printer -> setEmphasis(true); +$printer -> text("SALES INVOICE\n"); +$printer -> setEmphasis(false); + +/* Items */ +$printer -> setJustification(Escpos::JUSTIFY_LEFT); +$printer -> setEmphasis(true); +$printer -> text(new item('', '$')); +$printer -> setEmphasis(false); +foreach($items as $item) { + $printer -> text($item); +} +$printer -> setEmphasis(true); +$printer -> text($subtotal); +$printer -> setEmphasis(false); +$printer -> feed(); + +/* Tax and total */ +$printer -> text($tax); +$printer -> selectPrintMode(Escpos::MODE_DOUBLE_WIDTH); +$printer -> text($total); +$printer -> selectPrintMode(); + +/* Footer */ +$printer -> feed(2); +$printer -> setJustification(Escpos::JUSTIFY_CENTER); +$printer -> text("Thank you for shopping at ExampleMart\n"); +$printer -> text("For trading hours, please visit example.com\n"); +$printer -> feed(2); +$printer -> text($date . "\n"); + +/* Cut the receipt and open the cash drawer */ +$printer -> cut(); +$printer -> pulse(); + +$printer -> close(); + +/* A wrapper to do organise item names & prices into columns */ +class item { + private $name; + private $price; + private $dollarSign; + + public function __construct($name = '', $price = '', $dollarSign = false) { + $this -> name = $name; + $this -> price = $price; + $this -> dollarSign = $dollarSign; + } + + public function __toString() { + $rightCols = 10; + $leftCols = 38; + if($this -> dollarSign) { + $leftCols = $leftCols / 2 - $rightCols / 2; + } + $left = str_pad($this -> name, $leftCols) ; + + $sign = ($this -> dollarSign ? '$ ' : ''); + $right = str_pad($sign . $this -> price, $rightCols, ' ', STR_PAD_LEFT); + return "$left$right\n"; + } +} +?> diff --git a/htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc b/htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc new file mode 100644 index 00000000000..caca76c4479 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc @@ -0,0 +1,35 @@ + "Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.\n", + "German" => "Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.\n", + "Greek" => "Ξεσκεπάζω την ψυχοφθόρα βδελυγμία\n", + "English" => "The quick brown fox jumps over the lazy dog.\n", + "Spanish" => "El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.\n", + "French" => "Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë au delà des îles, près du mälström où brûlent les novæ.\n", + "Irish Gaelic" => "D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh.\n", + "Hungarian" => "Árvíztűrő tükörfúrógép.\n", + "Icelandic" => "Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa.\n", + "Latvian" => "Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus.\n", + "Polish" => "Pchnąć w tę łódź jeża lub ośm skrzyń fig.\n", + "Russian" => "В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!\n", + "Turkish" => "Pijamalı hasta, yağız şoföre çabucak güvendi.\n", + "Japanese (Katakana half-width)" => implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウイノオクヤマ ケフコエテ アサキユメミシ エヒモセスン")) . "\n" + ); + +/* + * These strings are not expected to print correctly, if at all, even on an Epson printer. This is due to a mix of + * escpos driver, printer, and PHP language support issues. + * + * They are included here as a collection of things not yet implemented. + */ +$inputsNotOk = array( + "Thai (No character encoder available)" => "นายสังฆภัณฑ์ เฮงพิทักษ์ฝั่ง ผู้เฒ่าซึ่งมีอาชีพเป็นฅนขายฃวด ถูกตำรวจปฏิบัติการจับฟ้องศาล ฐานลักนาฬิกาคุณหญิงฉัตรชฎา ฌานสมาธิ\n", + "Japanese (Hiragana)" => implode("\n", array("いろはにほへとちりぬるを", " わかよたれそつねならむ", "うゐのおくやまけふこえて", "あさきゆめみしゑひもせす")) . "\n", + "Japanese (Katakana full-width)" => implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン")) . "\n", + "Arabic (RTL not supported, encoding issues)" => "صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ" . "\n", + "Hebrew (RTL not supported, line break issues)" => "דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה" . "\n" + ); + diff --git a/htdocs/includes/mike42/escpos-php/example/resources/document.odt b/htdocs/includes/mike42/escpos-php/example/resources/document.odt new file mode 100644 index 00000000000..4bbd7f98b65 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/example/resources/document.odt differ diff --git a/htdocs/includes/mike42/escpos-php/example/resources/document.pdf b/htdocs/includes/mike42/escpos-php/example/resources/document.pdf new file mode 100644 index 00000000000..93a702337f1 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/example/resources/document.pdf differ diff --git a/htdocs/includes/mike42/escpos-php/example/resources/document.z b/htdocs/includes/mike42/escpos-php/example/resources/document.z new file mode 100644 index 00000000000..cb047387558 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/example/resources/document.z differ diff --git a/htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png b/htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png new file mode 100644 index 00000000000..3c74d2d57a2 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png differ diff --git a/htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png b/htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png new file mode 100644 index 00000000000..feee2d71cc2 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png differ diff --git a/htdocs/includes/mike42/escpos-php/example/resources/tux.png b/htdocs/includes/mike42/escpos-php/example/resources/tux.png new file mode 100644 index 00000000000..e32f8d94534 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/example/resources/tux.png differ diff --git a/htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php b/htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php new file mode 100644 index 00000000000..72dc7c8f2ed --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php @@ -0,0 +1,21 @@ + text("Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus\n"); +$printer -> cut(); +$printer -> close(); + +/* Option 2: Image-based output (formatting not available using this output) */ +$buffer = new ImagePrintBuffer(); +$connector = new FilePrintConnector("php://stdout"); +$printer = new Escpos($connector, $profile); +$printer -> setPrintBuffer($buffer); +$printer -> text("Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus\n"); +$printer -> cut(); +$printer -> close(); +?> \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php b/htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php new file mode 100644 index 00000000000..49eca235091 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php @@ -0,0 +1,36 @@ + getPrintConnector() -> write($cmd); +$printer -> text("Beispieltext in Deutsch\n"); +$printer -> cut(); +$printer -> close(); + +/* + * Hex-dump of output confirms that ESC V 1 being sent: + * + * 0000000 033 @ 033 V 001 B e i s p i e l t e x + * 0000010 t i n D e u t s c h \n 035 V A + * 0000020 003 + */ \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php b/htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php new file mode 100644 index 00000000000..7314ad21bf0 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php @@ -0,0 +1,16 @@ + text("El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.\n"); +$printer -> cut(); +$printer -> close(); + diff --git a/htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php b/htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php new file mode 100644 index 00000000000..b3d4b29edd1 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php @@ -0,0 +1,69 @@ + text("€ 9,95\n"); +$printer -> text("£ 9.95\n"); +$printer -> text("$ 9.95\n"); +$printer -> text("¥ 9.95\n"); +$printer -> cut(); +$printer -> close(); + +/* Option 2: Image-based output (formatting not available using this output). */ +$buffer = new ImagePrintBuffer(); +$connector = new FilePrintConnector("php://stdout"); +$printer = new Escpos($connector, $profile); +$printer -> setPrintBuffer($buffer); +$printer -> text("€ 9,95\n"); +$printer -> text("£ 9.95\n"); +$printer -> text("$ 9.95\n"); +$printer -> text("¥ 9.95\n"); +$printer -> cut(); +$printer -> close(); + +/* + Option 3: If the printer is configured to print in a specific code + page, you can set up a CapabilityProfile which either references its + iconv encoding name, or includes all of the available characters. + + Here, we make use of CP858 for its inclusion of currency symbols which + are not available in CP437. CP858 has good printer support, but is not + included in all iconv builds. +*/ +class CustomCapabilityProfile extends SimpleCapabilityProfile { + function getCustomCodePages() { + /* + * Example to print in a specific, user-defined character set + * on a printer which has been configured to use i + */ + return array( + 'CP858' => "ÇüéâäàåçêëèïîìÄÅ" . + "ÉæÆôöòûùÿÖÜø£Ø×ƒ" . + "áíóúñѪº¿®¬½¼¡«»" . + "░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐" . + "└┴┬├─┼ãÃ╚╔╩╦╠═╬¤" . + "ðÐÊËÈ€ÍÎÏ┘┌█▄¦Ì▀" . + "ÓßÔÒõÕµþÞÚÛÙýݯ´" . + " ±‗¾¶§÷¸°¨·¹³²■ "); + } + + function getSupportedCodePages() { + return array( + 0 => 'custom:CP858'); + } +} + +$connector = new FilePrintConnector("php://stdout"); +$profile = CustomCapabilityProfile::getInstance(); +$printer = new Escpos($connector, $profile); +$printer -> text("€ 9,95\n"); +$printer -> text("£ 9.95\n"); +$printer -> text("$ 9.95\n"); +$printer -> text("¥ 9.95\n"); + +$printer -> cut(); +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php b/htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php new file mode 100644 index 00000000000..4ef1a9575c2 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php @@ -0,0 +1,31 @@ + textRaw("\x9C"); // based on position in CP437 +$printer -> text(" 1.95\n"); + +// B) Manually encoded UTF8 pound symbol. Tests that the driver correctly +// encodes this as CP437. +$printer -> text(base64_decode("wqM=") . " 2.95\n"); + +// C) Pasted in file. Tests that your files are being saved as UTF-8, which +// escpos-php is able to convert automatically to a mix of code pages. +$printer -> text("£ 3.95\n"); + +$printer -> cut(); +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php b/htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php new file mode 100644 index 00000000000..2d88cf1a2a8 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php @@ -0,0 +1,16 @@ + text($text . "\n"); +$printer -> cut(); + +// Close the connection +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php b/htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php new file mode 100644 index 00000000000..316099cf304 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php @@ -0,0 +1,58 @@ + setJustification(Escpos::JUSTIFY_CENTER); + $printer -> graphics($img); + $printer -> cut(); + } finally { + // Always close the connection + $printer -> close(); + } +} catch (Exception $e) { + // Print out any errors: Eg. printer connection, image loading & external image manipulation. + echo $e -> getMessage() . "\n"; + echo $e -> getTraceAsString(); +} finally { + unlink ( $imgCombined_path ); + unlink ( $tmpf_path ); +} diff --git a/htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php b/htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php new file mode 100644 index 00000000000..c2b0fb5aa57 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php @@ -0,0 +1,47 @@ + utf8Glyphs($text); + +/* + * Set up and use the printer + */ +$buffer = new ImagePrintBuffer(); +$profile = EposTepCapabilityProfile::getInstance(); +$connector = new FilePrintConnector("php://output"); + // = WindowsPrintConnector("LPT2"); + // Windows LPT2 was used in the bug tracker + +$printer = new Escpos($connector, $profile); +$printer -> setPrintBuffer($buffer); +$printer -> text($text . "\n"); +$printer -> close(); diff --git a/htdocs/includes/mike42/escpos-php/example/specific/README.md b/htdocs/includes/mike42/escpos-php/example/specific/README.md new file mode 100644 index 00000000000..7433e7aa247 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/specific/README.md @@ -0,0 +1,7 @@ +Specific examples +----------------- + +These examples are designed for specific combinations of language, +printer and interface. + +They are documented here because the general examples all set up the printer in a similar way. diff --git a/htdocs/includes/mike42/escpos-php/example/text-size.php b/htdocs/includes/mike42/escpos-php/example/text-size.php new file mode 100644 index 00000000000..2f9272e434b --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/example/text-size.php @@ -0,0 +1,62 @@ + + */ +require_once(dirname(__FILE__) . "/../Escpos.php"); +$printer = new Escpos(); + +/* Initialize */ +$printer -> initialize(); + +/* Text of various (in-proportion) sizes */ +title($printer, "Change height & width\n"); +for($i = 1; $i <= 8; $i++) { + $printer -> setTextSize($i, $i); + $printer -> text($i); +} +$printer -> text("\n"); + +/* Width changing only */ +title($printer, "Change width only (height=4):\n"); +for($i = 1; $i <= 8; $i++) { + $printer -> setTextSize($i, 4); + $printer -> text($i); +} +$printer -> text("\n"); + +/* Height changing only */ +title($printer, "Change height only (width=4):\n"); +for($i = 1; $i <= 8; $i++) { + $printer -> setTextSize(4, $i); + $printer -> text($i); +} +$printer -> text("\n"); + +/* Very narrow text */ +title($printer, "Very narrow text:\n"); +$printer -> setTextSize(1, 8); +$printer -> text("The quick brown fox jumps over the lazy dog.\n"); + +/* Very flat text */ +title($printer, "Very wide text:\n"); +$printer -> setTextSize(4, 1); +$printer -> text("Hello world!\n"); + +/* Very large text */ +title($printer, "Largest possible text:\n"); +$printer -> setTextSize(8,8); +$printer -> text("Hello\nworld!\n"); + +$printer -> cut(); +$printer -> close(); + +function title(Escpos $printer, $text) { + $printer -> selectPrintMode(Escpos::MODE_EMPHASIZED); + $printer -> text("\n" . $text); + $printer -> selectPrintMode(); // Reset +} + +?> diff --git a/htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php new file mode 100644 index 00000000000..db2e8e01f58 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php @@ -0,0 +1,61 @@ + CodePage::CP437, + 1 => CodePage::CP932, + 2 => CodePage::CP850, + 3 => CodePage::CP860, + 4 => CodePage::CP863, + 5 => CodePage::CP865, + 6 => false, // Hiragana + 7 => false, // One-pass printing Kanji characters + 8 => false, // Page 8 [One-pass printing Kanji characters] + 11 => CodePage::CP851, + 12 => CodePage::CP853, + 13 => CodePage::CP857, + 14 => CodePage::CP737, + 15 => CodePage::ISO8859_7, + 16 => CodePage::CP1252, + 17 => CodePage::CP866, + 18 => CodePage::CP852, + 19 => CodePage::CP858, + 20 => false, // Thai Character Code 42 + 21 => CodePage::CP874, // Thai Character Code 11 + 22 => false, // Thai Character Code 13 + 23 => false, // Thai Character Code 14 + 24 => false, // Thai Character Code 16 + 25 => false, // Thai Character Code 17 + 26 => false, // Thai Character Code 18 + 30 => false, // TCVN-3: Vietnamese + 31 => false, // TCVN-3: Vietnamese + 32 => CodePage::CP720, + 33 => CodePage::CP775, + 34 => CodePage::CP855, + 35 => CodePage::CP861, + 36 => CodePage::CP862, + 37 => CodePage::CP864, + 38 => CodePage::CP869, + 39 => CodePage::ISO8859_2, + 40 => CodePage::ISO8859_15, + 41 => CodePage::CP1098, // PC1098: Farsi + 42 => CodePage::CP774, + 43 => CodePage::CP772, + 44 => CodePage::CP1125, + 45 => CodePage::CP1250, + 46 => CodePage::CP1251, + 47 => CodePage::CP1253, + 48 => CodePage::CP1254, + 49 => CodePage::CP1255, + 50 => CodePage::CP1256, + 51 => CodePage::CP1257, + 52 => CodePage::CP1258, + 53 => CodePage::RK1048, + 66 => false, // Devanagari + 67 => false, // Bengali + 68 => false, // Tamil + 69 => false, // Telugu + 70 => false, // Assamese + 71 => false, // Oriya + 72 => false, // Kannada + 73 => false, // Malayalam + 74 => false, // Gujarati + 75 => false, // Punjabi + 82 => false, // Marathi + 254 => false, + 255 => false); + } + + function getSupportsBarcodeB() { + return true; + } + + function getSupportsBitImage() { + return true; + } + + function getSupportsGraphics() { + return true; + } + + function getSupportsStarCommands() { + return false; + } + + function getSupportsQrCode() { + return true; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php b/htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php new file mode 100644 index 00000000000..e1a197b7c7b --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php @@ -0,0 +1,78 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Print connector that writes to nowhere, but allows the user to retrieve the + * buffered data. Used for testing. + */ +final class DummyPrintConnector implements PrintConnector { + /** + * @var array Buffer of accumilated data. + */ + private $buffer; + + /** + * @var string data which the printer will provide on next read + */ + private $readData; + + /** + * Create new print connector + */ + public function __construct() { + $this -> buffer = array(); + } + + public function __destruct() { + if($this -> buffer !== null) { + trigger_error("Print connector was not finalized. Did you forget to close the printer?", E_USER_NOTICE); + } + } + + public function finalize() { + $this -> buffer = null; + } + + /** + * @return string Get the accumulated data that has been sent to this buffer. + */ + public function getData() { + return implode($this -> buffer); + } + + /* (non-PHPdoc) + * @see PrintConnector::read() + */ + public function read($len) { + return $len >= strlen($this -> readData) ? $this -> readData : substr($this -> readData, 0, $len); + } + + public function write($data) { + $this -> buffer[] = $data; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php new file mode 100644 index 00000000000..2803fe3f69c --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php @@ -0,0 +1,4 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * 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. + */ +class EscposImage { + /** + * @var string The image's bitmap data (if it is a Windows BMP). + */ + protected $imgBmpData; + + /** + * @var string image data in rows: 1 for black, 0 for white. + */ + protected $imgData; + + /** + * @var string cached raster format data to avoid re-computation + */ + protected $imgRasterData; + + /** + * @var int height of the image + */ + protected $imgHeight; + + /** + * @var int width of the image + */ + protected $imgWidth; + + /** + * Load up an image from a filename + * + * @param string $imgPath The path to the image to load, or null to skip + * loading the image (some other functions are available for + * populating the data). Supported graphics types depend on your PHP configuration. + */ + public function __construct($imgPath = null) { + /* Can't use bitmaps yet */ + $this -> imgBmpData = null; + $this -> imgRasterData = null; + if($imgPath === null) { + // Blank image + $this -> imgHeight = 0; + $this -> imgWidth = 0; + $this -> imgData = ""; + return; + } + + /* Load up using GD */ + if(!file_exists($imgPath)) { + throw new Exception("File '$imgPath' does not exist."); + } + $ext = pathinfo($imgPath, PATHINFO_EXTENSION); + if($ext == "bmp") { + // The plan is to implement BMP handling directly in + // PHP, as some printers understand this format themselves. + // TODO implement PHP bitmap handling + throw new Exception("Native bitmaps not yet supported. Please convert the file to a supported raster format."); + } + if($this -> isGdSupported()) { + // Prefer to use gd. It is installed by default, so + // most systems will have it, giving a consistent UX. + switch($ext) { + case "png": + $im = @imagecreatefrompng($imgPath); + $this -> readImageFromGdResource($im); + return; + case "jpg": + $im = @imagecreatefromjpeg($imgPath); + $this -> readImageFromGdResource($im); + return; + case "gif": + $im = @imagecreatefromgif($imgPath); + $this -> readImageFromGdResource($im); + return; + } + } + if($this -> isImagickSupported()) { + $im = new Imagick(); + try { + // Throws an ImagickException if the format is not supported or file is not found + $im -> readImage($imgPath); + } catch(ImagickException $e) { + // Wrap in normal exception, so that classes which call this do not themselves require imagick as a dependency. + throw new Exception($e); + } + /* Flatten by doing a composite over white, in case of transparency */ + $flat = new Imagick(); + $flat -> newImage($im -> getimagewidth(), $im -> getimageheight(), "white"); + $flat -> compositeimage($im, Imagick::COMPOSITE_OVER, 0, 0); + $this -> readImageFromImagick($flat); + return; + } + throw new Exception("Images are not supported on your PHP. Please install either the gd or imagick extension."); + } + + /** + * @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); + } + + /** + * @return string binary data of the original file, for function which accept bitmaps. + */ + public function getWindowsBMPData() { + return $this -> imgBmpData; + } + + /** + * @return boolean True if the image was a windows bitmap, false otherwise + */ + public function isWindowsBMP() { + return $this -> imgBmpData != null; + } + + /** + * Load actual image pixels from GD resource. + * + * @param resouce $im GD resource to use + * @throws Exception Where the image can't be read. + */ + public function readImageFromGdResource($im) { + if(!is_resource($im)) { + throw new Exception("Failed to load image."); + } else if(!$this -> isGdSupported()) { + throw new Exception(__FUNCTION__ . " requires 'gd' extension."); + } + /* Make a string of 1's and 0's */ + $this -> imgHeight = imagesy($im); + $this -> imgWidth = imagesx($im); + $this -> imgData = str_repeat("\0", $this -> imgHeight * $this -> imgWidth); + for($y = 0; $y < $this -> imgHeight; $y++) { + for($x = 0; $x < $this -> imgWidth; $x++) { + /* Faster to average channels, blend alpha and negate the image here than via filters (tested!) */ + $cols = imagecolorsforindex($im, imagecolorat($im, $x, $y)); + $greyness = (int)(($cols['red'] + $cols['green'] + $cols['blue']) / 3) >> 7; // 1 for white, 0 for black + $black = (1 - $greyness) >> ($cols['alpha'] >> 6); // 1 for black, 0 for white, taking into account transparency + $this -> imgData[$y * $this -> imgWidth + $x] = $black; + } + } + } + + /** + * Load actual image pixels from Imagick object + * + * @param Imagick $im Image to load from + */ + public function readImageFromImagick(Imagick $im) { + /* Threshold */ + $im -> setImageType(Imagick::IMGTYPE_TRUECOLOR); // Remove transparency (good for PDF's) + $max = $im->getQuantumRange(); + $max = $max["quantumRangeLong"]; + $im -> thresholdImage(0.5 * $max); + /* Make a string of 1's and 0's */ + $geometry = $im -> getimagegeometry(); + $this -> imgHeight = $im -> getimageheight(); + $this -> imgWidth = $im -> getimagewidth(); + $this -> imgData = str_repeat("\0", $this -> imgHeight * $this -> imgWidth); + + for($y = 0; $y < $this -> imgHeight; $y++) { + for($x = 0; $x < $this -> imgWidth; $x++) { + /* Faster to average channels, blend alpha and negate the image here than via filters (tested!) */ + $cols = $im -> getImagePixelColor($x, $y); + $cols = $cols -> getcolor(); + $greyness = (int)(($cols['r'] + $cols['g'] + $cols['b']) / 3) >> 7; // 1 for white, 0 for black + $this -> imgData[$y * $this -> imgWidth + $x] = (1 - $greyness); // 1 for black, 0 for white + } + } + + } + + /** + * 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() { + if($this -> imgRasterData != null) { + /* Use previous calculation */ + return $this -> imgRasterData; + } + /* 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."); + } + $this -> imgRasterData = $data; + return $this -> imgRasterData; + } + + /** + * Output image in column format. This format results in padding at the base and right of the image, if its height and width are not divisible by 8. + */ + private function toColumnFormat() { + /* Note: This function is marked private, as it is not yet used/tested and may be buggy. */ + $widthPixels = $this -> getWidth(); + $heightPixels = $this -> getHeight(); + $widthBytes = $this -> getWidthBytes(); + $heightBytes = $this -> getHeightBytes(); + $x = $y = $bit = $byte = $byteVal = 0; + $data = str_repeat("\0", $widthBytes * $heightBytes * 8); + do { + $byteVal |= (int)$this -> imgData[$y * $widthPixels + $x] << (8 - $bit); + $y++; + $bit++; + if($y >= $heightPixels) { + $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) != ($widthBytes * $heightBytes * 8)) { + throw new Exception("Bug in " . __FUNCTION__ . ", wrong number of bytes. Should be " . ($widthBytes * $heightBytes * 8) . " but was " . strlen($data)); + } + return $data; + } + + /** + * @return boolean True if GD is supported, false otherwise (a wrapper for the static version, for mocking in tests) + */ + protected function isGdSupported() { + return self::isGdLoaded(); + } + + /** + * @return boolean True if Imagick is supported, false otherwise (a wrapper for the static version, for mocking in tests) + */ + protected function isImagickSupported() { + return self::isImagickLoaded(); + } + + + /** + * @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'); + } + + /** + * Load a PDF for use on the printer + * + * @param string $pdfFile The file to load + * @param string $pageWidth The width, in pixels, of the printer's output. The first page of the PDF will be scaled to approximately fit in this area. + * @param array $range array indicating the first and last page (starting from 0) to load. If not set, the entire document is loaded. + * @throws Exception Where Imagick is not loaded, or where a missing file or invalid page number is requested. + * @return multitype:EscposImage Array of images, retrieved from the PDF file. + */ + public static function loadPdf($pdfFile, $pageWidth = 550, array $range = null) { + if(!extension_loaded('imagick')) { + throw new Exception(__FUNCTION__ . " requires imagick extension."); + } + /* + * Load first page at very low density (resolution), to figure out what + * density to use to achieve $pageWidth + */ + try { + $image = new Imagick(); + $testRes = 2; // Test resolution + $image -> setresolution($testRes, $testRes); + $image -> readimage($pdfFile."[0]"); + $geo = $image -> getimagegeometry(); + $image -> destroy(); + $width = $geo['width']; + $newRes = $pageWidth / $width * $testRes; + /* Load actual document (can be very slow!) */ + $rangeStr = ""; // Set to [0] [0-1] page range if $range is set + if($range != null) { + if(count($range) != 2 || !isset($range[0]) || !is_integer($range[0]) || !isset($range[1]) || !is_integer($range[1]) || $range[0] > $range[1]) { + throw new Exception("Invalid range. Must be two numbers in the array: The start and finish page indexes, starting from 0."); + } + $rangeStr = "[" . ($range[0] == $range[1] ? $range[0] : implode($range, "-")) . "]"; + } + $image -> setresolution($newRes, $newRes); + $image -> readImage($pdfFile."$rangeStr"); + $pages = $image -> getNumberImages(); + /* Convert images to Escpos objects */ + $ret = array(); + for($i = 0;$i < $pages; $i++) { + $image -> setIteratorIndex($i); + $ep = new EscposImage(); + $ep -> readImageFromImagick($image); + $ret[] = $ep; + } + return $ret; + } catch(ImagickException $e) { + // Wrap in normal exception, so that classes which call this do not themselves require imagick as a dependency. + throw new Exception($e); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php b/htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php new file mode 100644 index 00000000000..703bc0a57c0 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php @@ -0,0 +1,304 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This class manages newlines and character encoding for the target printer, and + * can be interchanged for an image-bassed buffer (ImagePrintBuffer) if you can't + * get it operating properly on your machine. + */ +class EscposPrintBuffer implements PrintBuffer { + /** + * @var boolean True to cache output as .gz, false to leave un-compressed (useful for debugging) + */ + const COMPRESS_CACHE = true; + + /** + * @var string The input encoding of the buffer. + */ + const INPUT_ENCODING = "UTF-8"; + + /** + * @var string Un-recorgnised characters will be replaced with this. + */ + const REPLACEMENT_CHAR = "?"; + + /** + * This array Maps ESC/POS character tables to names iconv encodings + */ + private $available = null; + + /** + * @var array Maps of UTF-8 to code-pages + */ + private $encode = null; + + /** + * @var Escpos Printer for output + */ + private $printer; + + /** + * Empty print buffer. + */ + function __construct() { + $this -> printer = null; + } + + public function flush() { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + // TODO Not yet implemented for this buffer: This indicates that the printer needs the current line to be ended. + } + + public function getPrinter() { + return $this -> printer; + } + + public function setPrinter(Escpos $printer = null) { + $this -> printer = $printer; + if($printer != null) { + $this -> loadAvailableCharacters(); + } + } + + public function writeText($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + if($text == null) { + return; + } + if(!mb_detect_encoding($text, self::INPUT_ENCODING, true)) { + // Assume that the user has already put non-UTF8 into the target encoding. + return $this -> writeTextRaw($text); + } + $i = 0; + $j = 0; + $len = mb_strlen($text, self::INPUT_ENCODING); + while($i < $len) { + $matching = true; + if(($encoding = $this -> identifyText(mb_substr($text, $i, 1, self::INPUT_ENCODING))) === false) { + // Un-encodeable text + $encoding = $this -> getPrinter() -> getCharacterTable(); + } + $i++; + $j = 1; + do { + $char = mb_substr($text, $i, 1, self::INPUT_ENCODING); + $matching = !isset($this -> available[$char]) || isset($this -> available[$char][$encoding]); + if($matching) { + $i++; + $j++; + } + } while($matching && $i < $len); + $this -> writeTextUsingEncoding(mb_substr($text, $i - $j, $j, self::INPUT_ENCODING), $encoding); + } + } + + public function writeTextRaw($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + if(strlen($text) == 0) { + return; + } + // Pass only printable characters + for($i = 0; $i < strlen($text); $i++) { + $c = substr($text, $i, 1); + if(!self::asciiCheck($c, true)) { + $text[$i] = self::REPLACEMENT_CHAR; + } + } + $this -> write($text); + } + + /** + * Return an encoding which we can start to use for outputting this text. Later parts of the text need not be included in the returned code page. + * + * @param string $text Input text to check. + * @return boolean|integer Code page number, or FALSE if the text is not printable on any supported encoding. + */ + private function identifyText($text) { + // TODO Replace this with an algorithm to choose the encoding which will encode the farthest into the string, to minimise code page changes. + $char = mb_substr($text, 0, 1, self::INPUT_ENCODING); + if(!isset($this -> available[$char])) { + /* Character not available anywhere */ + return false; + } + foreach($this -> available[$char] as $encodingNo => $true) { + /* Return first code-page where it is available */ + return $encodingNo; + } + return false; + } + + /** + * Based on the printer's connector, compute (or load a cached copy of) maps of UTF character to unicode characters for later use. + */ + private function loadAvailableCharacters() { + $supportedCodePages = $this -> printer -> getPrinterCapabilityProfile() -> getSupportedCodePages(); + $capabilityClassName = get_class($this -> printer -> getPrinterCapabilityProfile()); + $cacheFile = dirname(__FILE__) . "/cache/Characters-" . $capabilityClassName . ".ser" . (self::COMPRESS_CACHE ? ".gz" : ""); + $cacheKey = md5(serialize($supportedCodePages)); + /* Check for pre-generated file */ + if(file_exists($cacheFile)) { + $cacheData = file_get_contents($cacheFile); + if(self::COMPRESS_CACHE) { + $cacheData = gzdecode($cacheData); + } + if($cacheData) { + $dataArray = unserialize($cacheData); + if(isset($dataArray["key"]) && isset($dataArray["available"]) && isset($dataArray["encode"]) && $dataArray["key"] == $cacheKey) { + $this -> available = $dataArray["available"]; + $this -> encode = $dataArray["encode"]; + return; + } + } + } + /* Generate conversion tables */ + $encode = array(); + $available = array(); + $custom = $this -> printer -> getPrinterCapabilityProfile() -> getCustomCodePages(); + + foreach($supportedCodePages as $num => $characterMap) { + $encode[$num] = array(); + if($characterMap === false) { + continue; + } else if(strpos($characterMap, ":") !== false) { + /* Load a pre-defined custom map (vendor-specific code pages) */ + $i = strpos($characterMap, ":"); + if(substr($characterMap, 0, $i) !== "custom") { + continue; + } + $i++; + $mapName = substr($characterMap, $i, strlen($characterMap) - $i); + if(!isset($custom[$mapName]) || mb_strlen($custom[$mapName], self::INPUT_ENCODING) != 128) { + throw new Exception("Capability profile referenced invalid custom map '$mapName'."); + } + $map = $custom[$mapName]; + for($char = 128; $char <= 255; $char++) { + $utf8 = mb_substr($map, $char - 128, 1, self::INPUT_ENCODING); + if($utf8 == " ") { // Skip placeholders + continue; + } + if(!isset($available[$utf8])) { + $available[$utf8] = array(); + } + $available[$utf8][$num] = true; + $encode[$num][$utf8] = chr($char); + } + } else { + /* Generate map using iconv */ + for($char = 128; $char <= 255; $char++) { + $utf8 = @iconv($characterMap, self::INPUT_ENCODING, chr($char)); + if($utf8 == '') { + continue; + } + if(iconv(self::INPUT_ENCODING, $characterMap, $utf8) != chr($char)) { + // Avoid non-canonical conversions + continue; + } + if(!isset($available[$utf8])) { + $available[$utf8] = array(); + } + $available[$utf8][$num] = true; + $encode[$num][$utf8] = chr($char); + } + } + } + /* Use generated data */ + $dataArray = array("available" => $available, "encode" => $encode, "key" => $cacheKey); + $this -> available = $dataArray["available"]; + $this -> encode = $dataArray["encode"]; + $cacheData = serialize($dataArray); + if(self::COMPRESS_CACHE) { + $cacheData = gzencode($cacheData); + } + /* Attempt to cache, but don't worry if we can't */ + @file_put_contents($cacheFile, $cacheData); + } + + /** + * Encode a block of text using the specified map, and write it to the printer. + * + * @param string $text Text to print, UTF-8 format. + * @param integer $encodingNo Encoding number to use- assumed to exist. + */ + private function writeTextUsingEncoding($text, $encodingNo) { + $encodeMap = $this -> encode[$encodingNo]; + $len = mb_strlen($text, self::INPUT_ENCODING); + $rawText = str_repeat(self::REPLACEMENT_CHAR, $len); + for($i = 0; $i < $len; $i++) { + $char = mb_substr($text, $i, 1, self::INPUT_ENCODING); + if(isset($encodeMap[$char])) { + $rawText[$i] = $encodeMap[$char]; + } else if(self::asciiCheck($char)) { + $rawText[$i] = $char; + } + } + if($this -> printer -> getCharacterTable() != $encodingNo) { + $this -> printer -> selectCharacterTable($encodingNo); + } + $this -> writeTextRaw($rawText); + } + + /** + * Write data to the underlying printer. + * + * @param string $data + */ + private function write($data) { + $this -> printer -> getPrintConnector() -> write($data); + } + + /** + * Return true if a character is an ASCII printable character. + * + * @param string $char Character to check + * @param boolean $extended True to allow 128-256 values also (excluded by default) + * @return boolean True if the character is printable, false if it is not. + */ + private static function asciiCheck($char, $extended = false) { + if(strlen($char) != 1) { + // Multi-byte string + return false; + } + $num = ord($char); + if($num > 31 && $num < 127) { // Printable + return true; + } + if($num == 10) { // New-line (printer will take these) + return true; + } + if($extended && $num > 127) { + return true; + } + return false; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php b/htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php new file mode 100644 index 00000000000..8d87f0a527a --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php @@ -0,0 +1,80 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PrintConnector for passing print data to a file. + */ +class FilePrintConnector implements PrintConnector { + /** + * @var resource The file pointer to send data to. + */ + protected $fp; + + /** + * Construct new connector, given a filename + * + * @param string $filename + */ + public function __construct($filename) { + $this -> fp = fopen($filename, "wb+"); + if($this -> fp === false) { + throw new Exception("Cannot initialise FilePrintConnector."); + } + } + + public function __destruct() { + if($this -> fp !== false) { + trigger_error("Print connector was not finalized. Did you forget to close the printer?", E_USER_NOTICE); + } + } + + /** + * Close file pointer + */ + public function finalize() { + fclose($this -> fp); + $this -> fp = false; + } + + /* (non-PHPdoc) + * @see PrintConnector::read() + */ + public function read($len) { + rewind($this -> fp); + return fgets($this -> fp, $len + 1); + } + + /** + * Write data to the file + * + * @param string $data + */ + public function write($data) { + fwrite($this -> fp, $data); + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php b/htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php new file mode 100644 index 00000000000..08ca64ac43e --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php @@ -0,0 +1,99 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This class renders text to small images on-the-fly. It attempts to mimic the + * behaviour of text output, whilst supporting any fonts & character encodings + * which your system can handle. This class currently requires Imagick. + */ +class ImagePrintBuffer implements PrintBuffer { + private $printer; + + function __construct() { + if(!EscposImage::isImagickLoaded()) { + throw new Exception("ImagePrintBuffer requires the imagick extension"); + } + } + + function flush() { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + } + + function getPrinter() { + return $this -> printer; + } + + function setPrinter(Escpos $printer = null) { + $this -> printer = $printer; + } + + function writeText($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + if($text == null) { + return; + } + $text = trim($text, "\n"); + /* Create Imagick objects */ + $image = new Imagick(); + $draw = new ImagickDraw(); + $color = new ImagickPixel('#000000'); + $background = new ImagickPixel('white'); + + /* Create annotation */ + //$draw -> setFont('Arial');// (not necessary?) + $draw -> setFontSize(24); // Size 21 looks good for FONT B + $draw -> setFillColor($color); + $draw -> setStrokeAntialias(true); + $draw -> setTextAntialias(true); + $metrics = $image -> queryFontMetrics($draw, $text); + $draw -> annotation(0, $metrics['ascender'], $text); + + /* Create image & draw annotation on it */ + $image -> newImage($metrics['textWidth'], $metrics['textHeight'], $background); + $image -> setImageFormat('png'); + $image -> drawImage($draw); + //$image -> writeImage("test.png"); + + /* Save image */ + $escposImage = new EscposImage(); + $escposImage -> readImageFromImagick($image); + $size = Escpos::IMG_DEFAULT; + $this -> printer -> bitImage($escposImage, $size); + } + + function writeTextRaw($text) { + if($this -> printer == null) { + throw new LogicException("Not attached to a printer."); + } + $this -> printer -> getPrintConnector() -> write($data); + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php b/htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php new file mode 100644 index 00000000000..4dd2c39f115 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php @@ -0,0 +1,39 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * PrintConnector for directly opening a network socket to a printer to send it commands. + */ +class NetworkPrintConnector extends FilePrintConnector { + public function __construct($ip, $port = "9100") { + $this -> fp = @fsockopen($ip, $port, $errno, $errstr); + if($this -> fp === false) { + throw new Exception("Cannot initialise NetworkPrintConnector: " . $errstr); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php new file mode 100644 index 00000000000..7c3b5abe6dd --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php @@ -0,0 +1,90 @@ + CodePage::CP437, + 1 => false, // Katakana + 2 => CodePage::CP850, + 3 => CodePage::CP860, + 4 => CodePage::CP863, + 5 => CodePage::CP865, + 6 => false, // Western Europe + 7 => false, // Greek + 8 => false, // Hebrew + 9 => false, // Eastern europe + 10 => false, // Iran + 16 => CodePage::CP1252 , + 17 => CodePage::CP866 , + 18 => CodePage::CP852 , + 19 => CodePage::CP858, + 20 => false, // Iran II + 21 => false, // latvian + 22 => false, //Arabic + 23 => false, // PT151, 1251 + 24 => CodePage::CP747, + 25 => CodePage::CP1257, + 27 => false, // Vietnam, + 28 => CodePage::CP864, + 29 => CodePage::CP1001, + 30 => false, // Uigur + 31 => false, // Hebrew + 32 => CodePage::CP1255, + 33 => CodePage::CP720, + 34 => CodePage::CP1256, + 35 => CodePage::CP1257, + 255 => false, // Thai + + 50 => CodePage::CP437, + 51 => false, // Jatakana, + 52 => CodePage::CP437, + 53 => CodePage::CP858, + 54 => CodePage::CP852, + 55 => CodePage::CP860, + 56 => CodePage::CP861, + 57 => CodePage::CP863, + 58 => CodePage::CP865, + 59 => CodePage::CP866, + 60 => CodePage::CP855, + 61 => CodePage::CP857, + 62 => CodePage::CP862, + 63 => CodePage::CP864, + 64 => CodePage::CP737, + 65 => CodePage::CP851, + 66 => CodePage::CP869, + 67 => CodePage::CP928, + 68 => CodePage::CP772, + 69 => CodePage::CP774, + 70 => CodePage::CP874, + 71 => CodePage::CP1252, + 72 => CodePage::CP1250, + 73 => CodePage::CP1251, + 74 => CodePage::CP3840, + 75 => CodePage::CP3841, + 76 => CodePage::CP3843, + 77 => CodePage::CP3844, + 78 => CodePage::CP3845, + 79 => CodePage::CP3846, + 80 => CodePage::CP3847, + 81 => CodePage::CP3848, + 82 => CodePage::CP1001, + 83 => CodePage::CP2001, + 84 => CodePage::CP3001, + 85 => CodePage::CP3002, + 86 => CodePage::CP3011, + 87 => CodePage::CP3012, + 88 => CodePage::CP3021, + 89 => CodePage::CP3041 + ); + } + + public function getSupportsGraphics() { + /* Ask the driver to use bitImage wherever possible instead of graphics */ + return false; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/PrintBuffer.php b/htdocs/includes/mike42/escpos-php/src/PrintBuffer.php new file mode 100644 index 00000000000..9e3b110085f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/PrintBuffer.php @@ -0,0 +1,75 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Print buffers manage newlines and character encoding for the target printer. + * They are used as a swappable component: text or image-based output. + * + * - Text output (EscposPrintBuffer) is the fast default, and is recommended for + * most people, as the text output can be more directly manipulated by ESC/POS + * commands. + * - Image output (ImagePrintBuffer) is designed to accept more encodings than the + * physical printer supports, by rendering the text to small images on-the-fly. + * This takes a lot more CPU than sending text, but is necessary for some users. + * - If your use case fits outside these, then a further speed/flexibility trade-off + * can be made by printing directly from generated HTML or PDF. + */ +interface PrintBuffer { + /** + * Cause the buffer to send any partial input and wait on a newline. + * If the printer is already on a new line, this does nothing. + */ + function flush(); + + /** + * Used by Escpos to check if a printer is set. + */ + function getPrinter(); + + /** + * Used by Escpos to hook up one-to-one link between buffers and printers. + * + * @param Escpos $printer New printer + */ + function setPrinter(Escpos $printer = null); + + /** + * Accept UTF-8 text for printing. + * + * @param string $text Text to print + */ + function writeText($text); + + /** + * Accept 8-bit text in the current encoding and add it to the buffer. + * + * @param string $text Text to print, already the target encoding. + */ + function writeTextRaw($text); +} +?> \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/src/PrintConnector.php b/htdocs/includes/mike42/escpos-php/src/PrintConnector.php new file mode 100644 index 00000000000..f1d37be9c55 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/PrintConnector.php @@ -0,0 +1,56 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Interface passed to Escpos class for receiving print data. Print connectors + * are responsible for transporting this to the actual printer. + */ +interface PrintConnector { + /** + * Print connectors should cause a NOTICE if they are deconstructed + * when they have not been finalized. + */ + public function __destruct(); + + /** + * Finish using this print connector (close file, socket, send + * accumulated output, etc). + */ + public function finalize(); + + /** + * @param string $data + * @return Data read from the printer, or false where reading is not possible. + */ + public function read($len); + + /** + * @param string $data + */ + public function write($data); +} diff --git a/htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php new file mode 100644 index 00000000000..7076b5e16cb --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php @@ -0,0 +1,17 @@ + CodePage::CP437); + } + + public function getSupportsGraphics() { + /* Ask the driver to use bitImage wherever possible instead of graphics */ + return false; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php b/htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php new file mode 100644 index 00000000000..f61774a5e30 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php @@ -0,0 +1,82 @@ + "ÇüéâäàåçêëèïîìÄÅ" . + "ÉæÆôöòûùÿÖÜ¢£¥₧ƒ" . + "áíóúñѪº¿⌐¬½¼¡«»" . + "░▒▓│┤Ā╢ņ╕╣║╗╝╜╛┐" . + "└┴┬├─┼ā╟╚╔╩╦╠═╬╧" . + "Š╤čČ╘╒ģĪī┘┌█▄ūŪ▀" . + "αßΓπΣσµτΦΘΩδ∞φε∩" . + "ĒēĢķĶļĻžŽ∙·√Ņš■ ", + 'CP3012' => "АБВГДЕЖЗИЙКЛМНОП" . + "РСТУФХЦЧШЩЪЫЬЭЮЯ" . + "абвгдежзийклмноп" . + "░▒▓│┤Ā╢ņ╕╣║╗╝Ō╛┐" . + "└┴┬├─┼ā╟╚╔╩╦╠═╬╧" . + "Š╤čČ╘╒ģĪī┘┌█▄ūŪ▀" . + "рстуфхцчшщъыьэюя" . + "ĒēĢķĶļĻžŽ∙·√Ņš■ " + ); + } + + function getSupportedCodePages() { + return array( + 0 => CodePage::CP437, // "Normal" + 1 => CodePage::CP437, + 2 => CodePage::CP932, + 3 => CodePage::CP437, + 4 => CodePage::CP858, + 5 => CodePage::CP852, + 6 => CodePage::CP860, + 7 => CodePage::CP861, + 8 => CodePage::CP863, + 9 => CodePage::CP865, + 10 => CodePage::CP866, + 11 => CodePage::CP855, + 12 => CodePage::CP857, + 13 => CodePage::CP862, + 14 => CodePage::CP864, + 15 => CodePage::CP737, + 16 => CodePage::CP851, + 17 => CodePage::CP869, + 18 => CodePage::CP928, + 19 => CodePage::CP772, + 20 => CodePage::CP774, + 21 => CodePage::CP874, + 32 => CodePage::CP1252, + 33 => CodePage::CP1250, + 34 => CodePage::CP1251, + 64 => CodePage::CP3840, + 65 => CodePage::CP3841, + 66 => CodePage::CP3843, + 67 => CodePage::CP3844, + 68 => CodePage::CP3845, + 69 => CodePage::CP3846, + 70 => CodePage::CP3847, + 71 => CodePage::CP3848, + 72 => CodePage::CP1001, + 73 => CodePage::CP2001, + 74 => CodePage::CP3001, + 75 => CodePage::CP3002, + 76 => 'custom:CP3011', + 77 => 'custom:CP3012', + 78 => CodePage::CP3021, + 79 => CodePage::CP3041, + 96 => false, // Thai Character Code 42 + 97 => false, // Thai Character Code 11 + 98 => false, // Thai Character Code 13 + 99 => false, // Thai Character Code 14 + 100 => false, // Thai Character Code 16 + 101 => false, // Thai Character Code 17 + 102 => false, // Thai Character Code 18 + 255 => false); + } + + function getSupportsStarCommands() { + /* Allows Escpos.php to substitute emulated ESC/POS commands with native ones for this printer. */ + return true; + } +} \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php b/htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php new file mode 100644 index 00000000000..717e9c1a572 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php @@ -0,0 +1,356 @@ +, + * incorporating modifications by: + * - Roni Saha + * - Gergely Radics + * - Warren Doyle + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Connector for sending print jobs to + * - local ports on windows (COM1, LPT1, etc) + * - shared (SMB) printers from any platform (\\server\foo) + * For USB printers or other ports, the trick is to share the printer with a generic text driver, then access it locally. + */ +class WindowsPrintConnector implements PrintConnector { + /** + * @var array Accumulated lines of output for later use. + */ + private $buffer; + + /** + * @var string The hostname of the target machine, or null if this is a local connection. + */ + private $hostname; + + /** + * @var boolean True if a port is being used directly (must be Windows), false if network shares will be used. + */ + private $isLocal; + + /** + * @var int Platform we're running on, for selecting different commands. See PLATFORM_* constants. + */ + private $platform; + + /** + * @var string The name of the target printer (eg "Foo Printer") or port ("COM1", "LPT1"). + */ + private $printerName; + + /** + * @var string Login name for network printer, or null if not using authentication. + */ + private $userName; + + /** + * @var string Password for network printer, or null if no password is required. + */ + private $userPassword; + + /** + * @var string Workgroup that the printer is located on + */ + private $workgroup; + + /** + * @var int represents Linux + */ + const PLATFORM_LINUX = 0; + + /** + * @var int represents Mac + */ + const PLATFORM_MAC = 1; + + /** + * @var int represents Windows + */ + const PLATFORM_WIN = 2; + + /** + * @var string Valid local ports. + */ + const REGEX_LOCAL = "/^(LPT\d|COM\d)$/"; + + /** + * @var string Valid printer name. + */ + const REGEX_PRINTERNAME = "/^[\w-]+(\s[\w-]+)*$/"; + + /** + * @var string Valid smb:// URI containing hostname & printer with optional user & optional password only. + */ + const REGEX_SMB = "/^smb:\/\/([\s\w-]+(:[\s\w-]+)?@)?[\w-]+\/([\w-]+\/)?[\w-]+(\s[\w-]+)*$/"; + + /** + * @param string $dest + * @throws BadMethodCallException + */ + public function __construct($dest) { + $this -> platform = $this -> getCurrentPlatform(); + $this -> isLocal = false; + $this -> buffer = null; + $this -> userName = null; + $this -> userPassword = null; + $this -> workgroup = null; + if(preg_match(self::REGEX_LOCAL, $dest) == 1) { + // Straight to LPT1, COM1 or other local port. Allowed only if we are actually on windows. + if($this -> platform !== self::PLATFORM_WIN) { + throw new BadMethodCallException("WindowsPrintConnector can only be used to print to a local printer ('".$dest."') on a Windows computer."); + } + $this -> isLocal = true; + $this -> hostname = null; + $this -> printerName = $dest; + } else if(preg_match(self::REGEX_SMB, $dest) == 1) { + // Connect to samba share, eg smb://host/printer + $part = parse_url($dest); + $this -> hostname = $part['host']; + /* Printer name and optional workgroup */ + $path = ltrim($part['path'], '/'); + if(strpos($path, "/") !== false) { + $pathPart = explode("/", $path); + $this -> workgroup = $pathPart[0]; + $this -> printerName = $pathPart[1]; + } else { + $this -> printerName = $path; + } + /* Username and password if set */ + if(isset($part['user'])) { + $this -> userName = $part['user']; + if(isset($part['pass'])) { + $this -> userPassword = $part['pass']; + } + } + } else if(preg_match(self::REGEX_PRINTERNAME, $dest) == 1) { + // Just got a printer name. Assume it's on the current computer. + $hostname = gethostname(); + if(!$hostname) { + $hostname = "localhost"; + } + $this -> hostname = $hostname; + $this -> printerName = $dest; + } else { + throw new BadMethodCallException("Printer '" . $dest . "' is not a valid printer name. Use local port (LPT1, COM1, etc) or smb://computer/printer notation."); + } + $this -> buffer = array(); + } + + public function __destruct() { + if($this -> buffer !== null) { + trigger_error("Print connector was not finalized. Did you forget to close the printer?", E_USER_NOTICE); + } + } + + public function finalize() { + $data = implode($this -> buffer); + $this -> buffer = null; + if($this -> platform == self::PLATFORM_WIN) { + $this -> finalizeWin($data); + } else if($this -> platform == self::PLATFORM_LINUX) { + $this -> finalizeLinux($data); + } else { + $this -> finalizeMac($data); + } + } + + /** + * Send job to printer -- platform-specific Linux code. + * + * @param string $data Print data + * @throws Exception + */ + protected function finalizeLinux($data) { + /* Non-Windows samba printing */ + $device = "//" . $this -> hostname . "/" . $this -> printerName; + if($this -> userName !== null) { + $user = ($this -> workgroup != null ? ($this -> workgroup . "\\") : "") . $this -> userName; + if($this -> userPassword == null) { + // No password + $command = sprintf("smbclient %s -U %s -c %s -N", + escapeshellarg($device), + escapeshellarg($user), + escapeshellarg("print -")); + $redactedCommand = $command; + } else { + // With password + $command = sprintf("smbclient %s %s -U %s -c %s", + escapeshellarg($device), + escapeshellarg($this -> userPassword), + escapeshellarg($user), + escapeshellarg("print -")); + $redactedCommand = sprintf("smbclient %s %s -U %s -c %s", + escapeshellarg($device), + escapeshellarg("*****"), + escapeshellarg($user), + escapeshellarg("print -")); + } + } else { + // No authentication information at all + $command = sprintf("smbclient %s -c %s -N", + escapeshellarg($device), + escapeshellarg("print -")); + $redactedCommand = $command; + } + $retval = $this -> runCommand($command, $outputStr, $errorStr, $data); + if($retval != 0) { + throw new Exception("Failed to print. Command \"$redactedCommand\" failed with exit code $retval: " . trim($outputStr)); + } + } + + protected function finalizeMac($data) { + throw new Exception("Mac printing not implemented."); + } + + /** + * Send data to printer -- platform-specific Windows code. + * + * @param string $data + */ + protected function finalizeWin($data) { + /* Windows-friendly printing of all sorts */ + if(!$this -> isLocal) { + /* Networked printing */ + $device = "\\\\" . $this -> hostname . "\\" . $this -> printerName; + if($this -> userName !== null) { + /* Log in */ + $user = "/user:" . ($this -> workgroup != null ? ($this -> workgroup . "\\") : "") . $this -> userName; + if($this -> userPassword == null) { + $command = sprintf("net use %s %s", + escapeshellarg($device), + escapeshellarg($user)); + $redactedCommand = $command; + } else { + $command = sprintf("net use %s %s %s", + escapeshellarg($device), + escapeshellarg($user), + escapeshellarg($this -> userPassword)); + $redactedCommand = sprintf("net use %s %s %s", + escapeshellarg($device), + escapeshellarg($user), + escapeshellarg("*****")); + } + $retval = $this -> runCommand($command, $outputStr, $errorStr); + if($retval != 0) { + throw new Exception("Failed to print. Command \"$redactedCommand\" failed with exit code $retval: " . trim($errorStr)); + } + } + /* Final print-out */ + $filename = tempnam(sys_get_temp_dir(), "escpos"); + file_put_contents($filename, $data); + if(!$this -> runCopy($filename, $device)){ + throw new Exception("Failed to copy file to printer"); + } + unlink($filename); + } else { + /* Drop data straight on the printer */ + if(!$this -> runWrite($data, $this -> printerName)) { + throw new Exception("Failed to write file to printer at " . $this -> printerName); + } + } + } + + /** + * @return string Current platform. Separated out for testing purposes. + */ + protected function getCurrentPlatform() { + if(PHP_OS == "WINNT") { + return self::PLATFORM_WIN; + } + if(PHP_OS == "Darwin") { + return self::PLATFORM_MAC; + } + return self::PLATFORM_LINUX; + } + + /* (non-PHPdoc) + * @see PrintConnector::read() + */ + public function read($len) { + /* Two-way communication is not supported */ + return false; + } + + /** + * Run a command, pass it data, and retrieve its return value, standard output, and standard error. + * + * @param string $command the command to run. + * @param string $outputStr variable to fill with standard output. + * @param string $errorStr variable to fill with standard error. + * @param string $inputStr text to pass to the command's standard input (optional). + * @return number + */ + protected function runCommand($command, &$outputStr, &$errorStr, $inputStr = null) { + $descriptors = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w"), + ); + $process = proc_open($command, $descriptors, $fd); + if (is_resource($process)) { + /* Write to input */ + if($inputStr !== null) { + fwrite($fd[0], $inputStr); + } + fclose($fd[0]); + /* Read stdout */ + $outputStr = stream_get_contents($fd[1]); + fclose($fd[1]); + /* Read stderr */ + $errorStr = stream_get_contents($fd[2]); + fclose($fd[2]); + /* Finish up */ + $retval = proc_close($process); + return $retval; + } else { + /* Method calling this should notice a non-zero exit and print an error */ + return -1; + } + } + + /** + * Copy a file. Separated out so that nothing is actually printed during test runs. + * + * @param string $from Source file + * @param string $to Destination file + * @return boolean True if copy was successful, false otherwise + */ + protected function runCopy($from, $to) { + return copy($from, $to); + } + + /** + * Write data to a file. Separated out so that nothing is actually printed during test runs. + * + * @param string $data Data to print + * @param string $to Destination file + * @return boolean True if write was successful, false otherwise + */ + protected function runWrite($data, $to) { + return file_put_contents($data, $to) !== false; + } + + public function write($data) { + $this -> buffer[] = $data; + } +} diff --git a/htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz b/htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz new file mode 100644 index 00000000000..43ad39d2295 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz differ diff --git a/htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz b/htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz new file mode 100644 index 00000000000..288b7d37407 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz differ diff --git a/htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz b/htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz new file mode 100644 index 00000000000..e0bb0e5fec5 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz differ diff --git a/htdocs/includes/mike42/escpos-php/test/bootstrap.php b/htdocs/includes/mike42/escpos-php/test/bootstrap.php new file mode 100644 index 00000000000..41b04f5ca6b --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/bootstrap.php @@ -0,0 +1,27 @@ + $c) { + $code = ord($c); + if($code < 32 || $code > 126) { + $chars[$i] = "\\x" . bin2hex($c); + } + } + return implode($chars); +} diff --git a/htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php b/htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php new file mode 100644 index 00000000000..689891e2432 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php @@ -0,0 +1,126 @@ + exampleDir = dirname(__FILE__) . "/../../example/"; + } + + public function testBitImage() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("bit-image.php"); + $this -> outpTest($outp, "bit-image.bin"); + } + + public function testCharacterEncodings() { + $outp = $this -> runExample("character-encodings.php"); + $this -> outpTest($outp, "character-encodings.bin"); + } + + public function testCharacterTables() { + $outp = $this -> runExample("character-tables.php"); + $this -> outpTest($outp, "character-tables.bin"); + } + + private function outpTest($outp, $fn) { + $file = dirname(__FILE__) . "/resources/output/".$fn; + if(!file_exists($file)) { + file_put_contents($file, $outp); + } + $this -> assertEquals($outp, file_get_contents($file)); + } + + public function testDemo() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("demo.php"); + $this -> outpTest($outp, "demo.bin"); + } + + public function testGraphics() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("graphics.php"); + $this -> outpTest($outp, "graphics.bin"); + } + + public function testReceiptWithLogo() { + $this -> requireGraphicsLibrary(); + $outp = $this -> runExample("receipt-with-logo.php"); + $this -> outpTest($outp, "receipt-with-logo.bin"); + } + + public function testQrCode() { + $outp = $this -> runExample("qr-code.php"); + $this -> outpTest($outp, "qr-code.bin"); + } + + public function testBarcode() { + $outp = $this -> runExample("barcode.php"); + $this -> outpTest($outp, "barcode.bin"); + } + + public function testTextSize() { + $outp = $this -> runExample("text-size.php"); + $this -> outpTest($outp, "text-size.bin"); + } + + /** + * @large + */ + public function testPrintFromPdf() { + if(!EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("imagick plugin required for this test"); + } + $outp = $this -> runExample("print-from-pdf.php"); + $this -> outpTest(gzcompress($outp, 9), "print-from-pdf.bin.z"); // Compressing output because it's ~1MB + } + + public function testInterfaceEthernet() { + // Test attempts DNS lookup on some machine + $outp = $this -> runExample("interface/ethernet.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceLinuxUSB() { + $outp = $this -> runExample("interface/linux-usb.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceWindowsUSB() { + // Output varies between platforms, not checking. + $outp = $this -> runExample("interface/windows-usb.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceSMB() { + // Output varies between platforms, not checking. + $outp = $this -> runExample("interface/smb.php"); + $this -> outpTest($outp, "interface.bin"); + } + + public function testInterfaceWindowsLPT() { + // Output varies between platforms, not checking. + $outp = $this -> runExample("interface/windows-lpt.php"); + $this -> outpTest($outp, "interface.bin"); + } + + private function runExample($fn) { + // Change directory and check script + chdir($this -> exampleDir); + $this -> assertTrue(file_exists($fn), "Script $fn not found."); + // Run command and save output + ob_start(); + passthru("php " . escapeshellarg($fn), $retval); + $outp = ob_get_contents(); + ob_end_clean(); + // Check return value + $this -> assertEquals(0, $retval, "Example $fn exited with status $retval"); + return $outp; + } + + protected function requireGraphicsLibrary() { + if(!EscposImage::isGdLoaded() && !EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("This test requires a graphics library."); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin new file mode 100644 index 00000000000..79a2daf803e Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin new file mode 100644 index 00000000000..462eb1a0bf3 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin new file mode 100644 index 00000000000..0a762c8c160 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin new file mode 100644 index 00000000000..f3fb990cea3 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin new file mode 100644 index 00000000000..6b426d91ff0 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/graphics.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/graphics.bin new file mode 100644 index 00000000000..8eeda76d297 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/graphics.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin new file mode 100644 index 00000000000..729d4f4481b --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin @@ -0,0 +1,2 @@ +@Hello World! +VA \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z new file mode 100644 index 00000000000..f89eb5d2115 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin new file mode 100644 index 00000000000..a1e687f151c Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/receipt-with-logo.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/receipt-with-logo.bin new file mode 100644 index 00000000000..a4ed9c35304 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/receipt-with-logo.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/integration/resources/output/text-size.bin b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/text-size.bin new file mode 100644 index 00000000000..4d36038e463 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/integration/resources/output/text-size.bin differ diff --git a/htdocs/includes/mike42/escpos-php/test/phpunit.xml b/htdocs/includes/mike42/escpos-php/test/phpunit.xml new file mode 100644 index 00000000000..f9a6579cf45 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/phpunit.xml @@ -0,0 +1,16 @@ + + + + unit + + + integration + + + \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php b/htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php new file mode 100644 index 00000000000..b3e72452a9f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php @@ -0,0 +1,69 @@ + profiles = array('DefaultCapabilityProfile', 'EposTepCapabilityProfile', 'SimpleCapabilityProfile', 'StarCapabilityProfile', 'P822DCapabilityProfile'); + $this -> checklist = array(); + foreach($this -> profiles as $profile) { + $this-> checklist[] = $profile::getInstance(); + } + } + + function testSupportedCodePages() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportedCodePages(); + $this -> assertTrue(is_array($check) && isset($check[0]) && $check[0] == 'CP437'); + $custom = $obj -> getCustomCodePages(); + foreach($check as $num => $page) { + $this -> assertTrue(is_numeric($num) && ($page === false || is_string($page))); + if($page === false || strpos($page, ":") === false) { + continue; + } + $part = explode(":", $page); + if(!array_shift($part) == "custom") { + continue; + } + $this -> assertTrue(isset($custom[implode(":", $part)])); + } + } + } + + function testCustomCodePages() { + foreach($this -> checklist as $obj) { + $check = $obj -> getCustomCodePages(); + $this -> assertTrue(is_array($check)); + foreach($check as $name => $customMap) { + $this -> assertTrue(is_string($name)); + $this -> assertTrue(is_string($customMap) && mb_strlen($customMap, 'UTF-8') == 128); + } + } + } + + function testSupportsBitImage() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportsBitImage(); + $this -> assertTrue(is_bool($check)); + } + } + + function testSupportsGraphics() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportsGraphics(); + $this -> assertTrue(is_bool($check)); + } + } + + function testSupportsQrCode() { + foreach($this -> checklist as $obj) { + $check = $obj -> getSupportsQrCode(); + $this -> assertTrue(is_bool($check)); + } + } +} +?> \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php b/htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php new file mode 100644 index 00000000000..f152f400a2f --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php @@ -0,0 +1,235 @@ + loadAndCheckImg(null, false, false, 0, 0, ""); + } + + /** + * BMP handling not yet implemented, but these will use + * a native PHP bitmap reader. + * This just tests that they are not being passed on to another library. + */ + public function testBmpBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg('not a real file.bmp', false, false, 1, 1, "\x80"); + } + + public function testBmpBlack() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg("canvas_black.bmp", false, false, 0, 0, "\x80"); + } + + public function testBmpBlackWhite() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg("black_white.bmp", false, false, 0, 0, "\xc0\x00"); + } + + public function testBmpWhite() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg("canvas_white.bmp", false, false, 0, 0, "\x00"); + } + + /** + * GD tests - Load tiny images and check how they are printed. + * These are skipped if you don't have gd. + */ + public function testGdBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg('not a real file.png', true, false, 1, 1, "\x80"); + } + + public function testGdBlack() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_black.' . $format, true, false, 1, 1, "\x80"); + } + } + + public function testGdBlackTransparent() { + foreach(array('png', 'gif') as $format) { + $this -> loadAndCheckImg('black_transparent.' . $format, true, false, 2, 2, "\xc0\x00"); + } + } + + public function testGdBlackWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('black_white.' . $format, true, false, 2, 2, "\xc0\x00"); + } + } + + public function testGdWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_white.' . $format, true, false, 1, 1, "\x00"); + } + } + + /** + * Imagick tests - Load tiny images and check how they are printed + * These are skipped if you don't have imagick + */ + public function testImagickBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckImg('not a real file.png', false, true, 1, 1, "\x80"); + } + + public function testImagickBlack() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_black.' . $format, false, true, 1, 1, "\x80"); + } + } + + public function testImagickBlackTransparent() { + foreach(array('png', 'gif') as $format) { + $this -> loadAndCheckImg('black_transparent.' . $format, false, true, 2, 2, "\xc0\x00"); + } + } + + public function testImagickBlackWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('black_white.' . $format, false, true, 2, 2, "\xc0\x00"); + } + } + + public function testImagickWhite() { + foreach(array('png', 'jpg', 'gif') as $format) { + $this -> loadAndCheckImg('canvas_white.' . $format, false, true, 1, 1, "\x00"); + } + } + + /** + * Mixed test - Same as above, but confirms that each tiny image can be loaded + * under any supported library configuration with the same results. + * These are skipped if you don't have gd AND imagick + */ + public function testLibraryDifferences() { + if(!EscposImage::isGdLoaded() || !EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("both gd and imagick plugin are required for this test"); + } + $inFile = array('black_white.png', 'canvas_black.png', 'canvas_white.png'); + foreach($inFile as $fn) { + // Default check + $im = new EscposImage(dirname(__FILE__) . "/resources/$fn"); + $width = $im -> getWidth(); + $height = $im -> getHeight(); + $data = $im -> toRasterFormat(); + // Gd check + $this -> loadAndCheckImg($fn, true, false, $width, $height, $data); + // Imagick check + $this -> loadAndCheckImg($fn, false, true, $width, $height, $data); + } + } + + /** + * PDF tests - load tiny PDF and check for well-formedness + * These are also skipped if you don't have imagick + * @medium + */ + public function testPdfAllPages() { + $this -> loadAndCheckPdf('doc.pdf', null, 1, 1, array("\x00", "\x80")); + } + + public function testPdfBadFilename() { + $this -> setExpectedException('Exception'); + $this -> loadAndCheckPdf('not a real file', null, 1, 1, array()); + } + + /** + * @medium + */ + public function testPdfBadRange() { + // Start page is after end page. + $this -> setExpectedException('Exception'); + $this -> loadAndCheckPdf('doc.pdf', array(1, 0), 1, 1, array("\x00", "\x80")); + } + + /** + * @medium + */ + public function testPdfFirstPage() { + $this -> loadAndCheckPdf('doc.pdf', array(0, 0), 1, 1, array("\x00")); + } + + /** + * @medium + */ + public function testPdfMorePages() { + $this -> loadAndCheckPdf('doc.pdf', array(1, 20), 1, 1, array("\x80")); + } + + /** + * @medium + */ + public function testPdfSecondPage() { + $this -> loadAndCheckPdf('doc.pdf', array(1, 1), 1, 1, array("\x80")); + } + + /** + * @medium + */ + public function testPdfStartPastEndOfDoc() { + // Doc only has pages 0 and 1, can't start reading from 2. + $this -> markTestIncomplete("Test needs revising- produces output due to apparent imagick bug."); + $this -> setExpectedException('ImagickException'); + $this -> loadAndCheckPdf('doc.pdf', array(2, 3), 1, 1, array()); + } + + /** + * Load an EscposImage with (optionally) certain libraries disabled and run a check. + */ + private function loadAndCheckImg($fn, $gd, $imagick, $width, $height, $rasterFormat = null) { + $img = $this -> getMockImage($fn === null ? null : dirname(__FILE__) . "/resources/$fn", $gd, $imagick); + $this -> checkImg($img, $width, $height, $rasterFormat); + } + + /** + * Same as above, loading document and checking pages against some expected values. + */ + private function loadAndCheckPdf($fn, array $range = null, $width, $height, array $rasterFormat = null) { + if(!EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("imagick plugin required for this test"); + } + $pdfPages = EscposImage::loadPdf(dirname(__FILE__) . "/resources/$fn", $width, $range); + $this -> assertTrue(count($pdfPages) == count($rasterFormat), "Got back wrong number of pages"); + foreach($pdfPages as $id => $img) { + $this -> checkImg($img, $width, $height, $rasterFormat[$id]); + } + } + + /** + * Check image against known width, height, output. + */ + private function checkImg(EscposImage $img, $width, $height, $rasterFormat = null) { + if($rasterFormat === null) { + echo "\nImage was: " . $img -> getWidth() . "x" . $img -> getHeight() . ", data \"" . friendlyBinary($img -> toRasterFormat()) . "\""; + } + $this -> assertTrue($img -> getHeight() == $height); + $this -> assertTrue($img -> getWidth() == $width); + $this -> assertTrue($img -> toRasterFormat() == $rasterFormat); + } + + /** + * Load up an EsposImage with given libraries disabled or enabled. Marks the test + * as skipped if you ask for a library which is not loaded. + */ + private function getMockImage($path, $gd, $imagick) { + /* Sanity checks */ + if($gd && !EscposImage::isGdLoaded()) { + $this -> markTestSkipped("gd plugin required for this test"); + } + if($imagick && !EscposImage::isImagickLoaded()) { + $this -> markTestSkipped("imagick plugin required for this test"); + } + $stub = $this -> getMockBuilder('EscposImage') + -> setMethods(array('isGdSupported', 'isImagickSupported')) + -> disableOriginalConstructor() + -> getMock(); + $stub -> method('isGdSupported') + -> willReturn($gd); + $stub -> method('isImagickSupported') + -> willReturn($imagick); + $stub -> __construct($path); + return $stub; + } +} \ No newline at end of file diff --git a/htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php b/htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php new file mode 100644 index 00000000000..2bbdb34c20c --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php @@ -0,0 +1,150 @@ + outputConnector = new DummyPrintConnector(); + $printer = new Escpos($this -> outputConnector); + $this -> buffer = $printer -> getPrintBuffer(); + } + + protected function checkOutput($expected = null) { + /* Check those output strings */ + $outp = $this -> outputConnector -> getData(); + if($expected === null) { + echo "\nOutput was:\n\"" . friendlyBinary($outp) . "\"\n"; + } + $this -> assertEquals($expected, $outp); + } + + protected function tearDown() { + $this -> outputConnector -> finalize(); + } + + public function testRawTextNonprintable() { + $this -> buffer -> writeTextRaw("Test" . Escpos::ESC . "v1\n"); + $this -> checkOutput("\x1b@Test?v1\x0a"); // ASCII ESC character is substituted for '?' + } + + public function testDanish() { + $this -> buffer -> writeText("Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.\n"); + $this -> checkOutput("\x1b@Quizdeltagerne spiste jordb\x91r med fl\x1bt\x02\x9bde, mens cirkusklovnen Wolther spillede p\x86 xylofon.\x0a"); + } + + public function testGerman() { + $this -> buffer -> writeText("Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.\n"); + $this -> checkOutput("\x1b@Falsches \x9aben von Xylophonmusik qu\x84lt jeden gr\x94\xe1eren Zwerg.\x0a"); + } + + public function testGreek() { + $this -> buffer -> writeText("Ξεσκεπάζω την ψυχοφθόρα βδελυγμία"); + $this -> checkOutput("\x1b@\x1bt\x0b\xbd\xde\xec\xe4\xde\xea\x9b\xe0\xfa \xee\xe1\xe7 \xf6\xf2\xf4\xe9\xf3\xe2\xa2\xeb\xd6 \xd7\xdd\xde\xe5\xf2\xd8\xe6\x9f\xd6"); + } + + public function testGreekWithDiacritics() { + // This is a string which is known to be un-printable in ESC/POS (the grave-accented letters are not in any code page), + // so we are checking the substitution '?' for unknown characters. + $this -> buffer -> writeText("Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο.\n"); + $this -> checkOutput("\x1b@\xe2\xe0\x1bt\x0b\xe0\x9d\xde\xed \xe4\xd6? \xe6\xf2\xeb\xee\xe3?\xed \xdd?\xe7 \xe2? \xd7\xeb? \xea\xe3? \xec\xee? \xf4\xeb\xf2\xec\xd6\xf3? \xe8\x9d\xf3\xfa\xee\xe9.\x0a"); + } + + public function testEnglish() { + $this -> buffer -> writeText("The quick brown fox jumps over the lazy dog.\n"); + $this -> checkOutput("\x1b@The quick brown fox jumps over the lazy dog.\n"); + } + + public function testSpanish() { + // This one does not require changing code-pages at all, so characters are just converted from Unicode to CP437. + $this -> buffer -> writeText("El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.\n"); + $this -> checkOutput("\x1b@El ping\x81ino Wenceslao hizo kil\xa2metros bajo exhaustiva lluvia y fr\xa1o, a\xa4oraba a su querido cachorro.\x0a"); + } + + public function testFrench() { + $this -> buffer -> writeText("Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en canoë au delà des îles, près du mälström où brûlent les novæ.\n"); + $this -> checkOutput("\x1b@Le c\x1bt\x10\x9cur d\xe9\xe7u mais l'\xe2me plut\xf4t na\xefve, Lou\xffs r\xeava de crapa\xfcter en cano\xeb au del\xe0 des \xeeles, pr\xe8s du m\xe4lstr\xf6m o\xf9 br\xfblent les nov\xe6.\x0a"); + } + + public function testIrishGaelic() { + // Note that some letters with diacritics cannot be printed for Irish Gaelic text, so text may need to be simplified. + $this -> buffer -> writeText("D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh.\n"); + $this -> checkOutput("\x1b@D'fhuascail \x1bt\x02\xd6osa, \xe9rmhac na h\xe0ighe Beannaithe, p\xa2r \x90ava agus \xb5dhaimh.\x0a"); + } + + public function testHungarian() { + $this -> buffer -> writeText("Árvíztűrő tükörfúrógép.\n"); + $this -> checkOutput("\x1b@\x1bt\x02\xb5rv\xa1zt\x1bt\x12\xfbr\x8b t\x81k\x94rf\xa3r\xa2g\x82p.\x0a"); + } + + public function testIcelandic() { + $this -> buffer -> writeText("Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa."); + $this -> checkOutput("\x1b@K\x91mi n\x1bt\x02\xec \x94xi h\x82r ykist \xe7j\xa2fum n\xa3 b\x91\xd0i v\xa1l og \xa0drepa."); + } + + public function testJapaneseHiragana() { + $this -> markTestIncomplete("Non-ASCII character sets not yet supported."); + $this -> buffer -> writeText(implode("\n", array("いろはにほへとちりぬるを", " わかよたれそつねならむ", "うゐのおくやまけふこえて", "あさきゆめみしゑひもせす")) . "\n"); + $this -> checkOutput(); + } + + public function testJapaneseKatakana() { + $this -> markTestIncomplete("Non-ASCII character sets not yet supported."); + $this -> buffer -> writeText(implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン")) . "\n"); + $this -> checkOutput("\x1b@\x1bt\x01\xb2\xdb\xca\xc6\xce\xcd\xc4 \xc1\xd8\xc7\xd9\xa6 \xdc\xb6\xd6\xc0\xda\xbf \xc2\xc8\xc5\xd7\xd1\x0a\xb3\xb2\xc9\xb5\xb8\xd4\xcf \xb9\xcc\xba\xb4\xc3 \xb1\xbb\xb7\xd5\xd2\xd0\xbc \xb4\xcb\xd3\xbe\xbd\xdd\x0a"); + } + + public function testJapaneseKataKanaHalfWidth() { + $this -> buffer -> writeText(implode("\n", array("イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム", "ウイノオクヤマ ケフコエテ アサキユメミシ エヒモセスン")) . "\n"); + $this -> checkOutput("\x1b@\x1bt\x01\xb2\xdb\xca\xc6\xce\xcd\xc4 \xc1\xd8\xc7\xd9\xa6 \xdc\xb6\xd6\xc0\xda\xbf \xc2\xc8\xc5\xd7\xd1\x0a\xb3\xb2\xc9\xb5\xb8\xd4\xcf \xb9\xcc\xba\xb4\xc3 \xb1\xbb\xb7\xd5\xd2\xd0\xbc \xb4\xcb\xd3\xbe\xbd\xdd\x0a"); + } + + public function testLatvian() { + $this -> buffer -> writeText("Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus.\n"); + $this -> checkOutput("\x1b@Gl\x1bt!\x83\xd8\xd5\xe9\xd7\xeca r\xd7\xe9\x8c\xd5i dz\x89rum\x83 \xd1iepj Baha koncertfl\x8c\x85e\xebu v\x83kus.\x0a"); + } + + public function testPolish() { + $this -> buffer -> writeText("Pchnąć w tę łódź jeża lub ośm skrzyń fig.\n"); + $this -> checkOutput("\x1b@Pchn\x1bt\x12\xa5\x86 w t\xa9 \x88\xa2d\xab je\xbea lub o\x98m skrzy\xe4 fig.\x0a"); + } + + public function testRussian() { + $this -> buffer -> writeText("В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!\n"); + $this -> checkOutput("\x1b@\x1bt\x11\x82 \xe7\xa0\xe9\xa0\xe5 \xee\xa3\xa0 \xa6\xa8\xab \xa1\xeb \xe6\xa8\xe2\xe0\xe3\xe1? \x84\xa0, \xad\xae \xe4\xa0\xab\xec\xe8\xa8\xa2\xeb\xa9 \xed\xaa\xa7\xa5\xac\xaf\xab\xef\xe0!\x0a"); + } + + public function testThai() { + $this -> markTestIncomplete("Non-ASCII character sets not yet supported."); + $this -> buffer -> writeText("นายสังฆภัณฑ์ เฮงพิทักษ์ฝั่ง ผู้เฒ่าซึ่งมีอาชีพเป็นฅนขายฃวด ถูกตำรวจปฏิบัติการจับฟ้องศาล ฐานลักนาฬิกาคุณหญิงฉัตรชฎา ฌานสมาธิ\n"); // Quotation from Wikipedia + $this -> checkOutput(); + } + + public function testTurkish() { + $this -> buffer -> writeText("Pijamalı hasta, yağız şoföre çabucak güvendi.\n"); + $this -> checkOutput("\x1b@Pijamal\x1bt\x02\xd5 hasta, ya\x1bt\x0d\xa7\x8dz \x9fof\x94re \x87abucak g\x81vendi.\x0a"); + } + + public function testArabic() { + $this -> markTestIncomplete("Right-to-left text not yet supported."); + $this -> buffer -> writeText("صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ" . "\n"); // Quotation from Wikipedia + $this -> checkOutput(); + } + + public function testHebrew() { + // RTL text is more complex than the above. + $this -> markTestIncomplete("Right-to-left text not yet supported."); + $this -> buffer -> writeText("דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה" . "\n"); + $this -> checkOutput(); + } +} + diff --git a/htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php b/htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php new file mode 100644 index 00000000000..6fd0bd7d204 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php @@ -0,0 +1,765 @@ + outputConnector = new DummyPrintConnector(); + $this -> printer = new Escpos($this -> outputConnector); + } + + protected function checkOutput($expected = null) { + /* Check those output strings */ + $outp = $this -> outputConnector -> getData(); + if($expected === null) { + echo "\nOutput was:\n\"" . friendlyBinary($outp) . "\"\n"; + } + $this -> assertEquals($expected, $outp); + } + + protected function tearDown() { + $this -> outputConnector -> finalize(); + } + + protected function requireGraphicsLibrary() { + if(!EscposImage::isGdLoaded() && !EscposImage::isImagickLoaded()) { + // If the test is about to do something which requires a library, + // something must throw an exception. + $this -> setExpectedException('Exception'); + } + } + + public function testInitializeOutput() { + $this -> checkOutput("\x1b\x40"); + } + + public function testTextStringOutput() { + $this -> printer -> text("The quick brown fox jumps over the lazy dog\n"); + $this -> checkOutput("\x1b@The quick brown fox jumps over the lazy dog\n"); + } + + public function testTextDefault() { + $this -> printer -> text(); + $this -> checkOutput("\x1b@"); + } + + public function testTextString() { + $this -> printer -> text("String"); + $this -> printer -> text(123); + $this -> printer -> text(); + $this -> printer -> text(null); + $this -> printer -> text(1.2); + $this -> printer -> text(new FooBar("FooBar")); + $this -> checkOutput("\x1b@String1231.2FooBar"); + } + + public function testTextObject() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> text(new DateTime()); + } + + public function testFeedDefault() { + $this -> printer -> feed(); + $this -> checkOutput("\x1b@\x0a"); + } + + public function testFeed3Lines() { + $this -> printer -> feed(3); + $this -> checkOutput("\x1b@\x1bd\x03"); + } + + public function testFeedZero() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feed(0); + } + + public function testFeedNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feed("ab"); + } + + public function testFeedTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feed(256); + } + + /* Print mode */ + public function testSelectPrintModeDefault() { + $this -> printer -> selectPrintMode(); + $this -> checkOutput("\x1b@\x1b!\x00"); + } + + public function testSelectPrintModeAcceptedValues() { + /* This iterates over a bunch of numbers, figures out which + ones contain invalid flags, and checks that the driver + rejects those, but accepts the good inputs */ + + for($i = -1; $i <= 256; $i++) { + $invalid = ($i < 0) || ($i > 255) || (($i & 2) == 2) || (($i & 4) == 4) || (($i & 64) == 64); + $failed = false; + try { + $this -> printer -> selectPrintMode($i); + } catch(Exception $e) { + $failed = true; + } + $this -> assertEquals($failed, $invalid); + } + } + + /* Underline */ + public function testSetUnderlineDefault() { + $this -> printer -> setUnderline(); + $this -> checkOutput("\x1b@\x1b-\x01"); + } + + public function testSetUnderlineOff() { + $this -> printer -> setUnderline(Escpos::UNDERLINE_NONE); + $this -> checkOutput("\x1b@\x1b-\x00"); + } + + public function testSetUnderlineOn() { + $this -> printer -> setUnderline(Escpos::UNDERLINE_SINGLE); + $this -> checkOutput("\x1b@\x1b-\x01"); + } + + public function testSetUnderlineDbl() { + $this -> printer -> setUnderline(Escpos::UNDERLINE_DOUBLE); + $this -> checkOutput("\x1b@\x1b-\x02"); + } + + public function testSetUnderlineAcceptedValues() { + $this -> printer -> setUnderline(0); + $this -> printer -> setUnderline(1); + $this -> printer -> setUnderline(2); + /* These map to 0 & 1 for interchangeability with setEmphasis */ + $this -> printer -> setUnderline(true); + $this -> printer -> setUnderline(false); + $this -> checkOutput("\x1b@\x1b-\x00\x1b-\x01\x1b-\x02\x1b-\x01\x1b-\x00"); + } + + public function testSetUnderlineTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setUnderline(3); + } + + public function testSetUnderlineNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setUnderline(-1); + } + + public function testSetUnderlineNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setUnderline("Hello"); + } + + /* Emphasis */ + public function testSetEmphasisDefault() { + $this -> printer -> setEmphasis(); + $this -> checkOutput("\x1b@\x1bE\x01"); + } + + public function testSetEmphasisOn() { + $this -> printer -> setEmphasis(true); + $this -> checkOutput("\x1b@\x1bE\x01"); + } + + public function testSetEmphasisOff() { + $this -> printer -> setEmphasis(false); + $this -> checkOutput("\x1b@\x1bE\x00"); + } + + public function testSetEmphasisNonBoolean() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setEmphasis(7); + } + + /* Double strike */ + public function testSetDoubleStrikeDefault() { + $this -> printer -> setDoubleStrike(); + $this -> checkOutput("\x1b@\x1bG\x01"); + } + + public function testSetDoubleStrikeOn() { + $this -> printer -> setDoubleStrike(true); + $this -> checkOutput("\x1b@\x1bG\x01"); + } + + public function testSetDoubleStrikeOff() { + $this -> printer -> setDoubleStrike(false); + $this -> checkOutput("\x1b@\x1bG\x00"); + } + + public function testSetDoubleStrikeNonBoolean() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setDoubleStrike(4); + } + + /* Font */ + public function testSetFontDefault() { + $this -> printer -> setFont(); + $this -> checkOutput("\x1b@\x1bM\x00"); + } + + public function testSetFontAcceptedValues() { + $this -> printer -> setFont(Escpos::FONT_A); + $this -> printer -> setFont(Escpos::FONT_B); + $this -> printer -> setFont(Escpos::FONT_C); + $this -> checkOutput("\x1b@\x1bM\x00\x1bM\x01\x1bM\x02"); + } + + public function testSetFontNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont(-1); + } + + + public function testSetFontTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont(3); + } + + public function testSetFontNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont('hello'); + } + + /* Justification */ + public function testSetJustificationDefault() { + $this -> printer -> setJustification(); + $this -> checkOutput("\x1b@\x1ba\x00"); + } + + public function testSetJustificationLeft() { + $this -> printer -> setJustification(Escpos::JUSTIFY_LEFT); + $this -> checkOutput("\x1b@\x1ba\x00"); + } + + public function testSetJustificationRight() { + $this -> printer -> setJustification(Escpos::JUSTIFY_RIGHT); + $this -> checkOutput("\x1b@\x1ba\x02"); + } + + public function testSetJustificationCenter() { + $this -> printer -> setJustification(Escpos::JUSTIFY_CENTER); + $this -> checkOutput("\x1b@\x1ba\x01"); + } + + public function testSetJustificationNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setJustification(-1); + } + + + public function testSetJustificationTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setFont(3); + } + + public function testSetJustificationNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setJustification('hello'); + } + + /* Reverse feed */ + public function testFeedReverseDefault() { + $this -> printer -> feedReverse(); + $this -> checkOutput("\x1b@\x1be\x01"); + } + + public function testFeedReverse3() { + $this -> printer -> feedReverse(3); + $this -> checkOutput("\x1b@\x1be\x03"); + } + + public function testFeedReverseNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feedReverse(-1); + } + + public function testFeedReverseTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feedReverse(256); + } + + public function testFeedReverseNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> feedReverse('hello'); + } + + /* Cut */ + public function testCutDefault() { + // TODO check what the accepted range of values should be for $line + // cut($mode = self::CUT_FULL, $lines = 3) + $this -> printer -> cut(); + $this -> checkOutput("\x1b@\x1dVA\x03"); + } + + /* Set barcode height */ + public function testSetBarcodeHeightDefault() { + $this -> printer -> setBarcodeHeight(); + $this -> checkOutput("\x1b@\x1dh\x08"); + } + + public function testBarcodeHeight10() { + $this -> printer -> setBarcodeHeight(10); + $this -> checkOutput("\x1b@\x1dh\x0a"); + } + + public function testSetBarcodeHeightNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeHeight(-1); + } + + public function testSetBarcodeHeightTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeHeight(256); + } + + public function tesSetBarcodeHeightNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeHeight('hello'); + } + + /* Barcode text position */ + public function testSetBarcodeTextPositionDefault() { + $this -> printer -> setBarcodeTextPosition(); + $this -> checkOutput("\x1b@\x1dH\x00"); + } + + public function testSetBarcodeTextPositionBelow() { + $this -> printer -> setBarcodeTextPosition(Escpos::BARCODE_TEXT_BELOW); + $this -> checkOutput("\x1b@\x1dH\x02"); + } + + public function testSetBarcodeTextPositionBoth() { + $this -> printer -> setBarcodeTextPosition(Escpos::BARCODE_TEXT_BELOW | Escpos::BARCODE_TEXT_ABOVE); + $this -> checkOutput("\x1b@\x1dH\x03"); + } + + public function testSetBarcodeTextPositionNegative() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeTextPosition(-1); + } + + public function testSetBarcodeTextPositionTooLarge() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeTextPosition(4); + } + + public function tesSetBarcodeTextPositionNonInteger() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setBarcodeTextPosition('hello'); + } + + /* Barcode - UPC-A */ + public function testBarcodeUpcaNumeric11Char() { + $this -> printer -> barcode("01234567890", Escpos::BARCODE_UPCA); + $this -> checkOutput("\x1b@\x1dkA\x0b01234567890"); + } + + public function testBarcodeUpcaNumeric12Char() { + $this -> printer -> barcode("012345678901", Escpos::BARCODE_UPCA); + $this -> checkOutput("\x1b@\x1dkA\x0c012345678901"); + } + + public function testBarcodeUpcaNumeric13Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("0123456789012", Escpos::BARCODE_UPCA); + } + + public function testBarcodeUpcaNonNumeric12Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A12345678901", Escpos::BARCODE_UPCA); + } + + /* Barcode - UPC-E */ + public function testBarcodeUpceNumeric6Char() { + $this -> printer -> barcode("123456", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x06123456"); + } + + public function testBarcodeUpceNumeric7Char() { + $this -> printer -> barcode("0123456", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x070123456"); + } + + public function testBarcodeUpceNumeric8Char() { + $this -> printer -> barcode("01234567", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x0801234567"); + } + + public function testBarcodeUpceNumeric11Char() { + $this -> printer -> barcode("01234567890", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x0b01234567890"); + } + + public function testBarcodeUpceNumeric12Char() { + $this -> printer -> barcode("012345678901", Escpos::BARCODE_UPCE); + $this -> checkOutput("\x1b@\x1dkB\x0c012345678901"); + } + + public function testBarcodeUpceNumeric9Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012345678", Escpos::BARCODE_UPCE); + } + + public function testBarcodeUpceNonNumeric12Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A12345678901", Escpos::BARCODE_UPCE); + } + + /* Barcode - JAN13 */ + public function testBarcodeJan13Numeric12Char() { + $this -> printer -> barcode("012345678901", Escpos::BARCODE_JAN13); + $this -> checkOutput("\x1b@\x1dkC\x0c012345678901"); + } + + public function testBarcodeJan13Numeric13Char() { + $this -> printer -> barcode("0123456789012", Escpos::BARCODE_JAN13); + $this -> checkOutput("\x1b@\x1dkC\x0d0123456789012"); + } + + public function testBarcodeJan13Numeric11Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("01234567890", Escpos::BARCODE_JAN13); + } + + public function testBarcodeJan13NonNumeric13Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A123456789012", Escpos::BARCODE_JAN13); + } + + /* Barcode - JAN8 */ + public function testBarcodeJan8Numeric7Char() { + $this -> printer -> barcode("0123456", Escpos::BARCODE_JAN8); + $this -> checkOutput("\x1b@\x1dkD\x070123456"); + } + + public function testBarcodeJan8Numeric8Char() { + $this -> printer -> barcode("01234567", Escpos::BARCODE_JAN8); + $this -> checkOutput("\x1b@\x1dkD\x0801234567"); + } + + public function testBarcodeJan8Numeric9Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012345678", Escpos::BARCODE_JAN8); + } + + public function testBarcodeJan8NonNumeric8Char() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A1234567", Escpos::BARCODE_JAN8); + } + + /* Barcode - Code39 */ + public function testBarcodeCode39AsDefault() { + $this -> printer -> barcode("1234"); + $this -> checkOutput("\x1b@\x1dkE\x041234"); + } + + public function testBarcodeCode39Text() { + $this -> printer -> barcode("ABC 012", Escpos::BARCODE_CODE39); + $this -> checkOutput("\x1b@\x1dkE\x07ABC 012"); + } + + public function testBarcodeCode39SpecialChars() { + $this -> printer -> barcode("$%+-./", Escpos::BARCODE_CODE39); + $this -> checkOutput("\x1b@\x1dkE\x06$%+-./"); + } + + public function testBarcodeCode39Asterisks() { + $this -> printer -> barcode("*TEXT*", Escpos::BARCODE_CODE39); + $this -> checkOutput("\x1b@\x1dkE\x06*TEXT*"); + } + + public function testBarcodeCode39AsterisksUnmatched() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("*TEXT", Escpos::BARCODE_CODE39); + } + + public function testBarcodeCode39AsteriskInText() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("12*34", Escpos::BARCODE_CODE39); + } + + public function testBarcodeCode39Lowercase() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("abcd", Escpos::BARCODE_CODE39); + } + + public function testBarcodeCode39Empty() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("**", Escpos::BARCODE_CODE39); + } + + /* Barcode - ITF */ + public function testBarcodeItfNumericEven() { + $this -> printer -> barcode("1234", Escpos::BARCODE_ITF); + $this -> checkOutput("\x1b@\x1dkF\x041234"); + } + + public function testBarcodeItfNumericOdd() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("123", Escpos::BARCODE_ITF); + } + + public function testBarcodeItfNonNumericEven() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("A234", Escpos::BARCODE_ITF); + } + + /* Barcode - Codabar */ + public function testBarcodeCodabarNumeric() { + $this -> printer -> barcode("A012345A", Escpos::BARCODE_CODABAR); + $this -> checkOutput("\x1b@\x1dkG\x08A012345A"); + } + + public function testBarcodeCodabarSpecialChars() { + $this -> printer -> barcode("A012$+-./:A", Escpos::BARCODE_CODABAR); + $this -> checkOutput("\x1b@\x1dkG\x0bA012$+-./:A"); + } + + public function testBarcodeCodabarNotWrapped() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012345", Escpos::BARCODE_CODABAR); + } + + public function testBarcodeCodabarStartStopWrongPlace() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("012A45", Escpos::BARCODE_CODABAR); + } + + /* Barcode - Code93 */ + public function testBarcodeCode93Valid() { + $this -> printer -> barcode("012abcd", Escpos::BARCODE_CODE93); + $this -> checkOutput("\x1b@\x1dkH\x07012abcd"); + } + + public function testBarcodeCode93Empty() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("", Escpos::BARCODE_CODE93); + } + + /* Barcode - Code128 */ + public function testBarcodeCode128ValidA() { + $this -> printer -> barcode("{A" . "012ABCD", Escpos::BARCODE_CODE128); + $this -> checkOutput("\x1b@\x1dkI\x09{A012ABCD"); + } + + public function testBarcodeCode128ValidB() { + $this -> printer -> barcode("{B" . "012ABCDabcd", Escpos::BARCODE_CODE128); + $this -> checkOutput("\x1b@\x1dkI\x0d{B012ABCDabcd"); + } + + public function testBarcodeCode128ValidC() { + $this -> printer -> barcode("{C" . chr ( 21 ) . chr ( 32 ) . chr ( 43 ), Escpos::BARCODE_CODE128); + $this -> checkOutput("\x1b@\x1dkI\x05{C\x15 +"); + } + + public function testBarcodeCode128NoCodeSet() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> barcode("ABCD", Escpos::BARCODE_CODE128); + } + + /* Pulse */ + function testPulseDefault() { + $this -> printer -> pulse(); + $this -> checkOutput("\x1b@\x1bp0 printer -> pulse(1); + $this -> checkOutput("\x1b@\x1bp1 printer -> pulse(0, 2, 2); + $this -> checkOutput("\x1b@\x1bp0\x01\x01"); + } + + function testPulseOddMs() { + $this -> printer -> pulse(0, 3, 3); // Should be rounded down and give same output + $this -> checkOutput("\x1b@\x1bp0\x01\x01"); + } + + function testPulseTooHigh() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> pulse(0, 512, 2); + } + + function testPulseTooLow() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> pulse(0, 0, 2); + } + + function testPulseNotANumber() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> pulse("fish"); + } + + /* Set reverse */ + public function testSetReverseColorsDefault() { + $this -> printer -> setReverseColors(); + $this -> checkOutput("\x1b@\x1dB\x01"); + } + + public function testSetReverseColorsOn() { + $this -> printer -> setReverseColors(true); + $this -> checkOutput("\x1b@\x1dB\x01"); + } + + public function testSetReverseColorsOff() { + $this -> printer -> setReverseColors(false); + $this -> checkOutput("\x1b@\x1dB\x00"); + } + + public function testSetReverseColorsNonBoolean() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setReverseColors(7); + } + + /* Bit image print */ + public function testBitImageBlack() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_black.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x01\x00\x80"); + } + + public function testBitImageWhite() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_white.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x01\x00\x00"); + } + + public function testBitImageBoth() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_white.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x02\x00\xc0\x00"); + } + + public function testBitImageTransparent() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_transparent.png"); + $this -> printer -> bitImage($img); + $this -> checkOutput("\x1b@\x1dv0\x00\x01\x00\x02\x00\xc0\x00"); + } + + /* Graphics print */ + public function testGraphicsWhite() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_white.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x00\x1d(L\x02\x0002"); + } + + public function testGraphicsBlack() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/canvas_black.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0b\x000p0\x01\x011\x01\x00\x01\x00\x80\x1d(L\x02\x0002"); + } + + public function testGraphicsBoth() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_white.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002"); + } + + public function testGraphicsTransparent() { + $this -> requireGraphicsLibrary(); + $img = new EscposImage(dirname(__FILE__)."/resources/black_transparent.png"); + $this -> printer -> graphics($img); + $this -> checkOutput("\x1b@\x1d(L\x0c\x000p0\x01\x011\x02\x00\x02\x00\xc0\x00\x1d(L\x02\x0002"); + } + + /* QR code */ + public function testQRCodeDefaults() { + // Test will fail if default values change + $this -> printer -> qrCode("1234"); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeDefaultsAreCorrect() { + // Below tests assume that defaults are as written here (output string should be same as above) + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_L, 3, Escpos::QR_MODEL_2); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeEmpty() { + $this -> printer -> qrCode(''); + $this -> checkOutput("\x1b@"); // No commands actually sent + } + + public function testQRCodeChangeEC() { + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_H); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E3\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeChangeSize() { + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_L, 7); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A2\x00\x1d(k\x03\x001C\x07\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + public function testQRCodeChangeModel() { + $this -> printer -> qrCode("1234", Escpos::QR_ECLEVEL_L, 3, Escpos::QR_MODEL_1); + $this -> checkOutput("\x1b@\x1d(k\x04\x001A1\x00\x1d(k\x03\x001C\x03\x1d(k\x03\x001E0\x1d(k\x07\x001P01234\x1d(k\x03\x001Q0"); + } + + /* Feed form - Required on page-mode only printers */ + public function testFeedForm() { + $this -> printer -> feedForm(); + $this -> checkOutput("\x1b@\x0c"); + } + + /* Get status */ + public function testGetStatus() { + $this -> markTestIncomplete("Status check test code not implemented."); + // TODO some unit testing here on statuses + // $a = $this -> printer -> getPrinterStatus(Escpos::STATUS_PRINTER); + } + + /* Set text size */ + public function testSetTextSizeNormal() { + $this -> printer -> setTextSize(1, 1); + $this -> checkOutput("\x1b@\x1d!\x00"); + } + + public function testSetTextSizeWide() { + $this -> printer -> setTextSize(4, 1); + $this -> checkOutput("\x1b@\x1d!0"); + } + + public function testSetTextSizeNarrow() { + $this -> printer -> setTextSize(1, 4); + $this -> checkOutput("\x1b@\x1d!\x03"); + } + + public function testSetTextSizeLarge() { + $this -> printer -> setTextSize(4, 4); + $this -> checkOutput("\x1b@\x1d!3"); + } + + public function testSetTextSizeInvalid() { + $this -> setExpectedException('InvalidArgumentException'); + $this -> printer -> setTextSize(0, 9); + } +} + +/* + * For testing that string-castable objects are handled + */ +class FooBar { + private $foo; + public function __construct($foo) { + $this -> foo = $foo; + } + + public function __toString() { + return $this -> foo; + } +} +?> diff --git a/htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php b/htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php new file mode 100644 index 00000000000..392775d3277 --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php @@ -0,0 +1,278 @@ + getMockConnector("LPT1", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runWrite') + -> with($this -> equalTo(''), $this -> equalTo("LPT1")); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testLptMac() { + // Cannot print to local printer on Mac with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("LPT1", WindowsPrintConnector::PLATFORM_MAC); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testLptLinux() { + // Cannot print to local printer on Linux with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("LPT1", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testComWindows() { + // Simple file write + $connector = $this -> getMockConnector("COM1", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runWrite') + -> with($this -> equalTo(''), $this -> equalTo("COM1")); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testComMac() { + // Cannot print to local printer on Mac with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("COM1", WindowsPrintConnector::PLATFORM_MAC); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testComLinux() { + // Cannot print to local printer on Linux with this connector + $this -> setExpectedException('BadMethodCallException'); + $connector = $this -> getMockConnector("COM1", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testLocalShareWindows() { + $connector = $this -> getMockConnector("Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> stringContains('\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindows() { + $connector = $this -> getMockConnector("smb://example-pc/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindowsUsername() { + $connector = $this -> getMockConnector("smb://bob@example-pc/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('net use \'\\\\example-pc\\Printer\' \'/user:bob\'')); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindowsUsernameDomain() { + $connector = $this -> getMockConnector("smb://bob@example-pc/home/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('net use \'\\\\example-pc\\Printer\' \'/user:home\\bob\'')); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterWindowsUsernamePassword() { + $connector = $this -> getMockConnector("smb://bob:secret@example-pc/Printer", WindowsPrintConnector::PLATFORM_WIN); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('net use \'\\\\example-pc\\Printer\' \'/user:bob\' \'secret\'')); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> once()) + -> method('runCopy') + -> with($this -> anything(), $this -> equalTo('\\\\example-pc\\Printer')); + $connector -> finalize(); + } + + public function testSharedPrinterMac() { + // Not implemented + $this -> setExpectedException('Exception'); + $connector = $this -> getMockConnector("smb://Guest@example-pc/Printer", WindowsPrintConnector::PLATFORM_MAC); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> expects($this -> exactly(0)) + -> method('runCommand'); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> finalize(); + } + + public function testSharedPrinterLinux() { + $connector = $this -> getMockConnector("smb://example-pc/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' -c \'print -\' -N')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + public function testSharedPrinterLinuxUsername() { + $connector = $this -> getMockConnector("smb://bob@example-pc/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' -U \'bob\' -c \'print -\' -N')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + public function testSharedPrinterLinuxUsernameDomain() { + $connector = $this -> getMockConnector("smb://bob@example-pc/home/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' -U \'home\\bob\' -c \'print -\' -N')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + public function testSharedPrinterLinuxUsernamePassword() { + $connector = $this -> getMockConnector("smb://bob:secret@example-pc/Printer", WindowsPrintConnector::PLATFORM_LINUX); + $connector -> expects($this -> once()) + -> method('runCommand') + -> with($this -> equalTo('smbclient \'//example-pc/Printer\' \'secret\' -U \'bob\' -c \'print -\'')); + $connector -> expects($this -> exactly(0)) + -> method('runCopy'); + $connector -> expects($this -> exactly(0)) + -> method('runWrite'); + $connector -> finalize(); + } + + private function getMockConnector($path, $platform) { + $stub = $this -> getMockBuilder('WindowsPrintConnector') + -> setMethods(array('runCopy', 'runCommand', 'getCurrentPlatform', 'runWrite')) + -> disableOriginalConstructor() + -> getMock(); + $stub -> method('runCommand') + -> willReturn(0); + $stub -> method('runCopy') + -> willReturn(true); + $stub -> method('runWrite') + -> willReturn(true); + $stub -> method('getCurrentPlatform') + -> willReturn($platform); + $stub -> __construct($path); + return $stub; + } + + /** + * Test for correct identification of bogus or non-supported Samba strings. + */ + public function testSambaRegex() { + $good = array("smb://foo/bar", + "smb://foo/bar baz", + "smb://bob@foo/bar", + "smb://bob:secret@foo/bar", + "smb://foo-computer/FooPrinter", + "smb://foo-computer/workgroup/FooPrinter", + "smb://foo-computer/Foo-Printer", + "smb://foo-computer/workgroup/Foo-Printer", + "smb://foo-computer/Foo Printer"); + $bad = array("", + "http://google.com", + "smb:/foo/bar", + "smb://", + "smb:///bar", + "smb://@foo/bar", + "smb://bob:@foo/bar", + "smb://:secret@foo/bar", + "smb://foo/bar/baz/quux", + "smb://foo-computer//FooPrinter"); + foreach($good as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_SMB, $item) == 1, "Windows samba regex should pass '$item'."); + } + foreach($bad as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_SMB, $item) != 1, "Windows samba regex should fail '$item'."); + } + } + + public function testPrinterNameRegex() { + $good = array("a", + "ab", + "a b", + "a-b", + "Abcd Efg-", + "-a" + ); + $bad = array("", + " ", + "a ", + " a", + " a ", + "a/B", + "A:b" + ); + foreach($good as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_PRINTERNAME, $item) == 1, "Windows printer name regex should pass '$item'."); + } + foreach($bad as $item) { + $this -> assertTrue(preg_match(WindowsPrintConnector::REGEX_PRINTERNAME, $item) != 1, "Windows printer name regex should fail '$item'."); + } + } +} diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif new file mode 100644 index 00000000000..6c54bad9eea Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.png b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.png new file mode 100644 index 00000000000..b43bbb85cc0 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.png differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp new file mode 100644 index 00000000000..0acbf66506f Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif new file mode 100644 index 00000000000..0a044a671f5 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg new file mode 100644 index 00000000000..6539cece59e Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png new file mode 100644 index 00000000000..33ba331fa1e Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp new file mode 100644 index 00000000000..2ee723c59d8 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif new file mode 100644 index 00000000000..49b19dbc179 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg new file mode 100644 index 00000000000..d059f431b26 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.png b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.png new file mode 100644 index 00000000000..52e6ba9ffd3 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.png differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp new file mode 100644 index 00000000000..e47e0ef6175 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif new file mode 100644 index 00000000000..7881ce629ac Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.jpg b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.jpg new file mode 100644 index 00000000000..516a965c73e Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.jpg differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png new file mode 100644 index 00000000000..4231a4bb540 Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png differ diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/demo.php b/htdocs/includes/mike42/escpos-php/test/unit/resources/demo.php new file mode 100644 index 00000000000..67ea53c1bfd --- /dev/null +++ b/htdocs/includes/mike42/escpos-php/test/unit/resources/demo.php @@ -0,0 +1,19 @@ + readImage("doc.pdf[5]"); + $im -> destroy(); +} catch(ImagickException $e) { + echo "Error: " . $e -> getMessage() . "\n"; +} + +$im = new Imagick(); +try { + ob_start(); + @$im -> readImage("doc.pdf[5]"); + ob_end_clean(); + $im -> destroy(); +} catch(ImagickException $e) { + echo "Error: " . $e -> getMessage() . "\n"; +} + diff --git a/htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf b/htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf new file mode 100644 index 00000000000..22247b918db Binary files /dev/null and b/htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf differ