94 lines
3.2 KiB
JavaScript
Executable File
94 lines
3.2 KiB
JavaScript
Executable File
// 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;
|
||
}
|