Skip to content

Commit

Permalink
Generate png and pdf from svg (#1428)
Browse files Browse the repository at this point in the history
  • Loading branch information
plessbd authored Aug 21, 2020
1 parent 03f4915 commit c479fc1
Show file tree
Hide file tree
Showing 3 changed files with 1,934 additions and 1,981 deletions.
2 changes: 1 addition & 1 deletion docs/software-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ added with this command for CentOS 7:
php-pear-MDB2 php-pear-MDB2-Driver-mysql \
java-1.8.0-openjdk java-1.8.0-openjdk-devel \
mariadb-server mariadb cronie logrotate \
ghostscript php-mbstring php-pecl-apcu jq \
perl-Image-ExifTool php-mbstring php-pecl-apcu jq \
chromium-headless librsvg2-tools

**NOTE**: After installing Apache and MySQL you must make sure that they
Expand Down
137 changes: 45 additions & 92 deletions libraries/charting.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,54 +50,40 @@ function exportHighchart(
$template = file_get_contents($html_dir . "/highchart_template.html");

$template = str_replace('_html_dir_', $html_dir, $template);
$template = str_replace('_chartOptions_', json_encode($chartConfig), $template);

$template = str_replace('_width_', $effectiveWidth, $template);
$template = str_replace('_height_', $effectiveHeight, $template);
$globalChartOptions = array('timezone' => date_default_timezone_get());
if ($globalChartConfig !== null) {
$globalChartOptions = array_merge($globalChartOptions, $globalChartConfig);
}
$template = str_replace('_globalChartOptions_', json_encode($globalChartOptions), $template);
$template = str_replace('_width_',$effectiveWidth, $template);
$template = str_replace('_height_',$effectiveHeight, $template);
$data = getScreenFromChromium($template, $effectiveWidth, $effectiveHeight, $format === 'pdf' ? 'svg' : $format);
if($format === 'pdf'){
$data = svg2pdf($data, round($width / 90.0 * 72.0), round($height / 90.0 * 72.0), $fileMetadata);
$template = str_replace('_chartOptions_', json_encode($chartConfig), $template);
$svg = getSvgFromChromium($template, $effectiveWidth, $effectiveHeight);
switch($format){
case 'png':
return convertSvg($svg, 'png', $effectiveWidth, $effectiveHeight, $fileMetadata);
break;
case 'pdf':
return convertSvg($svg, 'pdf', round($width / 90.0 * 72.0), round($height / 90.0 * 72.0), $fileMetadata);
break;
default:
return $svg;
}
return $data;
}

/**
* Use Chromium to generate png or svg.
*
* For svg generation uses chromium repl
* Use Chromium to generate svg.
*
* @param string $html html that should be used by chromium
* @param int $width desired width of output
* @param int $height desired height of output
* @param string $format of output (png or svg)
* @param array $pdfExtras extras used for pdf output
*
* @returns string contents of desired output
* @returns string svg
*
* @throws \Exception on invalid format, command execution failure, or non zero exit status
*/
function getScreenFromChromium($html, $width, $height, $format){
$repl = '';
$outputFile = null;
if ($format == 'svg'){
$repl = 'chart.getSVG(inputChartOptions);';
$outputType = '-repl';
}
elseif ($format == 'png'){
$outputFile = tempnam(sys_get_temp_dir(), 'xdmod-chromiumScreenshot-');
if ($outputFile === false) {
throw \Exception('Error creating temporary png file for chromium');
}
$outputType = '--screenshot=' . $outputFile;
}
else {
throw new \Exception('Invalid format "' . $format . '" specified, must be one of svg, pdf, or png.');
}
function getSvgFromChromium($html, $width, $height){

// Chromium requires the file to have a .html extension
// cant use datauri as it will not execute embdeeded javascript
Expand All @@ -118,7 +104,7 @@ function getScreenFromChromium($html, $width, $height, $format){
'--window-size=' . $width . ',' . $height,
'--disable-extensions',
'--incognito',
$outputType,
'-repl',
$tmpHtmlFile
);
$command = $chromiumPath . ' ' . implode(' ', $chromiumOptions);
Expand All @@ -131,13 +117,11 @@ function getScreenFromChromium($html, $width, $height, $format){
$process = proc_open($command, $descriptor_spec, $pipes);
if (!is_resource($process)) {
@unlink($tmpHtmlFile);
@unlink($outputFile);
throw new \Exception('Unable execute command: "'. $command . '". Details: ' . print_r(error_get_last(), true));
}
else {
fwrite($pipes[0], $repl);
fclose($pipes[0]);
}
fwrite($pipes[0], 'chart.getSVG(inputChartOptions);');
fclose($pipes[0]);

$out = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);
fclose($pipes[1]);
Expand All @@ -147,88 +131,57 @@ function getScreenFromChromium($html, $width, $height, $format){
@unlink($tmpHtmlFile);

if ($return_value != 0) {
@unlink($outputFile);
throw new \Exception('Unable execute command: "'. $command . '". Details: ' . $err);
}
if (!empty($repl)){
$result = json_decode(substr($out, 4, -6), true);
$data = $result['result']['value'];
}
else{
$data = file_get_contents($outputFile);
@unlink($outputFile);
}
return $data;
$result = json_decode(substr($out, 4, -6), true);
return $result['result']['value'];
}

/**
* Use rsvg-convert to convert svg to pdf
* Use rsvg-convert to convert svg
*
* @param string $svgData string of the SVG
* @param int $width the new paper size in postscript points (72 ppi).
* @param int $height the new paper size in postscript points (72 ppi).
* @param array $metaData array containing metadata fields
* @return string contents of pdf
* @throws Exception when unable to execute or non-zero return code
*/

function svg2pdf($svgData, $width, $height, $metaData){
$command = 'rsvg-convert -w ' .$width. ' -h '.$height.' -f pdf';
$pipes = array();
$descriptor_spec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
$process = proc_open($command, $descriptor_spec, $pipes);
if (!is_resource($process)) {
throw new \Exception('Unable execute command: "'. $command . '". Details: ' . print_r(error_get_last(), true));
}
fwrite($pipes[0], $svgData);
fclose($pipes[0]);
$out = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);

fclose($pipes[1]);
fclose($pipes[2]);

$return_value = proc_close($process);

if ($return_value != 0) {
throw new \Exception("$command returned $return_value, stdout: $out stderr: $err");
}
return getPdfWithMetadata($out, $metaData);
}

/**
* Use exiftool to set document metadata.
*
* @param string $pdf string representation of PDF file
* @param int $width desired width in proper size for format
* @param int $height desired height in proper size for format
* @param array $docmeta array containing metadata fields
*
* @return string PDF document
* @return string contents of the requested format
* @throws Exception when unable to execute or non-zero return code
*/
function getPdfWithMetadata($pdf, $docmeta){

function convertSvg($svgData, $format, $width, $height, $docmeta){
$author = isset($docmeta['author']) ? addcslashes($docmeta['author'], "()\n\\") : 'XDMoD';
$subject = isset($docmeta['subject']) ? addcslashes($docmeta['subject'], "()\n\\") : 'XDMoD chart';
$title = isset($docmeta['title']) ? addcslashes($docmeta['title'], "()\n\\") :'XDMoD PDF chart export';
$creator = addcslashes('XDMoD ' . OPEN_XDMOD_VERSION, "()\n\\");

$command = "exiftool -Title='$title' -Author='$author' -Subject='$subject' -Creator='$creator' -o - -";
switch($format){
case 'png':
$exifArgs = "-Title='$title' -Author='$author' -Description='$subject' -Source='$creator'";
break;
case 'pdf':
$exifArgs = "-Title='$title' -Author='$author' -Subject='$subject' -Creator='$creator'";
break;
default:
return $svgData;
}

$rsvgCommand = 'rsvg-convert -w ' .$width. ' -h '.$height.' -f ' . $format;
$exifCommand = 'exiftool ' . $exifArgs . ' -o - -';

$command = $rsvgCommand . ' | ' . $exifCommand;
$pipes = array();
$descriptor_spec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
$process = proc_open($command, $descriptor_spec, $pipes);

if (!is_resource($process)) {
throw new \Exception('Unable execute command: "'. $command . '". Details: ' . print_r(error_get_last(), true));
}
fwrite($pipes[0], $pdf);
fwrite($pipes[0], $svgData);
fclose($pipes[0]);

$out = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);

Expand Down
Loading

0 comments on commit c479fc1

Please sign in to comment.