// src/infrastructure/svg-to-png.js /** * @param {SVGElement} svg - виртуальный SVG (готовый для рендеринга) * @param {{x:number,y:number,width:number,height:number}} rect - область в единицах SVG user units * @param {number} scale - множитель (вызов передаёт, например, 3) * @param {(blob:Blob|null)=>void} callBack */ export function svgToPng(svg, rect, scale, callBack) { if (!svg || !rect || rect.width <= 0 || rect.height <= 0) { callBack(null); return; } // output размеры с учётом devicePixelRatio const dpr = window.devicePixelRatio || 1; const outputWidth = Math.round(rect.width * scale * dpr); const outputHeight = Math.round(rect.height * scale * dpr); // Сериализуем svg в строку и делаем blob/url let svgString; try { svgString = new XMLSerializer().serializeToString(svg); } catch (e) { callBack(null); return; } let url; try { url = URL.createObjectURL(new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' })); } catch (e) { callBack(null); return; } const img = new Image(); // crossOrigin можно добавить, если нужно: img.crossOrigin = 'anonymous'; img.onload = function () { try { const canvas = document.createElement('canvas'); canvas.width = outputWidth; canvas.height = outputHeight; canvas.style.width = `${outputWidth}px`; canvas.style.height = `${outputHeight}px`; const ctx = canvas.getContext('2d'); if (!ctx) { URL.revokeObjectURL(url); callBack(null); return; } ctx.imageSmoothingEnabled = false; // Если rect.x/rect.y отрицательные => смещение исходного изображения внутри canvas const sx = Math.max(0, rect.x * scale * dpr); const sy = Math.max(0, rect.y * scale * dpr); const sWidth = rect.width * scale * dpr; const sHeight = rect.height * scale * dpr; // dx/dy: смещение на canvas (если rect.x < 0, то мы сдвигаем вправо) const dx = rect.x < 0 ? -rect.x * scale * dpr : 0; const dy = rect.y < 0 ? -rect.y * scale * dpr : 0; // drawImage с указанием исходной области и целевой области ctx.drawImage( img, sx, // sx sy, // sy sWidth, // sWidth sHeight, // sHeight dx, // dx (на canvas) dy, // dy outputWidth, // dWidth outputHeight // dHeight ); URL.revokeObjectURL(url); canvas.toBlob(blob => { callBack(blob); }, 'image/png'); } catch (err) { URL.revokeObjectURL(url); callBack(null); } }; img.onerror = function () { try { URL.revokeObjectURL(url); } catch (e) {} callBack(null); }; img.src = url; }