diff --git a/.claude/settings.json b/.claude/settings.json
index 5e54705..10f8ca9 100644
--- a/.claude/settings.json
+++ b/.claude/settings.json
@@ -6,7 +6,9 @@
"Bash(python -c \"import docx; print\\('python-docx OK'\\)\")",
"Bash(pip install *)",
"Bash(python docs/convert_manual.py)",
- "Bash(grep -n -A3 dateOnlyFormatter __TRACKED_VAR__/Sync/SyncManager.swift)"
+ "Bash(grep -n -A3 dateOnlyFormatter __TRACKED_VAR__/Sync/SyncManager.swift)",
+ "Bash(python -c \"import ast; ast.parse\\(open\\('app/utils/pdf_export.py'\\).read\\(\\)\\); print\\('OK'\\)\")",
+ "Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/utils/pdf_export.py',encoding='utf-8'\\).read\\(\\)\\); print\\('OK'\\)\")"
]
}
}
diff --git a/app/utils/pdf_export.py b/app/utils/pdf_export.py
index c534ec7..518f423 100644
--- a/app/utils/pdf_export.py
+++ b/app/utils/pdf_export.py
@@ -43,6 +43,9 @@ C_GREEN = colors.HexColor('#16a34a')
C_YELLOW = colors.HexColor('#d97706')
C_RED = colors.HexColor('#dc2626')
C_WHITE = colors.white
+# Light table-header / banner fill — printer-friendly (low ink) replacement for the
+# former solid-dark headers. Distinct enough from the C_LIGHT zebra row shade.
+C_HEADER = colors.HexColor('#dbe4f0')
SEVERITY_COLORS = {
'critical': C_RED,
@@ -177,23 +180,29 @@ def _on_page(canvas, doc, title, generated_at):
w, h = letter
margin = 0.65 * inch
- # ── Dark header band ──
+ # ── Header band (light — printer-friendly) ──
+ # Was a full-width solid-dark rectangle; replaced with a light tint + a thin
+ # blue accent rule so it costs almost no ink to print.
canvas.saveState()
- canvas.setFillColor(C_DARK)
+ canvas.setFillColor(C_LIGHT)
canvas.rect(0, h - 1.1 * inch, w, 1.1 * inch, stroke=0, fill=1)
+ # Blue accent rule along the bottom edge of the band
+ canvas.setStrokeColor(C_BLUE)
+ canvas.setLineWidth(2)
+ canvas.line(0, h - 1.1 * inch, w, h - 1.1 * inch)
canvas.setFont('Helvetica-Bold', 13)
- canvas.setFillColor(C_WHITE)
+ canvas.setFillColor(C_DARK)
canvas.drawString(margin, h - 0.55 * inch, title)
canvas.setFont('Helvetica', 8)
- canvas.setFillColor(colors.HexColor('#94a3b8'))
+ canvas.setFillColor(C_SLATE)
canvas.drawString(margin, h - 0.78 * inch, 'Janitorial Quality Control System')
# Page number — right-aligned
page_txt = f'Page {doc.page}'
canvas.setFont('Helvetica', 8)
- canvas.setFillColor(colors.HexColor('#94a3b8'))
+ canvas.setFillColor(C_SLATE)
canvas.drawRightString(w - margin, h - 0.66 * inch, page_txt)
canvas.restoreState()
@@ -610,8 +619,9 @@ def _issues_section(issues):
sev_styles.append(('FONTNAME', (1, r), (1, r), 'Helvetica-Bold'))
tbl.setStyle(TableStyle([
- ('BACKGROUND', (0, 0), (-1, 0), C_DARK),
- ('TEXTCOLOR', (0, 0), (-1, 0), C_WHITE),
+ ('BACKGROUND', (0, 0), (-1, 0), C_HEADER),
+ ('TEXTCOLOR', (0, 0), (-1, 0), C_DARK),
+ ('LINEBELOW', (0, 0), (-1, 0), 0.75, C_BLUE),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, -1), 8),
('INNERGRID', (0, 0), (-1, -1), 0.25, C_BORDER),
@@ -808,18 +818,19 @@ def generate_issue_pdf(issue, static_folder: str) -> bytes:
Paragraph(f'{sev.upper()}',
ParagraphStyle('b1', fontName='Helvetica-Bold', fontSize=10,
textColor=C_WHITE, alignment=TA_CENTER)),
- Paragraph(f'Issue #{issue.id}',
+ Paragraph(f'Issue #{issue.id}',
ParagraphStyle('b2', fontName='Helvetica-Bold', fontSize=16,
- textColor=C_WHITE, alignment=TA_CENTER)),
- Paragraph(f'{stat_txt}',
+ textColor=C_DARK, alignment=TA_CENTER)),
+ Paragraph(f'{stat_txt}',
ParagraphStyle('b3', fontName='Helvetica-Bold', fontSize=10,
- textColor=C_WHITE, alignment=TA_CENTER)),
+ textColor=C_DARK, alignment=TA_CENTER)),
]]
banner = Table(banner_data, colWidths=[pw * 0.20, pw * 0.55, pw * 0.25])
banner.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, 0), sev_color),
- ('BACKGROUND', (1, 0), (1, 0), C_DARK),
- ('BACKGROUND', (2, 0), (2, 0), C_SLATE),
+ ('BACKGROUND', (1, 0), (1, 0), C_LIGHT),
+ ('BACKGROUND', (2, 0), (2, 0), C_HEADER),
+ ('LINEBELOW', (1, 0), (2, 0), 0.75, C_BLUE),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('TOPPADDING', (0, 0), (-1, -1), 10),
('BOTTOMPADDING', (0, 0), (-1, -1), 10),
@@ -1219,7 +1230,7 @@ def generate_issues_list_pdf(issues, filter_summary: str = '') -> bytes:
]
hdr_style = ParagraphStyle('ILH', fontName='Helvetica-Bold', fontSize=7.5,
- textColor=C_WHITE, leading=9)
+ textColor=C_DARK, leading=9)
val_style = ParagraphStyle('ILV', fontName='Helvetica', fontSize=7.5,
textColor=C_DARK, leading=9)
@@ -1298,7 +1309,8 @@ def generate_issues_list_pdf(issues, filter_summary: str = '') -> bytes:
tbl = Table(tbl_data, colWidths=col_w, repeatRows=1)
tbl.setStyle(TableStyle([
- ('BACKGROUND', (0, 0), (-1, 0), C_DARK),
+ ('BACKGROUND', (0, 0), (-1, 0), C_HEADER),
+ ('LINEBELOW', (0, 0), (-1, 0), 0.75, C_BLUE),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [C_WHITE, C_LIGHT]),
('INNERGRID', (0, 0), (-1, -1), 0.25, C_BORDER),
('BOX', (0, 0), (-1, -1), 0.5, C_BORDER),
@@ -1375,7 +1387,7 @@ def generate_inspections_list_pdf(inspections, filter_summary: str = '') -> byte
# ── Table header ──────────────────────────────────────────────────────────
hdr_style = ParagraphStyle('LH', fontName='Helvetica-Bold', fontSize=7.5,
- textColor=C_WHITE, leading=9)
+ textColor=C_DARK, leading=9)
val_style = ParagraphStyle('LV', fontName='Helvetica', fontSize=7.5,
textColor=C_DARK, leading=9)
@@ -1435,7 +1447,8 @@ def generate_inspections_list_pdf(inspections, filter_summary: str = '') -> byte
tbl = Table(tbl_data, colWidths=col_w, repeatRows=1)
tbl.setStyle(TableStyle([
- ('BACKGROUND', (0, 0), (-1, 0), C_DARK),
+ ('BACKGROUND', (0, 0), (-1, 0), C_HEADER),
+ ('LINEBELOW', (0, 0), (-1, 0), 0.75, C_BLUE),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [C_WHITE, C_LIGHT]),
('INNERGRID', (0, 0), (-1, -1), 0.25, C_BORDER),
('BOX', (0, 0), (-1, -1), 0.5, C_BORDER),