Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate png and pdf from svg #1428

Merged
merged 3 commits into from
Aug 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
plessbd marked this conversation as resolved.
Show resolved Hide resolved
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