Nếu bạn là engineer vừa được nói rằng “invoices phải là PDF/A-3 with Factur-X trước quý tới”, và toàn bộ context của bạn là một người bên pháp lý đã nói những từ đó, bài viết này dành cho bạn.
Chúng ta sẽ bỏ giọng văn của standards documents và giải thích các profiles này thật sự constrain điều gì, vì sao governments bắt đầu mandate chúng, và pipeline thực tế nhỏ nhất để emit một PDF compliant từ structured-data renderer.
PDF/A trong hai đoạn
PDF là một format linh hoạt. Quá linh hoạt — cùng một PDF spec cho phép bạn embed JavaScript, link tới external resources có thể không còn tồn tại sau 50 năm, encrypt content bằng cryptography có thể đảo ngược, reference external fonts, và nhiều thứ khác khiến document không còn self-contained.
PDF/A, với “A” là Archival, là một profile của PDF cấm những phần có thể ngăn document render y hệt sau 50 năm. Các rules cấp cao:
- Tất cả fonts phải embedded.
- Không JavaScript, không external links, không audio/video.
- Không encryption.
- Mọi transparency phải được flatten hoặc được profile version support.
- Colours phải device-independent; cần ICC profile.
- Toàn bộ content phải nằm trong file, không có references phụ thuộc network.
Có nhiều versions, mỗi version thêm tolerance cho newer features:
| Profile | Year | Thêm gì |
|---|---|---|
| PDF/A-1b | 2005 | Original baseline, nghiêm ngặt nhất |
| PDF/A-2b | 2011 | Cho phép JPEG2000, transparency, layers |
| PDF/A-3b | 2012 | Cho phép arbitrary file attachments, nền tảng của Factur-X |
| PDF/A-4 | 2020 | Dựa trên ISO 32000-2 (PDF 2.0), conformance levels đơn giản hơn |
Suffix “b” nghĩa là “basic” conformance, tức visual fidelity. Cũng có variants “u” cho unicode-mapped và “a” cho accessibility-tagged. Với hầu hết invoice/receipt workflows, “b” là thứ bạn cần, vì tax archival quan tâm đến visual reproducibility, không phải screen-reader semantics.
Practical takeaway: nếu renderer nói support PDF/A-3b, nó nên là một single config flag, như { profile: "PDF/A-3b" } hoặc tương đương. Nếu bạn phải chạy tool thứ hai như Ghostscript, qpdf hoặc Acrobat để convert sau đó, đó là workflow gap cần tính vào ops.
Vì sao PDF/A-3 đặc biệt quan trọng: nó là carrier của e-invoices
PDF/A-3 thêm một capability nghe có vẻ bình thường nhưng rất quan trọng: arbitrary file attachments bên trong PDF.
Nghe có vẻ chán. Nhưng không. Đây là toàn bộ technical foundation cho e-invoice mandates đang rollout khắp châu Âu.
Architecture: một PDF file duy nhất vừa là:
- Human-readable invoice, phần có visual layout, totals và branding.
- Machine-readable XML invoice, phần mà software của tax authority parse.
Cả hai nằm trong một file, cả hai đại diện cùng một invoice, và PDF/A-3 wrapper guarantee file vẫn parseable trong nhiều thập kỷ.
Các XML formats chính:
- Factur-X (France), XML profile dựa trên UN/CEFACT Cross Industry Invoice.
- ZUGFeRD (Germany), về bản chất giống Factur-X; hai standards đã merge kỹ thuật vào năm 2018.
- EN 16931, European norm mà cả hai implementations conform.
Trong hầu hết workflows, “Factur-X” và “ZUGFeRD” là terms có thể thay thế nhau. Chúng share schema, share embedding mechanism, và một PDF compliant với một chuẩn thường compliant với chuẩn còn lại.
Cái gì mandatory, ở đâu, khi nào
Snapshot không exhaustive cho engineers đang plan rollouts Q2/Q3 2026:
| Country | Status | Required format |
|---|---|---|
| Germany | B2B mandatory cho invoice receipt từ 2025-01-01; từ 2027 cũng mandatory cho issuing | EN 16931 (Factur-X / ZUGFeRD / XRechnung) |
| France | mandatory issuing cho large enterprises 2026-09; SMEs 2027-09 | Factur-X via Chorus Pro |
| Italy | B2B mandatory since 2019 | FatturaPA via SDI |
| Poland | Mandatory since 2024-07 | KSeF |
| Spain | Mandatory from 2026 (B2B) | Facturae via FACe |
| Belgium | Mandatory from 2026-01 | Peppol BIS 3 |
Pattern là: mọi EU member state đang implement một flavour nào đó của EN 16931-compliant e-invoicing trên timeline 2024-2027. Nếu customers của bạn operate ở bất kỳ market nào trong số này, PDF generator của bạn sẽ cần emit attached XML cùng với visual invoice.
Pipeline thực tế nhỏ nhất
Tạm quên những gì standards documents prescribe. Đây là engineering view:
┌─────────────────────┐
│ Your invoice data │ (đã là JSON object ở đâu đó)
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Build EN 16931 XML │ (deterministic mapping; có tested libs)
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Render PDF/A-3b + │
│ attach the XML │ (single API call tới gPdf, hoặc two-step ở nơi khác)
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Hand off to │
│ Chorus Pro / SDI / │
│ Peppol / etc │
└─────────────────────┘
Hai bước non-trivial:
Step 1: build XML
Việc này khó chịu nhưng mechanical. Bạn map invoice data như lines, taxes, totals và parties sang field names của EN 16931 XML. Có nhiều Java/Node/Python libraries làm việc này cho bạn; hãy search “factur-x library” trong language của bạn. Đừng viết từ scratch trừ khi bạn thật sự thích XML schema specs.
Step 2: render PDF/A-3 và attach XML
Đây là nơi renderer choice quan trọng.
Không có built-in support: bạn render ordinary PDF, rồi post-process bằng tool vừa convert sang PDF/A-3 vừa attach XML như embedded file. Common stacks: Ghostscript + qpdf, hoặc paid tool như Aspose. Hai extra steps, hai extra failure points, và bạn phải ensure post-processing không làm visual layout drift.
Có built-in support (cách của gPdf): one call.
curl -X POST https://gpdf.com/api/v1/e-invoice/render \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data '{
"document": {
"pages": [{ "size": "a4", "elements": [...] }]
},
"einvoice": {
"format": "factur-x",
"profile": "BASIC",
"xml": "<rsm:CrossIndustryInvoice>...</rsm:CrossIndustryInvoice>"
}
}' \
--output invoice-with-einvoice.pdf
Đó là toàn bộ pipeline. Renderer emit PDF/A-3b, attach XML của bạn thành factur-x.xml hoặc zugferd-invoice.xml, cả hai đều được consumers nhận ra, rồi return bytes.
Bẫy phổ biến
Một số điều nhiều người học theo cách đau:
“PDF/A” và “có PDF/A-compliant fonts” không giống nhau
PDF/A-3 file yêu cầu mọi fonts đều embedded với full character coverage cho glyphs đã dùng. Nếu invoice có tên khách hàng Nhật và renderer fallback sang font không fully embeddable, validation tools sẽ reject. Hãy check renderer của bạn embed CJK fonts trong PDF/A mode; nhiều renderer không làm mặc định.
Visual + XML phải khớp
XML invoice và visual invoice được kỳ vọng represent cùng một invoice. Tax auditors sẽ diff chúng. Nếu code emit XML với total: 119.00 nhưng visual PDF hiển thị Total: 120.00 vì rounding bug hoặc stale template, bạn có tax discrepancy trong file. Generate cả hai từ same source-of-truth, ideally trong cùng code path.
”Profile” levels trong EN 16931
Factur-X có profiles: MINIMUM, BASIC, EN 16931, EXTENDED. Chúng khác nhau ở lượng data trong XML. Dùng BASIC trừ khi customer specifically yêu cầu nhiều hơn; nó cover tax codes, line items, parties và totals, đủ cho khoảng 95% B2B invoicing. EN 16931 profile thêm detail cho edge cases.
Validation trước submission
Luôn validate generated PDF bằng PDF/A validator; veraPDF là open-source standard. Đồng thời validate XML với EN 16931 schema trước khi ship tới tax authority. Failed submissions tới Chorus Pro / SDI ảnh hưởng tới reliability metrics của bạn với regulator.
TL;DR
PDF/A là self-contained-document profile. PDF/A-3 cho phép attach files. Factur-X / ZUGFeRD nghĩa là “EN 16931 XML attached bên trong PDF/A-3”. E-invoice mandates khắp EU biến combination này thành de facto B2B invoice format giai đoạn 2025-2027.
Nếu renderer của bạn treat PDF/A-3 + Factur-X như single config flag, migration là mechanical. Nếu không, bạn đang xây multi-step ops pipeline. /api/v1/e-invoice/render của gPdf là single-flag version; API reference có full schema, hoặc thử sample render trong Playground.