1

I have a problem which is: draw a rectangle to cover all images in PDF. I already know that image can be written as an image, but it can be also written as graphical vectors.

I want to get locations (X,Y,Width,Height) of normal images (saved for example as bitmap), and draw rectangle over them, to dont cover other elements on page such as text under this image (i dont want to cover entire page, if an image is covering only 30% of it). So far i have written two functions for getting images :

  public List<Image> GetImagesFromPdf(string path)
    {
        List<Image> imageList = new List<Image>();
        PdfReader reader = new PdfReader(path);
        for (int i=1;i<reader.NumberOfPages;i++)
        {
            PdfDictionary pdfDict = reader.GetPageN(i);
            imageList.AddRange(GetImagesFromDocument(pdfDict, reader));
        }
        return imageList;
    }

private List<Image> GetImagesFromDocument(PdfDictionary dict, PdfReader doc)
    {
        List<Image> imageList = new List<Image>();
        PdfDictionary resources = (PdfReader.GetPdfObject(dict.Get(PdfName.RESOURCES))) as PdfDictionary;
        PdfDictionary objects = (PdfReader.GetPdfObject(resources.Get(PdfName.XOBJECT))) as PdfDictionary;
        if (objects != null)
        {
            foreach (PdfName objKey in objects.Keys)
            {
                PdfObject obj = objects.Get(objKey);
                if (obj.IsIndirect())
                {
                    PdfDictionary tg = (PdfDictionary)(PdfReader.GetPdfObject(obj));
                    PdfName objectType = (PdfName)(PdfReader.GetPdfObject(tg.Get(PdfName.SUBTYPE)));
                    if (
                            PdfName.IMAGE.Equals(objectType) 
                            ||PdfName.IMAGEB.Equals(objectType) 
                            || PdfName.IMAGEC.Equals(objectType) 
                            || PdfName.IMAGEI.Equals(objectType)
                        )
                    {
                        int xrefIdx = ((PRIndirectReference)obj).Number;
                        PdfObject pdfObj = doc.GetPdfObject(xrefIdx);
                        PdfStream str = (PdfStream)(pdfObj);

                        iTextSharp.text.pdf.parser.PdfImageObject pdfImage =
                            new iTextSharp.text.pdf.parser.PdfImageObject((PRStream)str);
                        System.Drawing.Image img = pdfImage.GetDrawingImage();

                        imageList.Add(img);
                    }
                    else if (PdfName.FORM.Equals(objectType) || PdfName.GROUP.Equals(objectType))
                    {
                        imageList.AddRange(GetImagesFromDocument(tg, doc));
                    }
                }
            }
        }
        return imageList;
    }

Im calling function GetImagesFromPdf, and then for each Page i run function: GetImagesFromDocument. This results in right quantity of images in pdf, but i dont really have a clue, how to get locations (X,Y,Width,Height) .... Is anything additional i can use ? Or i should use another mechanism for that ?

Any help will be appreciated. PS. i have noticed something... I also tried to implement IExtRenderListener interface, and i have noticed that when there is an image in pdf, the function RenderImage is being called. There is one function which im able to call on object renderInfo, and the result is instresting, i get that values (The function is called GetImageCTM()):

The function is called GetImageCTM()

