Artikel Teknis

Extension Schema PDF/A-3 untuk XMP Factur-X di Delphi

Anda telah membangun sebuah faktur Factur-X dan setiap pemeriksaan kontainer lolos. Catalog membawa array /AF, name tree EmbeddedFiles mengarah ke file specification yang benar, factur-x.xml yang tersemat memiliki /AFRelationship yang tepat berupa Alternative, dan ValidateFacturXInvoice bawaan mengembalikan 1. Lalu Anda menjalankan berkas yang sama melalui veraPDF, pemeriksa rujukan yang dipakai portal pajak, dan ia memutuskan bahwa keseluruhan dokumen bukan PDF/A-3 yang valid. Strukturnya benar. Metadatanya yang bermasalah, dan kegagalan ini termasuk yang paling mudah terlewat dalam seluruh alur kerja faktur elektronik

Alasannya layak dipahami sepenuhnya, karena ia menjelaskan satu kelas cacat PDF/A yang sama sekali tidak berkaitan dengan halaman yang terlihat atau lampirannya, melainkan sepenuhnya berkaitan dengan bagaimana XMP mendeskripsikan dirinya sendiri. Inilah jebakan yang bersembunyi di balik pemeriksaan kontainer yang hijau

Empat properti yang menjatuhkan berkas

Sebuah faktur Factur-X menuliskan empat properti kustom ke dalam paket XMP-nya agar perangkat lunak di hilir dapat membaca profil faktur tanpa mengurai XML yang tersemat. Semuanya berada di namespace Factur-X di bawah prefiks fx: fx:DocumentFileName, fx:DocumentType, fx:Version, dan fx:ConformanceLevel. Keempatnya persis metadata yang dibutuhkan pembaca untuk mengetahui bahwa PDF ini membawa faktur EN 16931 bernama factur-x.xml pada versi 1.0

Tak satu pun dari keempat properti itu merupakan bagian dari skema XMP mana pun yang telah didefinisikan sebelumnya oleh PDF/A. Skema identifikasi Dublin Core, XMP Basic, PDF, dan PDF/A dikenal oleh pembaca yang conforming, tetapi fx: tidak. Ketika veraPDF menelusuri XMP dan sampai pada properti yang namespace-nya tidak dikenalinya, ia mencari deklarasi yang akan memberitahunya arti properti tersebut. Bila deklarasi itu tidak ada, ia melaporkan kegagalan terhadap ISO 19005-3 klausul 6.6.2.3.1, yang mensyaratkan setiap properti yang tidak diambil dari skema terdefinisi harus dijelaskan dalam sebuah extension schema PDF/A. Empat properti tanpa deklarasi, empat cara berkas itu ditolak, dan tak satu pun terlihat oleh pemeriksaan kontainer

PDF Library for Delphi: sebuah validator menyisir keempat skema XMP terdefinisi dan tidak menemukan extension schema PDF/A untuk properti fx, sehingga faktur Factur-X gagal
veraPDF memburu deklarasi skema yang tidak pernah ditulis berkas itu — empat properti fx, empat peluang gagal pada klausul 6.6.2.3.1

Mengapa PDF/A menolak properti kustom yang telanjang

Aturannya terkesan bertele-tele sampai Anda mengingat untuk apa PDF/A ada. Format ini hadir agar sebuah berkas dapat dibuka dan dipahami puluhan tahun dari sekarang, oleh perangkat lunak yang tidak pernah diberitahu tentang konvensi tahun 2026. Pembaca yang conforming diharapkan dapat memahami dokumen dari dokumen itu sendiri, tanpa registri eksternal yang perlu dirujuk

