Skip to content

Navigation Menu

Sign in
Sign up

pdfa and facturex #352

Unanswered
tschmit asked this question in Q&A
Mar 27, 2026 · 1 comments · 2 replies
Discussion options

(thank you for pdfSharp)

hello, I build a personnal version ou pdfsharp 6.2.4 to allow to setup a pdf file to be a valid facturx pdf.
basically:

if (!LetMePlay) {
 Catalog.Elements.SetReference(PdfCatalog.Keys.Metadata, new PdfMetadata(this));
}

and I use pdfsharp api to create and save xmp medata.
It works just fine.

Now I'm trying PdfSharp 7 - preview.

my test code is:

string src = @"c:\temp\tests\loffice\testnota.pdf";
string dest = Path.Combine(
 Path.GetDirectoryName(src),
 Path.GetFileNameWithoutExtension(src) + ".a.pdf");
using (PdfDocument pdfDoc = PdfReader.Open(@"c:\temp\tests\loffice\testnota.pdf", PdfDocumentOpenMode.Modify)) {
 MetadataManager mm = MetadataManager.ForDocument(pdfDoc);
 mm.Strategy = DocumentMetadataStrategy.UserGenerated;
 pdfDoc.SetPdfA(PdfAFormat.ForcePdfAFormat(3, 'B'));
 pdfDoc.Events.CreateDocumentMetadata += Events_CreateDocumentMetadata;
 pdfDoc.Save(dest);
}
void Events_CreateDocumentMetadata(object sender, PdfSharp.Events.DocumentMetadataEventArgs e) {
 e.Metadata.SetMetadata(BuildXmpForPdfA());
}

It seems that SetPdfA does "nothing"
then I try with MetadataManager and BuildXmpForPdfA (from my working code with pdfsharp 6.2.4)

first I get a exception claiming that medata must start with <?xpackect...
mine starts with <?xml version....
and is validated by verapdf, so I thing that your test is a bit strong.

so I deleted the xml header and I got a new exception from

// pdfMetadata.cs line 517
if (bomTag.Length > 0)
{
 if (!Enum.TryParse<MetadataEncodingType>(bomTag, false, out encoding))
 throw new InvalidOperationException($"Unknown metadata encoding tag '{bomTag}'.");
}

my bom (read as bomTag) is:

string bom = "\uFEFF";

which is the expected bom (validated by verapdf)
but your enum is:

enum MetadataEncodingType
{
 // ReSharper disable InconsistentNaming
 /// <summary>
 /// Encodes metadata stream using UTF-8 encoding.
 /// This is the default and the only recommended option.
 /// </summary>
 UTF8,
 //...

so I give the correct bom for a pdf, but it seems you expect 0 or nothing, this is disturbing. It runs just fine if I set my bom to "".

do you plan to adapt your code to handle bom as string or will you keep it as it is ?

again, thank you for pdfSharp

======

my "final" code follows:

