This is it
This is it

Reputation: 789

Replace multiple different images on one PDF template page with itext (itextsharp)

We have an ASP.NET application that users use to generate certain reports. So far we had one PDF template that had one image on it, and we would just replace that image with our programatically generated one (graph).
We have used code from this site for that:http://blog.rubypdf.com/2007/12/12/how-to-replace-images-in-a-pdf/

Problem now is that we have two different images on one PDF page, and the code from link above selects both images on one page and replaces them all at once with our generated image.

Does anyone have any idea how to replace multiple different images on one page with itext?

Thanks

Upvotes: 3

Views: 7359

Answers (2)

mosheb
mosheb

Reputation: 704

Just a note that sometimes the image will be nested in a form, so it is wise to make a function that will be called recursively. Something like this:

public void StartHere()
{
    PdfReader pdf = new PdfReader("in.pdf");
    PdfStamper stp = new PdfStamper(pdf, new FileOutputStream("c:\\out.pdf"));
    PdfWriter writer = stp.getWriter();
    Image img = Image.getInstance("image.png");
    PdfDictionary pg = pdf.getPageN(1);
    replaceImage(pg, writer,img);
}

private void replaceImage(PdfDictionary pg, PdfWriter writer,Image img)
{
    PdfDictionary res = pg.getAsDict.get(PdfName.RESOURCES);
    PdfDictionary xobj = res.getAsDict(PdfName.XOBJECT);
    if (xobj != null) {
      for (Iterator<PdfName> it = xobj.getKeys().iterator(); it.hasNext(); ) {
        PdfObject obj = xobj.get(it.next());
        if (obj.isIndirect()) {
          PdfDictionary tg = (PdfDictionary)PdfReader.getPdfObject(obj);
          PdfName type = tg.getAsName(PdfName.SUBTYPE));
          if (PdfName.IMAGE.equals(type))
          {
            PdfReader.killIndirect(obj);
            Image maskImage = img.getImageMask();
            if (maskImage != null)
              writer.addDirectImageSimple(maskImage);
            writer.addDirectImageSimple(img, (PRIndirectReference)obj);
            break;
          }
          else if(PdfName.FORM.equals(type))
          {
              replaceImage(tg, writer,img);
          }
        }
      }
    }

Upvotes: 4

Mark Storer
Mark Storer

Reputation: 15868

Ugh. First, let me rewrite some of that source.

PdfReader pdf = new PdfReader("in.pdf");
PdfStamper stp = new PdfStamper(pdf, new FileOutputStream("c:\\out.pdf"));
PdfWriter writer = stp.getWriter();
Image img = Image.getInstance("image.png");
PdfDictionary pg = pdf.getPageN(1);
PdfDictionary res = pg.getAsDict.get(PdfName.RESOURCES);
PdfDictionary xobj = res.getAsDict(PdfName.XOBJECT);
if (xobj != null) {
  for (Iterator<PdfName> it = xobj.getKeys().iterator(); it.hasNext(); ) {
    PdfObject obj = xobj.get(it.next());
    if (obj.isIndirect()) {
      PdfDictionary tg = (PdfDictionary)PdfReader.getPdfObject(obj);
      PdfName type = tg.getAsName(PdfName.SUBTYPE));
      if (PdfName.IMAGE.equals(type)) {
        PdfReader.killIndirect(obj);
        Image maskImage = img.getImageMask();
        if (maskImage != null)
          writer.addDirectImageSimple(maskImage);
        writer.addDirectImageSimple(img, (PRIndirectReference)obj);
        break;
      }
    }
  }
}

Whew. the getAs functions can save you quite a bit of knuckle-grease and make your code much clearer.

Now. You need to be able to differentiate between the various images. If you're willing to hard-code things, you could find out what the resource names are and go that route:

String imageResName[] = {"Img1", "Img2" ... };
Image img[] = {Image.getInstance("foo.png"), Image.getInstance("bar.png"), ... };
for (int i = 0; i < imageResName.length; ++i) {
  PdfName curKey = new PdfName(imageResName[i]);
  PdfIndirectReference ref = xobj.getAsIndirect(curKey);
  PdfReader.killIndirect( ref );
  Image maskImage = img[i].getImageMask();
  if (maskImage != null) {
    writer.addDirectImageSimple(maskImage);
  }
  writer.addDirectImageSimple(img[i], (PRIndirectReference)ref);
}

If you're not willing to go with hardcoded resource names (and no one would fault you, quite the opposite, particularly when the order they appear (and thus the number on the end) depends on their order in a hash map... [shudder]), you may be able to differentiate based on image width and height.

//keep the original for loop, stepping through resource names
if (PdfName.IMAGE.equals(type)) {
  float width = tg.getAsNumber(PdfName.WIDTH).floatValue();
  float height = tg.getAsNumber(PdfName.HEIGHT).floatValue();

  Image img = getImageFromDimensions(width, height);

  Image maskImage = img.getImageMask();
  ...
}

Upvotes: 10

Related Questions