Odoo xử lý font ở ba tầng tách biệt: backend, báo cáo PDF và website. Cần sửa file nào cho từng tầng, kèm một hướng code chạy được cho mỗi tầng.
Bạn đã đổi biến SCSS, khởi động lại Odoo, mà PDF hóa đơn vẫn in ra font cũ. Đó không phải lỗi. Backend và báo cáo không dùng chung một luồng xử lý font, và cả hai cũng không dùng chung với website.
Các lời khuyên về font trong Odoo nằm rải rác trong tài liệu theme, hướng dẫn QWeb và các chủ đề trên diễn đàn, hiếm khi được ghi rõ là thuộc tầng nào. Bài viết này gom chúng lại. Đọc xong, bạn sẽ biết phải sửa file nào cho bề mặt nào, với một hướng code chạy được cho mỗi tầng và một danh sách ngắn các module dựng sẵn để khỏi phải viết code khi có thể.
Ví dụ code nhắm tới Odoo 17. Luồng xử lý báo cáo vẫn chạy trên wkhtmltopdf.
Định dạng và họ font trong 60 giây
Nếu đã nắm các định dạng font, hãy bỏ qua mục này.
Định dạng file:
- WOFF2 — lựa chọn mặc định cho web và cho asset của Odoo. Nén tốt nhất, mọi trình duyệt hiện đại đều hỗ trợ.
- WOFF — định dạng nén cũ hơn, giữ lại để dự phòng cho các trình duyệt rất cũ.
- TTF / OTF — định dạng không nén. Vẫn hữu ích vì một số bộ render PDF (đặc biệt là wkhtmltopdf) xử lý chúng ổn định hơn WOFF2.
Trong Odoo, thường bạn sẽ khai báo cả ba trong cùng một @font-face rồi để trình duyệt hoặc bộ render tự chọn.
Họ font: chọn theo chức năng, không theo thẩm mỹ. Odoo đã có sẵn mỗi vai trò một biến SCSS nên hiếm khi bạn cần nghĩ ra biến mới.
- Sans-serif — giao diện backend và phần chữ thân bài. Ví dụ: Roboto, Open Sans, Inter, Segoe UI.
- Serif — báo cáo in dạng dài, nơi người đọc quét qua nhiều dòng. Ví dụ: Merriweather, Source Serif, Noto Serif.
- Monospace — khối code và dữ liệu kỹ thuật. Ví dụ: Fira Code, JetBrains Mono, Source Code Pro.
Font nằm ở đâu trong Odoo
Bản đồ tư duy, mỗi tầng một gạch đầu dòng.
Web client backend
- Biến SCSS trong
web/static/src/scss/primary_variables.scss - Asset bundle:
web.assets_backend - Ghi đè bằng cách định nghĩa lại biến trong SCSS của module bạn
Báo cáo PDF
- Khai báo
@font-facetrongweb/static/fonts/fonts.scss - Asset bundle:
web.report_assets_common - Ghi đè bằng cách thêm
@font-facecủa riêng bạn trong một module rồi đẩy nó vào bundle báo cáo
Website
- Map SCSS
$o-theme-font-configstrongwebsite/static/src/scss/secondary_variables.scss - Asset bundle:
web.assets_frontend(từ Odoo 15.0 trở đi) - Ghi đè bằng cách mở rộng map, thường là trong
primary_variables.scsscủa một theme
Cứ xác định tầng trước, phần code sẽ tự hiện ra.
Font ở backend
Ba biến SCSS điều khiển web client backend:
$o-system-fonts: chuỗi font nền (Apple system, Segoe UI, Roboto, Helvetica Neue, v.v.)$o-font-family-sans-serif: chữ thân bài$o-headings-font-family: tiêu đề, mặc định là SF Pro Display
Một điểm tinh tế trước khi đụng vào chúng. Odoo bọc mọi họ font backend trong o-add-unicode-support-font(), hàm này xếp glyph Noto xuống lớp dưới để tiếng Việt, chữ Kirin và các hệ chữ không phải Latin vẫn hiển thị được khi font chính thiếu glyph. Hãy định nghĩa lại biến, đừng bỏ lớp bọc.
Một module font tùy chỉnh tối giản:
your_module/
├── __manifest__.py
└── static/
├── fonts/
│ ├── CorporateSans.woff2
│ ├── CorporateSans.woff
│ └── CorporateSans.ttf
└── src/scss/
├── fonts.scss
└── variables.scss
fonts.scss:
@font-face {
font-family: 'Corporate Sans';
src: url('/your_module/static/fonts/CorporateSans.woff2') format('woff2'),
url('/your_module/static/fonts/CorporateSans.woff') format('woff'),
url('/your_module/static/fonts/CorporateSans.ttf') format('truetype');
font-weight: 400;
font-style: normal;
}
variables.scss:
$o-font-family-sans-serif: o-add-unicode-support-font(('Corporate Sans', sans-serif));
__manifest__.py:
'assets': {
'web.assets_backend': [
'your_module/static/src/scss/fonts.scss',
'your_module/static/src/scss/variables.scss',
],
},
Thứ tự có ý nghĩa: phần ghi đè biến phải nạp sau primary_variables lõi của Odoo thì định nghĩa mới của bạn mới thắng.
Font trong báo cáo PDF
Bundle báo cáo tách riêng vì wkhtmltopdf render trong ngữ cảnh asset của chính nó. web.assets_backend không được nạp ở đó. Thay vào đó, mọi báo cáo QWeb đều gọi <t t-call-assets="web.report_assets_common"/>, và đó mới là bundle bạn cần mở rộng.
Cách làm giống hệt backend, chỉ hướng vào bundle báo cáo. Đưa @font-face của bạn vào một file SCSS trong module, nạp nó vào web.report_assets_common qua manifest, rồi tham chiếu họ font đó trong CSS báo cáo hoặc template QWeb.
'assets': {
'web.report_assets_common': [
'your_module/static/src/scss/report_fonts.scss',
],
},
@font-face {
font-family: 'Corporate Sans';
src: url('/your_module/static/fonts/CorporateSans.ttf') format('truetype');
font-weight: 400;
}
.o_report_layout, .header, .footer {
font-family: 'Corporate Sans', sans-serif;
}
Với bundle báo cáo, hãy ưu tiên TTF. Một số bản build wkhtmltopdf xử lý WOFF2 không nhất quán, và việc nén cũng chẳng đem lại lợi ích gì khi render ở phía máy chủ.
Glyph tiếng Việt và CJK: wkhtmltopdf thay thế font một cách âm thầm khi thiếu glyph, nên một font trông đúng trên trình duyệt vẫn có thể rụng dấu trong PDF. Có hai lối ra: dùng một font có bộ glyph phủ đúng các ngôn ngữ của bạn, hoặc dựa vào họ Odoo Unicode Support Noto có sẵn của Odoo (đã được web/static/fonts/fonts.scss nạp) như một phương án dự phòng khai báo tường minh trong CSS.
Font trên website
Luồng xử lý của website là thú vị nhất trong ba tầng, vì Odoo làm thay bạn nhiều việc hơn. Khi người biên tập site chọn một Google Font trong trình tùy biến theme, Odoo tải font đó về phía máy chủ, lưu các file WOFF2 thành bản ghi ir.attachment công khai, rồi viết lại các lệnh import SCSS để trỏ vào URL cục bộ. Kết quả là một font được host cục bộ, thân thiện với GDPR, mà không cần bước tải thủ công nào. Hành vi này được đưa vào ở commit b06ce21.
Map nằm ở trung tâm mọi thứ là $o-theme-font-configs. Mỗi mục có dạng:
$o-theme-font-configs: (
'Corporate Sans': (
'family': ('Corporate Sans', sans-serif),
'url': 'Corporate+Sans:300,400,700',
'name': 'Corporate Sans',
),
...
);
Giá trị url trỏ tới một chuỗi họ font của Google Fonts. Nếu người biên tập site lưu lại với font này đang được chọn, Odoo sẽ đổi url thành attachment và lưu font về máy. Muốn thêm font mới vào bộ chọn, hãy làm một theme (hoặc mở rộng theme có sẵn) và gộp các mục mới vào map trong primary_variables.scss. Cách làm trong theme_avantgarde là bản tham chiếu.
Unicode: cũng như ở backend, Odoo tự động áp o-add-unicode-support-font() cho mọi mục trong map. Bạn không cần tự thêm, và cũng không nên gỡ bỏ, nếu không các site tiếng Việt sẽ bắt đầu hiện ra ô vuông.
Nếu cần hẳn một giao diện cho người không phải lập trình viên tự tải font lên, thay vì bộ chọn Google Fonts, trình dựng WYSIWYG có hé lộ một phần khả năng này, nhưng luồng xử lý nằm ở tầng JavaScript và vượt ra ngoài phạm vi bài viết. Một bài tiếp theo sẽ nói về các module cộng đồng lấp khoảng trống này.
Tóm lại
- Backend: định nghĩa lại một biến SCSS, nạp vào
web.assets_backend. - Báo cáo: thêm
@font-face, nạp vàoweb.report_assets_common, tham chiếu trong CSS báo cáo. - Website: mở rộng
$o-theme-font-configstrong một theme, để Odoo lo phần tải về.
Chọn tầng đang chặn bạn hôm nay, chép đoạn code, đưa lên. Mô hình tư duy này sẽ nhớ được ngay từ lần đầu dùng thật. Font trong Odoo không khó, chúng chỉ là ba bài toán khoác lên mình bộ áo của một.