-
-
Notifications
You must be signed in to change notification settings - Fork 320
QR SVG RENDER with Hearts #150
-
Hi! I wanted to check with the group and see if this would be also beneficial to any of them.
1.) We want to use Chillerlan solution to create a beautiful QR code looking like this:
2.) in a second step the QR Code should be combined to a existing SVG or PNG with exact coordinates..
Is there a possibility to do this with Chillerlan? And how is the right way to achieve this?
Thanks a lot for Help!
BR Mike
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 5 comments 12 replies
-
Hey, yes, this would be possible with the SVG output module (dev-main, upcoming v5). The second example is possible with very few modifications - you would need to extend the QRMarkupSVG class and modify the module() method. The method would simply return an SVG path segment in the shape of a heart. It would look like this:
protected function module(int $x, int $y, int $M_TYPE):string{ if(!$this->options->drawLightModules && !$this->matrix->check($x, $y)){ return ''; } // modify the output of circular modules to return hearts instead // you can then use the respective option values to modify the behaviour if($this->options->drawCircularModules && $this->matrix->checkTypeNotIn($x, $y, $this->options->keepAsSquare)){ return sprintf('M%1$s %2$s m0.5,0.96 l-0.412,-0.412 a0.3 0.3 0 0 1 0.412,-0.435 a0.3 0.3 0 0 1 0.412,0.435Z', $x, $y); } return sprintf('M%1$s %2$s h1 v1 h-1Z', $x, $y); }
If you want to keep the circular modules, you'd need to add your own option values of course - you can find an example for that over here.
The path segment starts at the position of the module ($x, $y - top left corner) and is drawn with relative coordinates from there. You can find an example of shapes and positioning in this SVG image.
The first example is a bit more complex but can be solved by checking for the $M_TYPE of the current module (QRMatrix::M_FINDER, QRMatrix::M_FINDER_DOT). It is similar to the "melted" SVG example over here. You could also just omit the finder patterns and draw them manually - the downside of that is that the size of the QR Code and the manually added patterns need to be fixed.
Unfortunately it's not easy to convert an SVG image to a raster format in PHP with Imagick without losses, so you would instead need to call the Inkscape command line tool.
Beta Was this translation helpful? Give feedback.
All reactions
-
I forgot to mention that you will need to modify the canvas size of the SVG in the header - it's automatically set to the QR matrix dimensions. See header() and the respective MDN page for the <svg> element. You might need to put the QR code in a separate <g> to position it to your likes.
Beta Was this translation helpful? Give feedback.
All reactions
-
so this way can also be used, to use a big PNG or SVG in Background and to position QR Matrix inside the green circle (see example above)?
Beta Was this translation helpful? Give feedback.
All reactions
-
Yes. I embedded the GitHub logo in a similar way. You can just put a whole <svg> in a <g> element like so:
<svg ...> <image href="your background.png" /> <g transform="translate(1.234 1.234) scale(0.5)"> <svg ...> <!-- qrcode --> </svg> </g> </svg>
Beta Was this translation helpful? Give feedback.
All reactions
-
the included PNG file is not a external reference after inclusion into the SVG anymore? is this right? The resulting QR code with background should be able to be saved as PNG or SVG to webserver or as download and has to be standalone?
Beta Was this translation helpful? Give feedback.
All reactions
-
Hey, just like in HTML it depends on how you reference the image - it it's a path or URL, then it is not embedded, if you pass it as a base64 encoded data URI, then it will be part of the SVG.
Beta Was this translation helpful? Give feedback.
All reactions
-
So i made a quick example of how to modify the finder patterns and use custom shapes for them. As i mentioned before, there are several ways of achieving this.
The simplest is to omit the respective modules during generation and then alter the generated SVG. One way could be the <symbol>/<use> syntax, which is nice and clean, the downside however is that linear gradients won't stretch over the whole SVG but rather each <path> and <symbol> etc. which may cause unexpected and ugly results.
So in order to avoid this, everything that should be colored by the same gradient needs to be in the same <path d> element, which is shown in the code below. As a basis i chose the svgWithLogo example as this is closest to what we want to achieve. (i have put the whole example in a file over here)
class QRSvgWithLogoAndCustomShapes extends QRMarkupSVG{ /** * @inheritDoc */ protected function paths():string{ // make sure connect paths is enabled $this->options->connectPaths = true; // empty the default value to remove the fill* attributes from the <path> elements $this->options->markupDark = ''; $this->options->markupLight = ''; $size = (int)ceil($this->moduleCount * $this->options->svgLogoScale); // we're calling QRMatrix::setLogoSpace() manually, so QROptions::$addLogoSpace has no effect here $this->matrix->setLogoSpace($size, $size); // generate the path element(s) - in this case it's just one element as we've "disabled" several options $svg = parent::paths(); // now we're lazy modifying the generated path to add the custom shapes for the finder patterns $svg = str_replace('"/>', $this->getFinderPatterns().'"/>', $svg); // and add the custom logo $svg .= $this->getLogo(); return $svg; } /** * returns a path segment for a single module * * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d */ protected function module(int $x, int $y, int $M_TYPE):string{ if( !$this->matrix->check($x, $y) // we're skipping the finder patterns here || $this->matrix->checkType($x, $y, QRMatrix::M_FINDER) || $this->matrix->checkType($x, $y, QRMatrix::M_FINDER_DOT) ){ return ''; } // return a heart shape (or any custom shape for that matter) return sprintf('M%1$s %2$s m0.5,0.96 l-0.412,-0.412 a0.3 0.3 0 0 1 0.412,-0.435 a0.3 0.3 0 0 1 0.412,0.435Z', $x, $y); } /** * returns a custom path for the 3 finder patterns */ protected function getFinderPatterns():string{ $qz = $this->options->addQuietzone ? $this->options->quietzoneSize : 0; // the positions for the finder patterns (top left corner) // $this->moduleCount includes 2* the quiet zone size already so we need to take this into account $pos = [ [0 + $qz, 0 + $qz], [0 + $qz, $this->moduleCount - $qz - 7], [$this->moduleCount - $qz - 7, 0 + $qz], ]; // the custom path for one finder pattern - the first move (M) is parametrized, the rest are relative coordinates $path = 'M%1$s,%2$s m2,0 h3 q2,0 2,2 v3 q0,2 -2,2 h-3 q-2,0 -2,-2 v-3 q0,-2 2,-2z m0,1 q-1,0 -1,1 v3 q0,1 1,1 h3 q1,0 1,-1 v-3 q0,-1 -1,-1z m0,2.5 a1.5,1.5 0 1 0 3,0 a1.5,1.5 0 1 0 -3,0Z'; $finder = []; foreach($pos as $coord){ [$ix, $iy] = $coord; $finder[] = sprintf($path, $ix, $iy); } return implode('', $finder); } /** * returns a <g> element that contains the SVG logo and positions it properly within the QR Code * * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform */ protected function getLogo():string{ // @todo: customize the <g> element to your liking (css class, style...) return sprintf( '%5$s<g transform="translate(%1$s %1$s) scale(%2$s)" class="%3$s">%5$s %4$s%5$s</g>', ($this->moduleCount - ($this->moduleCount * $this->options->svgLogoScale)) / 2, $this->options->svgLogoScale, $this->options->svgLogoCssClass, file_get_contents($this->options->svgLogo), $this->options->eol ); } }
Here's the output:
svgWithLogoAndCustomShapes
Beta Was this translation helpful? Give feedback.
All reactions
-
❤️ 2
-
That looks great.. So I will analyze your code to understand it! Thank you so much!
Beta Was this translation helpful? Give feedback.
All reactions
-
🎉 3
-
I was playing around a bit with this example. I'd like to have the Finder markers more rounded like this, but inside not a circle.
How do you create such a simple path?
I used yours as example in the svg path editor:
https://yqnn.github.io/svg-path-editor/#P=M0,0_m2,0_h3_q2,0_2,2_v3_q0,2_-2,2_h-3_q-2,0_-2,-2_v-3_q0,-2_2,-2z_m0,1_q-1,0_-1,1_v3_q0,1_1,1_h3_q1,0_1,-1_v-3_q0,-1_-1,-1z_m0,2.5_a1.5,1.5_0_1_0_3,0_a1.5,1.5_0_1_0_-3,0Z
I make an SVG with a rounded square marker in the middle.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 7 7">
<path d="M2 0h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2Zm0 1a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1Zm1 1h1a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z"/>
</svg>
However, I can't find a way to change the path back to the example like:
$path = 'M%1$s,%2$s m2,0 h3 q2,0 2,2 v3 q0,2 -2,2 h-3 q-2,0 -2,-2 v-3 q0,-2 2,-2z m0,1 q-1,0 -1,1 v3 q0,1 1,1 h3 q1,0 1,-1 v-3 q0,-1 -1,-1z m0,2.5 a1.5,1.5 0 1 0 3,0 a1.5,1.5 0 1 0 -3,0Z';
I get that %1$s,%2$s will be replaced by the x and y value with sprintf($path, $ix, $iy);
If you can help me with this, that would be great.
By the way for people reading this too. I didn't wanted the hearts so I changed it back to the circle:
$r = $this->options->circleRadius;
return sprintf(
'M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0Z',
($x + 0.5 - $r),
($y + 0.5),
$r,
($r * 2)
);
Beta Was this translation helpful? Give feedback.
All reactions
-
The path has to be in relative format (the editor you linked has a button "convert to relative") - after conversion, replace the first m 0 0 (the coordinate of the top left corner) with an absolute move to the parametrized coordinates M%1$s,%2$s and replace the final z with a Z (i don't think it makes a difference technically, it helps to see where which subpath ends) and you should end up with this:
$path = 'M%1$s,%2$s m2 0h3a2 2 0 012 2v3a2 2 0 01-2 2h-3a2 2 0 01-2-2v-3a2 2 0 012-2zm0 1a1 1 0 00-1 1v3a1 1 0 001 1h3a1 1 0 001-1v-3a1 1 0 00-1-1zm1 1h1a1 1 0 011 1v1a1 1 0 01-1 1h-1a1 1 0 01-1-1v-1a1 1 0 011-1Z';
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
it's once again me! is there a possibility to define the SIZE of the created QR Code which is generated as PNG in PX e.g. 2000x2000px?
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi again! What do you mean by "generated as PNG"?
- When you convert the SVG to PNG? In that case it depends on the export settings of whatever tool you use.
- In case of the built-in ImageMagick and GdImage output modules, you'll have to adjust
QROptions::$scaleto get near (preferably larger than) the target size and then resize. You can either return the image resource to further modify or do that within your extended output class.
Beta Was this translation helpful? Give feedback.
All reactions
-
Hello im not able to put logo center of it can you help and also the hearth shape is not working too
Beta Was this translation helpful? Give feedback.
All reactions
-
Beta Was this translation helpful? Give feedback.
All reactions
-
Hey, it looks like you're using an example from dev-main which is not compatible with your installed version - see https://github.com/chillerlan/php-qrcode?tab=readme-ov-file#documentation
Please open a new discussion if your problem persists.
Beta Was this translation helpful? Give feedback.