Teknik Makale

Delphi'de Factur-X XMP için PDF/A-3 Genişletme Şemaları

Bir Factur-X faturası oluşturdunuz ve her kapsayıcı kontrolü geçiyor. Katalog bir /AF dizisi taşıyor, EmbeddedFiles ad ağacı doğru dosya belirtimine çözümleniyor, gömülü factur-x.xml doğru /AFRelationship değerine, yani Alternative değerine sahip ve yerleşik ValidateFacturXInvoice 1 döndürüyor. Sonra aynı dosyayı vergi portallarının kullandığı referans denetleyici veraPDF'ten geçiriyorsunuz ve o, belgenin tamamını geçerli bir PDF/A-3 olarak değerlendirmiyor. Yapı doğrudur. Sorun meta verilerdedir ve bu başarısızlık, e-fatura iş akışının tamamında gözden kaçırılması en kolay başarısızlıklardan biridir

Nedenini tam olarak anlamakta fayda var, çünkü bu, görünür sayfayla ya da eklerle hiçbir ilgisi olmayan, her şeyi XMP'nin kendini nasıl tanımladığıyla ilgili bir PDF/A kusuru sınıfını açıklar. Yeşil bir kapsayıcı kontrolünün arkasına gizlenen tuzak tam olarak budur

Dosyayı başarısızlığa uğratan dört özellik

Bir Factur-X faturası, XMP paketine dört özel özellik yazar; böylece aşağı akıştaki yazılımlar, gömülü XML'i ayrıştırmadan fatura profilini okuyabilir. Bunlar, fx öneki altındaki Factur-X ad alanında yaşar: fx:DocumentFileName, fx:DocumentType, fx:Version ve fx:ConformanceLevel. Bir okuyucunun, bu PDF'in 1.0 sürümünde, factur-x.xml adında bir EN 16931 faturası taşıdığını bilmesi için gereken meta veri tam olarak budur

Bu dört özelliğin hiçbiri, PDF/A'nın önceden tanımladığı herhangi bir XMP şemasının parçası değildir. Dublin Core, XMP Basic, PDF ve PDF/A kimlik şemaları uyumlu bir okuyucu tarafından bilinir; ancak fx: bilinmez. veraPDF, XMP'yi dolaşırken ad alanını tanımadığı bir özelliğe ulaştığında, o özelliğin ne anlama geldiğini söyleyecek bir bildirim arar. Bu bildirim yoksa, ISO 19005-3'ün 6.6.2.3.1 maddesine aykırılık bildirir; bu madde, önceden tanımlanmış bir şemadan gelmeyen her özelliğin bir PDF/A uzantı şemasında tanımlanmasını zorunlu kılar. Bildirilmeyen dört özellik, dosyanın reddedilmesi için dört yol ve hiçbiri bir kapsayıcı kontrolüne görünmez

PDF Library for Delphi: Bir doğrulayıcı dört öntanımlı XMP şemasını arar ve fx özellikleri için bir PDF/A uzantı şeması bulamaz; böylece Factur-X faturası başarısız olur
veraPDF dosyanın hiç yazmadığı bir şema bildirimi arar — dört fx özelliği, 6.6.2.3.1 maddesine takılmanın dört şansı

PDF/A neden çıplak bir özel özelliği reddeder?

Kural, PDF/A'nın ne için var olduğunu hatırlayana kadar fazla titiz görünür. Biçim, bir dosyanın onlarca yıl sonra, 2026'nın teamüllerinden haberdar edilmemiş bir yazılım tarafından açılıp anlaşılabilmesi için vardır. Uyumlu bir okuyucudan, belgeyi dışarıdan bir kayıt defterine danışmadan, yalnızca belgenin kendisinden anlaması beklenir

Özel meta veriler, dosya kendi tanımını taşımadığı sürece bu vaadi bozar. Çıplak bir fx:ConformanceLevel özelliği verildiğinde, gelecekteki bir okuyucu fx önekinin hangi ad alanı URI'sine bağlandığını, değerin metin mi tarih mi yoksa bir tamsayı mı olduğunu ya da özelliğin belgenin kendisini mi yoksa bir dış kaynağı mı tanımladığını bilemez. PDF/A uzantı şeması mekanizması bu boşluğu kapatır. Dosyanın, sabit bir XMP yapısı içinde ad alanını, öneki ve her özellik için bir değer tipi ile internal ya da external kategorisini bildirmesine izin verir. Bu bildirim yerindeyken özellik kendi kendini tanımlar hale gelir ve 6.6.2.3.1 maddesi karşılanır. Bildirim yokken doğrulayıcının başka seçeneği kalmaz: özelliği anlaşılmaz sayar ve dosyayı reddeder. Kategori ayrımı burada önemlidir: bu tür fatura özellikleri, PDF işlemcisinin dışından gelen verileri tanımlar; bu yüzden internal değil external olarak bildirilirler

