Erweiterungen
This commit is contained in:
Binary file not shown.
@@ -48,14 +48,23 @@ window.initProfileInvoiceGenerator = function() {
|
||||
// Page dimensions
|
||||
var padding = 10;
|
||||
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() {
|
||||
var w = canvas.width;
|
||||
var h = canvas.height;
|
||||
var availableWidth = w - 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;
|
||||
pageY = (h - pageHeight) / 2;
|
||||
}
|
||||
@@ -111,16 +120,17 @@ window.initProfileInvoiceGenerator = function() {
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(pageX, pageY, pageWidth, pageHeight);
|
||||
|
||||
// Grid
|
||||
// Grid (scaled by zoom factor)
|
||||
ctx.strokeStyle = 'rgba(200, 200, 200, 0.3)';
|
||||
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.moveTo(x, pageY);
|
||||
ctx.lineTo(x, pageY + pageHeight);
|
||||
ctx.stroke();
|
||||
}
|
||||
for (var y = pageY; y <= pageY + pageHeight; y += gridSize) {
|
||||
for (var y = pageY; y <= pageY + pageHeight; y += scaledGridSize) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(pageX, y);
|
||||
ctx.lineTo(pageX + pageWidth, y);
|
||||
@@ -141,14 +151,16 @@ window.initProfileInvoiceGenerator = function() {
|
||||
function drawElement(el) {
|
||||
ctx.save();
|
||||
|
||||
var x = pageX + el.x;
|
||||
var y = pageY + el.y;
|
||||
var w = el.width || 100;
|
||||
var h = el.height || 30;
|
||||
// Apply zoom factor to position and size
|
||||
var x = pageX + (el.x * zoomFactor);
|
||||
var y = pageY + (el.y * zoomFactor);
|
||||
var w = (el.width || 100) * zoomFactor;
|
||||
var h = (el.height || 30) * zoomFactor;
|
||||
var fontSize = (el.fontSize || 14) * zoomFactor;
|
||||
|
||||
if (el.type === 'line') {
|
||||
ctx.strokeStyle = el.color || '#333333';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.lineWidth = Math.max(1, zoomFactor);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + w, y);
|
||||
@@ -159,29 +171,32 @@ window.initProfileInvoiceGenerator = function() {
|
||||
ctx.strokeStyle = '#999999';
|
||||
ctx.strokeRect(x, y, w, h);
|
||||
ctx.fillStyle = '#666666';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.font = Math.max(8, 12 * zoomFactor) + 'px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('Bild', x + w / 2, y + h / 2);
|
||||
} else {
|
||||
// Text elements
|
||||
ctx.font = (el.fontStyle || '') + ' ' + (el.fontSize || 14) + 'px Arial';
|
||||
ctx.font = (el.fontStyle || '') + ' ' + fontSize + 'px Arial';
|
||||
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
|
||||
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.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.font = (el.fontStyle || '') + ' ' + (el.fontSize || 14) + 'px Arial';
|
||||
|
||||
var lines = (el.text || '').split('\n');
|
||||
var lineHeight = (el.fontSize || 14) * 1.2;
|
||||
var ty = y;
|
||||
ctx.font = (el.fontStyle || '') + ' ' + fontSize + 'px Arial';
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
lines.forEach(function(line) {
|
||||
ctx.fillText(line, x, ty);
|
||||
@@ -193,22 +208,27 @@ window.initProfileInvoiceGenerator = function() {
|
||||
}
|
||||
|
||||
function drawSelection(el) {
|
||||
var x = pageX + el.x;
|
||||
var y = pageY + el.y;
|
||||
var w = el.width || 100;
|
||||
var h = el.height || 30;
|
||||
var hs = 8; // handle size
|
||||
var x = pageX + (el.x * zoomFactor);
|
||||
var y = pageY + (el.y * zoomFactor);
|
||||
var w = (el.width || 100) * zoomFactor;
|
||||
var h = (el.height || 30) * zoomFactor;
|
||||
|
||||
ctx.strokeStyle = '#1976d2';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineWidth = Math.max(1, 2 * zoomFactor);
|
||||
ctx.setLineDash([5, 3]);
|
||||
ctx.strokeRect(x, y, w, h);
|
||||
ctx.setLineDash([]);
|
||||
|
||||
// Don't show resize handles for static elements
|
||||
if (el.isStatic) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handles
|
||||
var hs = Math.max(6, 8 * zoomFactor); // handle size scaled
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.strokeStyle = '#1976d2';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineWidth = Math.max(1, 2 * zoomFactor);
|
||||
|
||||
var positions = [
|
||||
[x - hs/2, y - hs/2],
|
||||
@@ -228,10 +248,10 @@ window.initProfileInvoiceGenerator = function() {
|
||||
}
|
||||
|
||||
function hitTest(x, y, el) {
|
||||
var ex = pageX + el.x;
|
||||
var ey = pageY + el.y;
|
||||
var ew = el.width || 100;
|
||||
var eh = el.height || 30;
|
||||
var ex = pageX + (el.x * zoomFactor);
|
||||
var ey = pageY + (el.y * zoomFactor);
|
||||
var ew = (el.width || 100) * zoomFactor;
|
||||
var eh = (el.height || 30) * zoomFactor;
|
||||
return x >= ex && x <= ex + ew && y >= ey && y <= ey + eh;
|
||||
}
|
||||
|
||||
@@ -271,15 +291,16 @@ window.initProfileInvoiceGenerator = function() {
|
||||
var y = e.clientY - rect.top;
|
||||
|
||||
if (isDragging && selectedElement) {
|
||||
var dx = x - dragStart.x;
|
||||
var dy = y - dragStart.y;
|
||||
// Adjust mouse movement by zoom factor to get stored coordinates
|
||||
var dx = (x - dragStart.x) / zoomFactor;
|
||||
var dy = (y - dragStart.y) / zoomFactor;
|
||||
|
||||
var newX = elementStart.x + dx;
|
||||
var newY = elementStart.y + dy;
|
||||
|
||||
// Constrain to page
|
||||
newX = Math.max(0, Math.min(newX, pageWidth - (selectedElement.width || 100)));
|
||||
newY = Math.max(0, Math.min(newY, pageHeight - (selectedElement.height || 30)));
|
||||
// Constrain to page (using base coordinates, not zoomed)
|
||||
newX = Math.max(0, Math.min(newX, basePageWidth - (selectedElement.width || 100)));
|
||||
newY = Math.max(0, Math.min(newY, basePageHeight - (selectedElement.height || 30)));
|
||||
|
||||
// Snap to grid
|
||||
selectedElement.x = snapToGrid(newX);
|
||||
@@ -325,7 +346,7 @@ window.initProfileInvoiceGenerator = function() {
|
||||
e.preventDefault();
|
||||
break;
|
||||
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;
|
||||
e.preventDefault();
|
||||
break;
|
||||
@@ -335,7 +356,7 @@ window.initProfileInvoiceGenerator = function() {
|
||||
e.preventDefault();
|
||||
break;
|
||||
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;
|
||||
e.preventDefault();
|
||||
break;
|
||||
@@ -362,9 +383,9 @@ window.initProfileInvoiceGenerator = function() {
|
||||
elementCounter++;
|
||||
var id = 'element-' + elementCounter;
|
||||
|
||||
// Convert to page coordinates
|
||||
var x = dropX - pageX - 50;
|
||||
var y = dropY - pageY - 15;
|
||||
// Convert to page coordinates (adjusting for zoom factor)
|
||||
var x = (dropX - pageX - 50 * zoomFactor) / zoomFactor;
|
||||
var y = (dropY - pageY - 15 * zoomFactor) / zoomFactor;
|
||||
x = Math.max(10, x);
|
||||
y = Math.max(10, y);
|
||||
x = snapToGrid(x);
|
||||
|
||||
@@ -389,7 +389,13 @@ public class EditProfileView extends HorizontalLayout {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
Notification.show("Template erfolgreich gespeichert", 3000, Notification.Position.BOTTOM_CENTER);
|
||||
} catch (Exception ex) {
|
||||
|
||||
@@ -261,8 +261,8 @@ public class CustomerInvoiceService {
|
||||
htmlBuilder.append("<style>");
|
||||
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(".element { position: absolute; }");
|
||||
htmlBuilder.append(".text { white-space: pre; }");
|
||||
htmlBuilder.append(".element { position: absolute; box-sizing: border-box; overflow: hidden; }");
|
||||
htmlBuilder.append(".text { white-space: nowrap; overflow: visible; }");
|
||||
htmlBuilder.append(".line { border-top: 1px solid #333; }");
|
||||
htmlBuilder.append(".image { max-width: 100%; max-height: 100%; }");
|
||||
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("height:").append(String.format(java.util.Locale.US, "%.2f", mmHeight)).append("mm;");
|
||||
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(";");
|
||||
if (!fontStyle.isEmpty()) {
|
||||
if (fontStyle.contains("bold")) htmlBuilder.append("font-weight:bold;");
|
||||
}
|
||||
// Vertically center content
|
||||
htmlBuilder.append("display:flex;align-items:center;");
|
||||
htmlBuilder.append("'");
|
||||
htmlBuilder.append(">");
|
||||
|
||||
@@ -308,7 +311,7 @@ public class CustomerInvoiceService {
|
||||
.replace("'", "'");
|
||||
|
||||
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)) {
|
||||
if (element.has("imageData")) {
|
||||
String imageData = element.get("imageData").asText();
|
||||
@@ -317,7 +320,8 @@ public class CustomerInvoiceService {
|
||||
htmlBuilder.append("[Bild]");
|
||||
}
|
||||
} 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>");
|
||||
|
||||
Reference in New Issue
Block a user