0

I'm trying to insert an image with id into a PDF document and allow to replace it later with another image. My process is as follows:

  1. Get an image from the client (with a unique ID).
  2. Try to find an existing image with the same ID, in the PDF document.
  3. If I find an existing image, try to delete it and put the new image instead, or try to replace the existing image with the new one. (tried both).
  4. If I don't find an existing image, insert the image in a position I choose.

I use code from Bruno Lowagie book:

The problem is that whenever I delete an existing image or replace it my document gets corrupted. What am I doing wrong? This is the code:

public static bool PdfInsertSignature(string path, string fileName, string signatureName, byte[] imageBytes)
{
    bool resultOk = true;
    string tmpFilename = string.Concat("tmp_", Guid.NewGuid().ToString(), ".pdf");
    // get file, copy to new file with signature
    using (Stream inputPdfStream = new FileStream(path + fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
    using (Stream outputPdfStream = new FileStream(path + tmpFilename, FileMode.Create, FileAccess.Write, FileShare.None))
    {
        using (var reader = new PdfReader(inputPdfStream))
        using (PdfStamper stamper = new PdfStamper(reader, outputPdfStream, '\0', false))
        {
            var img = System.Drawing.Image.FromStream(new MemoryStream(imageBytes));
            Image image = Image.GetInstance(img, BaseColor.WHITE);
            img.Dispose();
            var positions = stamper.AcroFields.GetFieldPositions(signatureName)[0];

            if (positions != null)
            {
                //DeleteExistingSignatureImage(reader, stamper, signatureName);

                image.SetAbsolutePosition(positions.position.Left + 20, positions.position.Top - 15);
                image.ScalePercent(0.2f * 100);
                image.BorderWidth = 0;

                PdfImage pdfImg = new PdfImage(image, "", null);
                pdfImg.Put(new PdfName("ITXT_SigImageId"), new PdfName(signatureName + "_img"));

                if (!ReplaceImage(reader, stamper, signatureName, pdfImg))
                {
                    PdfIndirectObject objRef = stamper.Writer.AddToBody(pdfImg);
                    image.DirectReference = objRef.IndirectReference;
                    PdfContentByte pdfContentByte = stamper.GetOverContent(positions.page);
                    pdfContentByte.AddImage(image);
                }                        
            }
            else
            {
                resultOk = false;
                logger.Error($"No matching Signature found for signatureName: {signatureName} in fileName: {fileName}.");
            }
        }
    }

    if (resultOk)
    {
        // delete old file and rename new file to old file's name
        File.Delete(path + fileName);
        File.Move(path + tmpFilename, path + fileName);
    }
    else
    {
        File.Delete(path + tmpFilename);
    }

    return resultOk;
}

private static bool ReplaceImage(PdfReader reader, PdfStamper stamper, string signatureName, PdfStream newImgStream)
{
    PdfName key = new PdfName("ITXT_SigImageId");
    PdfName value = new PdfName(signatureName + "_img");
    PdfObject obj;
    PRStream stream;

    for (int i = 1; i < reader.XrefSize; i++)
    {
        obj = reader.GetPdfObject(i);
        if (obj == null || !obj.IsStream())
        {
            continue;
        }
        stream = (PRStream)obj;
        PdfObject pdfSubtype = stream.Get(PdfName.SUBTYPE);

        if (pdfSubtype != null && pdfSubtype.ToString().Equals(PdfName.IMAGE.ToString()))
        {
            var streamVal = stream.Get(key);
            if (streamVal != null && value.Equals(streamVal))
            {
                stream.Clear();
                var ms = new MemoryStream();
                stream.WriteContent(ms);
                stream.SetData(ms.ToArray(), false);

                foreach (PdfName name in newImgStream.Keys)
                {
                    stream.Put(name, stream.Get(name));
                }

                return true;
            }
        }
    }

    return false;
}

private static void DeleteExistingSignatureImage(PdfReader reader, PdfStamper stamper, string signatureName)
{
    PdfName key = new PdfName("ITXT_SigImageId");
    PdfName value = new PdfName(signatureName + "_img");
    PdfObject obj;
    PRStream stream;

    for (int i = 1; i < reader.XrefSize; i++)
    {
        obj = reader.GetPdfObject(i);
        if (obj == null || !obj.IsStream())
        {
            continue;
        }
        stream = (PRStream)obj;
        PdfObject pdfSubtype = stream.Get(PdfName.SUBTYPE);

        if (pdfSubtype != null && pdfSubtype.ToString().Equals(PdfName.IMAGE.ToString()))
        {
            var streamVal = stream.Get(key);
            if (streamVal != null && value.Equals(streamVal))
            {
                stream.Clear();
                PdfReader.KillIndirect(stream);
                //PdfReader.KillIndirect(obj);
                //reader.RemoveUnusedObjects();
            }
        }
    }
}
SimonG
  • 304
  • 8
  • 18
amira
  • 21
  • 8
  • 1
    What do you exactly mean with "document gets corrupted"? – Gonzo345 Apr 17 '19 at 10:56
  • When trying to open in acrobat dc I get: "There was an error processing a page. There was a problem reading this document (18)." and the document looks messy. – amira Apr 17 '19 at 11:43

2 Answers2

1

The purpose of signing a PDF file is to prevent further changes without notice. You need to sign the document after you swap the image, or it will be corrupted.

Just do make it easier to find: This is Solution provided by amira.

Manfred Wippel
  • 1,106
  • 10
  • 13
  • Ok, so, about that.. my clients sign on an HTML form on a canvas and I send the signature image to the server. I could not find any way of creating a signature field with that image and inserting it into the document. – amira Apr 17 '19 at 11:46
  • 1
    Found the answer to this here: https://stackoverflow.com/questions/20660839/add-signature-image-on-pdf-without-digitally-signing-it-using-itextsharp – amira May 02 '19 at 14:39
0

This is the code i've used to replace a 'ButtonField' on my PDF template with a signature image :

string TempStampPath = Server.MapPath(TempPath + "BookingConfirmation.pdf");
            PdfReader pdfReader = new PdfReader(TempStampPath);
            PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(LocalFileName, FileMode.Create));
            AcroFields pdfFormFields = pdfStamper.AcroFields;
            try
            {
                pdfFormFields.SetField("NameSurname", NameSurname);
                pdfFormFields.SetField("IdNumber", IDNumber);
                pdfFormFields.SetField("CourseName", CourseName);
                pdfFormFields.SetField("Location", Venue);
                pdfFormFields.SetField("DateCompleted", CourseDate);
                pdfFormFields.SetField("FacilitatorName", Facilitator);

                try
                {
                    iTextSharp.text.Image signature = iTextSharp.text.Image.GetInstance(image, System.Drawing.Imaging.ImageFormat.Png);

                    PushbuttonField ad = pdfStamper.AcroFields.GetNewPushbuttonFromField("btnFacilitatorSignature");
                    ad.Layout = PushbuttonField.LAYOUT_ICON_ONLY;
                    ad.ProportionalIcon = true;
                    ad.Image = signature;
                    ad.BackgroundColor = iTextSharp.text.BaseColor.WHITE;
                    pdfFormFields.ReplacePushbuttonField("btnFacilitatorSignature", ad.Field);
                }
                catch (Exception ex)
                { }

                pdfStamper.FormFlattening = true;
                pdfStamper.Close();
                pdfStamper.Dispose();
                pdfReader.Close();
            }
            catch (Exception ex)
            {
                pdfStamper.Close();
                pdfStamper.Dispose();
                pdfReader.Close();
            }