mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-17 18:01:08 +00:00
✨ SVG conversion in javascript example
This commit is contained in:
+2
-1
@@ -22,7 +22,6 @@
|
||||
- [GD Image with rounded modules](./imageWithRoundedShapes.php): similar to the SVG "melted" modules example ([#215](https://github.com/chillerlan/php-qrcode/pull/215))
|
||||
- [ImageMagick with logo](./imagickWithLogo.php): a logo on top of the QR Code
|
||||
- [ImageMagick with image as background](./imagickImageAsBackground.php): an image as full size background of the QR Code
|
||||
- [ImageMagick SVG to raster conversion](./imagickConvertSVGtoPNG.php): use ImageMagick to convert SVG output to a raster image, e.g. PNG
|
||||
- [SVG with logo](./svgWithLogo.php): an SVG QR Code with embedded logo (that is also SVG)
|
||||
- [SVG with "melted" modules](./svgMeltedModules.php): an effect where the matrix appears to be like melted wax ([#127](https://github.com/chillerlan/php-qrcode/issues/127))
|
||||
- [SVG with randomly colored modules](./svgRandomColoredDots.php): a visual effect using multiple colors for the matrix modules ([#136](https://github.com/chillerlan/php-qrcode/discussions/136))
|
||||
@@ -35,6 +34,8 @@
|
||||
- [Authenticator](./authenticator.php): create a QR Code that displays an URI for a mobile authenticator (featuring [`chillerlan/php-authenticator`](https://github.com/chillerlan/php-authenticator))
|
||||
- [Interactive output](./qrcode-interactive.php): interactive demo (via [index.html](./index.html))
|
||||
- [Custom module shapes](./shapes.svg): SVG paths to customize the module shapes ([#150](https://github.com/chillerlan/php-qrcode/discussions/150))
|
||||
- [ImageMagick SVG to raster conversion](./imagickConvertSVGtoPNG.php): uses ImageMagick to convert SVG output to a raster image, e.g. PNG ([#216](https://github.com/chillerlan/php-qrcode/discussions/216))
|
||||
- [HTML canvas SVG to PNG conversion](./svgConvertViaCanvas.php): converts an SVG element or a data URI fom and image element to a PNG image via the [HTML canvas element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas), includes [a javascript class](./SVGConvert.js) which handles the conversion.
|
||||
|
||||
|
||||
Please note that the examples are self-contained, meaning that all custom classes are defined in an example file, so they don't necessarily respect the PSR-4 one file = one class principle.
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @see https://stackoverflow.com/a/28226736
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
|
||||
*
|
||||
* @created 25.09.2023
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2023 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export default class SVGConvert{
|
||||
|
||||
/**
|
||||
* Converts the SVG image from the source element into a raster image with the given size and format
|
||||
* and inserts the resulting data URI into the src attribute of the given destination image element.
|
||||
*
|
||||
* @param {SVGElement|HTMLImageElement} source
|
||||
* @param {HTMLImageElement} destination
|
||||
* @param {Number<int>} width
|
||||
* @param {Number<int>} height
|
||||
* @param {String} format
|
||||
* @return {void}
|
||||
*/
|
||||
static toDataURI(source, destination, width, height, format){
|
||||
// format is whatever the fuck the specification allows:
|
||||
// @see https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl-dev
|
||||
format = (format || 'image/png');
|
||||
|
||||
if(!destination instanceof HTMLImageElement){
|
||||
throw new Error('destination is not an instance of HTMLImageElement');
|
||||
}
|
||||
|
||||
if(source instanceof HTMLImageElement){
|
||||
source = SVGConvert.base64Decode(source);
|
||||
}
|
||||
|
||||
SVGConvert.toCanvas(source, width, height)
|
||||
.then(canvas => destination.src = canvas.toDataURL(format))
|
||||
.catch(error => console.log('(╯°□°)╯彡┻━┻ ', error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the given SVG source on a canvas of the given size and returns the canvas element
|
||||
*
|
||||
* @param {SVGElement} source
|
||||
* @param {Number<int>} width
|
||||
* @param {Number<int>} height
|
||||
* @return {Promise<HTMLCanvasElement>}
|
||||
*/
|
||||
static toCanvas(source, width, height){
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if(!source instanceof SVGElement){
|
||||
throw new Error('source is not an instance of SVGElement');
|
||||
}
|
||||
|
||||
// the source SVG element needs to be visible
|
||||
source.style.display = 'inline-block';
|
||||
source.style.visibility = 'visible';
|
||||
|
||||
// add/fix the width/height of the SVG element
|
||||
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=700533#c39
|
||||
source.width.baseVal.valueAsString = width;
|
||||
source.height.baseVal.valueAsString = height;
|
||||
|
||||
try{
|
||||
// stringify the SVG element and create an object URL for it
|
||||
let svgString = new XMLSerializer().serializeToString(source);
|
||||
let svgBlob = new Blob([svgString], {type: 'image/svg+xml;charset=utf-8'});
|
||||
let tempUrl = URL.createObjectURL(svgBlob);
|
||||
// create a temporary image
|
||||
let tempImg = new Image(width, height);
|
||||
// trigger the onLoad event with the temporary blob URL
|
||||
tempImg.src = tempUrl;
|
||||
// process the conversion in the onLoad event
|
||||
tempImg.onload = function(){
|
||||
// create a canvas element to draw the SVG on
|
||||
let canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.getContext('2d').drawImage(tempImg, 0, 0);
|
||||
// revoke the temporary blob
|
||||
URL.revokeObjectURL(tempUrl);
|
||||
// return the PNG image as data URI
|
||||
resolve(canvas);
|
||||
};
|
||||
}
|
||||
catch(error){
|
||||
reject(error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given SVG base64 data URI into a DOM element
|
||||
*
|
||||
* @param {HTMLImageElement} image
|
||||
* @return {SVGElement}
|
||||
*/
|
||||
static base64Decode(image){
|
||||
|
||||
if(!image instanceof HTMLImageElement){
|
||||
throw new Error('image is not an instance of HTMLImageElement');
|
||||
}
|
||||
|
||||
if(!image.src || !image.src.includes('base64,')){
|
||||
throw new Error('invalid image source');
|
||||
}
|
||||
|
||||
return new DOMParser().parseFromString(atob(image.src.split(',')[1]), 'image/svg+xml').firstChild;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* SVG to PNG conversion via javascript canvas example
|
||||
*
|
||||
* @created 25.09.2023
|
||||
* @author smiley <smiley@chillerlan.net>
|
||||
* @copyright 2023 smiley
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
|
||||
use chillerlan\QRCode\Data\QRMatrix;
|
||||
use chillerlan\QRCode\Output\QROutputInterface;
|
||||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
$options = new QROptions;
|
||||
|
||||
$options->version = 7;
|
||||
$options->outputType = QROutputInterface::MARKUP_SVG;
|
||||
$options->outputBase64 = false;
|
||||
$options->svgAddXmlHeader = false;
|
||||
$options->drawLightModules = false;
|
||||
$options->markupDark = '';
|
||||
$options->markupLight = '';
|
||||
$options->drawCircularModules = true;
|
||||
$options->circleRadius = 0.4;
|
||||
$options->connectPaths = true;
|
||||
|
||||
$options->keepAsSquare = [
|
||||
QRMatrix::M_FINDER_DARK,
|
||||
QRMatrix::M_FINDER_DOT,
|
||||
QRMatrix::M_ALIGNMENT_DARK,
|
||||
];
|
||||
|
||||
$options->svgDefs = '
|
||||
<linearGradient id="rainbow" x1="1" y2="1">
|
||||
<stop stop-color="#e2453c" offset="0"/>
|
||||
<stop stop-color="#e07e39" offset="0.2"/>
|
||||
<stop stop-color="#e5d667" offset="0.4"/>
|
||||
<stop stop-color="#51b95b" offset="0.6"/>
|
||||
<stop stop-color="#1e72b7" offset="0.8"/>
|
||||
<stop stop-color="#6f5ba7" offset="1"/>
|
||||
</linearGradient>
|
||||
<style><![CDATA[
|
||||
.dark{fill: url(#rainbow);}
|
||||
]]></style>';
|
||||
|
||||
|
||||
$qrcode = (new QRCode($options))->addByteSegment('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
|
||||
|
||||
// render the SVG
|
||||
$svg_raw = $qrcode->render();
|
||||
// switch to base64 output
|
||||
$options->outputBase64 = true;
|
||||
// render base64
|
||||
$svg_base64 = $qrcode->render();
|
||||
|
||||
// dump the output
|
||||
header('Content-type: text/html');
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>QRCode javascript canvas conversion example</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- raw SVG input -->
|
||||
<div>
|
||||
<?php echo $svg_raw ?>
|
||||
<img id="qr-svg-dest" />
|
||||
</div>
|
||||
<!-- base64 encoded datd URI from img tag -->
|
||||
<div>
|
||||
<img id="qr-svg-base64" src="<?php echo $svg_base64 ?>" style="width: 300px; height: 300px;" />
|
||||
<img id="qr-svg-base64-dest" />
|
||||
</div>
|
||||
<script type="module">
|
||||
import SVGConvert from './SVGConvert.js';
|
||||
|
||||
// SVG DOM element
|
||||
SVGConvert.toDataURI(document.querySelector('svg.qr-svg'), document.getElementById('qr-svg-dest'), 300, 300, 'image/jpeg');
|
||||
// base64 data URI in image element
|
||||
SVGConvert.toDataURI(document.getElementById('qr-svg-base64'), document.getElementById('qr-svg-base64-dest'), 300, 300);
|
||||
</script>
|
||||
</html>
|
||||
Reference in New Issue
Block a user