amira
amira

Reputation: 19

How do I delete/replace an image in a PDF file without breaking the file, using iTextsharp and C#

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();
            }
        }
    }
}

Upvotes: 0

Views: 3084

Answers (2)

Jannie Boshoff
Jannie Boshoff

Reputation: 61

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();
            }

Upvotes: 0

Manfred Wippel
Manfred Wippel

Reputation: 2066

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.

Upvotes: 1

Related Questions