Artikel Teknis

Skema Ekstensi PDF/A-3 untuk XMP Factur-X di Delphi

Anda telah merangkai faktur Factur-X dan setiap pemeriksaan kontainer berhasil dilewati. Katalog tersebut membawa array /AF, name tree EmbeddedFiles mengarah pada spesifikasi file yang tepat, XML factur-x.xml yang tertanam memiliki relasi /AFRelationship yang benar yakni Alternative, dan metode bawaan ValidateFacturXInvoice mengembalikan angka 1. Lalu Anda menjalankan file yang sama melalui veraPDF, penguji rujukan (reference checker) yang digunakan portal pajak, dan ia menyatakan bahwa keseluruhan dokumen tersebut bukanlah PDF/A-3 yang valid. Strukturnya sudah benar. Masalahnya ada pada metadata, dan kegagalan ini adalah salah satu yang paling mudah terlewatkan dalam seluruh alur kerja e-invoice

Alasannya patut dipahami secara utuh, karena hal ini menjelaskan kategori kecacatan PDF/A yang sama sekali tidak berhubungan dengan halaman yang kasat mata maupun lampirannya, melainkan sepenuhnya berkaitan dengan cara XMP mendeskripsikan dirinya sendiri. Inilah jebakan yang bersembunyi di balik pemeriksaan kontainer yang hijau (berhasil)

Empat properti yang menggagalkan file

Faktur Factur-X menuliskan empat properti kustom ke dalam paket XMP-nya sehingga perangkat lunak hilir (downstream) dapat membaca profil faktur tanpa harus mem-parsing XML yang tertanam. Keempatnya bernaung di dalam namespace Factur-X di bawah prefiks fx: fx:DocumentFileName, fx:DocumentType, fx:Version, dan fx:ConformanceLevel. Properti tersebut persis merupakan metadata yang dibutuhkan pembaca untuk mengetahui bahwa PDF ini membawa faktur EN 16931 bernama factur-x.xml pada versi 1.0

Tidak satu pun dari keempat properti tersebut merupakan bagian dari skema XMP yang telah didefinisikan sebelumnya (predefined) oleh PDF/A. Skema Dublin Core, XMP Basic, PDF, dan skema identifikasi PDF/A dikenal oleh sebuah conforming reader, tetapi fx: tidak. Ketika veraPDF menelusuri XMP dan menjumpai properti yang namespace-nya tidak dikenali, ia akan mencari deklarasi yang menjelaskan arti properti tersebut. Jika deklarasi itu tidak ada, ia akan melaporkan kegagalan terhadap ISO 19005-3 klausul 6.6.2.3.1, yang mensyaratkan bahwa setiap properti yang tidak diambil dari skema yang didefinisikan sebelumnya harus dideskripsikan dalam skema ekstensi (extension schema) PDF/A. Empat properti yang tidak dideklarasikan, empat cara agar file ditolak, dan tidak satu pun yang terlihat pada pemeriksaan kontainer

Mengapa PDF/A menolak properti kustom secara mentah-mentah

Aturan ini terlihat pedantik sampai Anda mengingat apa tujuan PDF/A. Format ini ada agar sebuah file dapat dibuka dan dipahami beberapa dekade dari sekarang, oleh perangkat lunak yang tidak pernah mengetahui tentang konvensi tahun 2026. Sebuah conforming reader diharapkan dapat memahami dokumen hanya dari dokumen itu sendiri, tanpa perlu berkonsultasi ke registri eksternal

Metadata kustom melanggar janji tersebut kecuali jika file membawa deskripsinya sendiri. Diberikan properti fx:ConformanceLevel saja, perangkat pembaca di masa depan tidak dapat mengetahui namespace URI yang diikat oleh prefiks fx, apakah nilainya berupa teks, tanggal, atau bilangan bulat, atau apakah properti tersebut mendeskripsikan dokumen itu sendiri atau sumber daya eksternal. Mekanisme skema ekstensi PDF/A menutup celah tersebut. Mekanisme ini memungkinkan file untuk mendeklarasikan, dalam struktur XMP yang tetap, namespace, prefiks, dan untuk setiap properti jenis nilai serta kategori internal atau external. Setelah deklarasi tersebut hadir, properti itu dapat mendeskripsikan dirinya sendiri (self-describing), dan klausul 6.6.2.3.1 terpenuhi. Tanpanya, validator tidak punya pilihan selain memperlakukan properti sebagai sesuatu yang tidak dapat dipahami dan menggagalkan file tersebut. Pembedaan kategori penting di sini: properti faktur semacam ini mendeskripsikan data yang berasal dari luar pemroses PDF, sehingga mereka dideklarasikan sebagai external bukan internal

Apa isi deklarasi skema ekstensi tersebut

Deklarasi ini adalah sebuah rdf:Description di dalam paket XMP yang menggunakan tiga namespace yang didefinisikan oleh AIIM yakni pdfaExtension, pdfaSchema, dan pdfaProperty. Di dalam wadah pdfaExtension:schemas terdapat satu entri skema yang menamai skema Factur-X, memberikan pdfaSchema:namespaceURI dan pdfaSchema:prefix, lalu mendaftar keempat properti dalam urutan (sequence) pdfaSchema:property. Setiap properti membawa sebuah nama, pdfaProperty:valueType bernilai Text, dan pdfaProperty:category bernilai external. Markup ilustratif di bawah ini menunjukkan bentuk dari blok tersebut

