diff --git a/Dockerfile b/Dockerfile index e856613..4c92330 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,15 @@ ENV PYTHONDONTWRITEBYTECODE=1 \ WORKDIR /app +RUN apt-get update && apt-get install -y --no-install-recommends \ + libpango-1.0-0 \ + libpangoft2-1.0-0 \ + libharfbuzz0b \ + libfontconfig1 \ + libcairo2 \ + fonts-liberation \ + && rm -rf /var/lib/apt/lists/* + RUN pip install --upgrade --no-cache-dir pip COPY requirements.txt ./ diff --git a/app.py b/app.py index 68a4c03..2ade3ef 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,8 @@ -from flask import Flask, render_template, jsonify +from flask import Flask, render_template, jsonify, send_file, request +from weasyprint import HTML from datetime import datetime, timezone import os +import io app = Flask(__name__) @@ -11,6 +13,17 @@ GIT_COMMIT = os.getenv('GIT_COMMIT', 'unknown') def index(): return render_template('index.html') +@app.route('/download') +def download_pdf(): + html = render_template('index.html', pdf_mode=True) + pdf = HTML(string=html, base_url=request.host_url).write_pdf() + return send_file( + io.BytesIO(pdf), + mimetype='application/pdf', + as_attachment=True, + download_name='Erika_Nielsen_CV.pdf' + ) + @app.route('/health') def health(): return jsonify({ diff --git a/requirements.txt b/requirements.txt index ab3471f..71579c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ flask>=3.0.0 gunicorn>=22.0.0 +weasyprint>=62.0 diff --git a/templates/index.html b/templates/index.html index 704cb54..e97dfa8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -379,26 +379,44 @@ } .pdf-btn:hover { background: #2f72d0; transform: scale(1.04); } - /* Print */ + /* Print / PDF via WeasyPrint */ @media print { - @page { size: A4; margin: 0; } - html, body { background: white; padding: 0; margin: 0; display: block; } - body { - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - } - .cv-wrapper { - box-shadow: none; - border-radius: 0; - width: 100%; - max-width: 100%; - transform-origin: top left; - transform: scale(0.78); - height: 128vh; - overflow: hidden; - } + @page { size: A4; margin: 8mm; } + html, body { background: white !important; padding: 0; margin: 0; } + body { -webkit-print-color-adjust: exact; print-color-adjust: exact; } + .cv-wrapper { box-shadow: none; border-radius: 0; width: 194mm; max-width: 194mm; } .sidebar { -webkit-print-color-adjust: exact; print-color-adjust: exact; } - .pdf-btn { display: none; } + .pdf-btn { display: none !important; } + /* Kompakt til 1 side */ + body { padding: 0; } + .hero { padding: 18px 22px 12px; } + .hero-name { font-size: 22px; } + .hero-tagline { font-size: 11px; margin-bottom: 6px; } + .hero-summary { font-size: 11px; line-height: 1.5; } + .main { padding: 0 18px 10px; } + section { margin-bottom: 10px; } + .section-title { font-size: 10px; padding: 5px 0 4px; margin-bottom: 8px; } + .timeline-item { padding: 7px 0; } + .timeline-period { font-size: 9px; } + .timeline-title { font-size: 12px; } + .timeline-subtitle { font-size: 10px; } + .timeline-bullets { font-size: 10px; margin-top: 2px; } + .timeline-bullets li { margin-bottom: 0; } + .edu-card { padding: 7px 10px; } + .edu-period { font-size: 9px; } + .edu-title { font-size: 11px; } + .edu-place { font-size: 10px; } + .lang-item label { font-size: 10px; margin-bottom: 3px; } + .lang-bar-track { height: 4px; } + .tag { font-size: 9px; padding: 2px 6px; } + .sidebar-name h1 { font-size: 17px; } + .sidebar-name p { font-size: 10px; } + .sidebar-section { margin-bottom: 16px; } + .sidebar-section-title { font-size: 8px; } + .sidebar-item { font-size: 11px; } + .sidebar-item small { font-size: 9px; } + .avatar-wrap { padding: 18px 0 10px; } + .avatar { width: 70px; height: 70px; font-size: 22px; } } /* Mobile */ @@ -604,7 +622,9 @@ - +{% if not pdf_mode %} + +{% endif %}