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

refactor: Convert to PNG without using files #394

Merged
Merged
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
86 changes: 60 additions & 26 deletions src/card.php
Original file line number Diff line number Diff line change
Expand Up @@ -500,50 +500,84 @@ function convertSvgToPng(string $svg): string
$svg = preg_replace("/(animation: currstreak[^;'\"]+)/m", "font-size: 28px;", $svg);
$svg = preg_replace("/<a \X*?>(\X*?)<\/a>/m", '\1', $svg);

// save svg to random file
$filename = uniqid();
file_put_contents("$filename.svg", $svg);
// escape svg for shell
$svg = escapeshellarg($svg);

// `--pipe`: read input from pipe (stdin)
// `--export-filename -`: write output to stdout
// `-w 495 -h 195`: set width and height of the output image
// `--export-type png`: set the output format to PNG
$cmd = "echo {$svg} | inkscape --pipe --export-filename - -w 495 -h 195 --export-type png";

// convert svg to png
$out = shell_exec("inkscape -w 495 -h 195 {$filename}.svg -o {$filename}.png"); // skipcq: PHP-A1009
if ($out !== null) {
throw new Exception("Error converting SVG to PNG: $out");
$png = shell_exec($cmd); // skipcq: PHP-A1009

// check if the conversion was successful
if (empty($png)) {
// `2>&1`: redirect stderr to stdout
$error = shell_exec("$cmd 2>&1"); // skipcq: PHP-A1009
throw new Exception("Failed to convert SVG to PNG: {$error}", 500);
}

// read png data and delete temporary files
$png = file_get_contents("{$filename}.png");
unlink("{$filename}.svg");
unlink("{$filename}.png");
// return the generated png
return $png;
}

/**
* Set headers and echo response based on type
* Return headers and response based on type
*
* @param string|array $output The stats (array) or error message (string) to display
*
* @return array The Content-Type header and the response body, and status code in case of an error
*/
function renderOutput(string|array $output, int $responseCode = 200): void
function generateOutput(string|array $output): array
{
$requestedType = $_REQUEST["type"] ?? "svg";
http_response_code($responseCode);

// output JSON data
if ($requestedType === "json") {
// set content type to JSON
header("Content-Type: application/json");
// generate array from output
$data = gettype($output) === "string" ? ["error" => $output] : $output;
// output as JSON
echo json_encode($data);
return [
"contentType" => "application/json",
"body" => json_encode($data),
];
}
// output SVG or PNG card
else {
// set content type to SVG or PNG
header("Content-Type: image/" . ($requestedType === "png" ? "png" : "svg+xml"));
// render SVG card
$svg = gettype($output) === "string" ? generateErrorCard($output) : generateCard($output);
// output PNG if PNG is requested, otherwise output SVG
echo $requestedType === "png" ? convertSvgToPng($svg) : $svg;
// Generate SVG card
$svg = gettype($output) === "string" ? generateErrorCard($output) : generateCard($output);
// output PNG card
if ($requestedType === "png") {
try {
$png = convertSvgToPng($svg);
return [
"contentType" => "image/png",
"body" => $png,
];
} catch (Exception $e) {
return [
"contentType" => "image/svg+xml",
"status" => 500,
"body" => generateErrorCard($e->getMessage()),
];
}
}
exit();
// output SVG card
return [
"contentType" => "image/svg+xml",
"body" => $svg,
];
}

/**
* Set headers and output response
*
* @param string|array $output The Content-Type header and the response body
* @param int $responseCode The HTTP response code to send
*/
function renderOutput(string|array $output, int $responseCode = 200): void
{
$response = generateOutput($output);
http_response_code($response["status"] ?? $responseCode);
header("Content-Type: {$response["contentType"]}");
exit($response["body"]);
}