Blog

Mühendisler için PDF/A ve Factur-X, hukuki dil olmadan

PDF/A profillerinin gerçekte neyi kısıtladığı, Factur-X'in Avrupa e-faturası için neden önemli olduğu ve JSON renderer'dan uyumlu PDF üretmenin en küçük pratik pipeline'ı.

Size az önce “gelecek çeyreğe kadar faturalar PDF/A-3 with Factur-X olmalı” dendiyse ve tek bağlamınız hukuk ekibinden bu kelimeleri duymaksa, bu yazı sizin için.

Standart belgelerinin tonunu kenara koyup bu profillerin gerçekte neyi kısıtladığını, hükümetlerin neden bunları zorunlu kılmaya başladığını ve yapılandırılmış veri renderer’ından uyumlu PDF üretmenin en küçük pratik pipeline’ını anlatacağız.

PDF/A iki paragrafta

PDF esnek bir formattır. Fazla esnek — aynı PDF spec JavaScript embed etmeye, 50 yıl sonra var olmayabilecek dış kaynaklara link vermeye, içeriği geri döndürülebilir kriptografiyle şifrelemeye, dış fontlara referans vermeye ve belgeyi self-contained olmaktan çıkaran yüzlerce şeye izin verir.

PDF/A içindeki “A”, Archival anlamına gelir. PDF’in, belgenin 50 yıl sonra aynı şekilde render edilmesini engelleyecek parçaları yasaklayan bir profilidir. Üst düzey kurallar:

  • Tüm fontlar embedded olmalı.
  • JavaScript yok, dış link yok, audio/video yok.
  • Şifreleme yok.
  • Tüm transparency flatten edilmeli veya profil sürümü tarafından desteklenmeli.
  • Renkler device-independent olmalı; ICC profile gerekli.
  • Tüm içerik file içinde olmalı; network’e bağlı referans yok.

Birkaç version vardır; her biri daha yeni features için tolerans ekler:

ProfilYılNe ekler
PDF/A-1b2005Orijinal baseline, en katı
PDF/A-2b2011JPEG2000, transparency, layers izinleri
PDF/A-3b2012Arbitrary file attachments, Factur-X’in temeli
PDF/A-42020ISO 32000-2 (PDF 2.0) tabanı, sadeleştirilmiş conformance levels

“b” suffix’i “basic” conformance, yani visual fidelity demektir. “u” unicode-mapped ve “a” accessibility-tagged variants da vardır. Çoğu invoice/receipt workflow’u için istediğiniz “b”dir, çünkü vergi arşivleme screen-reader semantiğiyle değil, görselin yeniden üretilebilirliğiyle ilgilenir.

Pratik sonuç: Renderer PDF/A-3b desteklediğini söylüyorsa bu tek bir config flag olmalı: { profile: "PDF/A-3b" } veya eşdeğeri. Sonradan dönüştürmek için Ghostscript, qpdf ya da Acrobat gibi ikinci bir araç çalıştırmanız gerekiyorsa, bu ops’a yansıtmanız gereken bir workflow boşluğudur.

PDF/A-3 neden özellikle önemli: e-faturanın taşıyıcısıdır

PDF/A-3 basit görünen ama dünyayı değiştiren bir capability ekledi: PDF içinde arbitrary file attachments.

Kulağa sıkıcı geliyor. Değil. Avrupa’da şu anda yayılan e-invoice mandates’in tüm teknik temeli bu.

Mimari: tek bir PDF file, hem:

  1. İnsan tarafından okunabilir fatura; görsel layout, totals, branding.
  2. Makine tarafından okunabilir XML fatura; vergi otoritesinin yazılımının parse ettiği bölüm.

İkisi de aynı file içinde, ikisi de aynı faturayı temsil eder, ve PDF/A-3 wrapper file’ın onlarca yıl sonra da parseable kalmasını garanti eder.

Başlıca XML formatları:

  • Factur-X (Fransa), UN/CEFACT Cross Industry Invoice tabanlı XML profile.
  • ZUGFeRD (Almanya), Factur-X ile özünde aynı; iki standard 2018’de teknik olarak birleşti.
  • EN 16931, iki implementation’ın uyduğu Avrupa normu.

Çoğu workflow’da “Factur-X” ve “ZUGFeRD” birbirinin yerine geçer. Aynı schema’yı ve embedding mechanism’i paylaşırlar; biriyle compliant olan tek bir PDF genelde diğeriyle de compliant olur.

Ne zorunlu, nerede, ne zaman

Q2/Q3 2026 rollout planlayan engineers için non-exhaustive snapshot:

CountryStatusRequired format
Germanyinvoice receipt için B2B mandatory from 2025-01-01; 2027’den itibaren issuing deEN 16931 (Factur-X / ZUGFeRD / XRechnung)
Francelarge enterprises için mandatory issuing 2026-09; SMEs 2027-09Factur-X via Chorus Pro
ItalyB2B mandatory since 2019FatturaPA via SDI
PolandMandatory since 2024-07KSeF
SpainMandatory from 2026 (B2B)Facturae via FACe
BelgiumMandatory from 2026-01Peppol BIS 3

