Selaa lähdekoodia

[Qual] Prevent GIT submodules in composer deps

Dependencies installed with composer should not be added as submodules.
Restored missing Escpos-php library.
Raphaël Doursenaud 9 vuotta sitten
vanhempi
commit
7c44198022
98 muutettua tiedostoa jossa 9823 lisäystä ja 2 poistoa
  1. 3 1
      .gitignore
  2. 0 1
      composer.lock
  3. 851 0
      htdocs/includes/mike42/escpos-php/Escpos.php
  4. 27 0
      htdocs/includes/mike42/escpos-php/LICENSE.md
  5. 352 0
      htdocs/includes/mike42/escpos-php/README.md
  6. 36 0
      htdocs/includes/mike42/escpos-php/composer.json
  7. 975 0
      htdocs/includes/mike42/escpos-php/composer.lock
  8. 9 0
      htdocs/includes/mike42/escpos-php/doc/Makefile
  9. 2352 0
      htdocs/includes/mike42/escpos-php/doc/escpos.conf
  10. 26 0
      htdocs/includes/mike42/escpos-php/example/README.md
  11. 181 0
      htdocs/includes/mike42/escpos-php/example/barcode.php
  12. 32 0
      htdocs/includes/mike42/escpos-php/example/bit-image.php
  13. 59 0
      htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php
  14. 58 0
      htdocs/includes/mike42/escpos-php/example/character-encodings.php
  15. 71 0
      htdocs/includes/mike42/escpos-php/example/character-tables.php
  16. 167 0
      htdocs/includes/mike42/escpos-php/example/demo.php
  17. 32 0
      htdocs/includes/mike42/escpos-php/example/graphics.php
  18. 8 0
      htdocs/includes/mike42/escpos-php/example/interface/README.md
  19. 22 0
      htdocs/includes/mike42/escpos-php/example/interface/ethernet.php
  20. 33 0
      htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php
  21. 51 0
      htdocs/includes/mike42/escpos-php/example/interface/smb.php
  22. 30 0
      htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php
  23. 32 0
      htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php
  24. 53 0
      htdocs/includes/mike42/escpos-php/example/print-from-html.php
  25. 71 0
      htdocs/includes/mike42/escpos-php/example/print-from-pdf.php
  26. 81 0
      htdocs/includes/mike42/escpos-php/example/qr-code.php
  27. 96 0
      htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php
  28. 35 0
      htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc
  29. BIN
      htdocs/includes/mike42/escpos-php/example/resources/document.odt
  30. BIN
      htdocs/includes/mike42/escpos-php/example/resources/document.pdf
  31. BIN
      htdocs/includes/mike42/escpos-php/example/resources/document.z
  32. BIN
      htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png
  33. BIN
      htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png
  34. BIN
      htdocs/includes/mike42/escpos-php/example/resources/tux.png
  35. 21 0
      htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php
  36. 36 0
      htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php
  37. 16 0
      htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php
  38. 69 0
      htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php
  39. 31 0
      htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php
  40. 16 0
      htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php
  41. 58 0
      htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php
  42. 47 0
      htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php
  43. 7 0
      htdocs/includes/mike42/escpos-php/example/specific/README.md
  44. 62 0
      htdocs/includes/mike42/escpos-php/example/text-size.php
  45. 61 0
      htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php
  46. 181 0
      htdocs/includes/mike42/escpos-php/src/CodePage.php
  47. 103 0
      htdocs/includes/mike42/escpos-php/src/DefaultCapabilityProfile.php
  48. 78 0
      htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php
  49. 4 0
      htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php
  50. 405 0
      htdocs/includes/mike42/escpos-php/src/EscposImage.php
  51. 304 0
      htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php
  52. 80 0
      htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php
  53. 99 0
      htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php
  54. 39 0
      htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php
  55. 90 0
      htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php
  56. 75 0
      htdocs/includes/mike42/escpos-php/src/PrintBuffer.php
  57. 56 0
      htdocs/includes/mike42/escpos-php/src/PrintConnector.php
  58. 17 0
      htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php
  59. 82 0
      htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php
  60. 356 0
      htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php
  61. BIN
      htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz
  62. BIN
      htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz
  63. BIN
      htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz
  64. 27 0
      htdocs/includes/mike42/escpos-php/test/bootstrap.php
  65. 126 0
      htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php
  66. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin
  67. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin
  68. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin
  69. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin
  70. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin
  71. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/graphics.bin
  72. 2 0
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin
  73. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z
  74. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin
  75. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/receipt-with-logo.bin
  76. BIN
      htdocs/includes/mike42/escpos-php/test/integration/resources/output/text-size.bin
  77. 16 0
      htdocs/includes/mike42/escpos-php/test/phpunit.xml
  78. 69 0
      htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php
  79. 235 0
      htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php
  80. 150 0
      htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php
  81. 765 0
      htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php
  82. 278 0
      htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php
  83. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif
  84. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.png
  85. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp
  86. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif
  87. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg
  88. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png
  89. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp
  90. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif
  91. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg
  92. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.png
  93. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp
  94. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif
  95. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.jpg
  96. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png
  97. 19 0
      htdocs/includes/mike42/escpos-php/test/unit/resources/demo.php
  98. BIN
      htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf

+ 3 - 1
.gitignore

@@ -17,4 +17,6 @@ doxygen_warnings.log
 *.iml
 Thumbs.db
 # Vagrant generated files
-.vagrant
+.vagrant
+# Composer installed repositories
+/htdocs/includes/**/.git

+ 0 - 1
composer.lock

@@ -5,7 +5,6 @@
         "This file is @generated automatically"
     ],
     "hash": "d88b5c84a9ea1af354d867ba2e2ab251",
-    "content-hash": "85e60ec7f8ab593387c7bd10d8db860d",
     "packages": [
         {
             "name": "ccampbell/chromephp",

+ 851 - 0
htdocs/includes/mike42/escpos-php/Escpos.php

@@ -0,0 +1,851 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ * 
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ * 
+ * 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.");
+		}
+	}
+}

+ 27 - 0
htdocs/includes/mike42/escpos-php/LICENSE.md

@@ -0,0 +1,27 @@
+escpos-php, a Thermal receipt printer library, for use with
+ESC/POS compatible printers.
+
+Copyright (c) 2014-15 Michael Billington <michael.billington@gmail.com>,
+incorporating modifications by:
+- Roni Saha <roni.cse@gmail.com>
+- Gergely Radics <gerifield@ustream.tv>
+- Warren Doyle <w.doyle@fuelled.co>
+
+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.
+

+ 352 - 0
htdocs/includes/mike42/escpos-php/README.md

