|
<?php |
|
|
|
/** |
|
* Class for handling printing using the pecl/printer extension |
|
*/ |
|
abstract class Printer { |
|
|
|
const MODE_TEXT = 'TEXT'; |
|
const MODE_RAW = 'RAW'; |
|
const MODE_EMF = 'EMF'; |
|
|
|
protected $name; |
|
protected $ptrResource; |
|
protected $documentStarted = false; |
|
protected $pageStarted = false; |
|
|
|
/** |
|
* Create a new printer object |
|
* @param string printerName The name of the printer (@see Printer::getPrinters) |
|
* @param string printerMode The mode to set the printer in (@see Printer::MODE_*) |
|
*/ |
|
public function __construct($printerName, $printerMode = null) { |
|
|
|
$this->name = $printerName; |
|
$this->ptrResource = printer_open($printerName); |
|
|
|
if(isset($printerMode)){ |
|
|
|
$this->setMode($printerMode); |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
* Clear the printer's spool queue |
|
*/ |
|
public function clearSpool() { |
|
|
|
printer_abort($this->ptrResource); |
|
|
|
} |
|
|
|
/** |
|
* Get the printer DPI |
|
* @return int[] [x-axis DPI, y-axis DPI] |
|
*/ |
|
public function getDPI() { |
|
|
|
return array( |
|
printer_get_option($this->ptrResource, PRINTER_RESOLUTION_X), |
|
printer_get_option($this->ptrResource, PRINTER_RESOLUTION_Y) |
|
); |
|
|
|
} |
|
|
|
/** |
|
* Get the current printer paper type |
|
* @return int The paper type (@see PRINTER_FORMAT_*) |
|
*/ |
|
public function getPaperType() { |
|
|
|
return printer_get_option($this->ptrResource, PRINTER_PAPER_FORMAT); |
|
|
|
} |
|
|
|
/** |
|
* Set the printer paper type |
|
* @param int type The paper type (@see PRINTER_FORMAT_*) |
|
* @param int width Paper width in dots (if type = PRINTER_FORMAT_CUSTOM) |
|
* @param int length Paper length in dots (if type = PRINTER_FORMAT_CUSTOM) |
|
*/ |
|
public function setPaperType($type, $width = null, $length = null){ |
|
|
|
printer_set_option($this->ptrResource, PRINTER_PAPER_FORMAT, $type); |
|
|
|
if($type == PRINTER_FORMAT_CUSTOM){ |
|
|
|
printer_set_option($this->ptrResource, PRINTER_PAPER_WIDTH, $width); |
|
printer_set_option($this->ptrResource, PRINTER_PAPER_WIDTH, $height); |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
* Get the dimensions of the current print paper format in inches |
|
* @return int[] [width in inches, length in inches] |
|
*/ |
|
public function getPaperDimensionsInches() { |
|
|
|
switch(printer_get_option($this->ptrResource, PRINTER_PAPER_FORMAT)){ |
|
case PRINTER_FORMAT_LETTER: |
|
return array(8.5, 11); |
|
case PRINTER_FORMAT_LEGAL: |
|
return array(8.5, 14); |
|
case PRINTER_FORMAT_FOLIO: |
|
return array(8.5, 13); |
|
default: |
|
return array( |
|
printer_get_option($this->ptrResource, PRINTER_PAPER_WIDTH)/25.4, |
|
printer_get_option($this->ptrResource, PRINTER_PAPER_LENGTH)/25.4 |
|
); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Get the dimensions of the current printer paper format in dots |
|
* @return int[] [width in dots, height in dots] |
|
*/ |
|
public function getPaperDimensionsDots() { |
|
|
|
$inches = $this->getPaperDimensionsInches(); |
|
$dpi = $this->getDPI(); |
|
return array( |
|
$inches[0] * $dpi[0], |
|
$inches[1] * $dpi[1], |
|
); |
|
|
|
} |
|
|
|
/** |
|
* Get the mode of the printer |
|
* @return string The current mode (@see Printer::MODE_*) |
|
*/ |
|
protected function getMode() { |
|
|
|
return printer_get_option($this->ptrResource, PRINTER_MODE); |
|
|
|
} |
|
|
|
/** |
|
* Set the mode of the printer |
|
* @param string mode Printer mode (@see Printer::MODE_*) |
|
*/ |
|
protected function setMode($mode) { |
|
|
|
printer_set_option($this->ptrResource, PRINTER_MODE, $mode); |
|
|
|
} |
|
|
|
protected function isDocumentStarted() { |
|
return $this->documentStarted; |
|
} |
|
|
|
protected function isPageStarted() { |
|
return $this->pageStarted; |
|
} |
|
|
|
/** |
|
* Start a new document |
|
* @param string name Name of the new document |
|
* @param bool autoEnd Whether to end the previous document, if there is one |
|
* @throws Exception If a new document couldn't be started or if there is a previously started document and autoEnd is false |
|
*/ |
|
protected function startDocument($name, $autoEnd = true) { |
|
|
|
if($this->isDocumentStarted()){ |
|
if($autoEnd){ |
|
$this->endDocument(); |
|
} else { |
|
throw new Exception('Previous document is not closed'); |
|
} |
|
} |
|
|
|
if(!($this->documentStarted = printer_start_doc($this->ptrResource, $name))){ |
|
throw new Exception('Failed to start document'); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* End a previously started document |
|
* @param bool force Whether to attempt to close a document regardless if one has been started |
|
* @throws Exception If the document could not be closed |
|
*/ |
|
protected function endDocument($force = false) { |
|
|
|
if($this->isDocumentStarted() || $force){ |
|
if(printer_end_doc($this->ptrResource)){ |
|
$this->documentStarted = false; |
|
} else { |
|
throw new Exception('Failed to end document'); |
|
} |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Start a new page |
|
* @param bool autoEnd Whether to end the previous page, if there is one |
|
* @throws Exception If a new page couldn't be started or if there is a previously started page and autoEnd is false |
|
*/ |
|
protected function startPage($autoEnd = true) { |
|
|
|
if($this->isPageStarted()){ |
|
if($autoEnd){ |
|
$this->endPage(); |
|
} else { |
|
throw new Exception('Previous page is not closed'); |
|
} |
|
} |
|
|
|
if(!($this->pageStarted = printer_start_page($this->ptrResource))){ |
|
throw new Exception('Failed to start page'); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* End a previously started page |
|
* @param bool force Whether to attempt to close a page regardless if one has been started |
|
* @throws Exception If the page could not be closed |
|
*/ |
|
protected function endPage($force = false) { |
|
|
|
if($this->isPageStarted() || $force){ |
|
if(printer_end_page($this->ptrResource)){ |
|
$this->pageStarted = false; |
|
} else { |
|
throw new Exception('Failed to end page'); |
|
} |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Draw a bitmap to the current page. Must be a BMP3 file. |
|
* @param string imageFile Path to the image file |
|
* @param int x Dot offset on the x-axis from the top-left corner (optional) |
|
* @param int y Dot offset on the y-axis from the top-left corner (optional) |
|
* @param int width Width of the image in dots (optional) |
|
* @param int height Height of the image in dots (optional) |
|
* @throws Exception If the bitmap couldn't be drawn |
|
*/ |
|
protected function drawBMP($imageFile, $x = 0, $y = 0, $width = null, $height = null) { |
|
|
|
if(!printer_draw_bmp($this->ptrResource, $imageFile, $x, $y, $width, $height)){ |
|
throw new Exception('Could not draw bitmap'); |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Get a list of available printers |
|
* @param int type Printer type to search for (@see PRINTER_ENUM_*, optional) |
|
* @param string name Name of the printer to search for (optional) |
|
* @param int level Printer level to search for (optional) |
|
* @return [][] Array of arrays containing printer names and other info |
|
*/ |
|
public static function getPrinters($type = PRINTER_ENUM_LOCAL, $name = '', $level = 1) { |
|
|
|
return printer_list($type, $name, $level); |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
* Class for printing PDF files using the pecl/printer extension |
|
*/ |
|
class PDFPrinter extends Printer { |
|
|
|
protected $tempDir; |
|
|
|
/** |
|
* Setup the printer |
|
* @param string printerName Printer name |
|
* @param string tempDir Directory to store temporary files in |
|
*/ |
|
public function PDFPrinter($printerName, $tempDir = null){ |
|
|
|
if(!isset($tempDir)){ |
|
$tempDir = sys_get_temp_dir(); |
|
} |
|
$this->tempDir = $tempDir; |
|
|
|
parent::__construct($printerName, self::MODE_RAW); |
|
|
|
} |
|
|
|
/** |
|
* Print a PDF |
|
* @param string documentFile Path to the PDF to print |
|
* @param string name Name of the document (optional) |
|
* @param int dpi DPI to render the PDF at (optional) |
|
* @throws Exception If there was an issue while printing the PDF |
|
*/ |
|
public function printPDF($documentFile, $name = '', $dpi = 300) { |
|
|
|
$im = new Imagick(); |
|
$im->setResolution($dpi, $dpi); |
|
$im->readImage($documentFile); |
|
|
|
$this->startDocument($name); |
|
|
|
$numImages = $im->getNumberImages(); |
|
$tempName = $this->tempDir.uniqid('ptr_').'.bmp3'; |
|
|
|
for($i = 0; $i < $numImages; $i++){ |
|
|
|
$this->startPage(); |
|
|
|
$im->setIteratorIndex($i); |
|
$im->writeImage($tempName); |
|
|
|
$imageGeom = $im->getImageGeometry(); |
|
list($x, $y, $width, $height) = $this->calculateBestFit($imageGeom['width'], $imageGeom['height']); |
|
|
|
$this->drawBMP($tempName, $x, $y, $width, $height); |
|
|
|
$this->endPage(); |
|
|
|
} |
|
|
|
unlink($tempName); |
|
|
|
$this->endDocument(); |
|
|
|
} |
|
|
|
/** |
|
* Get the temporary directory |
|
* @return string |
|
*/ |
|
public function getTempDir() { |
|
|
|
return $this->tempDir; |
|
|
|
} |
|
|
|
/** |
|
* Set the temporary directory |
|
* @param string |
|
*/ |
|
public function setTempDir($tempDir) { |
|
|
|
$this->tempDir = $tempDir; |
|
|
|
} |
|
|
|
/** |
|
* Calculate the optimal drawing dimensions for the given image dimensions |
|
* @param int imageX Width of the image in pixels |
|
* @param int imageY Height of the image in pixels |
|
* @param int[] [x-axis offset in dots, y-axis offset in dots, width in dots, height in dots] |
|
*/ |
|
protected function calculateBestFit($imageX, $imageY) { |
|
|
|
list($paperX, $paperY) = $this->getPaperDimensionsDots(); |
|
|
|
$paperAspectRatio = $paperX / $paperY; |
|
$imageAspectRatio = $imageX / $imageY; |
|
|
|
if($imageAspectRatio > $paperAspectRatio){ |
|
|
|
$width = $paperX; |
|
$height = round($paperX / $imageAspectRatio); |
|
$x = 0; |
|
$y = round(($paperY - $height) / 2); |
|
|
|
} else { |
|
|
|
$width = round($paperY * $imageAspectRatio); |
|
$height = $paperY; |
|
$x = round(($paperX - $width) / 2); |
|
$y = 0; |
|
|
|
} |
|
|
|
return array($x, $y, $width, $height); |
|
|
|
} |