-
Notifications
You must be signed in to change notification settings - Fork 257
(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);
}
}
}
}
All reactions
Replies: 1 comment 2 replies
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.
All reactions
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.
All reactions
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:
imagethe 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;
}