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,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;
});