@@ -0,0 +1,352 @@
+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.
+
+Basic usage
+-----------
+A "hello world" receipt can be generated easily (Call this `hello-world.php`):
+```php
+<?php
+require_once(dirname(__FILE__) . "/Escpos.php");
+$printer = new Escpos();
+$printer -> 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
+<?php
+require_once(dirname(__FILE__) . "/Escpos.php");
+$connector = new NetworkPrintConnector("10.x.x.x", 9100);
+$printer = new Escpos($connector);
+$printer -> text("Hello World!\n");
+$printer -> cut();
+$printer -> close();
+```
+
+Or to a local printer:
+```php
+<?php
+require_once(dirname(__FILE__) . "/Escpos.php");
+$connector = new FilePrintConnector("/dev/ttyS0");
+$printer = new Escpos($connector);
+$printer -> 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:
+
+<table>
+<tr>
+<th>&nbsp;</th>
+<th>Linux</th>
+<th>Mac</th>
+<th>Windows</th>
+</tr>
+<tr>
+<th>Ethernet</th>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/ethernet.php">Yes</a></td>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/ethernet.php">Yes</a></td>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/ethernet.php">Yes</a></td>
+</tr>
+<tr>
+<th>USB</th>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/linux-usb.php">Yes</a></td>
+<td>Not tested</td>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/windows-usb.php">Yes</a></td>
+</tr>
+<tr>
+<th>USB-serial</th>
+<td>Yes</td>
+<td>Yes</td>
+<td>Yes</td>
+</tr>
+<tr>
+<th>Serial</th>
+<td>Yes</td>
+<td>Yes</td>
+<td>Yes</td>
+</tr>
+<tr>
+<th>Parallel</th>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/windows-lpt.php">Yes</a></td>
+<td>Not tested</td>
+<td>Yes</td>
+</tr>
+<tr>
+<th>SMB shared</th>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/smb.php">Yes</a></td>
+<td>No</td>
+<td><a href="https://github.com/mike42/escpos-php/tree/master/example/interface/smb.php">Yes</a></td>
+</tr>
+</table>
+
+### Printers
+Many thermal receipt printers support ESC/POS to some degree. This driver has been known to work with:
+
+- EPOS TEP 220M
+- Epson TM-T88III
+- Epson TM-T88IV
+- Epson TM-T70
+- Epson TM-T82II
+- Epson TM-T20
+- Epson TM-T70II
+- Epson TM-U220
+- Epson FX-890 (requires `feedForm()` to release paper).
+- Okipos 80 Plus III
+- P-822D
+- SEYPOS PRP-300 (Also marketed as TYSSO PRP-300)
+- Star TSP-650
+- Star TUP-592
+- Xprinter XP-Q800
+- Zijang NT-58H
+- Zijang ZJ-5870
+- Zijang ZJ-5890T (Marketed as POS 5890T)
+
+If you use any other printer with this code, please let me know so I can add it to the list.
+
+Available methods
+-----------------
+
+### __construct(PrintConnector $connector, AbstractCapabilityProfile $profile)
+Construct new print object.
+
+Parameters:
+- `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.
+
+### barcode($content, $type)
+Print a barcode.
+
+Parameters:
+
+- `string $content`: The information to encode.
+- `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):
+
+- `BARCODE_UPCA`
+- `BARCODE_UPCE`
+- `BARCODE_JAN13`
+- `BARCODE_JAN8`
+- `BARCODE_CODE39`
+- `BARCODE_ITF`
+- `BARCODE_CODABAR`
+
+Note that some barcode standards can only encode numbers, so attempting to print non-numeric codes with them may result in strange behaviour.
+
+### bitImage(EscposImage $image, $size)
+See [graphics()](#graphicsescposimage-image-size) below.
+
+### cut($mode, $lines)
+Cut the paper.
+
+Parameters:
+
+- `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)
+Print and feed line / Print and feed n lines.
+
+Parameters:
+
+- `int $lines`: Number of lines to feed
+
+### feedForm()
+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.
+
+### feedReverse($lines)
+Print and reverse feed n lines.
+
+Parameters:
+
+- `int $lines`: number of lines to feed. If not specified, 1 line will be fed.
+
+### graphics(EscposImage $image, $size)
+Print an image to the printer.
+
+Parameters:
+
+- `EscposImage $img`: The image to print.
+- `int $size`: Output size modifier for the image.
+
+Size modifiers are:
+
+- `IMG_DEFAULT` (leave image at original size)
+- `IMG_DOUBLE_WIDTH`
+- `IMG_DOUBLE_HEIGHT`
+
+A minimal example:
+
+```php
+<?php
+$img = new EscposImage("logo.png");
+$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.
+
+### initialize()
+Initialize printer. This resets formatting back to the defaults.
+
+### pulse($pin, $on_ms, $off_ms)
+Generate a pulse, for opening a cash drawer if one is connected. The default settings (0, 120, 240) should open an Epson drawer.
+
+Parameters:
+
+- `int $pin`: 0 or 1, for pin 2 or pin 5 kick-out connector respectively.
+- `int $on_ms`: pulse ON time, in milliseconds.
+- `int $off_ms`: pulse OFF time, in milliseconds.
+
+### qrCode($content, $ec, $size, $model)
+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 `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 `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 `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:
+
+- `MODE_FONT_A`
+- `MODE_FONT_B`
+- `MODE_EMPHASIZED`
+- `MODE_DOUBLE_HEIGHT`
+- `MODE_DOUBLE_WIDTH`
+- `MODE_UNDERLINE`
+
+### setBarcodeHeight($height)
+Set barcode height.
+
+Parameters:
+
+- `int $height`: Height in dots. If not specified, 8 will be used.
+
+### setDoubleStrike($on)
+Turn double-strike mode on/off.
+
+Parameters:
+
+- `boolean $on`: true for double strike, false for no double strike.
+
+### setEmphasis($on)
+Turn emphasized mode on/off.
+
+Parameters:
+
+- `boolean $on`: true for emphasis, false for no emphasis.
+
+### setFont($font)
+Select font. Most printers have two fonts (Fonts A and B), and some have a third (Font C).
+
+Parameters:
+
+- `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 `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.
+
+Parameters:
+
+- `boolean $on`: True to enable, false to disable.
+
+### setTextSize($widthMultiplier, $heightMultiplier)
+Set the size of text, as a multiple of the normal size.
+
+Parameters:
+
+- `int $widthMultiplier`: Multiple of the regular height to use (range 1 - 8).
+- `int $heightMultiplier`: Multiple of the regular height to use (range 1 - 8).
+
+### setUnderline($underline)
+Set underline for printed text.
+
+Parameters:
+
+- `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.
+
+Parameters:
+
+- `string $str`: The string to print.
+
+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)
+
+Other versions
+--------------
+Some forks of this project have been developed by others for specific use cases. Improvements from the following projects have been incorporated into escpos-php:
+
+- [wdoyle/EpsonESCPOS-PHP](https://github.com/wdoyle/EpsonESCPOS-PHP)
+- [ronisaha/php-esc-pos](https://github.com/ronisaha/php-esc-pos)
+
+Vendor documentation
+--------------------
+Epson notes that not all of its printers support all ESC/POS features, and includes a table in their documentation:
+
+* [FAQ about ESC/POS from Epson](http://content.epson.de/fileadmin/content/files/RSD/downloads/escpos.pdf)
+
+Note that many printers produced by other vendors use the same standard, and are compatible by varying degrees.
+

+ 36 - 0
htdocs/includes/mike42/escpos-php/composer.json

@@ -0,0 +1,36 @@
+{
+	"name": "mike42/escpos-php",
+	"type": "library",
+	"description": "Thermal receipt printer library, for use with ESC/POS compatible printers",
+	"homepage": "https://github.com/mike42/escpos-php",
+	"keywords": ["receipt", "print", "escpos", "ESC-POS", "driver"],
+	"license": "MIT",
+	"authors": [
+		{
+			"name": "Michael Billington",
+			"email": "michael.billington@gmail.com"
+		},
+		{
+			"name": "Roni Saha",
+			"email": "roni.cse@gmail.com"
+		},
+		{
+			"name": "Gergely Radics",
+			"email": "gerifield@ustream.tv"
+		},
+		{
+			"name": "Warren Doyle",
+			"email": "w.doyle@fuelled.co"
+		},
+		{
+			"name": "vharo",
+			"email": "vharo@geepok.com"
+		}
+	],
+	"require": {
+		"php": ">=5.3.0"
+	},
+	"require-dev": {
+	    "phpunit/phpunit": "4.5.*"
+	}
+}

+ 975 - 0
htdocs/includes/mike42/escpos-php/composer.lock

@@ -0,0 +1,975 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "hash": "fd25f2b816df83dabf03fe7259ad4018",
+    "packages": [],
+    "packages-dev": [
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119",
+                "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3,<8.0-DEV"
+            },
+            "require-dev": {
+                "athletic/athletic": "~0.1.8",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpunit/phpunit": "~4.0",
+                "squizlabs/php_codesniffer": "2.0.*@ALPHA"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\Instantiator\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "http://ocramius.github.com/"
+                }
+            ],
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://github.com/doctrine/instantiator",
+            "keywords": [
+                "constructor",
+                "instantiate"
+            ],
+            "time": "2014-10-13 12:58:55"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
+                "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0"
+            },
+            "suggest": {
+                "dflydev/markdown": "~1.0",
+                "erusev/parsedown": "~1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "phpDocumentor": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "mike.vanriel@naenius.com"
+                }
+            ],
+            "time": "2015-02-03 12:10:50"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5",
+                "reference": "8724cd239f8ef4c046f55a3b18b4d91cc7f3e4c5",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "phpdocumentor/reflection-docblock": "~2.0",
+                "sebastian/comparator": "~1.1"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Prophecy\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "time": "2015-03-27 19:31:25"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "2.0.15",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "34cc484af1ca149188d0d9e91412191e398e0b67"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/34cc484af1ca149188d0d9e91412191e398e0b67",
+                "reference": "34cc484af1ca149188d0d9e91412191e398e0b67",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "phpunit/php-file-iterator": "~1.3",
+                "phpunit/php-text-template": "~1.2",
+                "phpunit/php-token-stream": "~1.3",
+                "sebastian/environment": "~1.0",
+                "sebastian/version": "~1.0"
+            },
+            "require-dev": {
+                "ext-xdebug": ">=2.1.4",
+                "phpunit/phpunit": "~4"
+            },
+            "suggest": {
+                "ext-dom": "*",
+                "ext-xdebug": ">=2.2.1",
+                "ext-xmlwriter": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "time": "2015-01-24 10:06:35"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "1.3.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
+                "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "File/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "time": "2013-10-10 15:34:57"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+                "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "Text/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "time": "2014-01-30 17:20:04"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+                "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "PHP/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                ""
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "time": "2013-08-02 07:42:54"
+        },
+        {
+            "name": "phpunit/php-token-stream",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+                "reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/db32c18eba00b121c145575fcbcd4d4d24e6db74",
+                "reference": "db32c18eba00b121c145575fcbcd4d4d24e6db74",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Wrapper around PHP's tokenizer extension.",
+            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+            "keywords": [
+                "tokenizer"
+            ],
+            "time": "2015-01-17 09:51:32"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "4.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "d6429b0995b24a2d9dfe5587ee3a7071c1161af4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d6429b0995b24a2d9dfe5587ee3a7071c1161af4",
+                "reference": "d6429b0995b24a2d9dfe5587ee3a7071c1161af4",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-pcre": "*",
+                "ext-reflection": "*",
+                "ext-spl": "*",
+                "php": ">=5.3.3",
+                "phpspec/prophecy": "~1.3,>=1.3.1",
+                "phpunit/php-code-coverage": "~2.0,>=2.0.11",
+                "phpunit/php-file-iterator": "~1.3.2",
+                "phpunit/php-text-template": "~1.2",
+                "phpunit/php-timer": "~1.0.2",
+                "phpunit/phpunit-mock-objects": "~2.3",
+                "sebastian/comparator": "~1.1",
+                "sebastian/diff": "~1.1",
+                "sebastian/environment": "~1.2",
+                "sebastian/exporter": "~1.2",
+                "sebastian/global-state": "~1.0",
+                "sebastian/version": "~1.0",
+                "symfony/yaml": "~2.0"
+            },
+            "suggest": {
+                "phpunit/php-invoker": "~1.1"
+            },
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.5.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "time": "2015-03-29 09:24:05"
+        },
+        {
+            "name": "phpunit/phpunit-mock-objects",
+            "version": "2.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
+                "reference": "74ffb87f527f24616f72460e54b595f508dccb5c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/74ffb87f527f24616f72460e54b595f508dccb5c",
+                "reference": "74ffb87f527f24616f72460e54b595f508dccb5c",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "~1.0,>=1.0.2",
+                "php": ">=5.3.3",
+                "phpunit/php-text-template": "~1.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "suggest": {
+                "ext-soap": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Mock Object library for PHPUnit",
+            "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+            "keywords": [
+                "mock",
+                "xunit"
+            ],
+            "time": "2015-04-02 05:36:41"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "1dd8869519a225f7f2b9eb663e225298fade819e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1dd8869519a225f7f2b9eb663e225298fade819e",
+                "reference": "1dd8869519a225f7f2b9eb663e225298fade819e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "sebastian/diff": "~1.2",
+                "sebastian/exporter": "~1.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "http://www.github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "time": "2015-01-29 16:28:08"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3",
+                "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "http://www.github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff"
+            ],
+            "time": "2015-02-22 15:13:53"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "1.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5a8c7d31914337b69923db26c4221b81ff5a196e",
+                "reference": "5a8c7d31914337b69923db26c4221b81ff5a196e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "time": "2015-01-01 10:01:08"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "84839970d05254c73cde183a721c7af13aede943"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/84839970d05254c73cde183a721c7af13aede943",
+                "reference": "84839970d05254c73cde183a721c7af13aede943",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "sebastian/recursion-context": "~1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "time": "2015-01-27 07:23:06"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
+                "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "suggest": {
+                "ext-uopz": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "time": "2014-10-06 09:23:50"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "3989662bbb30a29d20d9faa04a846af79b276252"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/3989662bbb30a29d20d9faa04a846af79b276252",
+                "reference": "3989662bbb30a29d20d9faa04a846af79b276252",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "time": "2015-01-24 09:48:32"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ab931d46cd0d3204a91e1b9a40c4bc13032b58e4",
+                "reference": "ab931d46cd0d3204a91e1b9a40c4bc13032b58e4",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "time": "2015-02-24 06:35:25"
+        },
+        {
+            "name": "symfony/yaml",
+            "version": "v2.6.6",
+            "target-dir": "Symfony/Component/Yaml",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/Yaml.git",
+                "reference": "174f009ed36379a801109955fc5a71a49fe62dd4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/Yaml/zipball/174f009ed36379a801109955fc5a71a49fe62dd4",
+                "reference": "174f009ed36379a801109955fc5a71a49fe62dd4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "~2.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\Yaml\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Symfony Yaml Component",
+            "homepage": "http://symfony.com",
+            "time": "2015-03-30 15:54:10"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": ">=5.3.0"
+    },
+    "platform-dev": []
+}

+ 9 - 0
htdocs/includes/mike42/escpos-php/doc/Makefile

@@ -0,0 +1,9 @@
+html: ../Escpos.php escpos.conf
+	doxygen escpos.conf
+
+latex: html
+	# Do nothing
+
+clean:
+	rm --preserve-root -Rf html latex doxygen_sqlite3.db
+

+ 2352 - 0
htdocs/includes/mike42/escpos-php/doc/escpos.conf

@@ -0,0 +1,2352 @@
+# Doxyfile 1.8.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "escpos-php"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           = ../example/images/escpos-php-small.png
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = ../Escpos.php ../src/ ../README.md
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = ../README.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# compiled with the --with-libclang option.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra stylesheet files is of importance (e.g. the last
+# stylesheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
+# for the replacement values of the other commands the user is refered to
+# HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+PLANTUML_JAR_PATH      =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES

+ 26 - 0
htdocs/includes/mike42/escpos-php/example/README.md

@@ -0,0 +1,26 @@
+Examples
+--------
+
+This folder contains a collectoion of feature examples.
+Generally, demo.php is the fastest way to find out which features your
+printer supports.
+
+## Subfolders
+- `interface/` - contains examples for output interfaces: eg, parallel, serial, USB, network, file-based.
+- `specific/` - examples made in response to issues & questions. These cover specific languages, printers and interfaces, so hit narrower use cases.
+
+## List of examples
+
+Each example prints to standard output, so either edit the print connector, or redirect the output to your printer to see it in action. They are designed for developers: open them in a text editor before you run them!
+
+- `bit-image.php` - Prints a images to the printer using the older "bit image" commands.
+- `demo.php` - Demonstrates output using a large subset of availale features.
+- `qr-code.php` - Prints QR codes, if your printer supports it.
+- `character-encodings.php` - Shows available character encodings. Change from the DefaultCapabilityProfile to get more useful output for your specific printer.
+- `graphics.php` - The same output as `bit-image.php`, printed with the newer graphics commands (not supported on many non-Epson printers)
+- `receipt-with-logo.php` - A simple receipt containing a logo and basic formating.
+- `character-encodings-with-images.php` - The same as `character-encodings.php`, but also prints each string using an `ImagePrintBuffer`, showing compatibility gaps.
+- `print-from-html.php` - Runs `wkhtmltoimage` to convert HTML to an image, and then prints the image. (This is very slow)
+- `character-tables.php` - Prints a compact character code table for each available character set. Used to debug incorrect output from `character-encodings.php`.
+- `print-from-pdf.php` - Loads a PDF and prints each page in a few different ways (very slow as well)
+

+ 181 - 0
htdocs/includes/mike42/escpos-php/example/barcode.php

@@ -0,0 +1,181 @@
+<?php
+require_once (dirname ( __FILE__ ) . "/../Escpos.php");
+$printer = new Escpos ();
+$printer->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 ();
+

+ 32 - 0
htdocs/includes/mike42/escpos-php/example/bit-image.php

@@ -0,0 +1,32 @@
+<?php
+/* Example print-outs using the older bit image print command */
+require_once(dirname(__FILE__) . "/../Escpos.php");
+$printer = new Escpos();
+
+try {
+	$tux = new EscposImage("resources/tux.png");
+
+	$printer -> 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();
+?>

+ 59 - 0
htdocs/includes/mike42/escpos-php/example/character-encodings-with-images.php

@@ -0,0 +1,59 @@
+<?php
+/* Change to the correct path if you copy this example! */
+require_once(dirname(__FILE__) . "/../Escpos.php");
+
+/**
+ * This example builds on character-encodings.php, also providing an image-based rendering.
+ * This is quite slow, since a) the buffers are changed dozens of
+ * times in the example, and b) It involves sending very wide images, which printers don't like!
+ * 
+ * There are currently no test cases around the image printing, since it is an experimental feature.
+ *
+ * It does, however, illustrate the way that more encodings are available when image output is used.
+ */
+include(dirname(__FILE__) . '/resources/character-encoding-test-strings.inc');
+
+try {
+	// Enter connector and capability profile
+	$connector = new FilePrintConnector("php://stdout");
+	$profile = DefaultCapabilityProfile::getInstance();
+	$buffers = array(new EscposPrintBuffer(), new ImagePrintBuffer());
+
+	/* Print a series of receipts containing i18n example strings */
+	$printer = new Escpos($connector, $profile);
+	$printer -> 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";
+}
+

+ 58 - 0
htdocs/includes/mike42/escpos-php/example/character-encodings.php

@@ -0,0 +1,58 @@
+<?php
+/* Change to the correct path if you copy this example! */
+require_once(dirname(__FILE__) . "/../Escpos.php");
+
+/**
+ * This demonstrates available character encodings. Escpos-php accepts UTF-8,
+ * and converts this to lower-level data to the printer. This is a complex area, so be
+ * prepared to code a model-specific hack ('CapabilityProfile') for your printer.
+ * 
+ * If you run into trouble, please file an issue on GitHub, including at a minimum:
+ * - A UTF-8 test string in the language you're working in, and
+ * - A test print or link to a technical document which lists the available
+ *      code pages ('character code tables') for your printer.
+ * 
+ * The DefaultCapabilityProfile works for Espson-branded printers. For other models, you
+ * must use/create a PrinterCapabilityProfile for your printer containing a list of code
+ * page numbers for your printer- otherwise you will get mojibake.
+ * 
+ * If you do not intend to use non-English characters, then use SimpleCapabilityProfile,
+ * which has only the default encoding, effectively disabling code page changes.
+ */
+
+include(dirname(__FILE__) . '/resources/character-encoding-test-strings.inc');
+try {
+	// Enter connector and capability profile (to match your printer)
+	$connector = new FilePrintConnector("php://stdout");
+	$profile = DefaultCapabilityProfile::getInstance();
+	
+	/* Print a series of receipts containing i18n example strings */
+	$printer = new Escpos($connector, $profile);
+	$printer -> 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";
+}
+

+ 71 - 0
htdocs/includes/mike42/escpos-php/example/character-tables.php

@@ -0,0 +1,71 @@
+<?php
+/**
+ * This demo prints out supported code pages on your printer. This is intended
+ * for debugging character-encoding issues: If your printer does not work with
+ * a built-in capability profile, you need to check its documentation for
+ * supported code pages.
+ * 
+ * These are then loaded into a capability profile, which maps code page
+ * numbers to iconv encoding names on your particular printer. This script
+ * will print all configured code pages, so that you can check that the chosen
+ * iconv encoding name matches the actual code page contents.
+ * 
+ * If this is correctly set up for your printer, then the driver will try its
+ * best to map UTF-8 text into these code pages for you, allowing you to accept
+ * arbitrary input from a database, without worrying about encoding it for the printer.
+ */
+require_once(dirname(__FILE__) . "/../Escpos.php");
+
+// Enter connector and capability profile (to match your printer)
+$connector = new FilePrintConnector("php://stdout");
+$profile = DefaultCapabilityProfile::getInstance();
+$verbose = false; // Skip tables which iconv wont convert to (ie, only print characters available with UTF-8 input)
+
+/* Print a series of receipts containing i18n example strings - Code below shouldn't need changing */
+$printer = new Escpos($connector, $profile);
+$codePages = $profile -> 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");
+ 	}
+}

+ 167 - 0
htdocs/includes/mike42/escpos-php/example/demo.php

@@ -0,0 +1,167 @@
+<?php
+/**
+ * This is a demo script for the functions of the PHP ESC/POS print driver,
+ * Escpos.php.
+ *
+ * Most printers implement only a subset of the functionality of the driver, so
+ * will not render this output correctly in all cases.
+ *
+ * @author Michael Billington <michael.billington@gmail.com>
+ */
+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();
+?>

+ 32 - 0
htdocs/includes/mike42/escpos-php/example/graphics.php

@@ -0,0 +1,32 @@
+<?php
+/* Print-outs using the newer graphics print command */
+
+require_once(dirname(__FILE__) . "/../Escpos.php");
+$printer = new Escpos();
+
+try {
+	$tux = new EscposImage("resources/tux.png");
+	
+	$printer -> 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();
+?>

+ 8 - 0
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

+ 22 - 0
htdocs/includes/mike42/escpos-php/example/interface/ethernet.php

@@ -0,0 +1,22 @@
+<?php
+/* Change to the correct path if you copy this example! */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+
+/* Most printers are open on port 9100, so you just need to know the IP 
+ * address of your receipt printer, and then fsockopen() it on that port.
+ */
+try {
+	$connector = null;
+	//$connector = new NetworkPrintConnector("10.x.x.x", 9100);
+	
+	/* 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";
+}
+

+ 33 - 0
htdocs/includes/mike42/escpos-php/example/interface/linux-usb.php

@@ -0,0 +1,33 @@
+<?php
+/* Change to the correct path if you copy this example! */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+
+/**
+ * On Linux, use the usblp module to make your printer available as a device
+ * file. This is generally the default behaviour if you don't install any
+ * vendor drivers.
+ *
+ * Once this is done, use a FilePrintConnector to open the device.
+ *
+ * Troubleshooting: On Debian, you must be in the lp group to access this file.
+ * dmesg to see what happens when you plug in your printer to make sure no
+ * other drivers are unloading the module.
+ */
+try {
+	// Enter the device file for your USB printer here
+	$connector = null;
+	//$connector = new FilePrintConnector("/dev/usb/lp0");
+	//$connector = new FilePrintConnector("/dev/usb/lp1");
+	//$connector = new FilePrintConnector("/dev/usb/lp2");
+
+	/* 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";
+}
+

+ 51 - 0
htdocs/includes/mike42/escpos-php/example/interface/smb.php

@@ -0,0 +1,51 @@
+<?php
+/* Change to the correct path if you copy this example! */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+
+/**
+ * Install the printer using USB printing support, and the "Generic / Text Only" driver,
+ * then share it.
+ * 
+ * Use a WindowsPrintConnector with the share name to print. This works on either
+ * Windows or Linux.
+ * 
+ * Troubleshooting: Fire up a command prompt/terminal, and ensure that (if your printer is
+ * shared as "Receipt Printer"), the following commands work.
+ * 
+ * Windows: (use an appropriate "net use" command if you need authentication)
+ * 	echo "Hello World" > 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";
+}

+ 30 - 0
htdocs/includes/mike42/escpos-php/example/interface/windows-lpt.php

@@ -0,0 +1,30 @@
+<?php
+/* Change to the correct path if you copy this example! */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+
+/**
+ * Assuming your printer is available at LPT1,
+ * simpy instantiate a WindowsPrintConnector to it.
+ * 
+ * When troubleshooting, make sure you can send it 
+ * data from the command-line first:
+ * 	echo "Hello World" > 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";
+}

+ 32 - 0
htdocs/includes/mike42/escpos-php/example/interface/windows-usb.php

@@ -0,0 +1,32 @@
+<?php
+/* Change to the correct path if you copy this example! */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+
+/**
+ * Install the printer using USB printing support, and the "Generic / Text Only" driver,
+ * then share it (you can use a firewall so that it can only be seen locally).
+ * 
+ * Use a WindowsPrintConnector with the share name to print.
+ * 
+ * Troubleshooting: Fire up a command prompt, and ensure that (if your printer is shared as
+ * "Receipt Printer), the following commands work:
+ * 
+ * 	echo "Hello World" > 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";
+}

+ 53 - 0
htdocs/includes/mike42/escpos-php/example/print-from-html.php

@@ -0,0 +1,53 @@
+<?php
+require_once(dirname(__FILE__)."/../Escpos.php");
+/*
+ * Due to its complxity, escpos-php does not support HTML input. To print HTML,
+ * either convert it to calls on the Escpos() object, or rasterise the page with
+ * wkhtmltopdf, an external package which is designed to handle HTML efficiently.
+ *
+ * This example is provided to get you started.
+ *
+ * Note: Depending on the height of your pages, it is suggested that you chop it
+ * into smaller sections, as printers simply don't have the buffer capacity for
+ * very large images.
+ *
+ * As always, you can trade off quality for capacity by halving the width
+ * (550 -> 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();
+}
+

+ 71 - 0
htdocs/includes/mike42/escpos-php/example/print-from-pdf.php

@@ -0,0 +1,71 @@
+<?php
+require_once(dirname(__FILE__) . '/../Escpos.php');
+/*
+ * This is three examples in one:
+ *  1: Print an entire PDF, normal quality.
+ *  2: Print at a lower quality for speed increase (CPU & transfer)
+ *  3: Cache rendered documents for a speed increase (removes CPU image processing completely on subsequent prints)
+ */
+
+/* 1: Print an entire PDF, start-to-finish (shorter form of the example) */
+$pdf = 'resources/document.pdf';
+try {
+	$pages = EscposImage::loadPdf($pdf);
+	$printer = new Escpos();
+	foreach($pages as $page) {
+		$printer -> 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)));
+}

+ 81 - 0
htdocs/includes/mike42/escpos-php/example/qr-code.php

@@ -0,0 +1,81 @@
+<?php
+/* Demonstration of available options on the qrCode() command */
+require_once(dirname(__FILE__) . "/../Escpos.php");
+$printer = new Escpos();
+
+// Most simple example
+title($printer, "QR code demo\n");
+$testStr = "Testing 123";
+$printer -> 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();
+}

+ 96 - 0
htdocs/includes/mike42/escpos-php/example/receipt-with-logo.php

@@ -0,0 +1,96 @@
+<?php
+require_once(dirname(__FILE__) . "/../Escpos.php");
+
+/* Information for the receipt */
+$items = array(
+	new item("Example item #1", "4.00"),
+	new item("Another thing", "3.50"),
+	new item("Something else", "1.00"),
+	new item("A final item", "4.45"),
+);
+$subtotal = new item('Subtotal', '12.95');
+$tax = new item('A local tax', '1.30');
+$total = new item('Total', '14.25', true);
+/* Date is kept the same for testing */
+// $date = date('l jS \of F Y h:i:s A');
+$date = "Monday 6th of April 2015 02:56:25 PM";
+
+/* Start the printer */
+$logo = new EscposImage("resources/escpos-php.png");
+$printer = new Escpos();
+
+/* Print top logo */
+$printer -> 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";
+	}
+}
+?>

+ 35 - 0
htdocs/includes/mike42/escpos-php/example/resources/character-encoding-test-strings.inc

@@ -0,0 +1,35 @@
+<?php
+/* All strings from EscposPrintBufferTest are included below- These are fully supported
+ * on the default profile, so you can use them to test modified profiles (using the wrong
+ * profile for a printer produces mojibake) */
+$inputsOk = array(
+		"Danish" => "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"
+		);
+

BIN
htdocs/includes/mike42/escpos-php/example/resources/document.odt


BIN
htdocs/includes/mike42/escpos-php/example/resources/document.pdf


BIN
htdocs/includes/mike42/escpos-php/example/resources/document.z


BIN
htdocs/includes/mike42/escpos-php/example/resources/escpos-php-small.png


BIN
htdocs/includes/mike42/escpos-php/example/resources/escpos-php.png


BIN
htdocs/includes/mike42/escpos-php/example/resources/tux.png


+ 21 - 0
htdocs/includes/mike42/escpos-php/example/specific/29-latvian-star-tup592.php

@@ -0,0 +1,21 @@
+<?php 
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+/* This example shows the printing of Latvian text on the Star TUP 592 printer */
+$profile = StarCapabilityProfile::getInstance();
+
+/* Option 1: Native character encoding */
+$connector = new FilePrintConnector("php://stdout");
+$printer = new Escpos($connector, $profile);
+$printer -> 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();
+?>

+ 36 - 0
htdocs/includes/mike42/escpos-php/example/specific/32-german-tm-t20-ii-custom-command.php

@@ -0,0 +1,36 @@
+<?php
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+/*
+ * This example shows how tok send a custom command to the printer-
+ * The use case here is an Epson TM-T20II and German text.
+ * 
+ * Background: Not all ESC/POS features are available in the driver, so sometimes
+ * you might want to send a custom commnad. This is useful for testing
+ * new features.
+ * 
+ * The Escpos::text() function removes non-printable characters as a precaution,
+ * so that commands cannot be injected into user input. To send raw data to
+ * the printer, you need to write bytes to the underlying PrintConnector.
+ * 
+ * If you get a new command working, please file an issue on GitHub with a code
+ * snippet so that it can be incorporated into escpos-php.
+ */
+
+/* Set up profile & connector */
+$connector = new FilePrintConnector("php://output");
+$profile = DefaultCapabilityProfile::getInstance(); // Works for Epson printers
+
+$printer = new Escpos($connector, $profile);
+$cmd = Escpos::ESC . "V" . chr(1); // Try out 90-degree rotation.
+$printer -> 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
+ */

+ 16 - 0
htdocs/includes/mike42/escpos-php/example/specific/33-spanish-seypos-prp-300.php

@@ -0,0 +1,16 @@
+<?php
+/*
+ * Example of printing Spanish text on SEYPOS PRP-300 thermal line printer.
+ * The characters in Spanish are available in code page 437, so no special
+ * code pages are needed in this case (SimpleCapabilityProfile).
+ *
+ * Use the hardware switch to activate "Two-byte Character Code"
+ */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+$connector = new FilePrintConnector("php://output");
+$profile = SimpleCapabilityProfile::getInstance();
+$printer = new Escpos($connector);
+$printer -> 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();
+

+ 69 - 0
htdocs/includes/mike42/escpos-php/example/specific/39-currency-symbols.php

@@ -0,0 +1,69 @@
+<?php 
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+$profile = DefaultCapabilityProfile::getInstance();
+// This is a quick demo of currency symbol issues in #39.
+
+/* Option 1: Native ESC/POS characters, depends on printer and is buggy. */
+$connector = new FilePrintConnector("php://stdout");
+$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();
+
+/* 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();

+ 31 - 0
htdocs/includes/mike42/escpos-php/example/specific/44-pound-symbol-star-tsp650.php

@@ -0,0 +1,31 @@
+<?php
+/* Example of printing the GBP pound symbol on a STAR TSP650
+ * 
+ * In this example, it's shown how to check that your PHP files are actually being
+ * saved in unicode. Sections B) and C) are identical in UTF-8, but different
+ * if you are saving to a retro format like Windows-1252.
+ */
+
+// Adjust these to your environment
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+$connector = new FilePrintConnector("php://stdout");
+
+// Start printer
+$profile = SimpleCapabilityProfile::getInstance();
+$printer = new Escpos($connector, $profile);
+
+// A) Raw pound symbol
+// This is the most likely thing to work, and bypasses all the fancy stuff.
+$printer -> 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();

+ 16 - 0
htdocs/includes/mike42/escpos-php/example/specific/50-P-822D-greek.php

@@ -0,0 +1,16 @@
+<?php
+/* Example of Greek text on the P-822D */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+
+// Setup the printer
+$connector = new FilePrintConnector("php://stdout");
+$profile = P822DCapabilityProfile::getInstance();
+$printer = new Escpos($connector, $profile);
+
+// Print a Greek pangram
+$text = "Ξεσκεπάζω την ψυχοφθόρα βδελυγμία";
+$printer -> text($text . "\n");
+$printer -> cut();
+
+// Close the connection
+$printer -> close();

+ 58 - 0
htdocs/includes/mike42/escpos-php/example/specific/54-gfx-sidebyside.php

@@ -0,0 +1,58 @@
+<?php
+/*
+ * Example of calling ImageMagick 'convert' to manipulate an image prior to printing.
+ * 
+ * Written as an example to remind you to do four things-
+ * - escape your command-line arguments with escapeshellarg
+ * - close the printer
+ * - delete any temp files
+ * - detect and handle external command failure
+ *
+ * Note that image operations are slow. You can and should serialise an EscposImage
+ * object into some sort of cache if you will re-use the output.
+ */
+require_once (dirname ( __FILE__ ) . "/../../Escpos.php");
+
+// Paths to images to combine
+$img1_path = dirname ( __FILE__ ) . "/../resources/tux.png";
+$img2_path = dirname ( __FILE__ ) . "/../resources/escpos-php.png";
+
+// Set up temp file with .png extension
+$tmpf_path = tempnam ( sys_get_temp_dir (), 'escpos-php' );
+$imgCombined_path = $tmpf_path . ".png";
+
+try {
+	// Convert, load image, remove temp files
+	$cmd = sprintf ("convert %s %s +append %s",
+			escapeshellarg ( $img1_path ),
+			escapeshellarg ( $img2_path ),
+			escapeshellarg ( $imgCombined_path ));
+	exec($cmd, $outp, $retval);
+	if($retval != 0) {
+		// Detect and handle command failure
+		throw new Exception("Command \"$cmd\" returned $retval." . implode("\n", $outp));
+	}
+	$img = new EscposImage ( $imgCombined_path );
+
+	// Setup the printer
+	$connector = new FilePrintConnector ( "php://stdout" );
+	$profile = DefaultCapabilityProfile::getInstance ();
+
+	// Run the actual print
+	$printer = new Escpos ( $connector, $profile );
+	try {
+		$printer -> 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 );
+}

+ 47 - 0
htdocs/includes/mike42/escpos-php/example/specific/6-arabic-epos-tep-220m.php

@@ -0,0 +1,47 @@
+<?php
+/*
+ * This example shows Arabic image-based output on the EPOS TEP 220m.
+ *
+ * Because escpos-php is not yet able to render Arabic correctly
+ * on thermal line printers, small images are generated and sent
+ * instead. This is a bit slower, and only limited formatting
+ * is currently available in this mode.
+ * 
+ * Requirements are:
+ *  - imagick extension (For the ImagePrintBuffer, which does not
+ *      support gd at the time of writing)
+ *  - Ar-PHP library, available from sourceforge, for the first
+ *      part of this example. Drop it in the folder listed below:
+ */
+require_once(dirname(__FILE__) . "/../../Escpos.php");
+require_once(dirname(__FILE__) . "/../../vendor/I18N/Arabic.php");
+
+/*
+ * First, convert the text into LTR byte order with joined letters,
+ * Using the Ar-PHP library.
+ * 
+ * The Ar-PHP library uses the default internal encoding, and can print
+ * a lot of errors depending on the input, so be prepared to debug
+ * the next four lines.
+ * 
+ * Note that this output shows that numerals are converted to placeholder
+ * characters, indicating that western numerals (123) have to be used instead.
+ */
+mb_internal_encoding("UTF-8");
+$Arabic = new I18N_Arabic('Glyphs');
+$text = "صِف خَلقَ خَودِ كَمِثلِ الشَمسِ إِذ بَزَغَت — يَحظى الضَجيعُ بِها نَجلاءَ مِعطارِ";
+$text = $Arabic -> 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();

+ 7 - 0
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.

+ 62 - 0
htdocs/includes/mike42/escpos-php/example/text-size.php

@@ -0,0 +1,62 @@
+<?php
+/**
+ * This print-out shows how large the available font sizes are. It is included
+ * separately due to the amount of text it prints.
+ *
+ * @author Michael Billington <michael.billington@gmail.com>
+ */
+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
+}
+
+?>

+ 61 - 0
htdocs/includes/mike42/escpos-php/src/AbstractCapabilityProfile.php

@@ -0,0 +1,61 @@
+<?php
+/**
+ * Not all printers support the same subset of available Esc/POS commands. Profiles allow you to specify
+ * which features are available on your printer, so that Escpos is less likely to send unsupported commands.
+ */
+abstract class AbstractCapabilityProfile {
+	/**
+	 * Sub-classes must be retrieved via getInstance(), so that validation
+	 * can be attached to guarantee that dud profiles are not used on an Escpos object.
+	 */
+	protected final function __construct() {
+		// This space intentionally left blank.
+	}
+
+	/**
+	 * If getSupportedCodePages contains custom code pages, their character maps must be provided here.
+	 */
+	abstract function getCustomCodePages();
+
+	/**
+	 * Return a map of code page numbers to names for this printer. Names
+	 * should match iconv code page names where possible (non-matching names will not be used).
+	 */
+	abstract function getSupportedCodePages();
+
+	/**
+	 * True to support barcode "function b", false to use only function A.
+	 */
+	abstract function getSupportsBarcodeB();
+
+	/**
+	 * True for bitImage support, false for no bitImage support.
+	 */
+	abstract function getSupportsBitImage();
+
+	/**
+	 * True for graphics support, false for no graphics support.
+	 */
+	abstract function getSupportsGraphics();
+
+	/**
+	 * True for 'STAR original' commands, false for standard ESC/POS only.
+	 */
+	abstract function getSupportsStarCommands();
+
+	/**
+	 * True if the printer renders its own QR codes, false to send an image.
+	 */
+	abstract function getSupportsQrCode();
+
+	/**
+	 * @return AbstractCapabilityProfile Instance of sub-class.
+	 */
+	public static final function getInstance() {
+		static $profile = null;
+		if ($profile === null) {
+			$profile = new static();
+		}
+		return $profile;
+	}
+}

+ 181 - 0
htdocs/includes/mike42/escpos-php/src/CodePage.php

@@ -0,0 +1,181 @@
+<?php
+abstract class CodePage {
+	/** Code page constants, exported from iconv -l. Can be cut down*/
+	const CP037 = "CP037";
+	const CP038 = "CP038";
+	const CP273 = "CP273";
+	const CP274 = "CP274";
+	const CP275 = "CP275";
+	const CP278 = "CP278";
+	const CP280 = "CP280";
+	const CP281 = "CP281";
+	const CP282 = "CP282";
+	const CP284 = "CP284";
+	const CP285 = "CP285";
+	const CP290 = "CP290";
+	const CP297 = "CP297";
+	const CP367 = "CP367";
+	const CP420 = "CP420";
+	const CP423 = "CP423";
+	const CP424 = "CP424";
+	const CP437 = "CP437";
+	const CP500 = "CP500";
+	const CP737 = "CP737";
+	const CP770 = "CP770";
+	const CP771 = "CP771";
+	const CP772 = "CP772";
+	const CP773 = "CP773";
+	const CP774 = "CP774";
+	const CP775 = "CP775";
+	const CP803 = "CP803";
+	const CP813 = "CP813";
+	const CP819 = "CP819";
+	const CP850 = "CP850";
+	const CP851 = "CP851";
+	const CP852 = "CP852";
+	const CP855 = "CP855";
+	const CP856 = "CP856";
+	const CP857 = "CP857";
+	const CP860 = "CP860";
+	const CP861 = "CP861";
+	const CP862 = "CP862";
+	const CP863 = "CP863";
+	const CP864 = "CP864";
+	const CP865 = "CP865";
+	const CP866 = "CP866";
+	const CP866NAV = "CP866NAV";
+	const CP868 = "CP868";
+	const CP869 = "CP869";
+	const CP870 = "CP870";
+	const CP871 = "CP871";
+	const CP874 = "CP874";
+	const CP875 = "CP875";
+	const CP880 = "CP880";
+	const CP891 = "CP891";
+	const CP901 = "CP901";
+	const CP902 = "CP902";
+	const CP903 = "CP903";
+	const CP904 = "CP904";
+	const CP905 = "CP905";
+	const CP912 = "CP912";
+	const CP915 = "CP915";
+	const CP916 = "CP916";
+	const CP918 = "CP918";
+	const CP920 = "CP920";
+	const CP921 = "CP921";
+	const CP922 = "CP922";
+	const CP930 = "CP930";
+	const CP932 = "CP932";//
+	const CP933 = "CP933";
+	const CP935 = "CP935";
+	const CP936 = "CP936";
+	const CP937 = "CP937";
+	const CP939 = "CP939";
+	const CP949 = "CP949";
+	const CP950 = "CP950";
+	const CP1004 = "CP1004";
+	const CP1008 = "CP1008";
+	const CP1025 = "CP1025";
+	const CP1026 = "CP1026";
+	const CP1046 = "CP1046";
+	const CP1047 = "CP1047";
+	const CP1070 = "CP1070";
+	const CP1079 = "CP1079";
+	const CP1081 = "CP1081";
+	const CP1084 = "CP1084";
+	const CP1089 = "CP1089";
+	const CP1097 = "CP1097";
+	const CP1112 = "CP1112";
+	const CP1122 = "CP1122";
+	const CP1123 = "CP1123";
+	const CP1124 = "CP1124";
+	const CP1125 = "CP1125";
+	const CP1129 = "CP1129";
+	const CP1130 = "CP1130";
+	const CP1132 = "CP1132";
+	const CP1133 = "CP1133";
+	const CP1137 = "CP1137";
+	const CP1140 = "CP1140";
+	const CP1141 = "CP1141";
+	const CP1142 = "CP1142";
+	const CP1143 = "CP1143";
+	const CP1144 = "CP1144";
+	const CP1145 = "CP1145";
+	const CP1146 = "CP1146";
+	const CP1147 = "CP1147";
+	const CP1148 = "CP1148";
+	const CP1149 = "CP1149";
+	const CP1153 = "CP1153";
+	const CP1154 = "CP1154";
+	const CP1155 = "CP1155";
+	const CP1156 = "CP1156";
+	const CP1157 = "CP1157";
+	const CP1158 = "CP1158";
+	const CP1160 = "CP1160";
+	const CP1161 = "CP1161";
+	const CP1162 = "CP1162";
+	const CP1163 = "CP1163";
+	const CP1164 = "CP1164";
+	const CP1166 = "CP1166";
+	const CP1167 = "CP1167";
+	const CP1250 = "CP1250";
+	const CP1251 = "CP1251";
+	const CP1252 = "CP1252";
+	const CP1253 = "CP1253";
+	const CP1254 = "CP1254";
+	const CP1255 = "CP1255";
+	const CP1256 = "CP1256";
+	const CP1257 = "CP1257";
+	const CP1258 = "CP1258";
+	const CP1282 = "CP1282";
+	const CP1361 = "CP1361";
+	const CP1364 = "CP1364";
+	const CP1371 = "CP1371";
+	const CP1388 = "CP1388";
+	const CP1390 = "CP1390";
+	const CP1399 = "CP1399";
+	const CP4517 = "CP4517";
+	const CP4899 = "CP4899";
+	const CP4909 = "CP4909";
+	const CP4971 = "CP4971";
+	const CP5347 = "CP5347";
+	const CP9030 = "CP9030";
+	const CP9066 = "CP9066";
+	const CP9448 = "CP9448";
+	const CP10007 = "CP10007";
+	const CP12712 = "CP12712";
+	const CP16804 = "CP16804";
+	const ISO8859_7 = "ISO_8859-7";
+	const ISO8859_2 = "ISO_8859-2";
+	const ISO8859_15 = "ISO_8859-15";
+	const RK1048 = "RK1048";
+	// Code pages which are not built in
+	// to default iconv on Debian.
+	const CP720 = false;
+	const CP853 = false;
+	const CP858 = false;
+	const CP928 = false;
+	const CP1098 = false;
+	const CP747 = false;
+	
+	/*
+	 * Below code pages appear to be vendor-specific (Star), so iconv wont use them
+	 * They are being merged gradually into the StarCapabilityProfile.
+	 */
+	const CP3840 = false;
+	const CP3841 = false;
+	const CP3843 = false;
+	const CP3844 = false;
+	const CP3845 = false;
+	const CP3847 = false;
+	const CP3846 = false;
+	const CP3848 = false;
+	const CP1001 = false;
+	const CP2001 = false;	
+	const CP3001 = false;
+	const CP3002 = false;
+	const CP3011 = false;
+	const CP3012 = false;
+	const CP3021 = false;
+	const CP3041 = false;
+}

+ 103 - 0
htdocs/includes/mike42/escpos-php/src/DefaultCapabilityProfile.php

@@ -0,0 +1,103 @@
+<?php
+/**
+ * This capability profile matches many recent Epson-branded thermal receipt printers.
+ * 
+ * For non-Epson printers, try the SimpleCapabilityProfile.
+ */
+class DefaultCapabilityProfile extends AbstractCapabilityProfile {
+	function getCustomCodePages() {
+		return array();
+	}
+
+	function getSupportedCodePages() {
+		/* Character code tables which the printer understands, mapping to known encoding standards we may be able to encode to.
+		 * 
+		 * See CodePage.php for the mapping of these standards to encoding names for use in the backing library.
+		 * 
+		 * Any entry with 'false' means I haven't compared the print-out of the code page to a table.
+		 */
+		return array(
+			0 => 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;
+	}
+}

+ 78 - 0
htdocs/includes/mike42/escpos-php/src/DummyPrintConnector.php

@@ -0,0 +1,78 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ *
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ *
+ * 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;
+	}
+}

+ 4 - 0
htdocs/includes/mike42/escpos-php/src/EposTepCapabilityProfile.php

@@ -0,0 +1,4 @@
+<?php
+class EposTepCapabilityProfile extends DefaultCapabilityProfile {
+	// TODO override list of code pages
+}

+ 405 - 0
htdocs/includes/mike42/escpos-php/src/EscposImage.php

@@ -0,0 +1,405 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ * 
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ * 
+ * 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);
+		}
+	}
+}

+ 304 - 0
htdocs/includes/mike42/escpos-php/src/EscposPrintBuffer.php

@@ -0,0 +1,304 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ * 
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ * 
+ * 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;
+	}
+}

+ 80 - 0
htdocs/includes/mike42/escpos-php/src/FilePrintConnector.php

@@ -0,0 +1,80 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ *
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ *
+ * 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);
+	}
+}

+ 99 - 0
htdocs/includes/mike42/escpos-php/src/ImagePrintBuffer.php

@@ -0,0 +1,99 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ *
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ *
+ * 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);
+	}
+}

+ 39 - 0
htdocs/includes/mike42/escpos-php/src/NetworkPrintConnector.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ *
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ *
+ * 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);
+		}
+	}
+}

+ 90 - 0
htdocs/includes/mike42/escpos-php/src/P822DCapabilityProfile.php

@@ -0,0 +1,90 @@
+<?php
+/**
+ * This capability profile is designed for the P-822D.
+ * 
+ * See
+ * https://github.com/mike42/escpos-php/issues/50
+ */
+class P822DCapabilityProfile extends DefaultCapabilityProfile {
+	function getSupportedCodePages() {
+		return array(
+			0 => 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;
+	}
+}

+ 75 - 0
htdocs/includes/mike42/escpos-php/src/PrintBuffer.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ * 
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ * 
+ * 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);
+}
+?>

+ 56 - 0
htdocs/includes/mike42/escpos-php/src/PrintConnector.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ *
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ *
+ * 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);
+}

+ 17 - 0
htdocs/includes/mike42/escpos-php/src/SimpleCapabilityProfile.php

@@ -0,0 +1,17 @@
+<?php
+/**
+ * This capability profile is designed for non-Epson printers sold online. Without knowing
+ * their character encoding table, only CP437 output is assumed, and graphics() calls will
+ * be disabled, as it usually prints junk on these models.
+ */
+class SimpleCapabilityProfile extends DefaultCapabilityProfile {
+	function getSupportedCodePages() {
+		/* Use only CP437 output */
+		return array(0 => CodePage::CP437);
+	}
+	
+	public function getSupportsGraphics() {
+		/* Ask the driver to use bitImage wherever possible instead of graphics */
+		return false;
+	}
+}

+ 82 - 0
htdocs/includes/mike42/escpos-php/src/StarCapabilityProfile.php

@@ -0,0 +1,82 @@
+<?php
+class StarCapabilityProfile extends DefaultCapabilityProfile {
+	function getCustomCodePages() {
+		// Code table reference: http://www.starmicronics.com/support/mannualfolder/sp2000pm.pdf
+		return array(
+			'CP3011' => "ÇüéâäàåçêëèïîìÄÅ" . 
+						"ÉæÆôöòûùÿÖÜ¢£¥₧ƒ" . 
+						"áíóúñѪº¿⌐¬½¼¡«»" . 
+						"░▒▓│┤Ā╢ņ╕╣║╗╝╜╛┐" . 
+						"└┴┬├─┼ā╟╚╔╩╦╠═╬╧" . 
+						"Š╤čČ╘╒ģĪī┘┌█▄ūŪ▀" . 
+						"αßΓπΣσµτΦΘΩδ∞φε∩" . 
+						"ĒēĢķĶļĻžŽ∙·√Ņš■ ",
+			'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;
+	}
+}

+ 356 - 0
htdocs/includes/mike42/escpos-php/src/WindowsPrintConnector.php

@@ -0,0 +1,356 @@
+<?php
+/**
+ * escpos-php, a Thermal receipt printer library, for use with
+ * ESC/POS compatible printers.
+ *
+ * Copyright (c) 2014-2015 Michael Billington <michael.billington@gmail.com>,
+ * 	incorporating modifications by:
+ *  - Roni Saha <roni.cse@gmail.com>
+ *  - Gergely Radics <gerifield@ustream.tv>
+ *  - Warren Doyle <w.doyle@fuelled.co>
+ *
+ * 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;
+	}
+}

BIN
htdocs/includes/mike42/escpos-php/src/cache/Characters-DefaultCapabilityProfile.ser.gz


BIN
htdocs/includes/mike42/escpos-php/src/cache/Characters-SimpleCapabilityProfile.ser.gz


BIN
htdocs/includes/mike42/escpos-php/src/cache/Characters-StarCapabilityProfile.ser.gz


+ 27 - 0
htdocs/includes/mike42/escpos-php/test/bootstrap.php

@@ -0,0 +1,27 @@
+<?php
+error_reporting(E_ALL);
+ini_set('display_errors', 1);
+
+require_once(dirname(__FILE__) . "/../vendor/autoload.php");
+require_once(dirname(__FILE__) . "/../Escpos.php");
+require_once(dirname(__FILE__) . "/../src/DummyPrintConnector.php");
+
+/**
+ * Used in many of the tests to to output known-correct
+ * strings for use in tests. 
+ */
+function friendlyBinary($in) {
+	if(strlen($in) == 0) {
+		return $in;
+	}
+	/* Print out binary data with PHP \x00 escape codes,
+	 for builting test cases. */
+	$chars = str_split($in);
+	foreach($chars as $i => $c) {
+		$code = ord($c);
+		if($code < 32 || $code > 126) {
+			$chars[$i] = "\\x" . bin2hex($c);
+		}
+	}
+	return implode($chars);
+}

+ 126 - 0
htdocs/includes/mike42/escpos-php/test/integration/ExampleTest.php

@@ -0,0 +1,126 @@
+<?php
+class ExampleTest extends PHPUnit_Framework_TestCase {
+	/* Verify that the examples don't fizzle out with fatal errors */
+	private $exampleDir;
+	
+	public function setup() {
+		$this -> 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.");
+		}
+	}
+}

BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/barcode.bin


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/bit-image.bin


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-encodings.bin


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/character-tables.bin


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/demo.bin


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/graphics.bin


+ 2 - 0
htdocs/includes/mike42/escpos-php/test/integration/resources/output/interface.bin

@@ -0,0 +1,2 @@
+@Hello World!
+VA

BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/print-from-pdf.bin.z


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/qr-code.bin


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/receipt-with-logo.bin


BIN
htdocs/includes/mike42/escpos-php/test/integration/resources/output/text-size.bin


+ 16 - 0
htdocs/includes/mike42/escpos-php/test/phpunit.xml

@@ -0,0 +1,16 @@
+<phpunit bootstrap="bootstrap.php"
+timeoutForSmallTests="1"
+timeoutForMediumTests="10"
+timeoutForLargeTests="60"
+verbose="true"
+strict="true"
+>
+  <testsuites>
+    <testsuite name="unit">
+      <directory>unit</directory>
+    </testsuite>
+    <testsuite name="integration">
+      <directory>integration</directory>
+    </testsuite>
+  </testsuites>
+</phpunit>

+ 69 - 0
htdocs/includes/mike42/escpos-php/test/unit/AbstractCapabilityProfileTest.php

@@ -0,0 +1,69 @@
+<?php
+/**
+ * Test that all sub-classes of AbstractCapabilityProfile
+ * are creating data in the right format.
+ */
+class EscposCapabilityProfileTest extends PHPUnit_Framework_TestCase {
+	private $profiles;
+	private $checklist;
+	
+	function setup() {
+		$this -> 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));
+		}
+	}
+}
+?>

+ 235 - 0
htdocs/includes/mike42/escpos-php/test/unit/EscposImageTest.php

@@ -0,0 +1,235 @@
+<?php
+class EscposImageTest extends PHPUnit_Framework_TestCase {
+	/**
+	 * Checking loading of an empty image - requires no libraries
+	 */
+	public function testNoLibrariesBlank() {
+		$this -> 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;
+	}
+}

+ 150 - 0
htdocs/includes/mike42/escpos-php/test/unit/EscposPrintBufferTest.php

@@ -0,0 +1,150 @@
+<?php
+/**
+ * Example strings are pangrams using different character sets, and are
+ * testing correct code-table switching.
+ * 
+ * When printed, they should appear the same as in this source file.
+ * 
+ * Many of these test strings are from:
+ * - http://www.cl.cam.ac.uk/~mgk25/ucs/examples/quickbrown.txt
+ * - http://clagnut.com/blog/2380/ (mirrored from the English Wikipedia)
+ */
+class EscposPrintBufferTest extends PHPUnit_Framework_TestCase {
+	protected $buffer;
+	protected $outputConnector;
+	
+	protected function setup() {
+		$this -> 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();
+	}
+}
+

+ 765 - 0
htdocs/includes/mike42/escpos-php/test/unit/EscposTest.php

@@ -0,0 +1,765 @@
+<?php
+class EscposTest extends PHPUnit_Framework_TestCase {
+	protected $printer;
+	protected $outputConnector;
+
+	protected function setup() {
+		/* Print to nowhere- for testing which inputs are accepted */
+		$this -> 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<x");
+	}
+
+	function testPulse1() {
+		$this -> printer -> pulse(1);
+		$this -> checkOutput("\x1b@\x1bp1<x");
+	}
+	
+	function testPulseEvenMs() {
+		$this -> 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;
+	}
+}
+?>

+ 278 - 0
htdocs/includes/mike42/escpos-php/test/unit/WindowsPrintConnectorTest.php

@@ -0,0 +1,278 @@
+<?php
+class WindowsPrintConnectorTest extends PHPUnit_Framework_TestCase {
+	private $connector;
+
+	public function testLptWindows() {
+		// Should attempt to send data to the local printer by writing to it
+		$connector = $this -> 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'.");
+		}
+	}
+}

BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.gif


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/black_transparent.png


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.bmp


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.gif


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.jpg


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/black_white.png


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.bmp


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.gif


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.jpg


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_black.png


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.bmp


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.gif


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.jpg


BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/canvas_white.png


+ 19 - 0
htdocs/includes/mike42/escpos-php/test/unit/resources/demo.php

@@ -0,0 +1,19 @@
+<?php
+$im = new Imagick();
+try {
+	$im -> 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";
+}
+

BIN
htdocs/includes/mike42/escpos-php/test/unit/resources/doc.pdf