<rdf:Description rdf:about=""
    xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
    xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
    xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">
  <pdfaExtension:schemas>
    <rdf:Bag>
      <rdf:li rdf:parseType="Resource">
        <pdfaSchema:schema>Factur-X PDFA Extension Schema</pdfaSchema:schema>
        <pdfaSchema:namespaceURI>urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>
        <pdfaSchema:prefix>fx</pdfaSchema:prefix>
        <pdfaSchema:property>
          <rdf:Seq>
            <rdf:li rdf:parseType="Resource">
              <pdfaProperty:name>DocumentFileName</pdfaProperty:name>
              <pdfaProperty:valueType>Text</pdfaProperty:valueType>
              <pdfaProperty:category>external</pdfaProperty:category>
              <pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>
            </rdf:li>
            <!-- DocumentType, Version, ConformanceLevel dideklarasikan dengan cara yang sama -->
          </rdf:Seq>
        </pdfaSchema:property>
      </rdf:li>
    </rdf:Bag>
  </pdfaExtension:schemas>
</rdf:Description>

URI namespace dan prefiks bukanlah string yang tetap. Keduanya mengikuti profil. Sebuah dokumen Factur-X menggunakan urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0# dengan prefiks fx, sementara file ZUGFeRD 2.0 yang dipilih melalui zugferd-invoice.xml mengarah ke URI yang berbeda di bawah nama skemanya sendiri. Skema ekstensi harus mendeklarasikan URI namespace yang sama persis dengan yang benar-benar digunakan oleh blok properti, atau jika tidak validator tetap tidak dapat menghubungkan keduanya. PDFlibPas memperoleh kedua nilai tersebut dari nama file dan versi yang Anda berikan, sehingga deklarasi dan blok properti akan selalu serasi

Cara fungsi pembantu menulis kedua bagiannya secara bersamaan

Dalam PDFlibPas Anda tidak merakit XML tersebut secara manual. Anda menetapkan dokumen ke dalam mode PDF/A-3 dan memanggil satu metode. Hal pertama yang harus diselesaikan adalah bendera kepatuhan, karena Factur-X mensyaratkan PDF/A-3. Memanggil SetPDFAMode(7) akan memilih tingkat PDF/A-3u, yang mana mengatur nilai pdfaid:part menjadi 3 dan pdfaid:conformance menjadi U pada skema identifikasi. Paket XMP tersebut sekarang membawa bagian (part) dan kepatuhan yang tepat sebelum metadata faktur apapun ditambahkan

var
  FileID: Integer;
begin
  PDF.SetPDFAMode(7);            // PDF/A-3u: pdfaid:part=3, conformance=U
  PDF.NewDocument;
  // gambar halaman faktur yang dapat dibaca manusia di sini

  FileID := PDF.AddFacturXAssociatedFileFromString(
    InvoiceXML,                  // byte XML UTF-8 mentah
    'EN16931',                   // ConformanceLevel
    'factur-x.xml',              // nama file yang tertanam
    'Factur-X invoice XML',      // teks deskripsi /Desc
    'Alternative',               // /AFRelationship
    '1.0',                       // versi profil
    '');                         // kode negara opsional
  if FileID = 0 then
    Exit;                        // bukan PDF/A-3, atau terjadi ketidakcocokan XML/profil

  PDF.SaveToFile('factur-x.pdf');
end;

Panggilan tunggal ke AddFacturXAssociatedFileFromString melakukan pekerjaan yang selama ini dilewatkan oleh file yang gagal tersebut. Fungsi ini menyematkan XML sebagai associated file PDF/A-3 dengan relasi yang Anda sebutkan, lalu ia mencatat empat properti fx beserta dengan nama skema, URI namespace, dan prefiks untuk profil yang dipilih. Ketika dokumen disimpan, sebuah langkah internal bernama ApplyFacturXMetadata menyuntikkan blok properti maupun deklarasi pdfaExtension:schemas yang cocok ke dalam paket XMP, sehingga properti kustom itu mendarat dalam keadaan telah dideskripsikan. Metode tersebut mengembalikan 0 jika dokumen tidak berada dalam mode PDF/A-3 atau jika XML tidak cocok dengan profil yang dideklarasikan, sebuah pengawalan yang sama yang menghentikan faktur cacat (malformed) agar tidak masuk ke dalam file sejak awal

Titik buta yang tidak bisa dilihat oleh pemeriksaan kontainer

