import QRCode from 'qrcode-svg'; import pretty from 'pretty'; const coastLogo = ` `.trim(); // Constants for rendering the QR code. These have no effect on the actual size of // the SVG output, just its aspect ratio and internal layout. const codeSize = 200; const borderWidth = 4; const logoWidth = 240; const logoHeight = 95; export interface CreateQRCodeOptions { url: string; width?: number; showBorder?: boolean; showLogo?: boolean; padding?: number; radius?: number; } export function createQRCode({ url, width, showBorder, showLogo = true, padding = 20, radius = 10, }: CreateQRCodeOptions): string { const code = new QRCode({ content: url, container: 'none', width: codeSize, height: codeSize, padding: 0, }); const svg = code.svg(); // first line of the svg is a 200x200 rect that we don't need const rects = svg.trim().split('\n').slice(1); return renderSvg(rects, code.qrcode, { url, width, showBorder, showLogo, padding, radius }); } function renderSvg(rects: string[], qrcode: QRCode.Model, options: CreateQRCodeOptions): string { const cellWidth = codeSize / qrcode.moduleCount; const { padding, radius } = options; const { aspectRatio, viewBoxWidth, viewBoxHeight, logoScale, logoLeft, logoTop } = getLayoutProps(options); // add corner radius to each cell and remove style attribute const dots = rects.map((rect) => { return rect.replace(/ `; const cornerOffset = codeSize - cornerSize; const topLeftCorner = `${corner}`; const topRightCorner = `${corner}`; const bottomLeftCorner = `${corner}`; const background = ``; const viewBox = `viewBox="0 0 ${viewBoxWidth} ${viewBoxHeight}"`; const width = options.width ? `width="${options.width}"` : ''; const height = options.width ? `height="${toFixed(options.width / aspectRatio)}"` : ''; const logo = ` ${coastLogo} `; const lines = [ ``, background, ``, dots.join('\n'), topLeftCorner, topRightCorner, bottomLeftCorner, '', options.showLogo ? logo : '', '', ].filter(Boolean); return pretty(lines.join('\n')); } function toFixed(num: number): string { return num.toFixed(3).replace(/\.?0+$/, ''); } function getLayoutProps(options: CreateQRCodeOptions) { const viewBoxWidth = codeSize + options.padding * 2; const aspectRatio = options.showLogo ? 0.75 : 1; const viewBoxHeight = viewBoxWidth / aspectRatio; const logoScale = viewBoxWidth / logoWidth; const logoLeft = viewBoxWidth / 2 - (logoWidth * logoScale) / 2; const logoTop = viewBoxHeight - logoHeight * logoScale - borderWidth; return { aspectRatio, viewBoxWidth, viewBoxHeight, logoScale, logoLeft, logoTop, }; }