Erweiterungen
This commit is contained in:
Binary file not shown.
@@ -48,14 +48,23 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
// Page dimensions
|
// Page dimensions
|
||||||
var padding = 10;
|
var padding = 10;
|
||||||
var pageX, pageY, pageWidth, pageHeight;
|
var pageX, pageY, pageWidth, pageHeight;
|
||||||
|
var zoomFactor = 1;
|
||||||
|
var basePageWidth = 595; // A4 width in pixels at 96 DPI
|
||||||
|
var basePageHeight = 842; // A4 height in pixels at 96 DPI
|
||||||
|
|
||||||
function updatePageDimensions() {
|
function updatePageDimensions() {
|
||||||
var w = canvas.width;
|
var w = canvas.width;
|
||||||
var h = canvas.height;
|
var h = canvas.height;
|
||||||
var availableWidth = w - padding * 2;
|
var availableWidth = w - padding * 2;
|
||||||
var availableHeight = h - padding * 2;
|
var availableHeight = h - padding * 2;
|
||||||
pageWidth = Math.min(availableWidth, availableHeight * 0.707);
|
|
||||||
pageHeight = pageWidth / 0.707;
|
// Calculate zoom factor based on available space
|
||||||
|
var zoomX = availableWidth / basePageWidth;
|
||||||
|
var zoomY = availableHeight / basePageHeight;
|
||||||
|
zoomFactor = Math.min(zoomX, zoomY);
|
||||||
|
|
||||||
|
pageWidth = basePageWidth * zoomFactor;
|
||||||
|
pageHeight = basePageHeight * zoomFactor;
|
||||||
pageX = (w - pageWidth) / 2;
|
pageX = (w - pageWidth) / 2;
|
||||||
pageY = (h - pageHeight) / 2;
|
pageY = (h - pageHeight) / 2;
|
||||||
}
|
}
|
||||||
@@ -111,16 +120,17 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1;
|
||||||
ctx.strokeRect(pageX, pageY, pageWidth, pageHeight);
|
ctx.strokeRect(pageX, pageY, pageWidth, pageHeight);
|
||||||
|
|
||||||
// Grid
|
// Grid (scaled by zoom factor)
|
||||||
ctx.strokeStyle = 'rgba(200, 200, 200, 0.3)';
|
ctx.strokeStyle = 'rgba(200, 200, 200, 0.3)';
|
||||||
ctx.lineWidth = 0.5;
|
ctx.lineWidth = 0.5;
|
||||||
for (var x = pageX; x <= pageX + pageWidth; x += gridSize) {
|
var scaledGridSize = gridSize * zoomFactor;
|
||||||
|
for (var x = pageX; x <= pageX + pageWidth; x += scaledGridSize) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x, pageY);
|
ctx.moveTo(x, pageY);
|
||||||
ctx.lineTo(x, pageY + pageHeight);
|
ctx.lineTo(x, pageY + pageHeight);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
for (var y = pageY; y <= pageY + pageHeight; y += gridSize) {
|
for (var y = pageY; y <= pageY + pageHeight; y += scaledGridSize) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(pageX, y);
|
ctx.moveTo(pageX, y);
|
||||||
ctx.lineTo(pageX + pageWidth, y);
|
ctx.lineTo(pageX + pageWidth, y);
|
||||||
@@ -141,14 +151,16 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
function drawElement(el) {
|
function drawElement(el) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
|
||||||
var x = pageX + el.x;
|
// Apply zoom factor to position and size
|
||||||
var y = pageY + el.y;
|
var x = pageX + (el.x * zoomFactor);
|
||||||
var w = el.width || 100;
|
var y = pageY + (el.y * zoomFactor);
|
||||||
var h = el.height || 30;
|
var w = (el.width || 100) * zoomFactor;
|
||||||
|
var h = (el.height || 30) * zoomFactor;
|
||||||
|
var fontSize = (el.fontSize || 14) * zoomFactor;
|
||||||
|
|
||||||
if (el.type === 'line') {
|
if (el.type === 'line') {
|
||||||
ctx.strokeStyle = el.color || '#333333';
|
ctx.strokeStyle = el.color || '#333333';
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = Math.max(1, zoomFactor);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x, y);
|
ctx.moveTo(x, y);
|
||||||
ctx.lineTo(x + w, y);
|
ctx.lineTo(x + w, y);
|
||||||
@@ -159,29 +171,32 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
ctx.strokeStyle = '#999999';
|
ctx.strokeStyle = '#999999';
|
||||||
ctx.strokeRect(x, y, w, h);
|
ctx.strokeRect(x, y, w, h);
|
||||||
ctx.fillStyle = '#666666';
|
ctx.fillStyle = '#666666';
|
||||||
ctx.font = '12px Arial';
|
ctx.font = Math.max(8, 12 * zoomFactor) + 'px Arial';
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
ctx.fillText('Bild', x + w / 2, y + h / 2);
|
ctx.fillText('Bild', x + w / 2, y + h / 2);
|
||||||
} else {
|
} else {
|
||||||
// Text elements
|
// Text elements
|
||||||
ctx.font = (el.fontStyle || '') + ' ' + (el.fontSize || 14) + 'px Arial';
|
ctx.font = (el.fontStyle || '') + ' ' + fontSize + 'px Arial';
|
||||||
ctx.fillStyle = el.color || '#333333';
|
ctx.fillStyle = el.color || '#333333';
|
||||||
ctx.textBaseline = 'top';
|
|
||||||
|
var lines = (el.text || '').split('\n');
|
||||||
|
var lineHeight = fontSize * 1.2;
|
||||||
|
var totalTextHeight = lines.length * lineHeight;
|
||||||
|
|
||||||
|
// Vertically center the text in the element
|
||||||
|
var ty = y + (h - totalTextHeight) / 2;
|
||||||
|
|
||||||
// Draw background highlight for static elements
|
// Draw background highlight for static elements
|
||||||
if (el.isStatic) {
|
if (el.isStatic) {
|
||||||
var textWidth = ctx.measureText(el.text || '').width + 10;
|
var textWidth = ctx.measureText(el.text || '').width + (10 * zoomFactor);
|
||||||
ctx.fillStyle = 'rgba(25, 118, 210, 0.1)'; // Light blue background
|
ctx.fillStyle = 'rgba(25, 118, 210, 0.1)'; // Light blue background
|
||||||
ctx.fillRect(x - 3, y - 2, Math.max(w, textWidth), h);
|
ctx.fillRect(x - (3 * zoomFactor), y - (2 * zoomFactor), Math.max(w, textWidth), h);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = el.color || '#333333';
|
ctx.fillStyle = el.color || '#333333';
|
||||||
ctx.font = (el.fontStyle || '') + ' ' + (el.fontSize || 14) + 'px Arial';
|
ctx.font = (el.fontStyle || '') + ' ' + fontSize + 'px Arial';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
var lines = (el.text || '').split('\n');
|
|
||||||
var lineHeight = (el.fontSize || 14) * 1.2;
|
|
||||||
var ty = y;
|
|
||||||
|
|
||||||
lines.forEach(function(line) {
|
lines.forEach(function(line) {
|
||||||
ctx.fillText(line, x, ty);
|
ctx.fillText(line, x, ty);
|
||||||
@@ -193,22 +208,27 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawSelection(el) {
|
function drawSelection(el) {
|
||||||
var x = pageX + el.x;
|
var x = pageX + (el.x * zoomFactor);
|
||||||
var y = pageY + el.y;
|
var y = pageY + (el.y * zoomFactor);
|
||||||
var w = el.width || 100;
|
var w = (el.width || 100) * zoomFactor;
|
||||||
var h = el.height || 30;
|
var h = (el.height || 30) * zoomFactor;
|
||||||
var hs = 8; // handle size
|
|
||||||
|
|
||||||
ctx.strokeStyle = '#1976d2';
|
ctx.strokeStyle = '#1976d2';
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = Math.max(1, 2 * zoomFactor);
|
||||||
ctx.setLineDash([5, 3]);
|
ctx.setLineDash([5, 3]);
|
||||||
ctx.strokeRect(x, y, w, h);
|
ctx.strokeRect(x, y, w, h);
|
||||||
ctx.setLineDash([]);
|
ctx.setLineDash([]);
|
||||||
|
|
||||||
|
// Don't show resize handles for static elements
|
||||||
|
if (el.isStatic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Handles
|
// Handles
|
||||||
|
var hs = Math.max(6, 8 * zoomFactor); // handle size scaled
|
||||||
ctx.fillStyle = '#ffffff';
|
ctx.fillStyle = '#ffffff';
|
||||||
ctx.strokeStyle = '#1976d2';
|
ctx.strokeStyle = '#1976d2';
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = Math.max(1, 2 * zoomFactor);
|
||||||
|
|
||||||
var positions = [
|
var positions = [
|
||||||
[x - hs/2, y - hs/2],
|
[x - hs/2, y - hs/2],
|
||||||
@@ -228,10 +248,10 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hitTest(x, y, el) {
|
function hitTest(x, y, el) {
|
||||||
var ex = pageX + el.x;
|
var ex = pageX + (el.x * zoomFactor);
|
||||||
var ey = pageY + el.y;
|
var ey = pageY + (el.y * zoomFactor);
|
||||||
var ew = el.width || 100;
|
var ew = (el.width || 100) * zoomFactor;
|
||||||
var eh = el.height || 30;
|
var eh = (el.height || 30) * zoomFactor;
|
||||||
return x >= ex && x <= ex + ew && y >= ey && y <= ey + eh;
|
return x >= ex && x <= ex + ew && y >= ey && y <= ey + eh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,15 +291,16 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
var y = e.clientY - rect.top;
|
var y = e.clientY - rect.top;
|
||||||
|
|
||||||
if (isDragging && selectedElement) {
|
if (isDragging && selectedElement) {
|
||||||
var dx = x - dragStart.x;
|
// Adjust mouse movement by zoom factor to get stored coordinates
|
||||||
var dy = y - dragStart.y;
|
var dx = (x - dragStart.x) / zoomFactor;
|
||||||
|
var dy = (y - dragStart.y) / zoomFactor;
|
||||||
|
|
||||||
var newX = elementStart.x + dx;
|
var newX = elementStart.x + dx;
|
||||||
var newY = elementStart.y + dy;
|
var newY = elementStart.y + dy;
|
||||||
|
|
||||||
// Constrain to page
|
// Constrain to page (using base coordinates, not zoomed)
|
||||||
newX = Math.max(0, Math.min(newX, pageWidth - (selectedElement.width || 100)));
|
newX = Math.max(0, Math.min(newX, basePageWidth - (selectedElement.width || 100)));
|
||||||
newY = Math.max(0, Math.min(newY, pageHeight - (selectedElement.height || 30)));
|
newY = Math.max(0, Math.min(newY, basePageHeight - (selectedElement.height || 30)));
|
||||||
|
|
||||||
// Snap to grid
|
// Snap to grid
|
||||||
selectedElement.x = snapToGrid(newX);
|
selectedElement.x = snapToGrid(newX);
|
||||||
@@ -325,7 +346,7 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
break;
|
break;
|
||||||
case 'ArrowDown':
|
case 'ArrowDown':
|
||||||
selectedElement.y = Math.min(pageHeight - (selectedElement.height || 30), selectedElement.y + step);
|
selectedElement.y = Math.min(basePageHeight - (selectedElement.height || 30), selectedElement.y + step);
|
||||||
moved = true;
|
moved = true;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
break;
|
break;
|
||||||
@@ -335,7 +356,7 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
break;
|
break;
|
||||||
case 'ArrowRight':
|
case 'ArrowRight':
|
||||||
selectedElement.x = Math.min(pageWidth - (selectedElement.width || 100), selectedElement.x + step);
|
selectedElement.x = Math.min(basePageWidth - (selectedElement.width || 100), selectedElement.x + step);
|
||||||
moved = true;
|
moved = true;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
break;
|
break;
|
||||||
@@ -362,9 +383,9 @@ window.initProfileInvoiceGenerator = function() {
|
|||||||
elementCounter++;
|
elementCounter++;
|
||||||
var id = 'element-' + elementCounter;
|
var id = 'element-' + elementCounter;
|
||||||
|
|
||||||
// Convert to page coordinates
|
// Convert to page coordinates (adjusting for zoom factor)
|
||||||
var x = dropX - pageX - 50;
|
var x = (dropX - pageX - 50 * zoomFactor) / zoomFactor;
|
||||||
var y = dropY - pageY - 15;
|
var y = (dropY - pageY - 15 * zoomFactor) / zoomFactor;
|
||||||
x = Math.max(10, x);
|
x = Math.max(10, x);
|
||||||
y = Math.max(10, y);
|
y = Math.max(10, y);
|
||||||
x = snapToGrid(x);
|
x = snapToGrid(x);
|
||||||
|
|||||||
@@ -389,7 +389,13 @@ public class EditProfileView extends HorizontalLayout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String templateData = result.toString();
|
String templateData;
|
||||||
|
if (result instanceof elemental.json.JsonValue) {
|
||||||
|
elemental.json.JsonValue jsonValue = (elemental.json.JsonValue) result;
|
||||||
|
templateData = jsonValue.toJson();
|
||||||
|
} else {
|
||||||
|
templateData = result.toString();
|
||||||
|
}
|
||||||
invoiceTemplateService.saveTemplate(currentUser.getId().toString(), templateData);
|
invoiceTemplateService.saveTemplate(currentUser.getId().toString(), templateData);
|
||||||
Notification.show("Template erfolgreich gespeichert", 3000, Notification.Position.BOTTOM_CENTER);
|
Notification.show("Template erfolgreich gespeichert", 3000, Notification.Position.BOTTOM_CENTER);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
|
|||||||
@@ -261,8 +261,8 @@ public class CustomerInvoiceService {
|
|||||||
htmlBuilder.append("<style>");
|
htmlBuilder.append("<style>");
|
||||||
htmlBuilder.append("@page { size: A4; margin: 0; }");
|
htmlBuilder.append("@page { size: A4; margin: 0; }");
|
||||||
htmlBuilder.append("body { margin: 0; padding: 0; width: 210mm; height: 297mm; position: relative; font-family: Arial, sans-serif; }");
|
htmlBuilder.append("body { margin: 0; padding: 0; width: 210mm; height: 297mm; position: relative; font-family: Arial, sans-serif; }");
|
||||||
htmlBuilder.append(".element { position: absolute; }");
|
htmlBuilder.append(".element { position: absolute; box-sizing: border-box; overflow: hidden; }");
|
||||||
htmlBuilder.append(".text { white-space: pre; }");
|
htmlBuilder.append(".text { white-space: nowrap; overflow: visible; }");
|
||||||
htmlBuilder.append(".line { border-top: 1px solid #333; }");
|
htmlBuilder.append(".line { border-top: 1px solid #333; }");
|
||||||
htmlBuilder.append(".image { max-width: 100%; max-height: 100%; }");
|
htmlBuilder.append(".image { max-width: 100%; max-height: 100%; }");
|
||||||
htmlBuilder.append("</style>");
|
htmlBuilder.append("</style>");
|
||||||
@@ -293,10 +293,13 @@ public class CustomerInvoiceService {
|
|||||||
htmlBuilder.append("width:").append(String.format(java.util.Locale.US, "%.2f", mmWidth)).append("mm;");
|
htmlBuilder.append("width:").append(String.format(java.util.Locale.US, "%.2f", mmWidth)).append("mm;");
|
||||||
htmlBuilder.append("height:").append(String.format(java.util.Locale.US, "%.2f", mmHeight)).append("mm;");
|
htmlBuilder.append("height:").append(String.format(java.util.Locale.US, "%.2f", mmHeight)).append("mm;");
|
||||||
htmlBuilder.append("font-size:").append(fontSize).append("pt;");
|
htmlBuilder.append("font-size:").append(fontSize).append("pt;");
|
||||||
|
htmlBuilder.append("line-height:").append(String.format(java.util.Locale.US, "%.2f", fontSize * 1.2)).append("pt;");
|
||||||
htmlBuilder.append("color:").append(color).append(";");
|
htmlBuilder.append("color:").append(color).append(";");
|
||||||
if (!fontStyle.isEmpty()) {
|
if (!fontStyle.isEmpty()) {
|
||||||
if (fontStyle.contains("bold")) htmlBuilder.append("font-weight:bold;");
|
if (fontStyle.contains("bold")) htmlBuilder.append("font-weight:bold;");
|
||||||
}
|
}
|
||||||
|
// Vertically center content
|
||||||
|
htmlBuilder.append("display:flex;align-items:center;");
|
||||||
htmlBuilder.append("'");
|
htmlBuilder.append("'");
|
||||||
htmlBuilder.append(">");
|
htmlBuilder.append(">");
|
||||||
|
|
||||||
@@ -308,7 +311,7 @@ public class CustomerInvoiceService {
|
|||||||
.replace("'", "'");
|
.replace("'", "'");
|
||||||
|
|
||||||
if ("line".equals(type)) {
|
if ("line".equals(type)) {
|
||||||
htmlBuilder.append("<hr style='margin:0;border:none;border-top:1px solid #333;height:0;'/>");
|
htmlBuilder.append("<hr style='margin:0;border:none;border-top:1px solid #333;height:0;width:100%;'/>");
|
||||||
} else if ("image".equals(type)) {
|
} else if ("image".equals(type)) {
|
||||||
if (element.has("imageData")) {
|
if (element.has("imageData")) {
|
||||||
String imageData = element.get("imageData").asText();
|
String imageData = element.get("imageData").asText();
|
||||||
@@ -317,7 +320,8 @@ public class CustomerInvoiceService {
|
|||||||
htmlBuilder.append("[Bild]");
|
htmlBuilder.append("[Bild]");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
htmlBuilder.append(text);
|
// Wrap text in a span to prevent flexbox issues
|
||||||
|
htmlBuilder.append("<span style='white-space:nowrap;'>").append(text).append("</span>");
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlBuilder.append("</div>");
|
htmlBuilder.append("</div>");
|
||||||
|
|||||||
Reference in New Issue
Block a user