From 5e941753e2ac36ff36e14500c159a1cdb84d8f40 Mon Sep 17 00:00:00 2001 From: verboomp Date: Fri, 6 Feb 2026 11:09:25 +0100 Subject: [PATCH] Tweaking padding in customer list and update look and feel pdf export --- .../core/utils/PdfUtils.java | 87 +++++++++++-------- .../core/utils/PdfUtilsTest.java | 2 +- .../pages/customer/customer_list_widget.dart | 2 + .../test/pages/picture_widget_test.dart | 1 - 4 files changed, 55 insertions(+), 37 deletions(-) diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java index f4def7b..39a0237 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java @@ -70,7 +70,8 @@ public class PdfUtils { float pageWidth = page.getMediaBox().getWidth(); float pageHeight = page.getMediaBox().getHeight(); float contentWidth = pageWidth - 2 * PAGE_MARGIN; - float halfWidth = contentWidth / 2F; + float imageWidth = contentWidth * 0.75F; + float metadataWidth = contentWidth * 0.25F; try (PDPageContentStream cs = new PDPageContentStream(document, page)) { float yPosition = pageHeight - 50F; @@ -87,10 +88,10 @@ public class PdfUtils { firstPage = false; } - // Left side: image (50% of content width) + // Left side: image (75% of content width) float imageX = PAGE_MARGIN; float imageY = yPosition; - float imageMaxWidth = halfWidth - 10F; + float imageMaxWidth = imageWidth - 10F; float imageMaxHeight = pageHeight - 2 * PAGE_MARGIN - 40F; if (picture.getImage() != null) { @@ -108,30 +109,25 @@ public class PdfUtils { } } - // Right side: metadata (top-aligned with image) - float rightX = PAGE_MARGIN + halfWidth + 10F; - float rightY = imageY - 32F; + // Right side: metadata (25% of content width, top-aligned with image) + float rightX = PAGE_MARGIN + imageWidth + 10F; + float rightY = imageY - 10F; - // Date (no label, bold, size 44) + // Date (bold, size 10 - matching labels) String dateStr = picture.getPictureDate() != null ? (DATE_FORMAT.format(picture.getPictureDate()) + " UHR") : ""; - cs.setFont(fontBold, 32); + cs.setFont(fontBold, 10); cs.setNonStrokingColor(COLOR_DATE); cs.beginText(); cs.newLineAtOffset(rightX, rightY); cs.showText(dateStr); cs.endText(); - rightY -= 54F; + rightY -= 24F; // Customer number - float kundenNummerY = rightY; rightY = drawLabel(cs, fontBold, "KUNDENNUMMER", rightX, rightY); rightY = drawValue(cs, fontRegular, nullSafe(customer.getCustomerNumber()), rightX, rightY); rightY -= 10F; - // Evaluation card with circles - float circlesX = rightX + 140F; - drawEvaluationCard(cs, fontBold, circlesX, kundenNummerY, picture.getEvaluation()); - // ZIP rightY = drawLabel(cs, fontBold, "PLZ", rightX, rightY); rightY = drawValue(cs, fontRegular, nullSafe(customer.getZip()), rightX, rightY); @@ -142,9 +138,27 @@ public class PdfUtils { rightY = drawValue(cs, fontRegular, nullSafe(customer.getCity()), rightX, rightY); rightY -= 10F; + // Evaluation card with circles + float evaluationY = rightY; + drawEvaluationCard(cs, fontBold, rightX, evaluationY, picture.getEvaluation()); + rightY -= 80F; + // Comment rightY = drawLabel(cs, fontBold, "KOMMENTAR", rightX, rightY); - drawWrappedText(cs, fontRegular, nullSafe(picture.getComment()), rightX, rightY, halfWidth - 20f); + String remainingComment = drawWrappedText(cs, fontRegular, nullSafe(picture.getComment()), rightX, rightY, metadataWidth - 20f, 50F); + + // Continue comment on additional pages if needed + while (remainingComment != null && !remainingComment.isEmpty()) { + PDPage continuationPage = new PDPage(new PDRectangle(PDRectangle.A4.getHeight(), PDRectangle.A4.getWidth())); + document.addPage(continuationPage); + + try (PDPageContentStream continuationCs = new PDPageContentStream(document, continuationPage)) { + float continuationY = continuationPage.getMediaBox().getHeight() - PAGE_MARGIN; + float continuationWidth = continuationPage.getMediaBox().getWidth() - 2 * PAGE_MARGIN; + continuationY = drawLabel(continuationCs, fontBold, "KOMMENTAR (FORTSETZUNG)", PAGE_MARGIN, continuationY); + remainingComment = drawWrappedText(continuationCs, fontRegular, remainingComment, PAGE_MARGIN, continuationY, continuationWidth, 50F); + } + } } } @@ -189,9 +203,9 @@ public class PdfUtils { return y - 14F; } - private void drawWrappedText(PDPageContentStream cs, PDFont font, String text, float x, float y, float maxWidth) throws IOException { + private String drawWrappedText(PDPageContentStream cs, PDFont font, String text, float x, float y, float maxWidth, float minY) throws IOException { if (text == null || text.isEmpty()) { - return; + return null; } cs.setFont(font, 10); cs.setNonStrokingColor(COLOR_TEXT_GRAY); @@ -199,11 +213,23 @@ public class PdfUtils { String[] words = text.split("\\s+"); StringBuilder line = new StringBuilder(); float currentY = y; + int wordIndex = 0; - for (String word : words) { + for (wordIndex = 0; wordIndex < words.length; wordIndex++) { + String word = words[wordIndex]; String testLine = line.isEmpty() ? word : (line + " " + word); float textWidth = font.getStringWidth(testLine) / 1000F * 10F; if (textWidth > maxWidth && !line.isEmpty()) { + // Check if we have room for this line + if (currentY < minY) { + // Return remaining text starting from current word + StringBuilder remaining = new StringBuilder(line); + for (int i = wordIndex; i < words.length; i++) { + if (!remaining.isEmpty()) remaining.append(" "); + remaining.append(words[i]); + } + return remaining.toString(); + } cs.beginText(); cs.newLineAtOffset(x, currentY); cs.showText(line.toString()); @@ -215,45 +241,36 @@ public class PdfUtils { } } if (!line.isEmpty()) { + if (currentY < minY) { + return line.toString(); + } cs.beginText(); cs.newLineAtOffset(x, currentY); cs.showText(line.toString()); cs.endText(); } + return null; } private void drawEvaluationCard(PDPageContentStream cs, PDFont fontBold, float x, float y, Integer evaluation) throws IOException { int eval = evaluation != null ? evaluation : 0; Color[] colors = { COLOR_GREEN, COLOR_YELLOW, COLOR_RED }; - float cardPadding = 10F; - float cardWidth = 2 * CIRCLE_SPACING + 2 * HIGHLIGHT_RADIUS + 2 * cardPadding; float labelHeight = 14F; - float cardHeight = labelHeight + 2 * HIGHLIGHT_RADIUS + 2 * cardPadding + 4F; - float cardX = x - HIGHLIGHT_RADIUS - cardPadding; - float cardY = y - cardHeight + cardPadding; - - // Draw card background (rounded rectangle) - cs.setStrokingColor(new Color(0xDD, 0xDD, 0xDD)); - cs.setNonStrokingColor(new Color(0xF8, 0xF8, 0xF8)); - cs.setLineWidth(1f); - drawRoundedRect(cs, cardX, cardY, cardWidth, cardHeight, 6F); - cs.fillAndStroke(); // Draw "BEWERTUNG" label above circles - float labelX = x; float labelY = y - labelHeight; - cs.setFont(fontBold, 9); + cs.setFont(fontBold, 10); cs.setNonStrokingColor(COLOR_CUSTOMER_NAME); cs.beginText(); - cs.newLineAtOffset(labelX, labelY); + cs.newLineAtOffset(x, labelY); cs.showText("BEWERTUNG"); cs.endText(); // Draw circles below the label - float circleY = labelY - cardPadding - HIGHLIGHT_RADIUS - 2F; + float circleY = labelY - HIGHLIGHT_RADIUS - 10F; for (int i = 0; i < 3; i++) { - float cx = x + i * CIRCLE_SPACING; + float cx = x + CIRCLE_RADIUS + i * CIRCLE_SPACING; // Highlight circle if this matches the evaluation (1=green, 2=yellow, 3=red) if (eval == i + 1) { diff --git a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtilsTest.java b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtilsTest.java index 6ddb059..7a83744 100644 --- a/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtilsTest.java +++ b/hartmann-foto-documentation-app/src/test/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtilsTest.java @@ -78,7 +78,7 @@ class PdfUtilsTest { byte[] pdfBytes = pdfUtils.createPdf(customer, pictures); try (PDDocument document = Loader.loadPDF(pdfBytes)) { - assertEquals(3, document.getNumberOfPages()); + assertEquals(4, document.getNumberOfPages()); } writeToFile(pdfBytes, "createPdf_multiplePictures_createsOnPagePerPicture.pdf"); } diff --git a/hartmann-foto-documentation-frontend/lib/pages/customer/customer_list_widget.dart b/hartmann-foto-documentation-frontend/lib/pages/customer/customer_list_widget.dart index e9ab497..0909ae8 100644 --- a/hartmann-foto-documentation-frontend/lib/pages/customer/customer_list_widget.dart +++ b/hartmann-foto-documentation-frontend/lib/pages/customer/customer_list_widget.dart @@ -147,6 +147,7 @@ class _CustomerListWidgetState extends State { return Padding( padding: const EdgeInsets.all(16.0), child: Row( + spacing: 8.0, children: [ const SizedBox(width: 48), Expanded( @@ -196,6 +197,7 @@ class _CustomerListWidgetState extends State { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Row( + spacing: 8.0, children: [ SizedBox( width: 48, diff --git a/hartmann-foto-documentation-frontend/test/pages/picture_widget_test.dart b/hartmann-foto-documentation-frontend/test/pages/picture_widget_test.dart index 710410e..e271513 100644 --- a/hartmann-foto-documentation-frontend/test/pages/picture_widget_test.dart +++ b/hartmann-foto-documentation-frontend/test/pages/picture_widget_test.dart @@ -117,7 +117,6 @@ void main() { when(mockCustomerController.getAll("", "")).thenAnswer((_) async => _list); provideMockedNetworkImages(() async { await pumpAppConfig(tester, "${GlobalRouter.pathHome}${GlobalRouter.pathCustomer}/1${GlobalRouter.pathPicture}/1"); - await tester.pump(Duration(seconds: 10)); await tester.pumpAndSettle(); // The comment field should be empty but the label should exist