Fix PDF generation not using DPI settings

This commit is contained in:
2026-06-29 09:41:02 +02:00
parent 8207e84fa2
commit cc9170de9d
+57 -63
View File
@@ -14,23 +14,14 @@ export function downloadBlob(blob: Blob, filename: string) {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
} }
export async function prepareSVG( export async function prepareSVG(handler: VectraceHandler, imageRender: CanvasImageEx): Promise<Blob> {
handler: VectraceHandler,
imageRender: CanvasImageEx
): Promise<Blob> {
if (imageRender.svg.scale !== imageRender.scale || imageRender.svg.dirty) { if (imageRender.svg.scale !== imageRender.scale || imageRender.svg.dirty) {
handler.redrawSvg(imageRender.id); handler.redrawSvg(imageRender.id);
} }
return new Blob([imageRender.svg.data], { type: "image/svg+xml" }); return new Blob([imageRender.svg.data], { type: "image/svg+xml" });
} }
function getGridCells(paperWidth: number, paperHeight: number, grid: string): GridCell[] {
function getGridCells(
paperWidth: number,
paperHeight: number,
grid: string
): GridCell[] {
const cols = grid === "3x3" ? 3 : grid === "2x2" ? 2 : 1; const cols = grid === "3x3" ? 3 : grid === "2x2" ? 2 : 1;
const rows = grid === "3x3" ? 3 : grid === "2x2" ? 2 : 1; const rows = grid === "3x3" ? 3 : grid === "2x2" ? 2 : 1;
const cellW = paperWidth / cols; const cellW = paperWidth / cols;
@@ -51,39 +42,51 @@ function getGridCells(
return cells; return cells;
} }
function getGridCellsMm(paperWidth: number, paperHeight: number, grid: string): GridCell[] {
const cols = grid === "3x3" ? 3 : grid === "2x2" ? 2 : 1;
const rows = grid === "3x3" ? 3 : grid === "2x2" ? 2 : 1;
const cellW = paperWidth / cols;
const cellH = paperHeight / rows;
const cells: GridCell[] = [];
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
cells.push({
col,
row,
x: col * cellW,
y: row * cellH,
w: cellW,
h: cellH,
});
}
}
return cells;
}
function createCellPdf( function createCellPdf(handler: VectraceHandler, cellMm: GridCell): jsPDF {
handler: VectraceHandler, const { DPI } = handler.globalSettingsHandler.settings;
cell: GridCell
): jsPDF {
const pxToMm = (px: number): number => (px / DPI) * MM_TO_INCH;
const doc = new jsPDF({ const doc = new jsPDF({
orientation: cell.w > cell.h ? "landscape" : "portrait", orientation: cellMm.w > cellMm.h ? "landscape" : "portrait",
unit: "mm", unit: "mm",
format: [cell.w, cell.h], format: [cellMm.w, cellMm.h],
}); });
for (const imageObject of handler.imagesRenders) { for (const imageObject of handler.imagesRenders) {
if (!imageObject.visible) continue; if (!imageObject.visible) continue;
const imgX = imageObject.x - cell.x; const imgXMm = pxToMm(imageObject.x) - cellMm.x;
const imgY = imageObject.y - cell.y; const imgYMm = pxToMm(imageObject.y) - cellMm.y;
const imgW = imageObject.image.naturalWidth * imageObject.scale; const imgWMm = pxToMm(imageObject.image.naturalWidth * imageObject.scale);
const imgH = imageObject.image.naturalHeight * imageObject.scale; const imgHMm = pxToMm(imageObject.image.naturalHeight * imageObject.scale);
const overlapX = imgX + imgW > 0 && imgX < cell.w; const overlapX = imgXMm + imgWMm > 0 && imgXMm < cellMm.w;
const overlapY = imgY + imgH > 0 && imgY < cell.h; const overlapY = imgYMm + imgHMm > 0 && imgYMm < cellMm.h;
if (!overlapX || !overlapY) continue; if (!overlapX || !overlapY) continue;
doc.addImage( doc.addImage(imageObject.image, "PNG", imgXMm, imgYMm, imgWMm, imgHMm);
imageObject.image,
"PNG",
imgX,
imgY,
imgW,
imgH
);
} }
return doc; return doc;
@@ -95,47 +98,44 @@ const gridConfigs: Record<string, number[]> = {
}; };
export function createPdf(handler: VectraceHandler, withGuides: boolean): jsPDF { export function createPdf(handler: VectraceHandler, withGuides: boolean): jsPDF {
const { DPI, paperHeight, paperWidth } = handler.globalSettingsHandler.settings; const { DPI, paperHeight, paperWidth } = handler.globalSettingsHandler.settings;
const width = (paperWidth / MM_TO_INCH) * DPI;
const height = (paperHeight / MM_TO_INCH) * DPI; const pxToMm = (px: number): number => (px / DPI) * MM_TO_INCH;
const doc = new jsPDF({ const doc = new jsPDF({
orientation: paperWidth > paperHeight ? "landscape" : "portrait", orientation: paperWidth > paperHeight ? "landscape" : "portrait",
unit: "mm", unit: "mm",
format: [width, height], format: [paperWidth, paperHeight],
}); });
if (withGuides) { if (withGuides) {
const setLine = () => {
doc.setDrawColor(64, 64, 64); doc.setDrawColor(64, 64, 64);
doc.setLineWidth(1); doc.setLineWidth(0.5);
};
const fractions = gridConfigs[handler.globalSettingsHandler.settings.grid]; const fractions = gridConfigs[handler.globalSettingsHandler.settings.grid];
if (fractions) { if (fractions) {
setLine(); fractions.forEach((f) => {
fractions.forEach(f => { doc.line(paperWidth * f, 0, paperWidth * f, paperHeight);
doc.line(width * f, 0, width * f, height); doc.line(0, paperHeight * f, paperWidth, paperHeight * f);
doc.line(0, height * f, width, height * f);
}); });
} }
} }
for (const imageObject of handler.imagesRenders) { for (const imageObject of handler.imagesRenders) {
if (imageObject.visible) { if (!imageObject.visible) continue;
doc.addImage( doc.addImage(
imageObject.image, imageObject.image,
"PNG", "PNG",
imageObject.x, pxToMm(imageObject.x),
imageObject.y, pxToMm(imageObject.y),
imageObject.image.naturalWidth * imageObject.scale, pxToMm(imageObject.image.naturalWidth * imageObject.scale),
imageObject.image.naturalHeight * imageObject.scale pxToMm(imageObject.image.naturalHeight * imageObject.scale),
); );
} }
}
return doc; return doc;
} }
export function createSVGDoc(handler: VectraceHandler) { export function createSVGDoc(handler: VectraceHandler) {
const { paperWidth, paperHeight, DPI } = handler.globalSettingsHandler.settings; const { paperWidth, paperHeight, DPI } = handler.globalSettingsHandler.settings;
const width = (paperWidth / MM_TO_INCH) * DPI; const width = (paperWidth / MM_TO_INCH) * DPI;
@@ -172,7 +172,7 @@ export async function downloadZip(handler: VectraceHandler): Promise<void> {
canvas.height = render.image.naturalHeight * render.scale; canvas.height = render.image.naturalHeight * render.scale;
ctx.drawImage(render.image, 0, 0, canvas.width, canvas.height); ctx.drawImage(render.image, 0, 0, canvas.width, canvas.height);
const blob = new Promise<Blob>((resolve, reject) => { const blob = new Promise<Blob>((resolve, reject) => {
canvas.toBlob(data => { canvas.toBlob((data) => {
if (data) { if (data) {
resolve(data); resolve(data);
} else { } else {
@@ -204,16 +204,17 @@ export async function downloadZip(handler: VectraceHandler): Promise<void> {
if (grid !== "none") { if (grid !== "none") {
const cells = getGridCells(width, height, grid); const cells = getGridCells(width, height, grid);
const cellsMm = getGridCellsMm(paperWidth, paperHeight, grid);
const cellPdfs = mainFolder.folder("cells/pdfs")!; const cellPdfs = mainFolder.folder("cells/pdfs")!;
const cellSvgs = mainFolder.folder("cells/svgs")!; const cellSvgs = mainFolder.folder("cells/svgs")!;
for (const cell of cells) { for (let i = 0; i < cells.length; i++) {
const label = `cell-r${cell.row}-c${cell.col}`; const label = `cell-r${cells[i].row}-c${cells[i].col}`;
const cellPdf = createCellPdf(handler, cell); const cellPdf = createCellPdf(handler, cellsMm[i]);
cellPdfs.file(`${label}.pdf`, cellPdf.output("blob")); cellPdfs.file(`${label}.pdf`, cellPdf.output("blob"));
const cellSvgBlob = await createCellSvg(handler, cell); const cellSvgBlob = await createCellSvg(handler, cells[i]);
cellSvgs.file(`${label}.svg`, cellSvgBlob); cellSvgs.file(`${label}.svg`, cellSvgBlob);
} }
} }
@@ -231,10 +232,7 @@ function collectPaths(node: INode): INode[] {
return results; return results;
} }
export async function createCellSvg( export async function createCellSvg(handler: VectraceHandler, cell: GridCell) {
handler: VectraceHandler,
cell: GridCell
) {
const pathElements: string[] = []; const pathElements: string[] = [];
for (const imageObject of handler.imagesRenders) { for (const imageObject of handler.imagesRenders) {
if (!imageObject.visible) continue; if (!imageObject.visible) continue;
@@ -249,11 +247,7 @@ export async function createCellSvg(
const imgH = imageObject.image.naturalHeight * imageObject.scale; const imgH = imageObject.image.naturalHeight * imageObject.scale;
// Does this image even overlap the cell at all? // Does this image even overlap the cell at all?
const overlaps = const overlaps = imgX + imgW > cell.x && imgX < cell.x + cell.w && imgY + imgH > cell.y && imgY < cell.y + cell.h;
imgX + imgW > cell.x &&
imgX < cell.x + cell.w &&
imgY + imgH > cell.y &&
imgY < cell.y + cell.h;
if (!overlaps) continue; if (!overlaps) continue;
@@ -271,7 +265,7 @@ export async function createCellSvg(
pathElements.push( pathElements.push(
`<g transform="translate(${imgX - cell.x} ${imgY - cell.y})">` + `<g transform="translate(${imgX - cell.x} ${imgY - cell.y})">` +
`<path d="${d}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}"/>` + `<path d="${d}" fill="${fill}" stroke="${stroke}" stroke-width="${strokeWidth}"/>` +
`</g>` `</g>`,
); );
} }
} }