First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2010 Matthieu Bontemps
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.

View file

@ -0,0 +1,47 @@
# Snappy
Snappy is a PHP5 library allowing thumbnail, snapshot or PDF generation from a url or a html page.
It uses the excellent webkit-based [wkhtmltopdf and wkhtmltoimage](http://code.google.com/p/wkhtmltopdf/)
available on OSX, linux, windows.
You will have to download wkhtmltopdf 0.10.0 >= rc2 in order to use Snappy.
## Usage
```php
<?php
require_once '/path/to/snappy/src/autoload.php';
use Knp\Snappy\Pdf;
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
// or you can do it in two steps
$snappy = new Pdf();
$snappy->setBinary('/usr/local/bin/wkhtmltopdf');
// Display the resulting image in the browser
// by setting the Content-type header to jpg
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="file.pdf"');
echo $snappy->getOutput('http://www.github.com');
// .. or simply save the PDF to a file
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
$snappy->generateFromHtml('<h1>Bill</h1><p>You owe me money, dude.</p>', '/tmp/bill-123.pdf');
// Pass options to snappy
// Type wkhtmltopdf -H to see the list of options
$snappy = new Pdf('/usr/local/bin/wkhtmltopdf');
$snappy->setOption('disable-javascript', true);
$snappy->setOption('no-background', true);
$snappy->setOption('allow', array('/path1', '/path2'));
$snappy->setOption('cookie', array('key' => 'value', 'key2' => 'value2'));
```
## Credits
Snappy has been originally developed by the [KnpLabs](http://knplabs.com) team.

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="src/autoload.php"
>
<testsuites>
<testsuite name="Snappy Test Suite">
<directory>./test</directory>
</testsuite>
</testsuites>
</phpunit>

View file

@ -0,0 +1,534 @@
<?php
namespace Knp\Snappy;
/**
* Base generator class for medias
*
* @package Snappy
*
* @author Matthieu Bontemps <matthieu.bontemps@knplabs.com>
* @author Antoine Hérault <antoine.herault@knplabs.com>
*/
abstract class AbstractGenerator implements GeneratorInterface
{
private $binary;
private $options = array();
private $defaultExtension;
/**
* Constructor
*
* @param string $binary
* @param array $options
*/
public function __construct($binary, array $options = array())
{
$this->configure();
$this->setBinary($binary);
$this->setOptions($options);
}
/**
* This method must configure the media options
*
* @see AbstractGenerator::addOption()
*/
abstract protected function configure();
/**
* Sets the default extension.
* Useful when letting Snappy deal with file creation
*
* @param string $defaultExtension
*/
public function setDefaultExtension($defaultExtension)
{
$this->defaultExtension = $defaultExtension;
}
/**
* Gets the default extension
*
* @return $string
*/
public function getDefaultExtension()
{
return $this->defaultExtension;
}
/**
* Sets an option. Be aware that option values are NOT validated and that
* it is your responsibility to validate user inputs
*
* @param string $name The option to set
* @param mixed $value The value (NULL to unset)
*/
public function setOption($name, $value)
{
if (!array_key_exists($name, $this->options)) {
throw new \InvalidArgumentException(sprintf('The option \'%s\' does not exist.', $name));
}
$this->options[$name] = $value;
}
/**
* Sets an array of options
*
* @param array $options An associative array of options as name/value
*/
public function setOptions(array $options)
{
foreach ($options as $name => $value) {
$this->setOption($name, $value);
}
}
/**
* Returns all the options
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* {@inheritDoc}
*/
public function generate($input, $output, array $options = array(), $overwrite = false)
{
if (null === $this->binary) {
throw new \LogicException(
'You must define a binary prior to conversion.'
);
}
$this->prepareOutput($output, $overwrite);
$command = $this->getCommand($input, $output, $options);
list($status, $stdout, $stderr) = $this->executeCommand($command);
$this->checkProcessStatus($status, $stdout, $stderr, $command);
$this->checkOutput($output, $command);
}
/**
* {@inheritDoc}
*/
public function generateFromHtml($html, $output, array $options = array(), $overwrite = false)
{
$filename = $this->createTemporaryFile($html, 'html');
$result = $this->generate($filename, $output, $options, $overwrite);
$this->unlink($filename);
}
/**
* {@inheritDoc}
*/
public function getOutput($input, array $options = array())
{
$filename = $this->createTemporaryFile(null, $this->getDefaultExtension());
$this->generate($input, $filename, $options);
$result = $this->getFileContents($filename);
$this->unlink($filename);
return $result;
}
/**
* {@inheritDoc}
*/
public function getOutputFromHtml($html, array $options = array())
{
$filename = $this->createTemporaryFile($html, 'html');
$result = $this->getOutput($filename, $options);
$this->unlink($filename);
return $result;
}
/**
* Defines the binary
*
* @param string $binary The path/name of the binary
*/
public function setBinary($binary)
{
$this->binary = $binary;
}
/**
* Returns the binary
*
* @return string
*/
public function getBinary()
{
return $this->binary;
}
/**
* Returns the command for the given input and output files
*
* @param string $input The input file
* @param string $output The ouput file
* @param array $options An optional array of options that will be used
* only for this command
*
* @return string
*/
public function getCommand($input, $output, array $options = array())
{
$options = $this->mergeOptions($options);
return $this->buildCommand($this->binary, $input, $output, $options);
}
/**
* Adds an option
*
* @param string $name The name
* @param mixed $default An optional default value
*/
protected function addOption($name, $default = null)
{
if (array_key_exists($name, $this->options)) {
throw new \InvalidArgumentException(sprintf('The option \'%s\' already exists.', $name));
}
$this->options[$name] = $default;
}
/**
* Adds an array of options
*
* @param array $options
*/
protected function addOptions(array $options)
{
foreach ($options as $name => $default) {
$this->addOption($name, $default);
}
}
/**
* Merges the given array of options to the instance options and returns
* the result options array. It does NOT change the instance options.
*
* @param array $options
*
* @return array
*/
protected function mergeOptions(array $options)
{
$mergedOptions = $this->options;
foreach ($options as $name => $value) {
if (!array_key_exists($name, $mergedOptions)) {
throw new \InvalidArgumentException(sprintf('The option \'%s\' does not exist.', $name));
}
$mergedOptions[$name] = $value;
}
return $mergedOptions;
}
/**
* Checks the specified output
*
* @param string $output The output filename
* @param string $command The generation command
*
* @throws RuntimeException if the output file generation failed
*/
protected function checkOutput($output, $command)
{
// the output file must exist
if (!$this->fileExists($output)) {
throw new \RuntimeException(sprintf(
'The file \'%s\' was not created (command: %s).',
$output, $command
));
}
// the output file must not be empty
if (0 === $this->filesize($output)) {
throw new \RuntimeException(sprintf(
'The file \'%s\' was created but is empty (command: %s).',
$output, $command
));
}
}
/**
* Checks the process return status
*
* @param int $status The exit status code
* @param string $stdout The stdout content
* @param string $stderr The stderr content
* @param string $command The run command
*
* @throws RuntimeException if the output file generation failed
*/
protected function checkProcessStatus($status, $stdout, $stderr, $command)
{
if (0 !== $status and '' !== $stderr) {
throw new \RuntimeException(sprintf(
'The exit status code \'%s\' says something went wrong:'."\n"
.'stderr: "%s"'."\n"
.'stdout: "%s"'."\n"
.'command: %s.',
$status, $stderr, $stdout, $command
));
}
}
/**
* Creates a temporary file.
* The file is not created if the $content argument is null
*
* @param string $content Optional content for the temporary file
* @param string $extension An optional extension for the filename
*
* @return string The filename
*/
protected function createTemporaryFile($content = null, $extension = null)
{
$filename = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('knp_snappy');
if (null !== $extension) {
$filename .= '.'.$extension;
}
if (null !== $content) {
file_put_contents($filename, $content);
}
return $filename;
}
/**
* Builds the command string
*
* @param string $binary The binary path/name
* @param string $input Url or file location of the page to process
* @param string $output File location to the image-to-be
* @param array $options An array of options
*
* @return string
*/
protected function buildCommand($binary, $input, $output, array $options = array())
{
$command = $binary;
foreach ($options as $key => $option) {
if (null !== $option && false !== $option) {
if (true === $option) {
$command .= ' --'.$key;
} elseif (is_array($option)) {
if ($this->isAssociativeArray($option)) {
foreach ($option as $k => $v) {
$command .= ' --'.$key.' '.escapeshellarg($k).' '.escapeshellarg($v);
}
} else {
foreach ($option as $v) {
$command .= " --".$key." ".escapeshellarg($v);
}
}
} else {
$command .= ' --'.$key." ".escapeshellarg($option);
}
}
}
$command .= ' '.escapeshellarg($input).' '.escapeshellarg($output);;
return $command;
}
/**
* Return true if the array is an associative array
* and not an indexed array
*
* @param array $array
* @return boolean
*/
protected function isAssociativeArray(array $array)
{
return (bool)count(array_filter(array_keys($array), 'is_string'));
}
/**
* Executes the given command via shell and returns the complete output as
* a string
*
* @param string $command
*
* @return array(status, stdout, stderr)
*/
protected function executeCommand($command)
{
$stdout = $stderr = $status = null;
$descriptorspec = array(
1 => array('pipe', 'w'), // stdout is a pipe that the child will write to
2 => array('pipe', 'w') // stderr is a pipe that the child will write to
);
$process = proc_open($command, $descriptorspec, $pipes);
if (is_resource($process)) {
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// 2 => readable handle connected to child stderr
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$status = proc_close($process);
}
return array($status, $stdout, $stderr);
}
/**
* Prepares the specified output
*
* @param string $filename The output filename
* @param boolean $overwrite Whether to overwrite the file if it already
* exist
*/
protected function prepareOutput($filename, $overwrite)
{
$directory = dirname($filename);
if ($this->fileExists($filename)) {
if (!$this->isFile($filename)) {
throw new \InvalidArgumentException(sprintf(
'The output file \'%s\' already exists and it is a %s.',
$filename, $this->isDir($filename) ? 'directory' : 'link'
));
} elseif (false === $overwrite) {
throw new \InvalidArgumentException(sprintf(
'The output file \'%s\' already exists.',
$filename
));
} elseif (!$this->unlink($filename)) {
throw new \RuntimeException(sprintf(
'Could not delete already existing output file \'%s\'.',
$filename
));
}
} elseif (!$this->isDir($directory) && !$this->mkdir($directory)) {
throw new \RuntimeException(sprintf(
'The output file\'s directory \'%s\' could not be created.',
$directory
));
}
}
/**
* Wrapper for the "file_get_contents" function
*
* @param string $filename
*
* @return string
*/
protected function getFileContents($filename)
{
return file_get_contents($filename);
}
/**
* Wrapper for the "file_exists" function
*
* @param string $filename
*
* @return boolean
*/
protected function fileExists($filename)
{
return file_exists($filename);
}
/**
* Wrapper for the "is_file" method
*
* @param string $filename
*
* @return boolean
*/
protected function isFile($filename)
{
return is_file($filename);
}
/**
* Wrapper for the "filesize" function
*
* @param string $filename
*
* @return integer or FALSE on failure
*/
protected function filesize($filename)
{
return filesize($filename);
}
/**
* Wrapper for the "unlink" function
*
* @param string $filename
*
* @return boolean
*/
protected function unlink($filename)
{
return unlink($filename);
}
/**
* Wrapper for the "is_dir" function
*
* @param string $filename
*
* @return boolean
*/
protected function isDir($filename)
{
return is_dir($filename);
}
/**
* Wrapper for the mkdir function
*
* @param string $pathname
*
* @return boolean
*/
protected function mkdir($pathname)
{
return mkdir($pathname, 0777, true);
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Knp\Snappy;
/**
* Interface for the media generators
*
* @package Snappy
*
* @author Matthieu Bontemps <matthieu.bontemps@knplabs.com>
* @author Antoine Hérault <antoine.herault@knplabs.com>*
*/
interface GeneratorInterface
{
/**
* Generates the output media file from the specified input HTML file
*
* @param string $input The input HTML filename or URL
* @param string $output The output media filename
* @param array $options An array of options for this generation only
* @param bool $overwrite Overwrite the file if it exists. If not, throw an InvalidArgumentException
*/
function generate($input, $output, array $options = array(), $overwrite = false);
/**
* Generates the output media file from the given HTML
*
* @param string $html The HTML to be converted
* @param string $output The output media filename
* @param array $options An array of options for this generation only
* @param bool $overwrite Overwrite the file if it exists. If not, throw an InvalidArgumentException
*/
function generateFromHtml($html, $output, array $options = array(), $overwrite = false);
/**
* Returns the output of the media generated from the specified input HTML
* file
*
* @param string $input The input HTML filename or URL
* @param array $options An array of options for this output only
*
* @return string
*/
function getOutput($input, array $options = array());
/**
* Returns the output of the media generated from the given HTML
*
* @param string $html The HTML to be converted
* @param array $options An array of options for this output only
*
* @return string
*/
function getOutputFromHtml($html, array $options = array());
}

View file

@ -0,0 +1,78 @@
<?php
namespace Knp\Snappy;
/**
* Use this class to create a snapshot / thumbnail from a HTML page
*
* @package Snappy
*
* @author Matthieu Bontemps <matthieu.bontemps@knplabs.com>
* @author Antoine Hérault <antoine.herault@knplabs.com>
*/
class Image extends AbstractGenerator
{
/**
* {@inheritDoc}
*/
public function __construct($binary = null, array $options = array())
{
$this->setDefaultExtension('jpg');
parent::__construct($binary, $options);
}
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOptions(array(
'allow' => null, // Allow the file or files from the specified folder to be loaded (repeatable)
'checkbox-checked-svg' => null, // Use this SVG file when rendering checked checkboxes
'checked-svg' => null, // Use this SVG file when rendering unchecked checkboxes
'cookie' => array(), // Set an additional cookie (repeatable)
'cookie-jar' => null, // Read and write cookies from and to the supplied cookie jar file
'crop-h' => null, // Set height for croping
'crop-w' => null, // Set width for croping
'crop-x' => null, // Set x coordinate for croping (default 0)
'crop-y' => null, // Set y coordinate for croping (default 0)
'custom-header' => array(), // Set an additional HTTP header (repeatable)
'custom-header-propagation' => null, // Add HTTP headers specified by --custom-header for each resource request.
'no-custom-header-propagation' => null, // Do not add HTTP headers specified by --custom-header for each resource request.
'debug-javascript' => null, // Show javascript debugging output
'no-debug-javascript' => null, // Do not show javascript debugging output (default)
'encoding' => null, // Set the default text encoding, for input
'format' => $this->getDefaultExtension(), // Output format
'height' => null, // Set screen height (default is calculated from page content) (default 0)
'images' => null, // Do load or print images (default)
'no-images' => null, // Do not load or print images
'disable-javascript' => null, // Do not allow web pages to run javascript
'enable-javascript' => null, // Do allow web pages to run javascript (default)
'javascript-delay' => null, // Wait some milliseconds for javascript finish (default 200)
'load-error-handling' => null, // Specify how to handle pages that fail to load: abort, ignore or skip (default abort)
'disable-local-file-access' => null, // Do not allowed conversion of a local file to read in other local files, unless explecitily allowed with allow
'enable-local-file-access' => null, // Allowed conversion of a local file to read in other local files. (default)
'minimum-font-size' => null, // Minimum font size
'password' => null, // HTTP Authentication password
'disable-plugins' => null, // Disable installed plugins (default)
'enable-plugins' => null, // Enable installed plugins (plugins will likely not work)
'post' => array(), // Add an additional post field
'post-file' => array(), // Post an additional file
'proxy' => null, // Use a proxy
'quality' => null, // Output image quality (between 0 and 100) (default 94)
'radiobutton-checked-svg' => null, // Use this SVG file when rendering checked radiobuttons
'radiobutton-svg' => null, // Use this SVG file when rendering unchecked radiobuttons
'run-script' => null, // Run this additional javascript after the page is done loading (repeatable)
'disable-smart-width' => null, // Use the specified width even if it is not large enough for the content
'stop-slow-scripts' => null, // Stop slow running javascripts
'no-stop-slow-scripts' => null, // Do not stop slow running javascripts (default)
'transparent' => null, // Make the background transparrent in pngs *
'user-style-sheet' => null, // Specify a user style sheet, to load with every page
'username' => null, // HTTP Authentication username
'width' => null, // Set screen width (default is 1024)
'window-status' => null, // Wait until window.status is equal to this string before rendering page
'zoom' => null, // Use this zoom factor (default 1)
));
}
}

View file

@ -0,0 +1,144 @@
<?php
namespace Knp\Snappy;
/**
* Use this class to transform a html/a url to a pdf
*
* @package Snappy
*
* @author Matthieu Bontemps <matthieu.bontemps@knplabs.com>
* @author Antoine Hérault <antoine.herault@knplabs.com>
*/
class Pdf extends AbstractGenerator
{
/**
* {@inheritDoc}
*/
public function __construct($binary = null, array $options = array())
{
$this->setDefaultExtension('pdf');
parent::__construct($binary, $options);
}
/**
* {@inheritDoc}
*/
protected function configure()
{
$this->addOptions(array(
'ignore-load-errors' => null, // old v0.9
'lowquality' => true,
'collate' => null,
'no-collate' => null,
'cookie-jar' => null,
'copies' => null,
'dpi' => null,
'extended-help' => null,
'grayscale' => null,
'help' => null,
'htmldoc' => null,
'image-dpi' => null,
'image-quality' => null,
'manpage' => null,
'margin-bottom' => null,
'margin-left' => null,
'margin-right' => null,
'margin-top' => null,
'orientation' => null,
'output-format' => null,
'page-height' => null,
'page-size' => null,
'page-width' => null,
'no-pdf-compression' => null,
'quiet' => null,
'read-args-from-stdin' => null,
'title' => null,
'use-xserver' => null,
'version' => null,
'dump-default-toc-xsl' => null,
'dump-outline' => null,
'outline' => null,
'no-outline' => null,
'outline-depth' => null,
'allow' => null,
'background' => null,
'no-background' => null,
'checkbox-checked-svg' => null,
'checkbox-svg' => null,
'cookie' => null,
'custom-header' => null,
'custom-header-propagation' => null,
'no-custom-header-propagation' => null,
'debug-javascript' => null,
'no-debug-javascript' => null,
'default-header' => null,
'encoding' => null,
'disable-external-links' => null,
'enable-external-links' => null,
'disable-forms' => null,
'enable-forms' => null,
'images' => null,
'no-images' => null,
'disable-internal-links' => null,
'enable-internal-links' => null,
'disable-javascript' => null,
'enable-javascript' => null,
'javascript-delay' => null,
'load-error-handling' => null,
'disable-local-file-access' => null,
'enable-local-file-access' => null,
'minimum-font-size' => null,
'exclude-from-outline' => null,
'include-in-outline' => null,
'page-offset' => null,
'password' => null,
'disable-plugins' => null,
'enable-plugins' => null,
'post' => null,
'post-file' => null,
'print-media-type' => null,
'no-print-media-type' => null,
'proxy' => null,
'radiobutton-checked-svg' => null,
'radiobutton-svg' => null,
'run-script' => null,
'disable-smart-shrinking' => null,
'enable-smart-shrinking' => null,
'stop-slow-scripts' => null,
'no-stop-slow-scripts' => null,
'disable-toc-back-links' => null,
'enable-toc-back-links' => null,
'user-style-sheet' => null,
'username' => null,
'window-status' => null,
'zoom' => null,
'footer-center' => null,
'footer-font-name' => null,
'footer-font-size' => null,
'footer-html' => null,
'footer-left' => null,
'footer-line' => null,
'no-footer-line' => null,
'footer-right' => null,
'footer-spacing' => null,
'header-center' => null,
'header-font-name' => null,
'header-font-size' => null,
'header-html' => null,
'header-left' => null,
'header-line' => null,
'no-header-line' => null,
'header-right' => null,
'header-spacing' => null,
'replace' => null,
'disable-dotted-lines' => null,
'toc-header-text' => null,
'toc-level-indentation' => null,
'disable-toc-links' => null,
'toc-text-size-shrink' => null,
'xsl-style-sheet' => null,
));
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* Simple autoloader that follow the PHP Standards Recommendation #0 (PSR-0)
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md for more informations.
*
* Code inspired from the SplClassLoader RFC
* @see https://wiki.php.net/rfc/splclassloader#example_implementation
*/
spl_autoload_register(function($className) {
$package = 'Knp\\Snappy';
$className = ltrim($className, '\\');
if (0 === strpos($className, $package)) {
$fileName = __DIR__ . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
if (is_file($fileName)) {
require $fileName;
return true;
}
}
return false;
});