Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon-Myrstad committed Mar 8, 2016
0 parents commit 88ecc20
Show file tree
Hide file tree
Showing 12 changed files with 440 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 @@
/vendor
composer.phar
composer.lock
.DS_Store
11 changes: 11 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
language: php

php:
- 5.3
- 5.4

before_script:
- curl -s http://getcomposer.org/installer | php
- php composer.phar install --dev

script: phpunit
23 changes: 23 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "kodebyraaet/prince",
"description": "Simple prince wrapper for Prince library",
"authors": [
{
"name": "Anthoni Giskegjerde",
"email": "[email protected]"
},
{
"name": "Jon Myrstad",
"email": "[email protected]"
}
],
"require": {
"php": ">=5.3.0",
"illuminate/support": "4.2.x"
},
"autoload": {
"psr-4": {
"Kodebyraaet\\Prince\\": "src/"
}
}
}
18 changes: 18 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
53 changes: 53 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Laravel PrinceXML Wrapper

Laravel PrinceXML Wrapper is a Laravel 4.2 package that wraps around the http://www.princexml.com/ PDF generator.

## Installation
There's a requirement to have the "prince" executable installed.
```
Install the "prince" executable from http://www.princexml.com/download/.
```
Run the following command to add Kodebyraaet/Prince to download and install.
```
composer require kodebyraaet/prince
```
Add the following .env variable to your .env.*.php.
```
PRINCE_EXECUTABLE_PATH=/path/to/prince
```
Add the following to your app.php file in the service provider and alias sections respectively. The alias/facade is optional.
````
'Kodebyraaet\Prince\PrinceServiceProvider',
'Prince' => 'Kodebyraaet\Prince\Facades\Prince',
````

# Usage
The Kodebyraaet\Prince\Prince class is bound in the Laravel IoC as a Kodebyraaet\Prince\PrinceInterface, so everywhere the IoC automatically resolves dependencies (ie. in controllers) this is the preferred way to use Prince. Optionally you can also use $app->make(...) or App::make(...); You can also use \Prince or Kodebyraaet\Prince\Facades\Prince directly anywhere.

## Methods
All methods are chainable so you can dynamically add more and more markup as you go to a Prince document. For example:
```
$prince->html('<html><body>')
->html('<div><h1>Appending more content.</h1></div>
->html('</body></html>');
```

The *html* method takes html as a string and appends it to the internally stored markup.
```
$prince->html('<div>Some HTML</div>');
```

The *view* method takes a Laravel view that has not yet been rendered and renders is and appends it to the internally stored markup.
```
$prince->view(View::make('someview',['somevar' => $somevalue]));
```

The *download* method returns a Response object that can be returned to the client for view in browser/download. For example, in a Controller you can return this for a direct view of a generated PDF.
```
return $prince->html('<html>...</html>')->download();
```

The *store* method required a path and returns the same path if successful.
```
$pdfPath = $prince->html('<html>...</html>')->store(public_path('/pdf/example.pdf'));
```
8 changes: 8 additions & 0 deletions src/Kodebyraaet/Prince/Exceptions/PrinceError.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php namespace Kodebyraaet\Prince\Exceptions;

use Exception;

class PrinceError extends Exception
{

}
8 changes: 8 additions & 0 deletions src/Kodebyraaet/Prince/Exceptions/UnableToExecute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php namespace Kodebyraaet\Prince\Exceptions;

use Exception;

class UnableToExecute extends Exception
{

}
21 changes: 21 additions & 0 deletions src/Kodebyraaet/Prince/Facades/Prince.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php namespace Kodebyraaet\Prince\Facades;

use Illuminate\Support\Facades\Facade;
use Kodebyraaet\Prince\PrinceInterface;

class Prince extends Facade
{

/**
* Get the registered name of the component.
*
* @return string
*
* @throws \RuntimeException
*/
protected static function getFacadeAccessor()
{
return PrinceInterface::class;
}

}
227 changes: 227 additions & 0 deletions src/Kodebyraaet/Prince/Prince.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<?php namespace Kodebyraaet\Prince;

