@liquid-js/qr-code-styling 5.0.2 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -83,7 +83,7 @@ Version `5.0.0` of the library introduces plugin support to replace the now-depr
83
83
  While the old API only supports a single extension, it is now possible to apply multiple plugins or even multiple instances of a single plugin. For example, to create a QR code with a complex border:
84
84
 
85
85
  ```ts
86
- import { BorderPlugin } from '@liquid-js/qr-code-styling/border-plugin'
86
+ import BorderPlugin from '@liquid-js/qr-code-styling/border-plugin'
87
87
 
88
88
  const qrCode = new QRCodeStyling({
89
89
  plugins: [
@@ -1,2 +1,2 @@
1
- let t=/\.?0+$/;function e(e){return"string"==typeof e?e:e.toFixed(7).replace(t,"")}function i(t,...i){return t.reduce((t,r,s)=>{let o="";return s&&(o=i[s-1]),"number"==typeof o&&(o=e(o)),`${t}${o}${r}`},"").trim().replace(/[\s\n\r]+/gim," ")}var r,s=r||={};s.top="top",s.bottom="bottom",s.left="left",s.right="right";export default class{pluginOptions;idSuffix;constructor(t,e=Math.random().toFixed(10).substring(2)){this.pluginOptions=t,this.idSuffix=e}postProcess(t,s){let{document:o}=s;var n=this.pluginOptions.size;let $=function(t,i){i=Math.max(i,0),t.setAttribute("width",e(parseFloat(t.getAttribute("width")||"0")+2*i)),t.setAttribute("height",e(parseFloat(t.getAttribute("height")||"0")+2*i));let r=(t.getAttribute("viewBox")||"").split(" ").map((t,e)=>parseFloat(t)+(e<2?-1:2)*i);if(t.setAttribute("viewBox",r.map(e).join(" ")),!(r.length<4))return{left:r[0],right:r[2]+r[0],top:r[1],bottom:r[3]+r[1]}}(t,n+(this.pluginOptions.margin||0));if($){n=Math.abs(n);var a=($.left+$.right)/2,h=($.top+$.bottom)/2,p=Math.min(s.width,s.height)/2*(1-(this.pluginOptions.round||0)),l=Math.min($.right-$.left-2*n,$.bottom-$.top-2*n)/2-p+n/2,u=this.pluginOptions.round?l:0,g=this.pluginOptions.round?p:p+l;(s=o.createElementNS("http://www.w3.org/2000/svg","rect")).setAttribute("width",e(2*(g+u))),s.setAttribute("height",e(2*(g+u))),s.setAttribute("x",e(a-g-u)),s.setAttribute("y",e(h-g-u)),s.setAttribute("rx",e(u)),s.setAttribute("fill","none"),s.setAttribute("stroke",this.pluginOptions.color),s.setAttribute("stroke-width",e(n)),this.pluginOptions.dasharray&&s.setAttribute("stroke-dasharray",this.pluginOptions.dasharray),t.appendChild(s),[r.left,r.top,r.right,r.bottom].forEach(s=>{var n=this.pluginOptions.text,$=n?.[s];if($){let d=Array.from(t.childNodes).find(t=>"DEFS"==t.tagName.toUpperCase());d||(d=o.createElementNS("http://www.w3.org/2000/svg","defs"),t.appendChild(d));let f=o.createElementNS("http://www.w3.org/2000/svg","path"),w=$.size||n.size||1;switch(u=this.pluginOptions.round?l-w/3:0,g=this.pluginOptions.round?p:p+(l-w/3),s){case r.top:f.setAttribute("d",i`M ${a} ${h+u+g} h ${-g} a ${u} ${u} 0 0 1 ${-u} ${-u} v ${-2*g} a ${u} ${u} 0 0 1 ${u} ${-u} h ${2*g} a ${u} ${u} 0 0 1 ${u} ${u} v ${2*g} a ${u} ${u} 0 0 1 ${-u} ${u} h ${-g}`);break;case r.bottom:this.pluginOptions.round?u+=2*w/3:g+=2*w/3,f.setAttribute("d",i`M ${a} ${h-u-g} h ${-g} a ${u} ${u} 0 0 0 ${-u} ${u} v ${2*g} a ${u} ${u} 0 0 0 ${u} ${u} h ${2*g} a ${u} ${u} 0 0 0 ${u} ${-u} v ${-2*g} a ${u} ${u} 0 0 0 ${-u} ${-u} h ${-g}`);break;case r.left:f.setAttribute("d",i`M ${a+u+g} ${h} v ${g} a ${u} ${u} 0 0 1 ${-u} ${u} h ${-2*g} a ${u} ${u} 0 0 1 ${-u} ${-u} v ${-2*g} a ${u} ${u} 0 0 1 ${u} ${-u} h ${2*g} a ${u} ${u} 0 0 1 ${u} ${u} v ${g}`);break;case r.right:f.setAttribute("d",i`M ${a-u-g} ${h} v ${-g} a ${u} ${u} 0 0 1 ${u} ${-u} h ${2*g} a ${u} ${u} 0 0 1 ${u} ${u} v ${2*g} a ${u} ${u} 0 0 1 ${-u} ${u} h ${-2*g} a ${u} ${u} 0 0 1 ${-u} ${-u} v ${-g}`)}s=`border-text-${s}-${this.idSuffix}`,f.setAttribute("id",s),f.setAttribute("fill","none");let b=o.createElementNS("http://www.w3.org/2000/svg","tspan");b.setAttribute("font-family",$.font||n.font||"sans-serif"),b.setAttribute("fill",$.color||n.color||"#000"),b.setAttribute("font-weight",e($.fontWeight||n.fontWeight||"normal")),b.setAttribute("font-style",$.fontStyle||n.fontStyle||"normal"),b.setAttribute("font-size",e(w)),b.textContent=$.content,(n=o.createElementNS("http://www.w3.org/2000/svg","textPath")).setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","#"+s),n.setAttribute("startOffset","50%"),n.setAttribute("text-anchor","middle"),n.appendChild(b),($=o.createElementNS("http://www.w3.org/2000/svg","text")).appendChild(n),d.appendChild(f),t.appendChild($)}}),this.pluginOptions.fontFaces&&((n=Array.from(t.childNodes).find(t=>"DEFS"==t.tagName.toUpperCase()))||(n=o.createElementNS("http://www.w3.org/2000/svg","defs"),t.appendChild(n)),(s=o.createElementNS("http://www.w3.org/2000/svg","style")).textContent=this.pluginOptions.fontFaces,n.appendChild(s))}}}
1
+ import{extendSVG as t,numToAttr as e,svgPath as i}from"./plugin-utils.js";var s,r=s||={};r.top="top",r.bottom="bottom",r.left="left",r.right="right";export default class{pluginOptions;idSuffix;constructor(t,e=Math.random().toFixed(10).substring(2)){this.pluginOptions=t,this.idSuffix=e}postProcess(r,o){let{document:n}=o,$=this.pluginOptions.proportional?o.size:1;var a=this.pluginOptions.size*$;let p=t(r,a+(this.pluginOptions.margin||0)*$);if(p){a=Math.abs(a);var h=(p.left+p.right)/2,l=(p.top+p.bottom)/2,u=o.size/2*(1-(this.pluginOptions.round||0)),d=Math.min(p.right-p.left-2*a,p.bottom-p.top-2*a)/2-u+a/2,g=this.pluginOptions.round?d:0,f=this.pluginOptions.round?u:u+d;(o=n.createElementNS("http://www.w3.org/2000/svg","rect")).setAttribute("width",e(2*(f+g))),o.setAttribute("height",e(2*(f+g))),o.setAttribute("x",e(h-f-g)),o.setAttribute("y",e(l-f-g)),o.setAttribute("rx",e(g)),o.setAttribute("fill","none"),o.setAttribute("stroke",this.pluginOptions.color),o.setAttribute("stroke-width",e(a)),this.pluginOptions.dasharray&&o.setAttribute("stroke-dasharray",this.pluginOptions.dasharray),r.appendChild(o),[s.left,s.top,s.right,s.bottom].forEach(t=>{var o=this.pluginOptions.text,a=o?.[t];if(a){let p=Array.from(r.childNodes).find(t=>"DEFS"==t.tagName.toUpperCase());p||(p=n.createElementNS("http://www.w3.org/2000/svg","defs"),r.appendChild(p));let w=n.createElementNS("http://www.w3.org/2000/svg","path"),b=(a.size||o.size||1)*$;switch(g=this.pluginOptions.round?d-b/3:0,f=this.pluginOptions.round?u:u+(d-b/3),t){case s.top:w.setAttribute("d",i`M ${h} ${l+g+f} h ${-f} a ${g} ${g} 0 0 1 ${-g} ${-g} v ${-2*f} a ${g} ${g} 0 0 1 ${g} ${-g} h ${2*f} a ${g} ${g} 0 0 1 ${g} ${g} v ${2*f} a ${g} ${g} 0 0 1 ${-g} ${g} h ${-f}`);break;case s.bottom:this.pluginOptions.round?g+=2*b/3:f+=2*b/3,w.setAttribute("d",i`M ${h} ${l-g-f} h ${-f} a ${g} ${g} 0 0 0 ${-g} ${g} v ${2*f} a ${g} ${g} 0 0 0 ${g} ${g} h ${2*f} a ${g} ${g} 0 0 0 ${g} ${-g} v ${-2*f} a ${g} ${g} 0 0 0 ${-g} ${-g} h ${-f}`);break;case s.left:w.setAttribute("d",i`M ${h+g+f} ${l} v ${f} a ${g} ${g} 0 0 1 ${-g} ${g} h ${-2*f} a ${g} ${g} 0 0 1 ${-g} ${-g} v ${-2*f} a ${g} ${g} 0 0 1 ${g} ${-g} h ${2*f} a ${g} ${g} 0 0 1 ${g} ${g} v ${f}`);break;case s.right:w.setAttribute("d",i`M ${h-g-f} ${l} v ${-f} a ${g} ${g} 0 0 1 ${g} ${-g} h ${2*f} a ${g} ${g} 0 0 1 ${g} ${g} v ${2*f} a ${g} ${g} 0 0 1 ${-g} ${g} h ${-2*f} a ${g} ${g} 0 0 1 ${-g} ${-g} v ${-f}`)}t=`border-text-${t}-${this.idSuffix}`,w.setAttribute("id",t),w.setAttribute("fill","none");let c=n.createElementNS("http://www.w3.org/2000/svg","tspan");c.setAttribute("font-family",a.font||o.font||"sans-serif"),c.setAttribute("fill",a.color||o.color||"#000"),c.setAttribute("font-weight",e(a.fontWeight||o.fontWeight||"normal")),c.setAttribute("font-style",a.fontStyle||o.fontStyle||"normal"),c.setAttribute("font-size",e(b)),c.textContent=a.content,(o=n.createElementNS("http://www.w3.org/2000/svg","textPath")).setAttributeNS("http://www.w3.org/1999/xlink","xlink:href","#"+t),o.setAttribute("startOffset","50%"),o.setAttribute("text-anchor","middle"),o.appendChild(c),(a=n.createElementNS("http://www.w3.org/2000/svg","text")).appendChild(o),p.appendChild(w),r.appendChild(a)}}),this.pluginOptions.fontFaces&&((a=Array.from(r.childNodes).find(t=>"DEFS"==t.tagName.toUpperCase()))||(a=n.createElementNS("http://www.w3.org/2000/svg","defs"),r.appendChild(a)),(o=n.createElementNS("http://www.w3.org/2000/svg","style")).textContent=this.pluginOptions.fontFaces,a.appendChild(o))}}}
2
2
  //# sourceMappingURL=border-plugin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"border-plugin.js","sources":["../src/utils/svg.ts","../src/plugins/border.ts"],"sourcesContent":["import { RotateFigureArgs } from '../types/helper.js'\r\n\r\nconst rx = /\\.?0+$/\r\n\r\nexport function numToAttr(value: number | string) {\r\n if (typeof value == 'string') return value\r\n return value.toFixed(7).replace(rx, '')\r\n}\r\n\r\nexport function svgPath(strings: TemplateStringsArray, ...values: unknown[]) {\r\n return strings\r\n .reduce((t, v, i) => {\r\n let p: unknown = ''\r\n if (i) p = values[i - 1]\r\n if (typeof p == 'number') p = numToAttr(p)\r\n return `${t}${p}${v}`\r\n }, '')\r\n .trim()\r\n .replace(/[\\s\\n\\r]+/gim, ' ')\r\n}\r\n\r\nexport function rotateFigure<T extends SVGElement | ReadonlyArray<SVGElement>>({ draw, ...args }: RotateFigureArgs<T>) {\r\n const { x, y, size, rotation = 0 } = args\r\n const cx = x + size / 2\r\n const cy = y + size / 2\r\n\r\n const element = draw(args)\r\n if (rotation) (Array.isArray(element) ? element : [element])\r\n .forEach(el => el.setAttribute('transform', `rotate(${numToAttr((180 * rotation) / Math.PI)},${cx},${cy})`))\r\n return element\r\n}\r\n\r\nexport function scalePath(path: Array<string | number>, { x, y, size }: {\r\n x: number\r\n y: number\r\n size: number\r\n}) {\r\n let move = false\r\n let i = 0\r\n return path\r\n .map((v) => {\r\n if (typeof v == 'string') {\r\n i = 0\r\n move = v.toUpperCase() == v\r\n return v\r\n }\r\n i++\r\n v = v * size\r\n if (move) {\r\n v += i % 2 == 1 ? x : y\r\n }\r\n return numToAttr(v)\r\n })\r\n .join(' ')\r\n}\r\n\r\nexport function extendSVG(svg: SVGSVGElement, padding: number) {\r\n padding = Math.max(padding, 0)\r\n svg.setAttribute('width', numToAttr(parseFloat(svg.getAttribute('width') || '0') + 2 * padding))\r\n svg.setAttribute('height', numToAttr(parseFloat(svg.getAttribute('height') || '0') + 2 * padding))\r\n const vb = (svg.getAttribute('viewBox') || '').split(' ').map((v, i) => parseFloat(v) + (i < 2 ? -1 : 2) * padding)\r\n svg.setAttribute('viewBox', vb.map(numToAttr).join(' '))\r\n if (vb.length < 4)\r\n return undefined\r\n return {\r\n left: vb[0],\r\n right: vb[2] + vb[0],\r\n top: vb[1],\r\n bottom: vb[3] + vb[1]\r\n }\r\n}\r\n","import { Options, Plugin } from '../utils/options.js'\r\nimport { extendSVG, numToAttr, svgPath } from './utils.js'\r\n\r\nenum TextPosition {\r\n top = 'top',\r\n bottom = 'bottom',\r\n left = 'left',\r\n right = 'right'\r\n}\r\n\r\nexport interface TextConfig {\r\n font?: string\r\n color?: string\r\n size?: number\r\n fontWeight?: 'normal' | 'bold' | number\r\n fontStyle?: 'normal' | 'italic' | 'oblique'\r\n}\r\n\r\nexport interface BorderPluginOptions {\r\n /** Border roundnes, from 0 (square) to 1 (circle) */\r\n round?: number\r\n size: number\r\n color: string\r\n dasharray?: string\r\n margin?: number\r\n text?: TextConfig & {\r\n [key in TextPosition]?: TextConfig & {\r\n content: string\r\n }\r\n }\r\n /** A string of embedded CSS @font-face rules */\r\n fontFaces?: string\r\n}\r\n\r\nexport class BorderPlugin implements Plugin {\r\n\r\n constructor(\r\n private readonly pluginOptions: BorderPluginOptions,\r\n /** @ignore Create unique id for SVG elements to prevent naming collisions; specify your own if neccesary e.g. for testing */\r\n private readonly idSuffix = Math.random().toFixed(10).substring(2)\r\n ) { }\r\n\r\n postProcess(svg: SVGSVGElement, options: Options) {\r\n const { document } = options\r\n const margin = this.pluginOptions.margin || 0\r\n let thickness = this.pluginOptions.size\r\n const drawArea = extendSVG(svg, thickness + margin)\r\n if (!drawArea)\r\n return\r\n\r\n thickness = Math.abs(thickness)\r\n\r\n const cx = (drawArea.left + drawArea.right) / 2\r\n const cy = (drawArea.top + drawArea.bottom) / 2\r\n const lineSize = Math.min(options.width, options.height) / 2 * (1 - (this.pluginOptions.round || 0))\r\n const r = (Math.min(drawArea.right - drawArea.left - 2 * thickness, drawArea.bottom - drawArea.top - 2 * thickness) / 2) - lineSize + thickness / 2\r\n\r\n let pathR = this.pluginOptions.round ? r : 0\r\n let pathL = this.pluginOptions.round ? lineSize : lineSize + r\r\n\r\n const borderEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect')\r\n borderEl.setAttribute('width', numToAttr((pathL + pathR) * 2))\r\n borderEl.setAttribute('height', numToAttr((pathL + pathR) * 2))\r\n borderEl.setAttribute('x', numToAttr(cx - pathL - pathR))\r\n borderEl.setAttribute('y', numToAttr(cy - pathL - pathR))\r\n borderEl.setAttribute('rx', numToAttr(pathR))\r\n borderEl.setAttribute('fill', 'none')\r\n borderEl.setAttribute('stroke', this.pluginOptions.color)\r\n borderEl.setAttribute('stroke-width', numToAttr(thickness))\r\n if (this.pluginOptions.dasharray) {\r\n borderEl.setAttribute('stroke-dasharray', this.pluginOptions.dasharray)\r\n }\r\n\r\n svg.appendChild(borderEl);\r\n\r\n [TextPosition.left, TextPosition.top, TextPosition.right, TextPosition.bottom].forEach(postion => {\r\n const textCfg = this.pluginOptions.text\r\n const config = textCfg?.[postion]\r\n if (config) {\r\n let defs = Array.from(svg.childNodes).find(v => (v as HTMLElement).tagName.toUpperCase() == 'DEFS')\r\n if (!defs) {\r\n defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\r\n svg.appendChild(defs)\r\n }\r\n const element = document.createElementNS('http://www.w3.org/2000/svg', 'path')\r\n const size = (config.size || textCfg.size || 1)\r\n pathR = this.pluginOptions.round ? r - size / 3 : 0\r\n pathL = this.pluginOptions.round ? lineSize : lineSize + (r - size / 3)\r\n switch (postion) {\r\n case TextPosition.top:\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx} ${cy + pathR + pathL}\r\n h ${-pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${-pathR}\r\n v ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${-pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${pathR}\r\n v ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${pathR}\r\n h ${-pathL}`\r\n )\r\n break\r\n case TextPosition.bottom:\r\n if (this.pluginOptions.round)\r\n pathR += size * 2 / 3\r\n else\r\n pathL += size * 2 / 3\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx} ${cy - pathR - pathL}\r\n h ${-pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${-pathR} ${pathR}\r\n v ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${pathR} ${pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${pathR} ${-pathR}\r\n v ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${-pathR} ${-pathR}\r\n h ${-pathL}`\r\n )\r\n break\r\n case TextPosition.left:\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx + pathR + pathL} ${cy}\r\n v ${pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${pathR}\r\n h ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${-pathR}\r\n v ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${-pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${pathR}\r\n v ${pathL}`\r\n )\r\n break\r\n case TextPosition.right:\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx - pathR - pathL} ${cy}\r\n v ${-pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${-pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${pathR}\r\n v ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${pathR}\r\n h ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${-pathR}\r\n v ${-pathL}`\r\n )\r\n break\r\n }\r\n const id = `border-text-${postion}-${this.idSuffix}`\r\n element.setAttribute('id', id)\r\n element.setAttribute('fill', 'none')\r\n\r\n const span = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')\r\n span.setAttribute('font-family', config.font || textCfg.font || 'sans-serif')\r\n span.setAttribute('fill', config.color || textCfg.color || '#000')\r\n span.setAttribute('font-weight', numToAttr(config.fontWeight || textCfg.fontWeight || 'normal'))\r\n span.setAttribute('font-style', config.fontStyle || textCfg.fontStyle || 'normal')\r\n span.setAttribute('font-size', numToAttr(size))\r\n span.textContent = config.content\r\n\r\n const textPath = document.createElementNS('http://www.w3.org/2000/svg', 'textPath')\r\n textPath.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + id)\r\n textPath.setAttribute('startOffset', '50%')\r\n textPath.setAttribute('text-anchor', 'middle')\r\n textPath.appendChild(span)\r\n\r\n const text = document.createElementNS('http://www.w3.org/2000/svg', 'text')\r\n text.appendChild(textPath)\r\n\r\n defs.appendChild(element)\r\n svg.appendChild(text)\r\n }\r\n })\r\n\r\n if (this.pluginOptions.fontFaces) {\r\n let defs = Array.from(svg.childNodes).find(v => (v as HTMLElement).tagName.toUpperCase() == 'DEFS')\r\n if (!defs) {\r\n defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\r\n svg.appendChild(defs)\r\n }\r\n const style = document.createElementNS('http://www.w3.org/2000/svg', 'style')\r\n style.textContent = this.pluginOptions.fontFaces\r\n defs.appendChild(style)\r\n }\r\n }\r\n}\r\n\r\nexport default BorderPlugin\r\n"],"names":["rx","numToAttr","value","toFixed","replace","svgPath","strings","values","reduce","t","v","i","p","trim","TextPosition","B","top","bottom","left","right","pluginOptions","idSuffix","constructor","Math","random","substring","this","postProcess","svg","options","document","thickness","size","drawArea","extendSVG","padding","max","setAttribute","parseFloat","getAttribute","split","map","vb","join","length","margin","abs","cx","cy","lineSize","min","width","height","round","pathR","r","pathL","borderEl","createElementNS","color","dasharray","appendChild","forEach","postion","text","textCfg","config","defs","Array","from","childNodes","find","tagName","toUpperCase","element","id","span","font","fontWeight","fontStyle","textContent","content","textPath","setAttributeNS","fontFaces","style"],"mappings":"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"}
1
+ {"version":3,"file":"border-plugin.js","sources":["../src/plugins/border.ts"],"sourcesContent":["import { Options, Plugin } from '../utils/options.js'\r\nimport { extendSVG, numToAttr, svgPath } from './utils.js'\r\n\r\nenum TextPosition {\r\n top = 'top',\r\n bottom = 'bottom',\r\n left = 'left',\r\n right = 'right'\r\n}\r\n\r\nexport interface TextConfig {\r\n font?: string\r\n color?: string\r\n size?: number\r\n fontWeight?: 'normal' | 'bold' | number\r\n fontStyle?: 'normal' | 'italic' | 'oblique'\r\n}\r\n\r\nexport interface BorderPluginOptions {\r\n /** Border roundnes, from 0 (square) to 1 (circle) */\r\n round?: number\r\n size: number\r\n color: string\r\n dasharray?: string\r\n margin?: number\r\n /**\r\n * If true, all dimensions are proportional to the code size,\r\n * i.e. `size: 0.1` means the border thickness is 1/10 of the QR code size\r\n *\r\n * This is useful to keep the border visually consistent regardless of the amount of data\r\n *\r\n * Note: if using dasharray, use percentage values to maintain consistency\r\n */\r\n proportional?: boolean\r\n text?: TextConfig & {\r\n [key in TextPosition]?: TextConfig & {\r\n content: string\r\n }\r\n }\r\n /** A string of embedded CSS @font-face rules */\r\n fontFaces?: string\r\n}\r\n\r\nexport default class BorderPlugin implements Plugin {\r\n\r\n constructor(\r\n private readonly pluginOptions: BorderPluginOptions,\r\n /** @ignore Create unique id for SVG elements to prevent naming collisions; specify your own if neccesary e.g. for testing */\r\n private readonly idSuffix = Math.random().toFixed(10).substring(2)\r\n ) { }\r\n\r\n postProcess(svg: SVGSVGElement, options: Options) {\r\n const { document } = options\r\n const sizeFactor = this.pluginOptions.proportional ? options.size : 1\r\n\r\n const margin = (this.pluginOptions.margin || 0) * sizeFactor\r\n let thickness = (this.pluginOptions.size) * sizeFactor\r\n const drawArea = extendSVG(svg, thickness + margin)\r\n if (!drawArea)\r\n return\r\n\r\n thickness = Math.abs(thickness)\r\n\r\n const cx = (drawArea.left + drawArea.right) / 2\r\n const cy = (drawArea.top + drawArea.bottom) / 2\r\n const lineSize = options.size / 2 * (1 - (this.pluginOptions.round || 0))\r\n const r = (Math.min(drawArea.right - drawArea.left - 2 * thickness, drawArea.bottom - drawArea.top - 2 * thickness) / 2) - lineSize + thickness / 2\r\n\r\n let pathR = this.pluginOptions.round ? r : 0\r\n let pathL = this.pluginOptions.round ? lineSize : lineSize + r\r\n\r\n const borderEl = document.createElementNS('http://www.w3.org/2000/svg', 'rect')\r\n borderEl.setAttribute('width', numToAttr((pathL + pathR) * 2))\r\n borderEl.setAttribute('height', numToAttr((pathL + pathR) * 2))\r\n borderEl.setAttribute('x', numToAttr(cx - pathL - pathR))\r\n borderEl.setAttribute('y', numToAttr(cy - pathL - pathR))\r\n borderEl.setAttribute('rx', numToAttr(pathR))\r\n borderEl.setAttribute('fill', 'none')\r\n borderEl.setAttribute('stroke', this.pluginOptions.color)\r\n borderEl.setAttribute('stroke-width', numToAttr(thickness))\r\n if (this.pluginOptions.dasharray) {\r\n borderEl.setAttribute('stroke-dasharray', this.pluginOptions.dasharray)\r\n }\r\n\r\n svg.appendChild(borderEl);\r\n\r\n [TextPosition.left, TextPosition.top, TextPosition.right, TextPosition.bottom].forEach(postion => {\r\n const textCfg = this.pluginOptions.text\r\n const config = textCfg?.[postion]\r\n if (config) {\r\n let defs = Array.from(svg.childNodes).find(v => (v as HTMLElement).tagName.toUpperCase() == 'DEFS')\r\n if (!defs) {\r\n defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\r\n svg.appendChild(defs)\r\n }\r\n const element = document.createElementNS('http://www.w3.org/2000/svg', 'path')\r\n const size = (config.size || textCfg.size || 1) * sizeFactor\r\n pathR = this.pluginOptions.round ? r - size / 3 : 0\r\n pathL = this.pluginOptions.round ? lineSize : lineSize + (r - size / 3)\r\n switch (postion) {\r\n case TextPosition.top:\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx} ${cy + pathR + pathL}\r\n h ${-pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${-pathR}\r\n v ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${-pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${pathR}\r\n v ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${pathR}\r\n h ${-pathL}`\r\n )\r\n break\r\n case TextPosition.bottom:\r\n if (this.pluginOptions.round)\r\n pathR += size * 2 / 3\r\n else\r\n pathL += size * 2 / 3\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx} ${cy - pathR - pathL}\r\n h ${-pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${-pathR} ${pathR}\r\n v ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${pathR} ${pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${pathR} ${-pathR}\r\n v ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 0 ${-pathR} ${-pathR}\r\n h ${-pathL}`\r\n )\r\n break\r\n case TextPosition.left:\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx + pathR + pathL} ${cy}\r\n v ${pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${pathR}\r\n h ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${-pathR}\r\n v ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${-pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${pathR}\r\n v ${pathL}`\r\n )\r\n break\r\n case TextPosition.right:\r\n element.setAttribute(\r\n 'd',\r\n svgPath`M ${cx - pathR - pathL} ${cy}\r\n v ${-pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${-pathR}\r\n h ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${pathR} ${pathR}\r\n v ${2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${pathR}\r\n h ${-2 * pathL}\r\n a ${pathR} ${pathR} 0 0 1 ${-pathR} ${-pathR}\r\n v ${-pathL}`\r\n )\r\n break\r\n }\r\n const id = `border-text-${postion}-${this.idSuffix}`\r\n element.setAttribute('id', id)\r\n element.setAttribute('fill', 'none')\r\n\r\n const span = document.createElementNS('http://www.w3.org/2000/svg', 'tspan')\r\n span.setAttribute('font-family', config.font || textCfg.font || 'sans-serif')\r\n span.setAttribute('fill', config.color || textCfg.color || '#000')\r\n span.setAttribute('font-weight', numToAttr(config.fontWeight || textCfg.fontWeight || 'normal'))\r\n span.setAttribute('font-style', config.fontStyle || textCfg.fontStyle || 'normal')\r\n span.setAttribute('font-size', numToAttr(size))\r\n span.textContent = config.content\r\n\r\n const textPath = document.createElementNS('http://www.w3.org/2000/svg', 'textPath')\r\n textPath.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + id)\r\n textPath.setAttribute('startOffset', '50%')\r\n textPath.setAttribute('text-anchor', 'middle')\r\n textPath.appendChild(span)\r\n\r\n const text = document.createElementNS('http://www.w3.org/2000/svg', 'text')\r\n text.appendChild(textPath)\r\n\r\n defs.appendChild(element)\r\n svg.appendChild(text)\r\n }\r\n })\r\n\r\n if (this.pluginOptions.fontFaces) {\r\n let defs = Array.from(svg.childNodes).find(v => (v as HTMLElement).tagName.toUpperCase() == 'DEFS')\r\n if (!defs) {\r\n defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')\r\n svg.appendChild(defs)\r\n }\r\n const style = document.createElementNS('http://www.w3.org/2000/svg', 'style')\r\n style.textContent = this.pluginOptions.fontFaces\r\n defs.appendChild(style)\r\n }\r\n }\r\n}\r\n"],"names":["extendSVG","numToAttr","TextPosition","g","v","top","bottom","left","right","pluginOptions","idSuffix","constructor","Math","random","toFixed","substring","this","postProcess","svg","options","document","sizeFactor","proportional","size","thickness","drawArea","margin","abs","cx","cy","round","min","lineSize","pathR","r","pathL","createElementNS","setAttribute","borderEl","color","dasharray","appendChild","forEach","postion","text","textCfg","config","defs","Array","from","childNodes","find","tagName","toUpperCase","element","svgPath","id","span","font","fontWeight","fontStyle","textContent","content","textPath","setAttributeNS","fontFaces","style"],"mappings":"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"}
@@ -15,7 +15,7 @@ export declare class QRSVG {
15
15
  private qr?;
16
16
  private document;
17
17
  private imageTools;
18
- constructor(options: Pick<Options, 'width' | 'height' | 'document' | 'imageTools' | 'image' | 'imageOptions' | 'dotsOptions' | 'cornersDotOptions' | 'cornersSquareOptions' | 'backgroundOptions' | 'shape' | 'plugins'> & {
18
+ constructor(options: Pick<Options, 'width' | 'height' | 'size' | 'document' | 'imageTools' | 'image' | 'imageOptions' | 'dotsOptions' | 'cornersDotOptions' | 'cornersSquareOptions' | 'backgroundOptions' | 'shape' | 'plugins'> & {
19
19
  errorCorrectionPercent: number;
20
20
  });
21
21
  get width(): number;
@@ -19,6 +19,15 @@ export interface BorderPluginOptions {
19
19
  color: string;
20
20
  dasharray?: string;
21
21
  margin?: number;
22
+ /**
23
+ * If true, all dimensions are proportional to the code size,
24
+ * i.e. `size: 0.1` means the border thickness is 1/10 of the QR code size
25
+ *
26
+ * This is useful to keep the border visually consistent regardless of the amount of data
27
+ *
28
+ * Note: if using dasharray, use percentage values to maintain consistency
29
+ */
30
+ proportional?: boolean;
22
31
  text?: TextConfig & {
23
32
  [key in TextPosition]?: TextConfig & {
24
33
  content: string;
@@ -27,7 +36,7 @@ export interface BorderPluginOptions {
27
36
  /** A string of embedded CSS @font-face rules */
28
37
  fontFaces?: string;
29
38
  }
30
- export declare class BorderPlugin implements Plugin {
39
+ export default class BorderPlugin implements Plugin {
31
40
  private readonly pluginOptions;
32
41
  /** @ignore Create unique id for SVG elements to prevent naming collisions; specify your own if neccesary e.g. for testing */
33
42
  private readonly idSuffix;
@@ -36,4 +45,4 @@ export declare class BorderPlugin implements Plugin {
36
45
  idSuffix?: string);
37
46
  postProcess(svg: SVGSVGElement, options: Options): void;
38
47
  }
39
- export default BorderPlugin;
48
+ export {};
@@ -72,6 +72,7 @@ let defaultOptions = {
72
72
  shape: ShapeType.square,
73
73
  width: void 0,
74
74
  height: void 0,
75
+ size: void 0,
75
76
  data: "",
76
77
  qrOptions: {
77
78
  typeNumber: TypeNumber[0],
@@ -693,16 +694,16 @@ class QRSVG {
693
694
  imageTools;
694
695
  constructor(options) {
695
696
  this.options = options, this.document = options.document, this._element = this.document.createElementNS("http://www.w3.org/2000/svg", "svg");
696
- let width = numToAttr(options.width), height = numToAttr(options.height);
697
- this._element.setAttribute("width", width), this._element.setAttribute("height", height),
698
- this._element.setAttribute("viewBox", `0 0 ${width} ${height}`), this.defs = this.document.createElementNS("http://www.w3.org/2000/svg", "defs"),
697
+ let size = numToAttr(options.size);
698
+ this._element.setAttribute("width", size), this._element.setAttribute("height", size),
699
+ this._element.setAttribute("viewBox", `0 0 ${size} ${size}`), this.defs = this.document.createElementNS("http://www.w3.org/2000/svg", "defs"),
699
700
  this._element.appendChild(this.defs), this.imageTools = options.imageTools || browserImageTools;
700
701
  }
701
702
  get width() {
702
- return this.options.width;
703
+ return this.options.size;
703
704
  }
704
705
  get height() {
705
- return this.options.height;
706
+ return this.options.size;
706
707
  }
707
708
  async drawQR(qr) {
708
709
  let count = qr.getModuleCount(), typeNumber = parseInt(((count - 17) / 4).toFixed(0), 10), dotSize = this.options.dotsOptions.size, drawImageSize = {
@@ -714,9 +715,9 @@ class QRSVG {
714
715
  if (this.qr = qr, this.options.image) {
715
716
  let size = await this.imageTools.getSize(this.options.image, this.options.imageOptions.crossOrigin), {imageOptions, errorCorrectionPercent} = this.options;
716
717
  if (imageOptions.mode == ImageMode.background) {
717
- let margin = this.options.backgroundOptions && this.options.backgroundOptions.margin || 0, maxWidth = (this.options.width - 2 * margin * dotSize) * imageOptions.imageSize, maxHeight = (this.options.height - 2 * margin * dotSize) * imageOptions.imageSize, {width, height} = size;
718
- height = height / width * maxWidth, width = maxWidth, height > maxHeight && (width = width / height * maxHeight,
719
- height = maxHeight), drawImageSize = {
718
+ let margin = this.options.backgroundOptions && this.options.backgroundOptions.margin || 0, maxSize = (this.options.size - 2 * margin * dotSize) * imageOptions.imageSize, {width, height} = size;
719
+ height = height / width * maxSize, width = maxSize, height > maxSize && (width = width / height * maxSize,
720
+ height = maxSize), drawImageSize = {
720
721
  hideXDots: 0,
721
722
  hideYDots: 0,
722
723
  width,
@@ -793,33 +794,34 @@ class QRSVG {
793
794
  additionalRotation: 0,
794
795
  x: 0,
795
796
  y: 0,
796
- height: options.height,
797
- width: options.width,
797
+ height: options.size,
798
+ width: options.size,
798
799
  name: "background-color"
799
800
  });
800
- let size = Math.min(options.width, options.height), element = this.document.createElementNS("http://www.w3.org/2000/svg", "rect");
801
+ let element = this.document.createElementNS("http://www.w3.org/2000/svg", "rect");
801
802
  [this.backgroundMask, this.backgroundMaskGroup] = this.createMask("mask-background-color"),
802
- this.defs.appendChild(this.backgroundMask), element.setAttribute("x", numToAttr((options.width - size) / 2)),
803
- element.setAttribute("y", numToAttr((options.height - size) / 2)), element.setAttribute("width", numToAttr(size)),
804
- element.setAttribute("height", numToAttr(size)), element.setAttribute("rx", numToAttr(size / 2 * (options.backgroundOptions.round || 0))),
803
+ this.defs.appendChild(this.backgroundMask), element.setAttribute("x", numToAttr(0)),
804
+ element.setAttribute("y", numToAttr(0)), element.setAttribute("width", numToAttr(options.size)),
805
+ element.setAttribute("height", numToAttr(options.size)), element.setAttribute("rx", numToAttr(options.size / 2 * (options.backgroundOptions.round || 0))),
805
806
  this.backgroundMaskGroup.appendChild(element);
806
807
  }
807
808
  }
808
809
  drawDots(filter) {
809
810
  if (!this.qr) throw new Error("QR code is not defined");
810
811
  let options = this.options, count = this.qr.getModuleCount();
811
- if (count > options.width || count > options.height) throw new Error("The canvas is too small");
812
- let dotSize = this.options.dotsOptions.size, minSize = Math.min(options.width, options.height);
813
- options.imageOptions.mode == ImageMode.background && (minSize -= 2 * dotSize * (options.imageOptions.margin || 0));
814
- let xBeginning = Math.floor((options.width - count * dotSize) / 2), yBeginning = Math.floor((options.height - count * dotSize) / 2), draw = getQrDotFigure(options.dotsOptions.type, options.plugins);
812
+ if (count > options.size) throw new Error("The canvas is too small");
813
+ let dotSize = this.options.dotsOptions.size;
814
+ options.imageOptions.mode == ImageMode.background && (options.size -= 2 * dotSize * (options.imageOptions.margin || 0));
815
+ let beginning = Math.floor((options.size - count * dotSize) / 2), draw = getQrDotFigure(options.dotsOptions.type, options.plugins);
815
816
  [this.dotsMask, this.dotsMaskGroup] = this.createMask("mask-dot-color"), this.defs.appendChild(this.dotsMask),
816
817
  options.imageOptions.mode == ImageMode.background && ([this.lightDotsMask, this.lightDotsMaskGroup] = this.createMask("mask-light-dot-color"),
817
818
  this.defs.appendChild(this.lightDotsMask));
818
819
  let margin = 0, additionalDots = 0, fakeCount = count;
819
820
  options.shape === ShapeType.circle ? (margin = this.options.backgroundOptions && this.options.backgroundOptions.margin || 0,
820
- additionalDots = Math.floor((minSize / dotSize - count - 2 * margin) / 2), fakeCount = count + 2 * additionalDots) : options.imageOptions.mode == ImageMode.background && (additionalDots = 1,
821
+ additionalDots = Math.floor((options.size / dotSize - count - 2 * margin) / 2),
822
+ fakeCount = count + 2 * additionalDots) : options.imageOptions.mode == ImageMode.background && (additionalDots = 1,
821
823
  fakeCount = count + 2 * additionalDots);
822
- let xFakeBeginning = xBeginning - additionalDots * dotSize, yFakeBeginning = yBeginning - additionalDots * dotSize, colorX = xFakeBeginning, colorY = yFakeBeginning, colorCount = count + 2 * additionalDots;
824
+ let fakeBeginning = beginning - additionalDots * dotSize, colorCount = count + 2 * additionalDots;
823
825
  this._fakeMatrix = new Array(fakeCount);
824
826
  let center = Math.floor(fakeCount / 2);
825
827
  for (let i = 0; i < fakeCount; i++) {
@@ -840,15 +842,15 @@ class QRSVG {
840
842
  let jAlign = alignment.find(v => j - additionalDots > v - 3 && j - additionalDots < v + 3);
841
843
  null != this._fakeMatrix[i][j] && (this.lightDotsMask && (draw = iAlign && jAlign && (iAlign != alignment[0] && jAlign != alignment[0] || jAlign != alignment[0] && jAlign != alignment[alignment.length - 1] || iAlign != alignment[0] && iAlign != alignment[alignment.length - 1]) ? getQrDotFigure(DotType.square) : getQrDotFigure(DotType.tinySquare)),
842
844
  this._fakeMatrix[i][j] ? this.dotsMaskGroup && this.dotsMaskGroup.appendChild(draw({
843
- x: xFakeBeginning + i * dotSize,
844
- y: yFakeBeginning + j * dotSize,
845
+ x: fakeBeginning + i * dotSize,
846
+ y: fakeBeginning + j * dotSize,
845
847
  size: dotSize,
846
848
  document: this.options.document,
847
849
  getNeighbor: (xOffset, yOffset) => !0 === this._fakeMatrix[i + xOffset]?.[j + yOffset],
848
850
  getPRandom: getRng(this._fakeMatrix, i, j)
849
851
  })) : this.lightDotsMask && this.lightDotsMaskGroup && this.lightDotsMaskGroup.appendChild(draw({
850
- x: xFakeBeginning + i * dotSize,
851
- y: yFakeBeginning + j * dotSize,
852
+ x: fakeBeginning + i * dotSize,
853
+ y: fakeBeginning + j * dotSize,
852
854
  size: dotSize,
853
855
  document: this.options.document,
854
856
  getNeighbor: (xOffset, yOffset) => !1 === this._fakeMatrix[i + xOffset]?.[j + yOffset],
@@ -860,8 +862,8 @@ class QRSVG {
860
862
  options: options.imageOptions.fill.gradient,
861
863
  color: options.imageOptions.fill.color,
862
864
  additionalRotation: 0,
863
- x: colorX,
864
- y: colorY,
865
+ x: fakeBeginning,
866
+ y: fakeBeginning,
865
867
  height: colorCount * dotSize,
866
868
  width: colorCount * dotSize,
867
869
  name: "light-dot-color"
@@ -869,8 +871,8 @@ class QRSVG {
869
871
  options: options.dotsOptions?.gradient,
870
872
  color: options.dotsOptions.color,
871
873
  additionalRotation: 0,
872
- x: colorX,
873
- y: colorY,
874
+ x: fakeBeginning,
875
+ y: fakeBeginning,
874
876
  height: colorCount * dotSize,
875
877
  width: colorCount * dotSize,
876
878
  name: "dot-color"
@@ -880,9 +882,9 @@ class QRSVG {
880
882
  if (!this.qr) throw new Error("QR code is not defined");
881
883
  let element = this._element, options = this.options;
882
884
  if (!element) throw new Error("Element code is not defined");
883
- let count = this.qr.getModuleCount(), dotSize = this.options.dotsOptions.size, cornersSquareSize = 7 * dotSize, cornersDotSize = 3 * dotSize, xBeginning = Math.floor((options.width - count * dotSize) / 2), yBeginning = Math.floor((options.height - count * dotSize) / 2);
885
+ let count = this.qr.getModuleCount(), dotSize = this.options.dotsOptions.size, cornersSquareSize = 7 * dotSize, cornersDotSize = 3 * dotSize, beginning = Math.floor((options.size - count * dotSize) / 2);
884
886
  [ [ 0, 0, 0 ], [ 1, 0, Math.PI / 2 ], [ 0, 1, -Math.PI / 2 ] ].forEach(([column, row, rotation]) => {
885
- let x = xBeginning + column * dotSize * (count - 7), y = yBeginning + row * dotSize * (count - 7), cornersSquareMask = this.dotsMask, cornersSquareMaskGroup = this.dotsMaskGroup, cornersDotMask = this.dotsMask, cornersDotMaskGroup = this.dotsMaskGroup;
887
+ let x = beginning + column * dotSize * (count - 7), y = beginning + row * dotSize * (count - 7), cornersSquareMask = this.dotsMask, cornersSquareMaskGroup = this.dotsMaskGroup, cornersDotMask = this.dotsMask, cornersDotMaskGroup = this.dotsMaskGroup;
886
888
  (options.cornersSquareOptions?.gradient || options.cornersSquareOptions?.color) && ([cornersSquareMask, cornersSquareMaskGroup] = this.createMask(`mask-corners-square-color-${column}-${row}`),
887
889
  this.defs.appendChild(cornersSquareMask), cornersDotMask = cornersSquareMask, cornersDotMaskGroup = cornersSquareMaskGroup,
888
890
  this.createColor({
@@ -989,9 +991,9 @@ class QRSVG {
989
991
  });
990
992
  }
991
993
  async drawImage({width, height, count, dotSize}) {
992
- let options = this.options, xBeginning = Math.floor((options.width - count * dotSize) / 2), yBeginning = Math.floor((options.height - count * dotSize) / 2), margin = options.imageOptions.margin * dotSize;
994
+ let options = this.options, beginning = Math.floor((options.size - count * dotSize) / 2), margin = options.imageOptions.margin * dotSize;
993
995
  options.imageOptions.mode == ImageMode.background && (margin = 0);
994
- let dx = xBeginning + margin + (count * dotSize - width) / 2, dy = yBeginning + margin + (count * dotSize - height) / 2, dw = width - 2 * margin, dh = height - 2 * margin, imageUrl = await this.imageTools.toDataURL(options.image || ""), image = this.document.createElementNS("http://www.w3.org/2000/svg", "image");
996
+ let dx = beginning + margin + (count * dotSize - width) / 2, dy = beginning + margin + (count * dotSize - height) / 2, dw = width - 2 * margin, dh = height - 2 * margin, imageUrl = await this.imageTools.toDataURL(options.image || ""), image = this.document.createElementNS("http://www.w3.org/2000/svg", "image");
995
997
  if (image.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", imageUrl || ""),
996
998
  imageUrl.startsWith("data:image/svg+xml;")) {
997
999
  let data = imageUrl.substring(19), i = data.indexOf(","), encoding = data.substring(0, i);
@@ -1046,8 +1048,8 @@ class QRSVG {
1046
1048
  createMask(id) {
1047
1049
  let options = this.options, mask = this.document.createElementNS("http://www.w3.org/2000/svg", "mask");
1048
1050
  mask.setAttribute("id", id), mask.setAttribute("maskUnits", "userSpaceOnUse"), mask.setAttribute("x", "0"),
1049
- mask.setAttribute("y", "0"), mask.setAttribute("width", numToAttr(options.width)),
1050
- mask.setAttribute("height", numToAttr(options.height));
1051
+ mask.setAttribute("y", "0"), mask.setAttribute("width", numToAttr(options.size)),
1052
+ mask.setAttribute("height", numToAttr(options.size));
1051
1053
  let group = this.document.createElementNS("http://www.w3.org/2000/svg", "g");
1052
1054
  return group.setAttribute("fill", "#fff"), mask.appendChild(group), [ mask, group ];
1053
1055
  }
@@ -1125,16 +1127,19 @@ let QRCodeStyling$1 = class {
1125
1127
  ...this.options,
1126
1128
  width: size,
1127
1129
  height: size,
1130
+ size,
1128
1131
  errorCorrectionPercent: ErrorCorrectionPercents[this.options.qrOptions.errorCorrectionLevel]
1129
1132
  }), this.svgDrawingPromise = this.qrSVG.drawQR(this.qr).then(() => {
1130
1133
  this.qrSVG?.element && (this.options.plugins?.forEach(plugin => plugin.postProcess?.(this.qrSVG.element, {
1131
1134
  ...this.options,
1132
1135
  width: size,
1133
- height: size
1136
+ height: size,
1137
+ size
1134
1138
  })), this.extension?.(this.qrSVG.element, {
1135
1139
  ...this.options,
1136
1140
  width: size,
1137
- height: size
1141
+ height: size,
1142
+ size
1138
1143
  }));
1139
1144
  });
1140
1145
  }