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 = [
`',
].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,
};
}