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),