Skip to content

Commit

Permalink
Merge pull request #236 from kozakdenys/bugfix/fix_extra_margin
Browse files Browse the repository at this point in the history
Add dotOptions.roundSize to prevent extra margin
  • Loading branch information
kozakdenys authored Oct 17, 2024
2 parents f60b77c + 984c700 commit a778471
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 31 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,12 @@ When QR type is svg, the image may not load in certain applications as it is sav

`options.dotsOptions` structure

Property|Type |Default Value|Description
--------|------------------------------------------------------------------------------|-------------|-------------------
color |string |`'#000'` |Color of QR dots
gradient|object | |Gradient of QR dots
type |string (`'rounded' 'dots' 'classy' 'classy-rounded' 'square' 'extra-rounded'`)|`'square'` |Style of QR dots
Property | Type | Default Value |Description
-------- |--------------------------------------------------------------------------------|---------------|-------------------
color | string | `'#000'` |Color of QR dots
gradient | object | |Gradient of QR dots
type | string (`'rounded' 'dots' 'classy' 'classy-rounded' 'square' 'extra-rounded'`) | `'square'` |Style of QR dots
roundSize| boolean | true |Whether to round dots size to integer. `true` value might create extra margin around qr code. If `false`, [shape-rendering="crispEdges"](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/shape-rendering#crispedges) will be applied to SVG element.

`options.backgroundOptions` structure

Expand Down
Binary file modified src/assets/test/image_from_readme.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/assets/test/image_from_readme.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/core/QROptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface RequiredOptions extends Options {
type: DotType;
color: string;
gradient?: Gradient;
roundSize?: boolean;
};
backgroundOptions: {
round: number;
Expand Down Expand Up @@ -56,7 +57,8 @@ const defaultOptions: RequiredOptions = {
},
dotsOptions: {
type: "square",
color: "#000"
color: "#000",
roundSize: true,
},
backgroundOptions: {
round: 0,
Expand Down
59 changes: 35 additions & 24 deletions src/core/QRSVG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export default class QRSVG {
this._element.setAttribute("width", String(options.width));
this._element.setAttribute("height", String(options.height));
this._element.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
if (!options.dotsOptions.roundSize) {
this._element.setAttribute("shape-rendering", "crispEdges");
}
this._element.setAttribute("viewBox", `0 0 ${options.width} ${options.height}`);
this._defs = this._window.document.createElementNS("http://www.w3.org/2000/svg", "defs");
this._element.appendChild(this._defs);

Expand Down Expand Up @@ -86,7 +90,7 @@ export default class QRSVG {
const count = qr.getModuleCount();
const minSize = Math.min(this._options.width, this._options.height) - this._options.margin * 2;
const realQRSize = this._options.shape === shapeTypes.circle ? minSize / Math.sqrt(2) : minSize;
const dotSize = realQRSize / count;
const dotSize = this._roundSize(realQRSize / count);
let drawImageSize = {
hideXDots: 0,
hideYDots: 0,
Expand Down Expand Up @@ -151,19 +155,24 @@ export default class QRSVG {
if (element) {
const gradientOptions = options.backgroundOptions?.gradient;
const color = options.backgroundOptions?.color;
let height = options.height;
let width = options.width;

if (gradientOptions || color) {
const size = Math.min(options.width, options.height);
const element = this._window.document.createElementNS("http://www.w3.org/2000/svg", "rect");
this._backgroundClipPath = this._window.document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
this._backgroundClipPath.setAttribute("id", `clip-path-background-color-${this._instanceId}`);
this._defs.appendChild(this._backgroundClipPath);

element.setAttribute("x", String((options.width - size) / 2));
element.setAttribute("y", String((options.height - size) / 2));
element.setAttribute("width", String(size));
element.setAttribute("height", String(size));
element.setAttribute("rx", String((size / 2) * options.backgroundOptions.round));
if (options.backgroundOptions?.round) {
height = width = Math.min(options.width, options.height);
element.setAttribute("rx", String((height / 2) * options.backgroundOptions.round));
}

element.setAttribute("x", String(this._roundSize((options.width - width) / 2)));
element.setAttribute("y", String(this._roundSize((options.height - height) / 2)));
element.setAttribute("width", String(width));
element.setAttribute("height", String(height));

this._backgroundClipPath.appendChild(element);

Expand Down Expand Up @@ -195,9 +204,9 @@ export default class QRSVG {

const minSize = Math.min(options.width, options.height) - options.margin * 2;
const realQRSize = options.shape === shapeTypes.circle ? minSize / Math.sqrt(2) : minSize;
const dotSize = realQRSize / count;
const xBeginning = (options.width - count * dotSize) / 2;
const yBeginning = (options.height - count * dotSize) / 2;
const dotSize = this._roundSize(realQRSize / count);
const xBeginning = this._roundSize((options.width - count * dotSize) / 2);
const yBeginning = this._roundSize((options.height - count * dotSize) / 2);
const dot = new QRDot({
svg: this._element,
type: options.dotsOptions.type,
Expand Down Expand Up @@ -246,12 +255,12 @@ export default class QRSVG {
}

if (options.shape === shapeTypes.circle) {
const additionalDots = Math.floor((minSize / dotSize - count) / 2);
const additionalDots = this._roundSize((minSize / dotSize - count) / 2);
const fakeCount = count + additionalDots * 2;
const xFakeBeginning = xBeginning - additionalDots * dotSize;
const yFakeBeginning = yBeginning - additionalDots * dotSize;
const fakeMatrix: number[][] = [];
const center = Math.floor(fakeCount / 2);
const center = this._roundSize(fakeCount / 2);

for (let row = 0; row < fakeCount; row++) {
fakeMatrix[row] = [];
Expand Down Expand Up @@ -316,11 +325,11 @@ export default class QRSVG {
const count = this._qr.getModuleCount();
const minSize = Math.min(options.width, options.height) - options.margin * 2;
const realQRSize = options.shape === shapeTypes.circle ? minSize / Math.sqrt(2) : minSize;
const dotSize = realQRSize / count;
const dotSize = this._roundSize(realQRSize / count);
const cornersSquareSize = dotSize * 7;
const cornersDotSize = dotSize * 3;
const xBeginning = (options.width - count * dotSize) / 2;
const yBeginning = (options.height - count * dotSize) / 2;
const xBeginning = this._roundSize((options.width - count * dotSize) / 2);
const yBeginning = this._roundSize((options.height - count * dotSize) / 2);

[
[0, 0, 0],
Expand Down Expand Up @@ -450,11 +459,6 @@ export default class QRSVG {

imageToBlob(): void {
if (!this._image) return;
// fix blurry svg
if (/(\.svg$)|(^data:image\/svg)/.test(this._options.image ?? "")) {
this._image.width = this._options.width;
this._image.height = this._options.height;
}
if (this._options.imageOptions.saveAsBlob && this._canvas) {
const ctx = this._canvas.getContext("2d");
if (ctx) {
Expand Down Expand Up @@ -513,10 +517,10 @@ export default class QRSVG {
dotSize: number;
}): Promise<void> {
const options = this._options;
const xBeginning = (options.width - count * dotSize) / 2;
const yBeginning = (options.height - count * dotSize) / 2;
const dx = xBeginning + options.imageOptions.margin + (count * dotSize - width) / 2;
const dy = yBeginning + options.imageOptions.margin + (count * dotSize - height) / 2;
const xBeginning = this._roundSize((options.width - count * dotSize) / 2);
const yBeginning = this._roundSize((options.height - count * dotSize) / 2);
const dx = xBeginning + this._roundSize(options.imageOptions.margin + (count * dotSize - width) / 2);
const dy = yBeginning + this._roundSize(options.imageOptions.margin + (count * dotSize - height) / 2);
const dw = width - options.imageOptions.margin * 2;
const dh = height - options.imageOptions.margin * 2;

Expand Down Expand Up @@ -629,4 +633,11 @@ export default class QRSVG {

this._element.appendChild(rect);
}

_roundSize = (value: number) => {
if (this._options.dotsOptions.roundSize) {
return Math.floor(value);
}
return value;
}
}

0 comments on commit a778471

Please sign in to comment.