Ini adalah bagian yang harus dinamakan secara gamblang, karena hal inilah yang menjadi alasan mengapa bug tersebut bersembunyi. ValidateFacturXInvoice memeriksa kontainer. Ia mengonfirmasi bahwa katalog memiliki entri /AF, name tree EmbeddedFiles ada, XML faktur tersedia, nama file yang tertanam sesuai dengan profil, ID panduan dalam XML selaras dengan tingkat kepatuhan, dan /AFRelationship adalah nilai yang diizinkan oleh PDF/A-3. Semua ini adalah pemeriksaan nyata dan mereka menangkap kecacatan yang sungguhan. GetFacturXValidationIssues melaporkan masalah-masalah ini dengan sebutan namanya, melalui identifier seperti MissingCatalogAF, NotPDFA3, ConformanceGuidelineMismatch, InvalidAFRelationship, dan InvalidFileNameProfile

Yang tidak diperiksa olehnya adalah apakah skema ekstensi XMP itu hadir dan benar. Sebuah file yang kontainernya tidak bercela tetapi properti fx miliknya tidak dideklarasikan akan melewati setiap pemeriksaan masalah dan mengembalikan angka 1, karena tidak ada satupun dalam daftar tes tersebut yang menginspeksi blok pdfaExtension:schemas. Hal inilah persis yang menjadi alasan mengapa faktur buatan tangan (hand-built), atau yang dihasilkan oleh alur pemrosesan (pipeline) yang menulis blok properti tanpa deklarasinya, dapat berlayar mulus melewati validator bawaan namun kemudian tetap gagal uji veraPDF pada klausul 6.6.2.3.1. Validator kontainer dan validator metadata PDF/A menjawab pertanyaan yang berbeda, dan hanya pemeriksa PDF/A penuh (full PDF/A checker) yang dapat menjawab pertanyaan yang kedua

Membaca masalah agar Anda mengetahui lapisan mana yang rusak

Karena kedua lapisan tersebut gagal secara independen, kebiasaan diagnostik yang tepat adalah membaca isu-isu kontainer terlebih dahulu dan memperlakukan hasil yang bersih hanya sebagai sebuah pernyataan tentang kontainer saja, dan pantang menafsirkannya sebagai jaminan kelolosan metadata PDF/A. Jalankan validasi bawaan, himpun daftar isunya, dan selesaikan terlebih dahulu sebelum Anda beralih menggunakan piranti eksternal

var
  Issues: WideString;
begin
  if PDF.ValidateFacturXInvoice = 0 then
  begin
    Issues := PDF.GetFacturXValidationIssues('|');
    // identifier level kontainer, sebagai contoh:
    //   MissingCatalogAF, NotPDFA3, MissingEmbeddedFilesNameTree,
    //   ConformanceGuidelineMismatch, InvalidAFRelationship
    WriteLn('Isu kontainer: ', Issues);
  end
  else
    WriteLn('Kontainer OK; verifikasi skema ekstensi XMP menggunakan pemeriksa PDF/A.');
end;

Ketika panggilan tersebut mengembalikan sebuah nama isu, kesalahannya ada di dalam kontainer dan pesan tersebut memberi tahu Anda pada bagian mana letak masalahnya. Apabila ia memberikan hasil yang bersih namun veraPDF masih menolak file tersebut, sumber masalahnya nyaris selalu terletak pada skema ekstensi XMP, dan satu-satunya obat penawar perbaikannya adalah dengan membiarkan AddFacturXAssociatedFileFromString yang menulis metadata tersebut alih-alih merakit sendiri blok properti Anda secara manual. Merawat sekat pemisah di antara kedua wilayah persoalan ini di dalam benak Anda adalah kunci yang mampu menyulap penolakan membingungkan (baffling rejection) menjadi sekadar diagnosis baris tunggal: problematika wadah kontainer muncul terdeteksi merentasi senarai urutan isu persoalan, sementara masalah absennya deklarasi skema bakal terlihat menonjol semata bilamana melalui penelusuran validator spesifik PDF/A, lalu tindak kelalaian ceroboh dengan meyakini kedua ranah ini sebagai hal serupa sesungguhnya adalah kelakuan tunggal yang memuluskan kutu (bug) itu sukses mencari persembunyian

Ulasan tentang kepatuhan PDF/A dan PDF/UA yang lebih luas, melingkupi rincian ihwal cara melangsungkan sesi uji pra-kelayakan (preflight pass) pada momen urung wujud dokumen meluncur lepas meranjak dari ranah meja rute perakitan rakitan pabrik pengerjaan program Anda, tercakup di dalam panduan uji pra-kelayakan PDF/A dan PDF/UA. Semisal nota faktur Anda kelak turut dituntut supaya mudah terakses (accessible), struktur susunan pohon yang PDF/A-3a dan PDF ber-tag sandarkan adalah merupakan bahan perbincangan pada ranah artikel uraian aksesibilitas PDF ber-tag. Penanganan struktur skema tipe ekstensi yang dideskripsikan di halaman sini turut mendarat diangkut laksana kelengkapan bagian sarana paket kelengkapan perkakas instrumen piranti dari Delphi PDF Library (PDFlibPas) beriringan menggamit bersebelahan seiring bersama fasilitas pasokan bekal porsi layanan profil Factur-X, ZUGFeRD, dan sokongan profil wujud rupa XRechnung yang didokumentasikan membentang menelusuri muka halaman seantero blog ini