diff --git a/backend/api_server.py b/backend/api_server.py
index 1e7b287..5850057 100755
--- a/backend/api_server.py
+++ b/backend/api_server.py
@@ -1247,6 +1247,66 @@ def generate_html_response(report_data, filename, save_to_file=False, session_id
else:
return Response(html_content, mimetype='text/html')
+def _render_technical_section_html(report):
+ """Render the technical pre-flight report as an HTML block. Empty string if no report."""
+ if not report or report.get('kind') in (None, 'unknown'):
+ return ''
+ kind = report['kind']
+ rows = []
+ size_mb = report.get('file_size_mb')
+ if size_mb is not None:
+ rows.append(f'
File size: {size_mb} MB
')
+ dims = report.get('dimensions')
+ if dims:
+ rows.append(f'Dimensions: {dims["width"]} × {dims["height"]}
')
+ fmt = report.get('format')
+ if fmt:
+ rows.append(f'Format: {fmt}
')
+ dpi = report.get('dpi')
+ if dpi:
+ rows.append(f'DPI: {dpi[0]} × {dpi[1]}
')
+ mode = report.get('mode')
+ if mode:
+ rows.append(f'Color mode: {mode}
')
+ pc = report.get('page_count')
+ if pc is not None:
+ rows.append(f'Pages: {pc}
')
+ pdf_ver = report.get('pdf_version')
+ if pdf_ver:
+ rows.append(f'PDF version: {pdf_ver}
')
+ duration = report.get('duration_seconds')
+ if duration is not None:
+ rows.append(f'Duration: {duration}s
')
+ codec = report.get('video_codec')
+ if codec:
+ rows.append(f'Video codec: {codec}
')
+ fps = report.get('fps')
+ if fps:
+ rows.append(f'Frame rate: {fps} fps
')
+ fonts = report.get('embedded_fonts')
+ if fonts:
+ suffix = ' …' if len(fonts) > 8 else ''
+ rows.append(f'Embedded fonts: {", ".join(fonts[:8])}{suffix}
')
+ fm = report.get('filename_match')
+ if fm:
+ if fm['match']:
+ badge = '✓ Matches filename'
+ else:
+ badge = '⚠ Filename mismatch'
+ rows.append(f'{badge} {fm["detail"]}
')
+ errors = report.get('errors', [])
+ if errors:
+ rows.append(f'Inspection notes: {"; ".join(errors)}
')
+ if not rows:
+ return ''
+ return f'''
+
+
🔧 Technical Details (machine-inspected, no AI)
+
{''.join(rows)}
+
+ '''
+
+
def generate_comprehensive_html_report(analysis_result, filename, file_path=None):
"""Generate comprehensive HTML report similar to the web UI format"""
summary = analysis_result.get('summary', {})
@@ -1308,7 +1368,9 @@ def generate_comprehensive_html_report(analysis_result, filename, file_path=None
avg_individual_score = overall_score / 10 # Normalize to 1-10 scale
grade_text = 'Pass' if avg_individual_score >= 6 else 'Fail'
score_color = '#28a745' if avg_individual_score >= 6 else '#dc3545'
-
+
+ technical_html = _render_technical_section_html(analysis_result.get('technical_report', {}))
+
return f'''
@@ -1332,6 +1394,9 @@ def generate_comprehensive_html_report(analysis_result, filename, file_path=None
.summary {{ background: linear-gradient(135deg, #FFF9E6 0%, #FFFBF0 100%); padding: 25px; border-radius: 15px; margin: 30px 0; border-left: 5px solid #FFC407; }}
.summary-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-top: 15px; }}
.summary-item {{ background: white; padding: 15px; border-radius: 10px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.05); }}
+ .technical {{ background: linear-gradient(135deg, #e3f2fd 0%, #f0f7ff 100%); padding: 25px; border-radius: 15px; margin: 30px 0; border-left: 5px solid #1565c0; }}
+ .technical-grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 8px 24px; margin-top: 12px; }}
+ .tech-row {{ padding: 4px 0; color: #495057; font-size: 0.95em; word-break: break-word; }}
.score-display {{ font-size: 2.5em; font-weight: bold; color: {score_color}; margin-bottom: 5px; }}
.grade {{ font-size: 1.3em; font-weight: bold; color: #495057; }}
.expandable-section {{ margin-bottom: 15px; border: 2px solid #e9ecef; border-radius: 12px; overflow: hidden; background: white; }}
@@ -1392,7 +1457,9 @@ def generate_comprehensive_html_report(analysis_result, filename, file_path=None
-
+
+ {technical_html}
+
🔍 Detailed Analysis Results
Click on any section below to expand and view detailed analysis