Write PDF/A-2b and PDF/A-3u files in C# from documents made or edited with OnePDF, attach data to PDF/A-3, and check files from elsewhere first.
OnePDF writes PDF/A for documents it made or edited, and for existing files that already meet the requirements. It doesn’t convert arbitrary files: it embeds no fonts into content it didn’t create and rewrites no colors or transparency. A file that breaks a requirement is refused, with every violation listed, and nothing is written.
dotnet add package RecursiveAI.OnePdf --version 1.11.0 dotnet add package RecursiveAI.OnePdf.Layout --version 1.11.0
using OnePdf; using OnePdf.Layout; // A new document, written straight to PDF/A-2b. LayoutDocument minutes = LayoutDocument.Compose(doc => { doc.Title("Minutes, October meeting").Language("en-US"); doc.Page(page => { page.Margin(54); page.Content().Text("The committee approved the harbor trial report."); }); }); using (FileStream file = File.Create("minutes-pdfa.pdf")) minutes.Save(file, new PdfSaveOptions { PdfA = new PdfAOptions { Part = PdfAPart.Part2, Conformance = PdfAConformance.B } }); // A file OnePDF made earlier, as PDF/A-3u with its data attached. using (PdfDocument doc = PdfDocument.Open("invoice.pdf", new PdfOpenOptions { AllowEditing = true })) { doc.AttachFile("invoice.xml", File.ReadAllBytes("invoice.xml"), new PdfAttachmentOptions { MimeType = "text/xml", Relationship = PdfAttachmentRelationship.Data, ModificationDate = File.GetLastWriteTimeUtc("invoice.xml"), // PDF/A-3 requires a date on every attachment }); doc.Save("invoice-pdfa3.pdf", new PdfSaveOptions { PdfA = new PdfAOptions { Part = PdfAPart.Part3, Conformance = PdfAConformance.U } }); } // A file from elsewhere: make the repairs OnePDF can make, then see what is still wrong before saving. using (PdfDocument doc = PdfDocument.Open("of345.pdf", new PdfOpenOptions { AllowEditing = true })) { var options = new PdfAOptions { Part = PdfAPart.Part2, Conformance = PdfAConformance.B }; IReadOnlyList<PdfAViolation> problems = doc.PreparePdfA(options); if (problems.Count == 0) { doc.Save("of345-pdfa.pdf", new PdfSaveOptions { PdfA = options }); } else { Console.WriteLine($"of345.pdf cannot be PDF/A-2b as it is: {problems.Count} violations, for example:"); foreach (PdfAViolation v in problems.Take(4)) Console.WriteLine($" {v.Clause}: {v.Message} (object {v.ObjectNumber})"); } }
of345.pdf cannot be PDF/A-2b as it is: 6 violations, for example: 6.2.11.4.1: The font ZapfDingbats is not embedded. (object 20) 6.2.11.4.1: The font Times-Roman is not embedded. (object 191) 6.3.3: A button widget's /N must be a dictionary of appearance states. (object 188) 6.4.1: A widget annotation has an /A or /AA entry. (object 188)
The veraPDF validator passes minutes-pdfa.pdf as PDF/A-2b and invoice-pdfa3.pdf as PDF/A-3u.
What a PDF/A save does
A save with PdfSaveOptions.PdfA makes the repairs that need no judgement and then checks the result:
- adds an sRGB output intent with a color profile generated by OnePDF,
- writes the XMP metadata, taken from the document information so the two agree,
- embeds Liberation faces for standard fonts that OnePDF itself added (Helvetica, Times, Courier), when the
RecursiveAI.OnePdf.Fontspackage is registered, - sets the print flag on annotations and generates missing form-field appearances,
- writes the header and file identifiers the standard asks for.
Parts 2 and 3 are supported, at levels B (the pages look the same everywhere), U (B, plus Unicode for all text) and A (U, plus tagged structure: see Create PDF/UA). OnePDF writes attachments only in PDF/A-3 files, and each needs a modification date: the sample sets one from the file.
Good to know
PreparePdfAmakes the repairs in memory and returns what is still wrong.CheckPdfAonly reports, and works on read-only documents. A save that fails throwsPdfAExceptionwith the same list inViolations.- OnePDF is not a validator. A clean check covers what OnePDF can verify from the file’s structure, not every rule of the standard. Run a validator such as veraPDF before you rely on a file.
- DeviceCMYK color is refused, because OnePDF’s output intent is RGB.
- An encrypted document needs
Encryption = PdfEncryptionAction.Removein the same save, since PDF/A forbids encryption. - Existing signatures don’t survive a PDF/A save, which rewrites the file. Sign afterwards.
- A part of the file that can’t be read, such as a damaged font program, makes the save fail rather than leave the part out.
What is written, checked and not checked, clause by clause, is in PDF/A.
A longer walk-through with PDF/A-3 XML attachments: Create PDF/A in C#.