diff --git a/src/main/bundles/dev.bundle b/src/main/bundles/dev.bundle index c9d4589..0fc7d9c 100644 Binary files a/src/main/bundles/dev.bundle and b/src/main/bundles/dev.bundle differ diff --git a/src/main/frontend/invoice-generator/profile-invoice-generator.js b/src/main/frontend/invoice-generator/profile-invoice-generator.js index 0b2d684..a27fb6c 100644 --- a/src/main/frontend/invoice-generator/profile-invoice-generator.js +++ b/src/main/frontend/invoice-generator/profile-invoice-generator.js @@ -45,6 +45,9 @@ window.initProfileInvoiceGenerator = function() { var elementStart = { x: 0, y: 0 }; var gridSize = 5; + // Image cache to prevent flickering during resize + var imageCache = {}; + // Page dimensions var padding = 10; var pageX, pageY, pageWidth, pageHeight; @@ -167,35 +170,58 @@ window.initProfileInvoiceGenerator = function() { ctx.stroke(); } else if (el.type === 'image') { if (el.imageData) { - // Draw the uploaded image with aspect ratio preserved - var img = new Image(); - img.onload = function() { + // Check if image is already cached + if (imageCache[el.imageData]) { + // Use cached image for flicker-free rendering + var img = imageCache[el.imageData]; // Calculate dimensions to maintain aspect ratio (object-fit: contain) var imgAspect = img.width / img.height; var boxAspect = w / h; var drawW, drawH, drawX, drawY; if (imgAspect > boxAspect) { - // Image is wider than box - fit to width drawW = w; drawH = w / imgAspect; drawX = x; - drawY = y + (h - drawH) / 2; // Center vertically + drawY = y + (h - drawH) / 2; } else { - // Image is taller than box - fit to height drawW = h * imgAspect; drawH = h; - drawX = x + (w - drawW) / 2; // Center horizontally + drawX = x + (w - drawW) / 2; drawY = y; } ctx.drawImage(img, drawX, drawY, drawW, drawH); - // Redraw selection if this element is selected - if (selectedElement && selectedElement.id === el.id) { - drawSelection(el); - } - }; - img.src = el.imageData; + } else { + // Load and cache the image + var img = new Image(); + img.onload = function() { + imageCache[el.imageData] = img; + // Calculate dimensions to maintain aspect ratio + var imgAspect = img.width / img.height; + var boxAspect = w / h; + var drawW, drawH, drawX, drawY; + + if (imgAspect > boxAspect) { + drawW = w; + drawH = w / imgAspect; + drawX = x; + drawY = y + (h - drawH) / 2; + } else { + drawW = h * imgAspect; + drawH = h; + drawX = x + (w - drawW) / 2; + drawY = y; + } + + ctx.drawImage(img, drawX, drawY, drawW, drawH); + // Redraw selection if this element is selected + if (selectedElement && selectedElement.id === el.id) { + drawSelection(el); + } + }; + img.src = el.imageData; + } } else { // Draw placeholder ctx.fillStyle = '#f0f0f0'; @@ -799,10 +825,41 @@ window.initProfileInvoiceGenerator = function() { }; // Get canvas data + // Helper functions for percentage conversion + function toPercentX(value) { + return (value / basePageWidth * 100).toFixed(2); + } + function toPercentY(value) { + return (value / basePageHeight * 100).toFixed(2); + } + function fromPercentX(percent) { + return Math.round(parseFloat(percent) / 100 * basePageWidth); + } + function fromPercentY(percent) { + return Math.round(parseFloat(percent) / 100 * basePageHeight); + } + window.getProfileCanvasData = function() { saveState(); + // Convert pixel values to percentages for storage + var elementsWithPercent = elements.map(function(el) { + return { + id: el.id, + type: el.type, + text: el.text, + xPercent: toPercentX(el.x), + yPercent: toPercentY(el.y), + widthPercent: toPercentX(el.width), + heightPercent: toPercentY(el.height), + fontSize: el.fontSize, + fontStyle: el.fontStyle, + color: el.color, + isStatic: el.isStatic, + imageData: el.imageData + }; + }); return { - elements: elements + elements: elementsWithPercent }; }; @@ -868,6 +925,21 @@ window.initProfileInvoiceGenerator = function() { elementCounter++; el.id = 'element-' + elementCounter; } + + // Convert percentages to pixels if they exist, otherwise use legacy pixel values + if (el.xPercent !== undefined) { + el.x = fromPercentX(el.xPercent); + } + if (el.yPercent !== undefined) { + el.y = fromPercentY(el.yPercent); + } + if (el.widthPercent !== undefined) { + el.width = fromPercentX(el.widthPercent); + } + if (el.heightPercent !== undefined) { + el.height = fromPercentY(el.heightPercent); + } + elements.push(el); }); diff --git a/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java b/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java index a014397..3e30e14 100644 --- a/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java +++ b/src/main/java/de/assecutor/votianlt/service/CustomerInvoiceService.java @@ -273,19 +273,44 @@ public class CustomerInvoiceService { for (com.fasterxml.jackson.databind.JsonNode element : elements) { String type = element.has("type") ? element.get("type").asText("text") : "text"; String text = element.has("text") ? element.get("text").asText("") : ""; - double x = element.has("x") ? element.get("x").asDouble(0) : 0; - double y = element.has("y") ? element.get("y").asDouble(0) : 0; - double width = element.has("width") ? element.get("width").asDouble(150) : 150; - double height = element.has("height") ? element.get("height").asDouble(30) : 30; + + // Use percentage values if available, otherwise fall back to legacy pixel values + double xPercent, yPercent, widthPercent, heightPercent; + if (element.has("xPercent")) { + xPercent = element.get("xPercent").asDouble(0); + } else { + // Legacy: convert pixels to percent + double x = element.get("x").asDouble(0); + xPercent = x / 595.0 * 100; + } + if (element.has("yPercent")) { + yPercent = element.get("yPercent").asDouble(0); + } else { + double y = element.get("y").asDouble(0); + yPercent = y / 842.0 * 100; + } + if (element.has("widthPercent")) { + widthPercent = element.get("widthPercent").asDouble(15); + } else { + double width = element.get("width").asDouble(150); + widthPercent = width / 595.0 * 100; + } + if (element.has("heightPercent")) { + heightPercent = element.get("heightPercent").asDouble(3); + } else { + double height = element.get("height").asDouble(30); + heightPercent = height / 842.0 * 100; + } + int fontSize = element.has("fontSize") ? element.get("fontSize").asInt(14) : 14; String fontStyle = element.has("fontStyle") ? element.get("fontStyle").asText("") : ""; String color = element.has("color") ? element.get("color").asText("#333333") : "#333333"; - // Convert canvas coordinates to mm (assuming 96 DPI) - double mmX = x * 25.4 / 96; - double mmY = y * 25.4 / 96; - double mmWidth = width * 25.4 / 96; - double mmHeight = height * 25.4 / 96; + // Convert percentages to mm (A4 is 210mm x 297mm) + double mmX = xPercent / 100.0 * 210.0; + double mmY = yPercent / 100.0 * 297.0; + double mmWidth = widthPercent / 100.0 * 210.0; + double mmHeight = heightPercent / 100.0 * 297.0; htmlBuilder.append("