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