From f3b88d225fa1d81ea140470004e2d79ae2143ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9E=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=9F=D0=B0=D0=BD=D0=B0=D1=81=D0=B5=D0=BD=D0=BA=D0=BE?= Date: Wed, 19 Aug 2026 16:31:12 +0300 Subject: [PATCH] Print the visual representation of a digital signature The widget annotation of a signature field got no /F entry, so its annotation flags are 0. According to 12.5.3 Annotation flags, the print flag must be set for an annotation to be printed: "If clear, never print the annotation, even if it is displayed on screen." The visual representation of a signature was therefore visible on screen, but missing on paper and in every export that prints the pages. Other producers set the flag, e.g. every annotation of the Adobe generated IRS form fw4.pdf has /F 4. The flag is now set when the signature field is created. Hidden and NoView stay clear, so nothing else about the visibility changes. The test needs no certificate, no assets and no network, because it uses a dummy signer. --- .../Pdf.Signatures/DigitalSignatureHandler.cs | 4 + .../SignatureAppearanceTests.cs | 101 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/foundation/src/PDFsharp/tests/PdfSharp.Tests/Pdf.Signatures/SignatureAppearanceTests.cs diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DigitalSignatureHandler.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DigitalSignatureHandler.cs index a6d00f3c..91c84494 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DigitalSignatureHandler.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Signatures/DigitalSignatureHandler.cs @@ -218,6 +218,10 @@ PdfFormSignatureField GetSignatureField(PdfSignature signatureDic) // #US321 TOD signatureField.Elements.Add(PdfAnnotation.Keys.Rect, new PdfRectangle(Options.Rectangle)); + // Without the print flag, a viewer never prints the annotation, so the visual representation + // of the signature would be missing on paper. See 12.5.3 Annotation flags. + signatureField.Elements.Add(PdfAnnotation.Keys.F, new PdfInteger((int)PdfAnnotationFlags.Print)); + // TODO COMPILE signatureField.CustomAppearanceHandler = Options.AppearanceHandler ?? new DefaultSignatureAppearanceHandler() { diff --git a/src/foundation/src/PDFsharp/tests/PdfSharp.Tests/Pdf.Signatures/SignatureAppearanceTests.cs b/src/foundation/src/PDFsharp/tests/PdfSharp.Tests/Pdf.Signatures/SignatureAppearanceTests.cs new file mode 100644 index 00000000..983e0ba5 --- /dev/null +++ b/src/foundation/src/PDFsharp/tests/PdfSharp.Tests/Pdf.Signatures/SignatureAppearanceTests.cs @@ -0,0 +1,101 @@ +// PDFsharp - A .NET library for processing PDF +// See the LICENSE file in the solution root for more information. + +#if WPF +using System.IO; +#endif +using FluentAssertions; +using PdfSharp.Diagnostics; +using PdfSharp.Drawing; +using PdfSharp.Pdf; +using PdfSharp.Pdf.Annotations; +using PdfSharp.Pdf.Forms; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Signatures; +#if CORE +using PdfSharp.Fonts; +using PdfSharp.Quality; +#endif +using Xunit; + +namespace PdfSharp.Tests.Pdf.Signatures +{ + [Collection("PDFsharp")] + public class SignatureAppearanceTests : IDisposable + { + public SignatureAppearanceTests() + { + PdfSharpCore.ResetAll(); +#if CORE + GlobalFontSettings.FontResolver = new UnitTestFontResolver(); +#endif + } + + public void Dispose() + { + PdfSharpCore.ResetAll(); + } + + [Fact] + public void Signature_is_printed() + { + using var document = new PdfDocument(); + document.AddPage(); + + var options = new DigitalSignatureOptions + { + Reason = "License Agreement", + Rectangle = new XRect(36, 36, 200, 50), + AppearanceHandler = new EmptyAppearanceHandler() + }; + _ = DigitalSignatureHandler.ForDocument(document, new TestSigner(), options); + + using var stream = new MemoryStream(); + document.Save(stream, false); + + using var signedDocument = PdfReader.Open(new MemoryStream(stream.ToArray()), PdfDocumentOpenMode.Import); + var field = signedDocument.Catalog.GetAcroForm()!.Elements + .GetArray(PdfForm.Keys.Fields)!.Elements.GetRequiredDictionary(0); + + var flags = (PdfAnnotationFlags)field.Elements.GetInteger(PdfAnnotation.Keys.F); + flags.Should().HaveFlag(PdfAnnotationFlags.Print, + "a signature that is not printed is missing on the paper the document is printed on"); + flags.Should().NotHaveFlag(PdfAnnotationFlags.Hidden); + flags.Should().NotHaveFlag(PdfAnnotationFlags.NoView); + } + + /// + /// A signer that creates a deterministic dummy signature, so that no certificate is needed. + /// + class TestSigner : IDigitalSigner + { + public string CertificateName => "PDFsharp unit test"; + + public Task GetSignatureSizeAsync() => Task.FromResult(SignatureSize); + + public Task GetSignatureAsync(Stream stream) + { + // Read the stream to ensure the ranges to be signed are readable. + var buffer = new byte[4096]; + while (stream.Read(buffer, 0, buffer.Length) > 0) + { } + + var signature = new byte[SignatureSize]; + for (int idx = 0; idx < signature.Length; idx++) + signature[idx] = (byte)idx; + return Task.FromResult(signature); + } + + const int SignatureSize = 512; + } + + /// + /// An appearance handler that draws nothing, so that no font is needed. + /// + class EmptyAppearanceHandler : IAnnotationAppearanceHandler + { + public void DrawAppearance(XGraphics gfx, XRect rect) + { } + } + } +}