Print SVG
tsx
import { createRoot } from 'react-dom/client'
type SvgInput = React.FC | SVGSVGElement | string // string = URL
const delay = (ms: number) =>
new Promise<void>((resolve) => {
setTimeout(resolve, ms)
})
export async function printInvoice(
input: SvgInput,
map?: Record<string, string>
) {
// Create a container to render the SVG component
const container = document.createElement('div')
document.body.appendChild(container)
let svg: SVGSVGElement | null = null
if (typeof input === 'function') {
// Case: React Component
const root = createRoot(container)
root.render(<input />)
await delay(100) // Wait for render
svg = container.querySelector('svg')
// Unmount component and cleanup after cloning
root.unmount()
document.body.removeChild(container)
} else if (typeof input === 'string') {
// Case: URL to SVG
const localContainer = container // freeze reference before await
const response = await fetch(input)
const svgText = await response.text()
localContainer.innerHTML = svgText
svg = localContainer.querySelector('svg')
document.body.removeChild(localContainer)
} else if (input instanceof SVGSVGElement) {
// Case: Already an SVG element
svg = input.cloneNode(true) as SVGSVGElement
document.body.removeChild(container)
}
if (!svg) {
// eslint-disable-next-line no-console
console.error('SVG not found or failed to load')
return
}
if (map) {
// Replace text inside elements with matching IDs
for (const [id, value] of Object.entries(map)) {
const element = svg.querySelector(`#${id}`)
if (element) {
element.textContent = value
}
}
}
const fontCss = `
/* Regular Weight */
@font-face {
font-family: 'PingARLT';
font-style: normal;
font-weight: 400; /* 'normal' or '400' */
src: url('${window.location.origin}/app/assets/fonts/PingARLT-Regular.woff2') format('woff2');
}
/* Medium Weight */
@font-face {
font-family: 'PingARLT';
font-style: normal;
font-weight: 500; /* 'medium' or '500' */
src: url('${window.location.origin}/app/assets/fonts/PingARLT-Medium.woff2') format('woff2');
}
/* Bold Weight */
@font-face {
font-family: 'PingARLT';
font-style: normal;
font-weight: 700; /* 'bold' or '700' */
src: url('${window.location.origin}/app/assets/fonts/PingARLT-Bold.woff2') format('woff2');
}
/* --- General Styling --- */
@media print {
@page { margin: 0; size: auto; }
body { margin: 0; }
}
body {
/* Set the default font for the entire page */
font-family: 'PingARLT', sans-serif;
}
svg {
max-width: 100%;
max-height: 100vh;
/* Ensure the font is also applied inside the SVG */
font-family: 'PingARLT', sans-serif;
}
/*
THE FIX: Use the universal selector (*) with !important.
This forces every single element to use your desired font,
overriding any inline styles or classes from the SVG file.
*/
* {
font-family: 'PingARLT', sans-serif !important;
}
`
const printWindow = window.open('', '_blank')
if (printWindow) {
// 1. Write a basic HTML structure to the new window.
printWindow.document.write(`
<html>
<head>
<title>Print</title>
<style>
${fontCss}
</style>
</head>
<body>
</body>
</html>
`)
// 2. Inject the SVG into the body of the new window.
printWindow.document.body.innerHTML = svg.outerHTML
printWindow.document.close() // Important: End the document writing stream.
// 3. Set the event handler from the parent window. This cannot be overwritten by scripts in the SVG.
// printWindow.onafterprint = () => {
// printWindow.close()
// }
// 4. Call print. We wrap it in a timeout to ensure the content has had a moment to render.
setTimeout(() => {
printWindow.focus() // Focus the new window before printing
printWindow.print()
}, 250) // A small delay can help ensure rendering is complete.
}
}