Metadata kustom mematahkan janji itu kecuali berkasnya membawa deskripsinya sendiri. Diberi properti fx:ConformanceLevel telanjang, pembaca di masa depan tidak dapat mengetahui URI namespace yang diikat prefiks fx, apakah nilainya berupa teks atau tanggal atau bilangan bulat, ataupun apakah properti itu menggambarkan dokumen itu sendiri atau suatu sumber daya eksternal. Mekanisme extension schema PDF/A menutup celah tersebut. Ia membuat berkas dapat mendeklarasikan, dalam struktur XMP yang baku, namespace, prefiks, dan untuk tiap properti sebuah value type serta kategori internal atau external. Begitu deklarasi itu hadir, properti tersebut mendeskripsikan dirinya sendiri, dan klausul 6.6.2.3.1 terpenuhi. Tanpanya, validator tak punya pilihan selain memperlakukan properti itu sebagai tak terpahami dan menjatuhkan berkasnya. Pembedaan kategori penting di sini: properti faktur seperti ini menggambarkan data yang datang dari luar prosesor PDF, sehingga dideklarasikan sebagai external alih-alih internal

Apa isi deklarasi extension schema

Deklarasinya adalah sebuah rdf:Description di dalam paket XMP yang memakai tiga namespace yang didefinisikan AIIM, yaitu pdfaExtension, pdfaSchema, dan pdfaProperty. Di dalam bag pdfaExtension:schemas terdapat satu entri skema yang menamai skema Factur-X, memberikan pdfaSchema:namespaceURI dan pdfaSchema:prefix-nya, lalu mendaftar keempat properti dalam sebuah urutan pdfaSchema:property. Setiap properti membawa nama, pdfaProperty:valueType berupa Text, dan pdfaProperty:category berupa external. Markup ilustratif di bawah ini menunjukkan bentuk blok tersebut

PDF Library for Delphi: anatomi extension schema PDF/A-3 yang mendeklarasikan skema Factur-X beserta URI namespace, prefiks fx, dan empat properti Text bertipe external
Setiap klaim yang dibuat deklarasi itu harus cocok dengan blok fx yang benar-benar ditulis faktur, atau validator tetap menolak berkasnya
<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 prefiksnya bukan string tetap. Keduanya mengikuti profil. Dokumen Factur-X memakai urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0# dengan prefiks fx, sedangkan berkas ZUGFeRD 2.0 yang dipilih lewat zugferd-invoice.xml mengarah ke URI berbeda di bawah nama skemanya sendiri. Extension schema harus mendeklarasikan URI namespace yang sama dengan yang benar-benar dipakai blok properti, atau validator tetap tidak dapat menghubungkan keduanya. PDF Library for Delphi menurunkan kedua nilai itu dari nama berkas dan versi yang Anda berikan, sehingga deklarasi dan blok properti selalu sepakat

Bagaimana helper menulis kedua bagiannya sekaligus

Di PDF Library for Delphi Anda tidak merakit XML itu dengan tangan. Anda menempatkan dokumen ke dalam mode PDF/A-3 lalu memanggil satu metode. Hal pertama yang harus ditetapkan adalah flag conformance, karena Factur-X mensyaratkan PDF/A-3. Memanggil SetPDFAMode(7) memilih level PDF/A-3u, yang menyetel pdfaid:part ke 3 dan pdfaid:conformance ke U di skema identifikasi. Paket XMP kini membawa part dan conformance yang benar sebelum metadata faktur apa pun ditambahkan

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

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

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

Satu panggilan ke AddFacturXAssociatedFileFromString mengerjakan apa yang tidak dimiliki berkas yang gagal itu. Ia menyematkan XML sebagai associated file PDF/A-3 dengan relasi yang Anda sebutkan, dan ia mencatat keempat properti fx beserta nama skema, URI namespace, dan prefiks untuk profil yang dipilih. Ketika dokumen disimpan, langkah internal bernama ApplyFacturXMetadata menyuntikkan baik blok properti maupun deklarasi pdfaExtension:schemas yang cocok ke dalam paket XMP, sehingga properti kustom itu tiba dalam keadaan sudah terdeskripsikan. Metode ini mengembalikan 0 bila dokumen tidak berada dalam mode PDF/A-3 atau bila XML-nya tidak cocok dengan profil yang dideklarasikan, penjaga yang sama yang mencegah faktur cacat mencapai berkas sejak awal

Titik buta yang tak terlihat oleh pemeriksaan kontainer

