Erweiterungen
This commit is contained in:
@@ -209,13 +209,22 @@ public class PdfBoxService {
|
||||
}
|
||||
|
||||
private float drawItemsTable(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException {
|
||||
float tableWidth = PDRectangle.A4.getWidth() - 2 * MARGIN;
|
||||
float[] columnWidths = {60, 300, 100, 100}; // Menge, Bezeichnung, Einzelpreis, Gesamt
|
||||
float rowHeight = 20;
|
||||
|
||||
// Calculate actual table width based on column widths
|
||||
float actualTableWidth = 0;
|
||||
for (float width : columnWidths) {
|
||||
actualTableWidth += width;
|
||||
}
|
||||
|
||||
// Add 2cm margin to the right of the gray header
|
||||
float rightMargin = 2 * 28.35f; // 2cm in points (1cm = 28.35pt)
|
||||
float headerWidth = actualTableWidth + rightMargin;
|
||||
|
||||
// Table header
|
||||
contentStream.setNonStrokingColor(230/255f, 230/255f, 230/255f); // Light gray background
|
||||
contentStream.addRect(MARGIN, yPosition - rowHeight, tableWidth, rowHeight);
|
||||
contentStream.addRect(MARGIN, yPosition - rowHeight, headerWidth, rowHeight);
|
||||
contentStream.fill();
|
||||
|
||||
contentStream.setNonStrokingColor(0f, 0f, 0f); // Black
|
||||
@@ -340,37 +349,77 @@ public class PdfBoxService {
|
||||
private float drawPaymentTerms(PDPageContentStream contentStream, float yPosition, InvoiceData data) throws IOException {
|
||||
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), FONT_SIZE);
|
||||
|
||||
// Payment terms
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(MARGIN, yPosition);
|
||||
contentStream.showText(data.getPaymentTerms() != null ? data.getPaymentTerms() : "");
|
||||
contentStream.endText();
|
||||
// Payment terms with text wrapping
|
||||
String paymentTerms = data.getPaymentTerms();
|
||||
if (paymentTerms != null && !paymentTerms.trim().isEmpty()) {
|
||||
float maxWidth = PDRectangle.A4.getWidth() - 2 * MARGIN;
|
||||
yPosition = drawWrappedText(contentStream, paymentTerms, MARGIN, yPosition, maxWidth, FONT_SIZE);
|
||||
}
|
||||
|
||||
return yPosition - 40;
|
||||
return yPosition - 20;
|
||||
}
|
||||
|
||||
private float drawWrappedText(PDPageContentStream contentStream, String text, float x, float y, float maxWidth, float fontSize) throws IOException {
|
||||
String[] words = text.split(" ");
|
||||
StringBuilder currentLine = new StringBuilder();
|
||||
float currentY = y;
|
||||
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(x, currentY);
|
||||
|
||||
for (String word : words) {
|
||||
String testLine = currentLine.length() > 0 ? currentLine + " " + word : word;
|
||||
|
||||
// Estimate text width (rough calculation)
|
||||
float textWidth = estimateTextWidth(testLine, fontSize);
|
||||
|
||||
if (textWidth > maxWidth && currentLine.length() > 0) {
|
||||
// Print current line and start new line
|
||||
contentStream.showText(currentLine.toString());
|
||||
currentLine = new StringBuilder(word);
|
||||
currentY -= LINE_HEIGHT;
|
||||
contentStream.newLineAtOffset(0, -LINE_HEIGHT);
|
||||
} else {
|
||||
currentLine = new StringBuilder(testLine);
|
||||
}
|
||||
}
|
||||
|
||||
// Print remaining text
|
||||
if (currentLine.length() > 0) {
|
||||
contentStream.showText(currentLine.toString());
|
||||
currentY -= LINE_HEIGHT;
|
||||
}
|
||||
|
||||
contentStream.endText();
|
||||
return currentY;
|
||||
}
|
||||
|
||||
private float estimateTextWidth(String text, float fontSize) {
|
||||
// Rough estimation: average character width is about 0.6 * font size
|
||||
return text.length() * fontSize * 0.6f;
|
||||
}
|
||||
|
||||
private void drawFooter(PDPageContentStream contentStream, PDPage page, InvoiceData data) throws IOException {
|
||||
contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 8);
|
||||
|
||||
// Calculate footer position - start from bottom of page
|
||||
float footerStartY = MARGIN + 60; // 60 points from bottom
|
||||
|
||||
// Footer text (company details) - positioned at bottom
|
||||
// Footer text (company details) - positioned at absolute bottom
|
||||
String footerText = data.getFooterText();
|
||||
if (footerText != null) {
|
||||
String[] footerLines = footerText.split("<br>");
|
||||
float currentY = footerStartY;
|
||||
|
||||
// Calculate the total height needed for footer text
|
||||
float totalFooterHeight = footerLines.length * 12;
|
||||
currentY = footerStartY + totalFooterHeight - 12; // Start from top of footer area
|
||||
// Calculate footer position from bottom of page
|
||||
float lineSpacing = 10; // 10 points between lines
|
||||
float footerBottomMargin = 30; // 30 points from absolute bottom
|
||||
|
||||
// Start from the bottom and work upwards
|
||||
float currentY = footerBottomMargin + (footerLines.length - 1) * lineSpacing;
|
||||
|
||||
for (String line : footerLines) {
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(MARGIN, currentY);
|
||||
contentStream.showText(line.trim());
|
||||
contentStream.endText();
|
||||
currentY -= 12;
|
||||
currentY -= lineSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
margin: 1.5cm 1cm 1.5cm 2cm;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 12pt;
|
||||
@@ -17,6 +22,8 @@
|
||||
border: 2px solid #1b12b9;
|
||||
max-width: 21.001cm;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -162,11 +169,16 @@
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
text-align: center;
|
||||
font-size: 8pt;
|
||||
font-family: Arial;
|
||||
margin-top: 2cm;
|
||||
margin-top: auto;
|
||||
padding: 0.5cm 1cm;
|
||||
}
|
||||
|
||||
.clear {
|
||||
@@ -175,85 +187,84 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<p class="company-name">
|
||||
<b>{{companyName}}</b>
|
||||
<div class="page-content">
|
||||
<div class="header">
|
||||
<p class="company-name">
|
||||
<b>{{companyName}}</b>
|
||||
</p>
|
||||
<hr>
|
||||
<p class="company-subtitle">
|
||||
<b>{{companySubtitle}}</b>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="recipient-box">
|
||||
<p class="sender-line">{{senderLine}}</p>
|
||||
<p class="recipient-content">{{recipientName}}</p>
|
||||
<p class="recipient-content">{{recipientDepartment}}</p>
|
||||
<p class="recipient-content">{{recipientStreet}}</p>
|
||||
<p class="recipient-content">{{recipientCity}}</p>
|
||||
</div>
|
||||
|
||||
<div class="company-address">
|
||||
<p>{{companyStreet}}</p>
|
||||
<p>{{companyCity}}</p>
|
||||
<p> </p>
|
||||
<p>Tel.: {{companyPhone}}</p>
|
||||
<p>{{companyWebsite}}</p>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
<p class="contact-info">
|
||||
Fax
|
||||
<span style="margin-left: 60px">eMail</span>
|
||||
<br/>
|
||||
{{companyFax}}
|
||||
<span>{{companyEmail}}</span>
|
||||
</p>
|
||||
<hr>
|
||||
<p class="company-subtitle">
|
||||
<b>{{companySubtitle}}</b>
|
||||
|
||||
<p class="invoice-date">
|
||||
<span>Datum<br></span>
|
||||
<span>{{invoiceDate}}</span>
|
||||
</p>
|
||||
|
||||
<p class="subject">Rechnung {{invoiceNumber}}</p>
|
||||
|
||||
<p class="text-body">{{invoiceText}}</p>
|
||||
|
||||
<table class="invoice-table">
|
||||
<tr>
|
||||
<td class="table-header" style="width:2cm;">Menge</td>
|
||||
<td class="table-header" style="width:9.999cm;">Bezeichnung</td>
|
||||
<td class="table-header" style="width:3cm;">Einzelpreis</td>
|
||||
<td class="table-header" style="width:3cm;">Gesamt</td>
|
||||
</tr>
|
||||
{{invoiceItems}}
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-header" style="width:3cm;">Nettobetrag</td>
|
||||
<td class="table-cell summary-row table-cell-right" style="width:3cm;">{{netAmount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-cell table-cell-right" style="width:3cm;">+ {{vatRate}}% MwSt.</td>
|
||||
<td class="table-cell table-cell-right" style="width:3cm;">{{vatAmount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-header" style="width:3cm;">Endbetrag</td>
|
||||
<td class="table-cell summary-row table-cell-right" style="width:3cm;">{{totalAmount}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="text-body">{{paymentTerms}}</p>
|
||||
</div>
|
||||
|
||||
<div class="recipient-box">
|
||||
<p class="sender-line">{{senderLine}}</p>
|
||||
<p class="recipient-content">{{recipientName}}</p>
|
||||
<p class="recipient-content">{{recipientDepartment}}</p>
|
||||
<p class="recipient-content">{{recipientStreet}}</p>
|
||||
<p class="recipient-content">{{recipientCity}}</p>
|
||||
</div>
|
||||
|
||||
<div class="company-address">
|
||||
<p>{{companyStreet}}</p>
|
||||
<p>{{companyCity}}</p>
|
||||
<p> </p>
|
||||
<p>Tel.: {{companyPhone}}</p>
|
||||
<p>{{companyWebsite}}</p>
|
||||
</div>
|
||||
|
||||
<div class="clear"></div>
|
||||
|
||||
<p class="contact-info">
|
||||
Fax
|
||||
<span style="margin-left: 60px">eMail</span>
|
||||
<br/>
|
||||
{{companyFax}}
|
||||
<span>{{companyEmail}}</span>
|
||||
</p>
|
||||
|
||||
<p class="invoice-date">
|
||||
<span>Datum<br></span>
|
||||
<span>{{invoiceDate}}</span>
|
||||
</p>
|
||||
|
||||
<p class="subject">Rechnung {{invoiceNumber}}</p>
|
||||
|
||||
<p class="text-body">{{invoiceText}}</p>
|
||||
|
||||
<table class="invoice-table">
|
||||
<tr>
|
||||
<td class="table-header" style="width:2cm;">Menge</td>
|
||||
<td class="table-header" style="width:9.999cm;">Bezeichnung</td>
|
||||
<td class="table-header" style="width:3cm;">Einzelpreis</td>
|
||||
<td class="table-header" style="width:3cm;">Gesamt</td>
|
||||
</tr>
|
||||
{{invoiceItems}}
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-header" style="width:3cm;">Nettobetrag</td>
|
||||
<td class="table-cell summary-row table-cell-right" style="width:3cm;">{{netAmount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-cell table-cell-right" style="width:3cm;">+ {{vatRate}}% MwSt.</td>
|
||||
<td class="table-cell table-cell-right" style="width:3cm;">{{vatAmount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="table-cell" style="width:2cm;"> </td>
|
||||
<td class="table-cell" style="width:9.999cm;"> </td>
|
||||
<td class="table-header" style="width:3cm;">Endbetrag</td>
|
||||
<td class="table-cell summary-row table-cell-right" style="width:3cm;">{{totalAmount}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
<p class="text-body">{{paymentTerms}}</p>
|
||||
<br><br><br>
|
||||
|
||||
<p class="footer-text">
|
||||
<span>{{footerText}}</span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user