Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rob Gridley committed Jun 5, 2015
0 parents commit 512a303
Show file tree
Hide file tree
Showing 13 changed files with 480 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
/.idea
/vendor
/composer.lock
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2015 Rob Gridley <[email protected]>

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.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Zebra
PHP ZPL builder and a basic client for network-connected Zebra label printers.

Requires: PHP 5.6 with the GD module.

* Create ZPL code in PHP that is clean and easy to read.
* Convert images to ASCII hex bitmaps (JPEG, PNG, GIF, WBMP, and GD2 supported).
* Simple wrapper for PHP sockets to send ZPL to the printer via raw TCP/IP (port 9100).

## Examples
The following example will print a label with an image positioned 50 dots from the top left.
```php
use Zebra\Client;
use Zebra\Zpl\Image;
use Zebra\Zpl\Builder;

$zpl = new Builder();
$zpl->fo(50, 50);

$image = new Image(file_get_contents('example.png'));
$zpl->gf($image);

$client = new Client('10.0.0.50');
$client->send($zpl);
```
The same example using static constructors and method chaining:
```php
$image = new Image(file_get_contents('example.png'));
$zpl = Zpl::start()->fo(50, 50)->gf($image);
Client::printer('10.0.0.50')->send($zpl);
```
21 changes: 21 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "robgridley/zebra",
"description": "PHP ZPL builder and a basic client for network-connected Zebra label printers.",
"require": {
"php": ">=5.6.0"
},
"require-dev": {
"phpspec/phpspec": "~2.0"
},
"authors": [
{
"name": "Rob Gridley",
"email": "[email protected]"
}
],
"autoload": {
"psr-4": {
"Zebra\\": "src/"
}
}
}
4 changes: 4 additions & 0 deletions phpspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
suites:
acme_suite:
namespace: Zebra
psr4_prefix: Zebra
32 changes: 32 additions & 0 deletions spec/Zpl/BuilderSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php namespace spec\Zebra\Zpl;

use Zebra\Zpl\Image;
use Prophecy\Argument;
use PhpSpec\ObjectBehavior;

class BuilderSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Zebra\Zpl\Builder');
}

function it_creates_zpl_commands_with_string_number_and_boolean_parameters()
{
$this->command('BC', 'N', 100, true)->__toString()->shouldReturn("^XA\n^BCN,100,Y\n^XZ");
}

function it_creates_zpl_commands_from_magic_methods_and_their_arguments()
{
$this->fo(50, 50)->__toString()->shouldReturn("^XA\n^FO50,50\n^XZ");
}

function it_accepts_a_single_argument_of_image_for_gf_command(Image $image)
{
$image->__toString()->willReturn("FF00");
$image->widthInBytes()->willReturn(2);
$image->height()->willReturn(16);

$this->gf($image)->__toString()->shouldReturn("^XA\n^GFA,32,32,2,FF00\n^XZ");
}
}
41 changes: 41 additions & 0 deletions spec/Zpl/ImageSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php namespace spec\Zebra\Zpl;

use Prophecy\Argument;
use PhpSpec\ObjectBehavior;

class ImageSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith(file_get_contents(__DIR__ . '/../test.png'));
}

function it_is_initializable()
{
$this->shouldHaveType('Zebra\Zpl\Image');
}

function it_converts_images_to_ascii_hexadecimal_bitmaps()
{
$this->__toString()->shouldReturn('00400000400002E80003F80003F80013F9001BFB003FFF80FFFFE07FFFC03FFF801FFF000FFE0007FC000E4E00004000004000004000');
}

function it_ignores_the_colour_palette_order_of_the_incoming_image()
{
$this->beConstructedWith(file_get_contents(__DIR__ . '/../test.gif'));

$this->__toString()->shouldReturn('FFBFE0FFBFE0FD17E0FC07E0FC07E0EC06E0E404E0C00060000000800020C00060E000E0F001E0F803E0F1B1E0FFBFE0FFBFE0FFBFE0');
}

function it_gets_the_dimensions_of_the_image_in_pixels()
{
$this->width()->shouldReturn(19);

$this->height()->shouldReturn(18);
}

function it_gets_the_width_of_the_image_in_bytes()
{
$this->widthInBytes()->shouldReturn(3);
}
}
Binary file added spec/test.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added spec/test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php namespace Zebra;

class Client
{
/**
* The endpoint.
*
* @var resource
*/
protected $socket;

/**
* Create an instance.
*
* @param $host
* @param int $port
*/
public function __construct($host, $port = 9100)
{
$this->connect($host, $port);
}

/**
* Destroy an instance.
*/
public function __destruct()
{
$this->disconnect();
}

/**
* Create an instance statically.
*
* @param string $host
* @param int $port
* @return self
*/
public static function printer($host, $port = 9100)
{
return new static($host, $port);
}

/**
* Connect to printer.
*
* @param string $host
* @param int $port
* @return bool
*/
protected function connect($host, $port)
{
$this->socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

if ( ! $this->socket || ! @socket_connect($this->socket, $host, $port)) {
$error = $this->getLastError();
throw new ClientException($error['message'], $error['code']);
}
}

/**
* Close connection to printer.
*/
protected function disconnect()
{
@socket_close($this->socket);
}

/**
* Send ZPL data to printer.
*
* @param string $zpl
*/
public function send($zpl)
{
if ( ! @socket_write($this->socket, $zpl)) {
$error = $this->getLastError();
throw new ClientException($error['message'], $error['code']);
}
}

/**
* Get the last socket error.
*
* @return array
*/
protected function getLastError()
{
$code = socket_last_error($this->socket);
$message = socket_strerror($code);

return compact('code', 'message');
}
}
8 changes: 8 additions & 0 deletions src/ClientException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php namespace Zebra;

use RuntimeException;

class ClientException extends RuntimeException
{
//
}
103 changes: 103 additions & 0 deletions src/Zpl/Builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php namespace Zebra\Zpl;

class Builder
{
/**
* ZPL commands.
*
* @var array
*/
protected $zpl = array();

/**
* Create a new instance statically.
*
* @return self
*/
public static function start()
{
return new static;
}

/**
* Add a command.
*
* @param string $command
* @param mixed $parameters,...
* @return self
*/
public function command($command, ...$parameters)
{
$parameters = array_map([$this, 'convert'], $parameters);
$this->zpl[] = '^' . strtoupper($command) . implode(',', $parameters);

return $this;
}

/**
* Convert native types to their ZPL representations.
*
* @param mixed $parameter
* @return mixed
*/
protected function convert($parameter)
{
if (is_bool($parameter)) {
return $parameter ? 'Y' : 'N';
}

return $parameter;
}

/**
* Handle dynamic method calls.
*
* @param $method
* @param $arguments
* @return self
*/
public function __call($method, $arguments)
{
return $this->command($method, ...$arguments);
}

/**
* Add GF command.
*
* @param mixed $parameters,...
* @return self
*/
public function gf(...$parameters)
{
if (func_num_args() === 1 && ($image = $parameters[0]) instanceof Image) {

$bytesPerRow = $image->widthInBytes();
$byteCount = $fieldCount = $bytesPerRow * $image->height();

return $this->command('GF', 'A', $byteCount, $fieldCount, $bytesPerRow, $image);
}

return $this->command('GF', ...$parameters);
}

/**
* Convert instance to ZPL.
*
* @return string
*/
public function toZpl()
{
return implode("\n", array_merge(['^XA'], $this->zpl, ['^XZ']));
}

/**
* Convert instance to string.
*
* @return string
*/
public function __toString()
{
return $this->toZpl();
}

}
Loading

0 comments on commit 512a303

Please sign in to comment.