  • Nice evolution
  • SetPdfA at least embed the icc profil (but does not seem to set xmp metadata)
  • May be you should fix the annotations (see code below) or rise an exception if they are not conform

My humble conclusion: thank you, I can build facturx pdf with the official code.

string src = @"c:\temp\tests\loffice\testnota.pdf";
string xmlSrc = @"c:\temp\tests\loffice\first.xml";
string dest = Path.Combine(
 Path.GetDirectoryName(src),
 Path.GetFileNameWithoutExtension(src) + ".a.pdf");
using (PdfDocument pdfDoc = PdfReader.Open(@"c:\temp\tests\loffice\testnota.pdf", PdfDocumentOpenMode.Modify)) {
 MetadataManager mm = MetadataManager.ForDocument(pdfDoc);
 mm.Strategy = DocumentMetadataStrategy.UserGenerated;
 EmbeddedFilesManager fm = EmbeddedFilesManager.ForDocument(pdfDoc);
 fm.AddFile(new EmbeddedFileInfo {
 FileName = "factur-x.xml",
 FileType = "application/xml",
 Description = "Factur-X XML attachment",
 AFRelationship = PdfAFRelationship.Data,
 Data = File.ReadAllBytes(xmlSrc)
 }, false);
 pdfDoc.SetPdfA(PdfAFormat.ForcePdfAFormat(3, 'B'));
 FixAnnotations(pdfDoc);
 pdfDoc.Events.CreateDocumentMetadata += Events_CreateDocumentMetadata;
 pdfDoc.Save(dest);
}
void Events_CreateDocumentMetadata(object sender, PdfSharp.Events.DocumentMetadataEventArgs e) {
 e.Metadata.SetMetadata(BuildXmpForPdfA(false, bom: ""));
}
void FixAnnotations(PdfDocument document) {
 foreach (var page in document.Pages) {
 if (!page.Elements.ContainsKey("/Annots"))
 continue;
 var annots = page.Elements["/Annots"] as PdfArray;
 if (annots == null) continue;
 foreach (var item in annots.Elements) {
 var annRef = item as PdfReference;
 var ann = (annRef?.Value ?? item) as PdfDictionary;
 if (ann == null) continue;
 // Ajouter /F = 4 si absent
 if (!ann.Elements.ContainsKey("/F")) {
 ann.Elements["/F"] = new PdfInteger(4);
 }
 }
 }
}
You must be logged in to vote

Replies: 1 comment 2 replies

Comment options

document.SetPdfA(PdfAFormats.PdfA_3b); works fine for me. It sets "3B" in the auto-generated XMP. If you replace the XMP, the effect will be gone.

string bom = "\uFEFF"; This is the UTF-16 BOM. According to the specifications, XMP in PDF must always use UTF-8 and PDFsharp uses the UTF-8 bom. The placeholder text "UTF8" is replaced by 0xEF 0xBB 0xBF when the file is written.

I have no clue why you set "/F" to "4". We can consider that change when we understand the purpose.

You must be logged in to vote
2 replies
Comment options

for the annotation, it is from the VeraPDF Conformance Checker, rule 6.3.2-1 (found her: https://github.com/veraPDF/veraPDF-validation-profiles/wiki/PDFA-Parts-2-and-3-rules/) (And 4 is an almost arbitrary value allowing to pass the check (means printable))
Rule 6.3.2-1
Requirement

Except for annotation dictionaries whose Subtype value is Popup, all annotation dictionaries shall contain the F key.

Error details

A dictionary of a non-Popup annotation does not contain F key.

Object type: PDAnnot
Test condition: Subtype == "Popup" || F != null
Specifications: ISO 19005-2:2011, ISO 19005-3:2012
Levels: A, B, U

I will check document.SetPdfA(PdfAFormats.PdfA_3b); but I thing it may not pass the conformance check unless you add (and I miss it) the metada metada for fields DocumentType, DocumentFileName, Version and ConformanceLevel (I'm not talking of their value but of their description including 'name', 'valueType', 'category' and 'description'). Again I refer to veraPDF conformance checker.

Comment options

i just test the following code:

string src = @"c:\temp\tests\loffice\testnota.pdf";
string xmlSrc = @"c:\temp\tests\loffice\first.xml";
string dest = Path.Combine(
 Path.GetDirectoryName(src),
 Path.GetFileNameWithoutExtension(src) + ".a.pdf");
using (PdfDocument pdfDoc = PdfReader.Open(@"c:\temp\tests\loffice\testnota.pdf", PdfDocumentOpenMode.Modify)) {
 //MetadataManager mm = MetadataManager.ForDocument(pdfDoc);
 //mm.Strategy = DocumentMetadataStrategy.UserGenerated;
 EmbeddedFilesManager fm = EmbeddedFilesManager.ForDocument(pdfDoc);
 fm.AddFile(new EmbeddedFileInfo {
 FileName = "factur-x.xml",
 FileType = "application/xml",
 Description = "Factur-X XML attachment",
 AFRelationship = PdfAFRelationship.Data,
 Data = File.ReadAllBytes(xmlSrc)
 }, false);
 pdfDoc.SetPdfA(PdfAFormats.PdfA_3b);
 //FixAnnotations(pdfDoc);
 //pdfDoc.Events.CreateDocumentMetadata += Events_CreateDocumentMetadata;
 pdfDoc.Save(dest);
}

no pdf/a banner in acrobat reader and the following validation fails:

image

the meta meta can be build with something like:

XmlElement BuildFactureXXmpMeta(XmlDocument xml) {
 var desc = xml.CreateElement("rdf", "Description", rdfNs);
 desc.SetAttribute("about", rdfNs, "");
 desc.SetAttribute("xmlns:fx", fxNs);
 desc.SetAttribute("xmlns:pdfaExtension", pdfaExtensionNs);
 desc.SetAttribute("xmlns:pdfaSchema", pdfaSchemaNs);
 desc.SetAttribute("xmlns:pdfaProperty", pdfaPropertyNs);
 XmlElement schemas = xml.CreateElement("pdfaExtension", "schemas", pdfaExtensionNs);
 desc.AppendChild(schemas);
 XmlElement bag = xml.CreateElement("rdf", "Bag", rdfNs);
 schemas.AppendChild(bag);
 XmlElement li = CreateLi(xml, bag, true);
 CreateChild(xml, li, "pdfaSchema", "schema", pdfaSchemaNs, "Factur-X PDFA Extension Schema");
 CreateChild(xml, li, "pdfaSchema", "namespaceURI", pdfaSchemaNs, cidkey);
 CreateChild(xml, li, "pdfaSchema", "prefix", pdfaSchemaNs, "fx");
 var prop = CreateChild(xml, li, "pdfaSchema", "property", pdfaSchemaNs);
 var seq = CreateChild(xml, prop, "rdf", "Seq", rdfNs);
 var resources = new[] {
 ("DocumentType", "Text", "external", "Factur-X document type"),
 ("DocumentFileName", "Text", "external", "Factur-X file name"),
 ("Version", "Text", "external", "Factur-X version"),
 ("ConformanceLevel", "Text", "external", "Factur-X conformance level")
 };
 foreach (var (name, type, category, description) in resources) {
 var ili = CreateLi(xml, seq, true);
 CreateChild(xml, ili, "pdfaProperty", "name", pdfaPropertyNs, name);
 CreateChild(xml, ili, "pdfaProperty", "valueType", pdfaPropertyNs, type);
 CreateChild(xml, ili, "pdfaProperty", "category", pdfaPropertyNs, category);
 CreateChild(xml, ili, "pdfaProperty", "description", pdfaPropertyNs, description);
 }
 return desc;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
Cannot Reproduce https://xkcd.com/583/

AltStyle によって変換されたページ (->オリジナル) /