Pattern açık: Her EU member state 2024-2027 timeline’ında EN 16931-compliant e-invoicing’in bir çeşidini implement ediyor. Customers bu markets’in herhangi birinde operate ediyorsa, PDF generator’ınız visual invoice’ın yanında attached XML de emit etmek zorunda kalacak.

En küçük pratik pipeline

Standards documents’ın ne buyurduğunu unutun. Engineering view:

   ┌─────────────────────┐
   │  Your invoice data  │  (zaten bir yerde JSON object)
   └─────────┬───────────┘


   ┌─────────────────────┐
   │ Build EN 16931 XML  │  (deterministic mapping; tested libs var)
   └─────────┬───────────┘


   ┌─────────────────────┐
   │ Render PDF/A-3b +   │
   │ attach the XML      │  (gPdf'e single API call, elsewhere two-step)
   └─────────┬───────────┘


   ┌─────────────────────┐
   │  Hand off to        │
   │  Chorus Pro / SDI / │
   │  Peppol / etc       │
   └─────────────────────┘

İki non-trivial step:

Step 1: XML’i build edin

Bu can sıkıcı ama mekanik. Invoice data’nızı, yani lines, taxes, totals ve parties alanlarını EN 16931 XML field names’e map edersiniz. Java/Node/Python için bunu yapan birkaç library var; kendi language’inizde “factur-x library” arayın. XML schema specs gerçekten hoşunuza gitmiyorsa baştan yazmayın.

Step 2: PDF/A-3 render edin ve XML’i attach edin

Renderer seçimi burada önemlidir.

Built-in support yoksa: ordinary PDF render edersiniz, sonra PDF/A-3’e convert eden ve XML’i embedded file olarak attach eden bir tool ile post-process edersiniz. Common stacks: Ghostscript + qpdf veya Aspose gibi paid tool. İki extra step, iki extra failure point, ayrıca post-processing’in visual layout’u drift ettirmediğinden emin olmanız gerekir.

Built-in support varsa (gPdf approach): 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

Pipeline’ın tamamı bu. Renderer PDF/A-3b emit eder, XML’inizi factur-x.xml veya zugferd-invoice.xml olarak attach eder ve bytes döner. İki filename de consumers tarafından tanınır.

Yaygın tuzaklar

İnsanların zor yoldan öğrendiği birkaç şey:

“PDF/A” ve “PDF/A-compliant fontlarla” aynı şey değildir

PDF/A-3 file, kullanılan glyphs için full character coverage ile tüm fontların embedded olmasını gerektirir. Invoice içinde Japanese customer name varsa ve renderer fully embeddable olmayan bir fallback font’a düşerse, validation tools file’ı reject eder. Renderer’ınızın PDF/A mode’da CJK fonts embed ettiğini kontrol edin; çoğu default olarak yapmaz.

Visual + XML aynı olmalı

XML invoice ve visual invoice aynı invoice’ı temsil etmelidir. Tax auditors bunları diff eder. Code XML’de total: 119.00 emit ederken visual PDF Total: 120.00 gösteriyorsa, rounding bug veya stale template yüzünden file içinde tax discrepancy oluşur. İkisini aynı source-of-truth’tan, ideally aynı code path içinde generate edin.

EN 16931’de “profile” seviyeleri

Factur-X profiles: MINIMUM, BASIC, EN 16931, EXTENDED. Fark, XML’deki data miktarıdır. Customer specifically daha fazlasını istemedikçe BASIC kullanın; tax codes, line items, parties ve totals kapsar, bu da B2B invoicing’in yaklaşık %95’i için yeterlidir. EN 16931 profile edge cases için daha fazla detail ekler.

Submission öncesi validation

Generated PDF’i her zaman bir PDF/A validator ile validate edin; veraPDF open-source standard’dır. XML’i de tax authority’ye ship etmeden önce EN 16931 schema’ya karşı validate edin. Chorus Pro / SDI failed submissions regulator tarafındaki reliability metrics’inizi etkiler.

TL;DR

PDF/A self-contained-document profile’dır. PDF/A-3 files attach etmeye izin verir. Factur-X / ZUGFeRD, “PDF/A-3 içine attached EN 16931 XML” demektir. EU e-invoice mandates 2025-2027 arasında bu kombinasyonu de facto B2B invoice formatı yapıyor.

Renderer’ınız PDF/A-3 + Factur-X’i single config flag gibi ele alıyorsa migration mekaniktir. Değilse multi-step ops pipeline kuruyorsunuz. gPdf’in /api/v1/e-invoice/render endpoint’i single-flag version’dır; full schema API reference içinde, sample render için Playground’ı deneyin.