Uzantı şeması bildirimi neler içerir?

Bildirim, XMP paketinde pdfaExtension, pdfaSchema ve pdfaProperty ad alanlarını kullanan bir rdf:Description öğesidir. Bir pdfaExtension:schemas bag'inin içinde, Factur-X şemasını adlandıran, ona pdfaSchema:namespaceURI ve pdfaSchema:prefix veren ve ardından dört özelliği bir pdfaSchema:property dizisinde listeleyen tek bir şema girdisi oturur. Her özellik bir ad, Text değerinde bir pdfaProperty:valueType ve external değerinde bir pdfaProperty:category taşır. Aşağıdaki örnek işaretleme, bu bloğun biçimini gösterir

PDF Library for Delphi: PDF/A-3 uzantı şemasının anatomisi; Factur-X şemasını ad alanı URI'si, fx öneki ve dört dış Text özelliğiyle bildirir
Bildirimin yaptığı her iddia, faturanın gerçekten yazdığı fx bloğuyla eşleşmelidir; yoksa doğrulayıcı dosyayı yine reddeder
<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 declared the same way -->
          </rdf:Seq>
        </pdfaSchema:property>
      </rdf:li>
    </rdf:Bag>
  </pdfaExtension:schemas>
</rdf:Description>

Ad alanı URI'si ve önek sabit dizgeler değildir; profili izlerler. Bir Factur-X belgesi, fx önekiyle urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0# değerini kullanırken, zugferd-invoice.xml üzerinden seçilen bir ZUGFeRD 2.0 dosyası kendi şema adı altında farklı bir URI'ye çözümlenir. Uzantı şeması, özellik bloğunun gerçekten kullandığı ad alanı URI'sini bildirmek zorundadır; aksi halde doğrulayıcı ikisini birbirine bağlamayı yine başaramaz. PDF Library for Delphi her iki değeri geçtiğiniz dosya adından ve sürümden türetir; böylece bildirim ile özellik bloğu her zaman uyuşur

Yardımcı işlev iki yarımı birlikte nasıl yazar?

PDF Library for Delphi'de o XML'i elle oluşturmazsınız. Belgeyi bir PDF/A-3 moduna sokup tek bir yöntem çağırırsınız. Önce uygunluk bayrağını netleştirmek gerekir, çünkü Factur-X PDF/A-3 ister. SetPDFAMode(7) çağrısı PDF/A-3u düzeyini seçer; bu, kimlik şemasında pdfaid:part değerini 3'e, pdfaid:conformance değerini U'ya ayarlar. Böylece XMP paketi, herhangi bir fatura meta verisi eklenmeden önce doğru part ve conformance değerlerini taşır

var
  FileID: Integer;
begin
  PDF.SetPDFAMode(7);            // PDF/A-3u: pdfaid:part=3, conformance=U
  PDF.NewDocument;
  // insan tarafından okunabilir fatura sayfasını burada çiz

  FileID := PDF.AddFacturXAssociatedFileFromString(
    InvoiceXML,                  // raw UTF-8 XML bytes
    'EN16931',                   // ConformanceLevel
    'factur-x.xml',              // gömülü dosya adı
    'Factur-X invoice XML',      // /Desc text
    'Alternative',               // /AFRelationship
    '1.0',                       // profile version
    '');                         // isteğe bağlı ülke kodu
  if FileID = 0 then
    Exit;                        // not PDF/A-3, or XML/profile mismatch

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

AddFacturXAssociatedFileFromString'e yapılan tek çağrı, başarısız olan dosyada eksik olan işi yapar. XML'i, adlandırdığınız ilişkiyle bir PDF/A-3 ilişkili dosyası olarak gömer ve seçilen profil için şema adı, ad alanı URI'si ve önekle birlikte dört fx özelliğini kaydeder. Belge kaydedildiğinde ApplyFacturXMetadata adlı dahili bir adım, hem özellik bloğunu hem de eşleşen pdfaExtension:schemas bildirimini XMP paketine enjekte eder; böylece özel özellikler hâlihazırda tanımlanmış olarak gelir. Yöntem, belge bir PDF/A-3 modunda değilse ya da XML bildirilen profille uyuşmuyorsa 0 döndürür; bu, hatalı biçimlendirilmiş bir faturanın baştan dosyaya ulaşmasını engelleyen aynı korumadır

Kapsayıcı kontrolü neleri kapsar?

