Xuất đề bài ra PDF (Pdfoid)
Cài Pdfoid (tùy chọn) để server tự tạo file PDF đề bài tại
/problem/<mã>/pdf. Không cài thì LCOJ vẫn cho người dùng in đề ra PDF bằng trình duyệt.⏱ ~45 phút · 👤 Người vận hành · 🔑 SSH + quyền chạy docker trên máy chủ
Bạn có cần trang này không?
Pdfoid là dịch vụ của DMOJ, dùng Chromium chạy ngầm (headless) để chuyển HTML đề bài thành file PDF trên server.
- LCOJ chạy tốt khi không có Pdfoid. Nút Xem dạng PDF trên trang đề bài khi đó mở bản in
/problem/<mã>/rawrồi gọi hộp thoại in của trình duyệt, người dùng chọn "Lưu dưới dạng PDF". - Chỉ cài Pdfoid khi bạn cần link PDF cố định (
/problem/<mã>/pdf) do server tạo, ví dụ để phát đề hoặc in hàng loạt cho kỳ thi offline. - Nếu bạn đã có sẵn file PDF đề bài, không cần Pdfoid: dùng trường tải lên file PDF đề (
statement_file) khi sửa bài.
Trạng thái trong LCOJ
| Thành phần | Trạng thái trong cấu hình đi kèm |
|---|---|
DMOJ_PDF_PDFOID_URL | Tắt: chỉ có dòng ví dụ bị comment trong dmoj/config/local_settings.py |
DMOJ_PDF_PROBLEM_CACHE, DMOJ_PDF_PROBLEM_INTERNAL | Tắt: bị comment |
Dịch vụ Pdfoid trong docker-compose.yml | Không có |
| Nút "Xem dạng PDF" | Dùng chế độ in của trình duyệt |
Cách hoạt động
Một số điểm cần biết:
- HTML gửi sang Pdfoid là template
problem/raw.html. Template này tải MathJax qua URL đầy đủ của site (ví dụhttps://lcoj.example.com/static/...), nên container Pdfoid phải truy cập được website của bạn. - Nếu đặt
DMOJ_PDF_PROBLEM_CACHE, file PDF được lưu với tên<MÃ>.<ngôn ngữ>.pdfvà tự bị xóa khi bài được lưu lại. Lần xem sau sẽ render lại. - Việc render diễn ra ngay trong request của uWSGI (không qua Celery).
Trước khi bắt đầu
Cài đặt (tùy chọn)
Pdfoid không có trong docker-compose.yml. Bạn chạy nó thành một container riêng, cùng network site với container site.
Bước 1: Tạo image Pdfoid
Pdfoid không có trên PyPI, cài trực tiếp từ github.com/DMOJ/pdfoid. Nó cần Chromium, ChromeDriver và exiftool, đọc đường dẫn từ biến CHROME_PATH, CHROMEDRIVER_PATH, EXIFTOOL_PATH.
Tạo dmoj/addons/pdfoid/Dockerfile (mẫu tham khảo, hãy thử trên máy dev trước khi dùng thật):
FROM python:3.11-slim
RUN apt-get update && \
apt-get install -y --no-install-recommends \
chromium chromium-driver libimage-exiftool-perl \
fonts-dejavu fonts-liberation git && \
rm -rf /var/lib/apt/lists/* && \
pip install --no-cache-dir git+https://github.com/DMOJ/pdfoid.git && \
useradd -m pdfoid
ENV CHROME_PATH=/usr/bin/chromium \
CHROMEDRIVER_PATH=/usr/bin/chromedriver \
EXIFTOOL_PATH=/usr/bin/exiftool
USER pdfoid
EXPOSE 8888
CMD ["pdfoid", "--port=8888", "--address=0.0.0.0"]WARNING
Pdfoid mặc định chỉ nghe trên localhost. Trong container bắt buộc phải có --address=0.0.0.0.
Bước 2: Thêm service vào Compose
Tạo (hoặc bổ sung) dmoj/docker-compose.override.yml. Compose tự gộp file này với docker-compose.yml khi bạn chạy lệnh trong thư mục dmoj/.
services:
pdfoid:
build: ./addons/pdfoid
restart: unless-stopped
networks: [site]Bước 3: Khai báo settings
Site đọc cấu hình từ dmoj/repo/dmoj/local_settings.py. File này được ./scripts/initialize chép từ dmoj/config/local_settings.py. Hãy sửa file trong config/ rồi chép lại (hoặc sửa cả hai), xem Biến môi trường và cấu hình.
DMOJ_PDF_PDFOID_URL = 'http://pdfoid:8888/'Bước 4: Khởi động
cd dmoj
docker compose up -d --build pdfoid
docker compose restart siteBật cache PDF (khuyến nghị khi đã dùng Pdfoid)
Không có cache, mỗi lượt xem PDF đều khởi động một Chromium mới. Để cache:
Thêm một volume dùng chung cho
sitevànginxtrongdmoj/docker-compose.override.yml:yamlservices: site: volumes: - pdfcache:/pdfcache/ nginx: volumes: - pdfcache:/pdfcache/ volumes: pdfcache:Thêm location nội bộ vào
dmoj/nginx/conf.d/nginx.conf, giống cách/userdatacacheđang làm:nginxlocation /pdfcache { internal; root /; }Khai báo settings:
pythonDMOJ_PDF_PROBLEM_CACHE = '/pdfcache' # thư mục phải tồn tại và site ghi được DMOJ_PDF_PROBLEM_INTERNAL = '/pdfcache' # đường dẫn nginx dùng cho X-Accel-RedirectTạo lại container
sitevànginxđể gắn volume mới (nginx cũng đọc lại cấu hình khi được tạo lại):shdocker compose up -d site nginx
Kiểm tra kết quả
- Mở một bài bất kỳ, ví dụ
https://lcoj.example.com/problem/APLUSB. - Nút Xem dạng PDF giờ trỏ tới
/problem/APLUSB/pdf. - Bấm vào, sau vài giây trình duyệt hiển thị file PDF.
- Nếu đã bật cache: thư mục cache có file
APLUSB.<ngôn ngữ>.pdf, và lần mở thứ hai trả về gần như ngay lập tức.
Các setting của Pdfoid
| Setting | Mặc định (dmoj/settings.py) | Ý nghĩa |
|---|---|---|
DMOJ_PDF_PDFOID_URL | None | URL của Pdfoid. Khác None thì bật tính năng |
DMOJ_PDF_PROBLEM_CACHE | None | Thư mục cache PDF (tùy chọn) |
DMOJ_PDF_PROBLEM_INTERNAL | None | Đường dẫn nội bộ nginx trỏ tới thư mục cache (tùy chọn) |
Chỉ ba setting trên có tác dụng
Một số hướng dẫn DMOJ khác nhắc tới DMOJ_PDF_PROBLEM_TIMEOUT, DMOJ_PDF_PROBLEM_EXTRA_CSS, DMOJ_PDF_PROBLEM_HEADER, DMOJ_PDF_PROBLEM_FOOTER, DMOJ_PDF_PROBLEM_CACHE_TIME, DMOJ_PDF_PROBLEM_COMPRESS, DMOJ_PDF_PDFOID_URLS. LCOJ không đọc các setting này, đặt chúng không có tác dụng gì. Thời gian chờ MathJax cố định 15 giây; muốn đổi phải sửa judge/utils/pdfoid.py trong dmoj/repo.
Sử dụng
| Cách | Ví dụ |
|---|---|
| Theo ngôn ngữ giao diện hiện tại | https://lcoj.example.com/problem/APLUSB/pdf |
Chỉ định ngôn ngữ (vi hoặc en) | https://lcoj.example.com/problem/APLUSB/pdf/vi |
Lệnh quản trị, ghi ra APLUSB.pdf trong dmoj/repo/ | ./scripts/manage.py render_pdf APLUSB -l vi |
TIP
Trang PDF kiểm tra quyền xem bài giống trang đề: ai không có quyền xem bài sẽ nhận lỗi 404.
Sự cố thường gặp
| Triệu chứng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
/problem/<mã>/pdf trả 404 | DMOJ_PDF_PDFOID_URL chưa đặt, hoặc chưa restart site | Kiểm tra settings, chạy docker compose restart site |
Lỗi 500, log site có ConnectionError | Site không gọi được Pdfoid | docker compose ps pdfoid; kiểm tra service ở network site và nghe 0.0.0.0 |
Log có PDF rendering timed out | Chromium không tải được MathJax từ URL site trong 15 giây | Kiểm tra container Pdfoid truy cập được website (DNS, kết nối Internet, tường lửa) |
| Log Pdfoid báo Chromium không khởi động (sandbox) | Hạn chế của Docker với sandbox Chromium | Chạy bằng user thường (như Dockerfile trên); nếu vẫn lỗi, xem tài liệu Chromium về sandbox trong container |
| Chữ tiếng Việt lỗi font | Thiếu font trong image | Cài thêm font (fonts-dejavu, fonts-noto) rồi build lại |
| Sửa đề mà PDF cũ vẫn còn | File cache chỉ bị xóa khi bài được lưu | Lưu lại bài, hoặc xóa file <MÃ>.<ngôn ngữ>.pdf trong thư mục cache |
Xem log: docker compose logs -f pdfoid và docker compose logs -f site (logger judge.problem.pdf).
Tiếp theo
- Công thức toán học: MathJax cũng là thứ Pdfoid phải chờ tải xong.
- Kiến trúc hệ thống: vị trí của
site,nginxvà các network trong stack. - Lệnh quản trị: lệnh
render_pdfvà các lệnh khác.
Cần hỗ trợ?
Tạo issue tại github.com/luyencode/lcoj-docker/issues, xem thêm tại behitek.com hoặc liên hệ qua luyencode.net/about/#lien-he.