When i was trying to cover images written as graphical vectors, i have failed. None image was covered, but i got like 15 000 very small rectangles, unfortunatelly none was visible. I used this code:

  public void ModifyPath(PathConstructionRenderInfo renderInfo)
    {
        if (renderInfo.Operation == PathConstructionRenderInfo.RECT)
        {
            float x = renderInfo.SegmentData[0];
            float y = renderInfo.SegmentData[1];
            float w = renderInfo.SegmentData[2];
            float h = renderInfo.SegmentData[3];
            Vector a = new Vector(x, y, 1).Cross(renderInfo.Ctm);
            Vector b = new Vector(x + w, y, 1).Cross(renderInfo.Ctm);
            Vector c = new Vector(x + w, y + h, 1).Cross(renderInfo.Ctm);
            Vector d = new Vector(x, y + h, 1).Cross(renderInfo.Ctm);

            Rectangle rect = new Rectangle(x,y,x+w, y+h); //is that correct ?
        }
        else
        {
            for (int i = 0; i < renderInfo.SegmentData.Count - 1; i += 2)
            {
                float x = renderInfo.SegmentData[i];
                float y = renderInfo.SegmentData[i + 1];
                Vector a = new Vector(x, y, 1).Cross(renderInfo.Ctm);
              //  Rectangle rect = new Rectangle(x, y, x + ..., y + ...); What to do here ?
            }
        }
        modifyPathCounter++;
    }

OK its already solved, solution is here:

 if (renderInfo.Operation == PathConstructionRenderInfo.RECT)
        {
            float x = renderInfo.SegmentData[0];
            float y = renderInfo.SegmentData[1];
            float w = renderInfo.SegmentData[2];
            float h = renderInfo.SegmentData[3];
            Vector a = new Vector(x, y, 1).Cross(renderInfo.Ctm);
            Vector b = new Vector(x + w, y, 1).Cross(renderInfo.Ctm);
            Vector c = new Vector(x + w, y + h, 1).Cross(renderInfo.Ctm);
            Vector d = new Vector(x, y + h, 1).Cross(renderInfo.Ctm);

            Rectangle rect = new Rectangle(a[0],a[1],c[0], c[1]); 
            //SquaresToDraw image = new SquaresToDraw(0, rect.Left, rect.Bottom, rect.Right, rect.Top);
          //  squaresToDraw.Add(image);
        }
        else
        {
            if (renderInfo.SegmentData!=null)
            {
                for (int i = 0; i < renderInfo.SegmentData.Count - 1; i += 2)
                {
                    float x = renderInfo.SegmentData[i];
                    float y = renderInfo.SegmentData[i + 1];
                    Vector a = new Vector(x, y, 1).Cross(renderInfo.Ctm);
                    Rectangle rect = new Rectangle(x, y, a[0], a[1]); 
                    SquaresToDraw image = new SquaresToDraw(0, rect.Left, rect.Bottom, rect.Right, rect.Top);
                      squaresToDraw.Add(image);
                                                                      //  Rectangle rect = new Rectangle(x, y, x + ..., y + ...); What to do here ?
                }
            }

        }
Bartosz Olchowik
  • 1,129
  • 8
  • 22

1 Answers1

1

Your two methods give you the resources associated with your page (directly or indirectly) but that's it, they cannot tell you where (or if at all) such a resource is used on the page. So this does not give rise to anything.

But your IExtRenderListener approach is indeed the way you go. And your observation is correct, the ImageCtm is the matrix representing the affine transformation applied to the original image compared to a 1x1 square at the origin.

Simply apply that transformation to the corners of the 1x1 unit square to get the corners of the image on your page.

mkl
  • 90,588
  • 15
  • 125
  • 265
  • is somewhere wrote, which part of this matrix is responsible for X,Y,Width,Height ? What to add, what to subtract ? Btw. when i was trying to find locations written as graphical vectors i found 15000 rectangles (PathConstructionRenderInfo), they were very small, and i guess thats why i dont even see them in pdf, after i draw them. I'll update my question. – Bartosz Olchowik Dec 15 '18 at 14:47
  • More details next week. Concerning the many rectangles: I don't know your specific pdf but some software first creates a clip path (mostly one rectangle) before writing a bit of text. Do you don't *see* those rectangles anyways as they are not drawn per se. – mkl Dec 15 '18 at 17:40
  • alright MKL, i arleady solved it. Solution was in right use of Vectors, i managed to find the way to get right coordinates, and your's another topic in stackoverflow helped me. – Bartosz Olchowik Dec 15 '18 at 18:33