Açıkça ayırt edilmesi gereken kısım burasıdır, çünkü sorunun gizlenmesinin nedeni budur. ValidateFacturXInvoice kapsayıcıyı kontrol eder. Katalogda bir /AF girişi olduğunu, EmbeddedFiles ad ağacının mevcut olduğunu, fatura XML'inin var olduğunu, gömülü dosya adının profille eşleştiğini, XML'deki kılavuz kimliğinin uygunluk düzeyiyle uyuştuğunu ve /AFRelationship değerinin PDF/A-3'ün izin verdiği değerlerden olduğunu doğrular. Bunlar gerçek kontrollerdir ve gerçek kusurları yakalarlar. GetFacturXValidationIssues bunları MissingCatalogAF, NotPDFA3, ConformanceGuidelineMismatch, InvalidAFRelationship ve InvalidFileNameProfile gibi tanımlayıcılarla adlarıyla raporlar

XMP uzantı şeması farklı bir katmana aittir. Kapsayıcısı kusursuz ama fx özellikleri bildirilmemiş bir dosya, her kapsayıcı kontrolünden geçer ve 1 döndürür; çünkü XMP özelliklerini bildirmek bir kapsayıcı sorusu değil, PDF/A meta veri sorusudur. Elle oluşturulmuş bir faturanın ya da özellik bloğunu bildirim olmadan yazan bir boru hattının ürettiği bir faturanın, kapsayıcı doğrulamasından geçip yine de veraPDF'te 6.6.2.3.1 maddesinde başarısız olmasının tam nedeni budur. Kapsayıcı doğrulayıcı ile PDF/A meta veri doğrulayıcı farklı sorulara yanıt verir ve ikincisini yalnızca tam PDF/A denetleyicisi yanıtlar

PDF Library for Delphi: Aynı Factur-X faturası, ValidateFacturXInvoice'daki her kapsayıcı kontrolünden geçerken veraPDF tarafından reddedilir; çünkü pdfaExtension şemaları bloğu, kapsayıcı kontrollerinin dışında kalan bir PDF/A meta veri sorusudur
Kapsayıcı doğrulayıcı ile PDF/A doğrulayıcı aynı dosya hakkında farklı sorulara yanıt verir

Hangi katmanın kırıldığını anlamak için sorunları okumak

İki katman bağımsız olarak başarısız olduğundan, doğru teşhis alışkanlığı önce kapsayıcı sorunlarını okumak ve temiz bir sonucu yalnızca kapsayıcı hakkında bir beyan olarak görmektir; asla PDF/A meta verisi hakkında değil. Yerleşik doğrulamayı çalıştırın, sorun listesini toplayın ve harici bir araca uzanmadan önce ona göre hareket edin

var
  Issues: WideString;
begin
  if PDF.ValidateFacturXInvoice = 0 then
  begin
    Issues := PDF.GetFacturXValidationIssues('|');
    // container-level identifiers, for example:
    //   MissingCatalogAF, NotPDFA3, MissingEmbeddedFilesNameTree,
    //   ConformanceGuidelineMismatch, InvalidAFRelationship
    WriteLn('Container issues: ', Issues);
  end
  else
    WriteLn('Container OK; verify XMP extension schema with a PDF/A checker.');
end;

Bu çağrı bir sorun adı döndürdüğünde kusur kapsayıcıdadır ve ileti hangi parçada olduğunu söyler. Çağrı temiz döndüğünde ve veraPDF dosyayı hâlâ reddettiğinde, kusur neredeyse her zaman XMP uzantı şemasındadır; çözüm, özellik bloğunu kendiniz oluşturmak yerine meta veriyi AddFacturXAssociatedFileFromString'e yazdırmaktır. İki soruyu kendi zihninizde ayrı tutmak, akıl almaz bir reddi tek satırlık bir teşhise dönüştüren şeydir: kapsayıcı sorunları sorun listesi üzerinden ortaya çıkar, şema bildirimi sorunları yalnızca bir PDF/A doğrulayıcı üzerinden ortaya çıkar ve ikisini karıştırmak, sorunun gizlenmesine izin veren şeydir

Dosya derlemenizden çıkmadan önce bir ön uçuş (preflight) turunun nasıl çalıştırılacağı da dâhil olmak üzere daha geniş PDF/A ve PDF/UA uygunluk tablosu, PDF/A ve PDF/UA ön uçuş kılavuzunda ele alınır. Faturanızın aynı zamanda erişilebilir olması gerekiyorsa, PDF/A-3a ile etiketli PDF'in dayandığı yapı ağacı etiketli-PDF erişilebilirlik makalesinin konusudur. Burada anlatılan uzantı şeması işleyişi, bu blog boyunca belgelenen Factur-X, ZUGFeRD ve XRechnung profil desteğiyle birlikte PDF Library for Delphi Delphi PDF Kütüphanesinin bir parçası olarak gelir