use File;
use Illuminate\View\View;
use Illuminate\Http\Response;
use Response as ResponseFacade;
use Illuminate\Foundation\Application;
use Kodebyraaet\Prince\Exceptions\PrinceError;
use Kodebyraaet\Prince\Exceptions\UnableToExecute;

class Prince implements PrinceInterface
{

/**
* App Instance.
*
* @var Application
*/
private $app;

/**
* Path to the Prince executable.
*
* @var string
*/
private $executable;

/**
* Commandline arguments to send to the executable.
*
* @var string
*/
private $arguments;

/**
* Echo out error messages.
*
* @var bool
*/
private $displayErrors;

/**
* Enable UTF8
*
* @var bool
*/
private $useUtf8;

/**
* Holds the markup that should be sent to the prince executable.
*
* @var string
*/
private $markup = '';

/**
* Prince constructor.
*
* @param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;

$this->executable = getenv('PRINCE_EXECUTABLE_PATH') ? getenv('PRINCE_EXECUTABLE_PATH') : '/usr/bin/prince';

$this->arguments = ' --server -i "html" --silent -';

$this->displayErrors = true;

$this->useUtf8 = false;
}

/**
* Takes a View and returns $this to further chain methods on.
*
* @param View $view
* @return $this
*/
public function view(View $view)
{
$this->markup .= $view->render();

return $this;
}

/**
* Takes a HTML string and returns $this to further chain methods on.
*
* @param $html
* @return $this
*/
public function html($html)
{
$this->markup .= $html;

return $this;
}

/**
* Returns the generated PDF as as a Response.
*
* @return Response
*/
public function download()
{
$data = $this->generate();

$this->reset();

return ResponseFacade::stream(function () use ($data) {
$out = fopen('php://output', 'w');
fputs($out, $data);
fclose($out);
}, 200, [
'Content-Type' => 'application/pdf'
]);

}

/**
* Stores the generated PDF in at a provided path.
*
* @param $path
* @return mixed
*/
public function store($path)
{
$data = $this->generate();

File::put($path, $data);

$this->reset();

return $path;
}

/**
* Does the actual generation of a PDF. Returns raw PDF data for further
* processing.
*
* @return string
* @throws UnableToExecute
*/
private function generate()
{
$markup = ($this->useUtf8) ? utf8_decode($this->markup) : $this->markup;

// The different ways we want to communicate with the executable.
$descriptorSpec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);

// Tries to open up a process of prince at $this->executable path,
// appends $this->arguments to the executable call.
if (!is_resource($process = proc_open($this->executable . $this->arguments, $descriptorSpec, $pipes))) {
throw new UnableToExecute('Unable to open a prince executable at path ' . $this->executable . '.');
}

// Write all the markup to the pipe we can write to
// and tell the executable we're done sending markup.
fwrite($pipes[0], $markup);
fclose($pipes[0]);

// Get the pure response from the executable.
$rawData = stream_get_contents($pipes[1]);
fclose($pipes[1]);

// Check if the executable gave us any errors.
$this->parseErrors($pipes[2]);
fclose($pipes[2]);

// End the executable in it's entirety.
proc_close($process);

$this->reset();

return $rawData;
}

/**
* Checks if there are any errors in the pipeline.
*
* @param resource $pipe
* @throws PrinceError
* @return bool
*/
protected function parseErrors($pipe)
{
// While the executable is still piping data.
while (!feof($pipe)) {
$line = fgets($pipe);

if ($line) {
$tag = substr($line, 0, 3);
$body = substr($line, 4);

if ($tag == 'fin') {
// End of data.
return false;
}

$messages[] = $body;
}
}

// If we have any errors we throw a PrinceError.
if (isset($messages)) {
$errorMessage = 'Prince error: ' . implode(', ', $messages);

throw new PrinceError($errorMessage);
}

return true;
}

/**
* Prepares the object for re-use.
*/
protected function reset()
{
$this->markup = '';
}

}
Loading

0 comments on commit 88ecc20

Please sign in to comment.