Inilah bagian yang perlu disebut terus terang, karena inilah alasan bug tersebut bersembunyi. ValidateFacturXInvoice memeriksa kontainer. Ia memastikan catalog memiliki entri /AF, name tree EmbeddedFiles hadir, XML faktur ada, nama berkas tersemat cocok dengan profil, ID pedoman di dalam XML sesuai dengan level conformance, dan /AFRelationship adalah salah satu yang diizinkan PDF/A-3. Semua itu pemeriksaan sungguhan dan menangkap cacat sungguhan. GetFacturXValidationIssues melaporkannya berdasarkan nama, dengan pengenal seperti MissingCatalogAF, NotPDFA3, ConformanceGuidelineMismatch, InvalidAFRelationship, dan InvalidFileNameProfile

Yang tidak diperiksanya adalah apakah extension schema XMP hadir dan benar. Berkas yang kontainernya sempurna tetapi properti fx-nya tak dideklarasikan akan lolos setiap pemeriksaan isu dan mengembalikan 1, karena tidak ada satu pun dalam daftar itu yang memeriksa blok pdfaExtension:schemas. Itulah persisnya sebabnya faktur yang dibangun tangan, atau yang dihasilkan pipeline yang menulis blok properti tanpa deklarasinya, dapat melenggang melewati validator bawaan dan tetap gagal di 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 yang menjawab pertanyaan kedua

PDF Library for Delphi: faktur Factur-X yang sama lolos setiap pemeriksaan kontainer di ValidateFacturXInvoice sementara veraPDF menolaknya karena tidak ada lapisan yang membaca blok pdfaExtension schemas
Validator kontainer dan validator PDF/A menjawab pertanyaan yang berbeda tentang berkas yang sama

Membaca isu agar Anda tahu lapisan mana yang rusak

Karena kedua lapisan itu gagal secara terpisah, kebiasaan diagnostik yang tepat adalah membaca isu kontainer lebih dahulu dan memperlakukan hasil bersih sebagai pernyataan tentang kontainer saja, tidak pernah tentang metadata PDF/A. Jalankan validasi bawaan, kumpulkan daftar isunya, dan tindak lanjuti sebelum Anda meraih perkakas eksternal

var
  Issues: WideString;
begin
  if PDF.ValidateFacturXInvoice = 0 then
  begin
    Issues := PDF.GetFacturXValidationIssues('|');
    // pengenal di tingkat kontainer, misalnya:
    //   MissingCatalogAF, NotPDFA3, MissingEmbeddedFilesNameTree,
    //   ConformanceGuidelineMismatch, InvalidAFRelationship
    WriteLn('Container issues: ', Issues);
  end
  else
    WriteLn('Container OK; verify XMP extension schema with a PDF/A checker.');
end;

Ketika panggilan itu mengembalikan sebuah nama isu, kesalahannya ada di kontainer dan pesannya memberitahu Anda bagian mana. Ketika ia mengembalikan hasil bersih dan veraPDF tetap menolak berkasnya, kesalahannya hampir selalu ada pada extension schema XMP, dan perbaikannya adalah membiarkan AddFacturXAssociatedFileFromString yang menulis metadata alih-alih Anda menyusun sendiri blok propertinya. Menjaga kedua pertanyaan itu tetap terpisah dalam benak Anda sendiri adalah yang mengubah penolakan yang membingungkan menjadi diagnosis satu baris: masalah kontainer muncul lewat daftar isu, masalah deklarasi skema hanya muncul lewat validator PDF/A, dan mencampuradukkan keduanya adalah yang membuat bug ini bisa bersembunyi

Gambaran conformance PDF/A dan PDF/UA yang lebih luas, termasuk cara menjalankan lintasan preflight sebelum sebuah berkas meninggalkan build Anda, dibahas dalam panduan preflight PDF/A dan PDF/UA. Jika faktur Anda juga harus dapat diakses, structure tree yang menjadi sandaran PDF/A-3a dan tagged PDF menjadi pokok bahasan artikel aksesibilitas tagged PDF. Penanganan extension schema yang dijelaskan di sini hadir sebagai bagian dari PDF Library for Delphi berdampingan dengan dukungan profil Factur-X, ZUGFeRD, dan XRechnung yang didokumentasikan di seluruh blog ini