Add PNG Image to PDF Document - inserted image is upside down and at wrong coordinates

Hello,

I have two .pdf documents and the method which inserts the same .png image into them using Aspose.Pdf.Document. Surprisingly, into one of them image is inserted upside-down and at wrong coordinates.

Demo project where the problem is shown:
https://drive.google.com/file/d/0B-CEaN01bFt3blhCRnQyZkd5dmM/view?usp=sharing

Method which is used to insert an image:


    Public Shared Sub InsertPngImageIntoDocument(ByVal document As IO.Stream,
                                             ByVal image As IO.Stream,
                                             ByVal pageNumber As Integer,
                                             ByVal imageHeight As Double,
                                             ByVal imageWidth As Double,
                                             ByVal imageLeft As Double,
                                             ByVal imageBottom As Double,
                                             ByRef output As IO.Stream)
        If document Is Nothing OrElse image Is Nothing Then Return

        ' register Aspose license
        Dim PdfLicense As New Aspose.Pdf.License
        PdfLicense.SetLicense("Aspose.Total.lic")

        Dim PdfDocument As New Aspose.Pdf.Document(document)
        'Get the page where image needs to be added
        Dim Page As Aspose.Pdf.Page = PdfDocument.Pages(pageNumber)

        'Set coordinates
        Dim LowerLeftX As Double = imageLeft
        Dim LowerLeftY As Double = imageBottom
        Dim UpperRightX As Double = imageLeft + imageWidth
        Dim UpperRightY As Double = imageBottom + imageHeight

        'Add image to Images collection of Page Resources
        Page.Resources.Images.Add(image)

        'Using GSave operator: this operator saves current graphics state
        Page.Contents.Add(New Aspose.Pdf.Operator.GSave())

        'Create Rectangle and Matrix objects
        Dim Rectangle As New Aspose.Pdf.Rectangle(LowerLeftX, LowerLeftY, UpperRightX, UpperRightY)
        Dim Matrix As New Aspose.Pdf.Matrix(New Double() {Rectangle.URX - Rectangle.LLX, 0, 0, Rectangle.URY - Rectangle.LLY, Rectangle.LLX, Rectangle.LLY})

        'Using ConcatenateMatrix (concatenate matrix) operator: defines how image must be placed
        Page.Contents.Add(New Aspose.Pdf.Operator.ConcatenateMatrix(Matrix))
        Dim Ximage As Aspose.Pdf.XImage = Page.Resources.Images(Page.Resources.Images.Count)

        'Using Do operator: this operator draws image
        Page.Contents.Add(New Aspose.Pdf.Operator.Do(Ximage.Name))

        'Using GRestore operator: this operator restores graphics state
        Page.Contents.Add(New Aspose.Pdf.Operator.GRestore())

        PdfDocument.Save(output)
    End Sub

@tak

Thanks for contacting support.

We have tested the scenario with Aspose.Pdf for .NET 17.9 and observed the same issue, which you have mentioned. Hence, we have logged it as PDFNET-43444 in our issue tracking system. We will further look into the details of the issue and keep you informed with the status of its rectification. Please be patient and spare us little time.

We are sorry for the inconvenience.

Hi
Please let me know if there is any new information on this issue.

@tak

Thanks for your inquiry.

I am afraid that earlier logged issue has not been yet resolved as there are large number of pending issues in the queue. As soon as we have some significant updates regarding resolution progress, we will surely let you know. Please be patient and spare us little time.

We are sorry for the inconvenience.

We have been a customer for 12 years, and now we are told that if we would like some attention to a serious bug, we need to pay $1,699. It is not satisfying. Pay and you will fix it, some way of treating customers. I understand that you have this paying option, and it will get us into a shorter queue, but as I see it I pay, and then there is a risk that you don’t fix your serious bug anyway. Can I get a guarantee that you will fix it within a couple of weeks if we pay? Otherwise it is cheaper and more relevant to look for another product.
Best Regards
Bent Kjeldsen
Development Manager

@intranote

Thanks for contacting support.

Please note that every reported/logged issue possesses equal significance and receives equal attention from us. We provide fix to each logged issue - however, issues have been resolved on first come first serve basis, which we believe is the fairest policy for everyone. In addition to that, the issues logged under paid/priority support model, have precedence over the issues logged under normal/free support mode. Which is why we suggest our customers to subscribe for paid support, in case their issue is a blocker and needs to be resolved on urgent basis.

Please also note that, paid support does not guarantee any immediate resolution, but it only expedites the investigation process - which will eventually leads to quicker resolution than in free/normal support model. Nevertheless, we have raised your concerns with product team - so that they can review logged issue PDFNET-43444 and share their feedback. For further information regarding Paid Support, you may please visit Paid Support FAQ page. We will let you know once receive some feedback from product team regarding your issue. Please spare us little time.

We are sorry for the inconvenience.

@intranote, @tak

Thanks for your patience.

We have investigated the earlier logged issue and found that first operator of first page in Test111.pdf was:

0.750000 0.000000 0.000000 -0.750000 0.000000 612.000000 cm

Which was why for all page contents specified transformation matrix applied which rotated contents by 180 degrees. So when image was added it was also rotated.

Please consider following possible solutions:

Determine transformation at the end of contents, and multiply your matrix by reversed current transformation matrix:

public void InsertPngImageIntoDocument(Stream document, Stream image, int pageNumber, double imageHeight,
                            double imageWidth, double imageLeft, double imageBottom, ref MemoryStream output)
                    {
                        if (document == null || image == null)
                            return;
                        document.Seek(0, SeekOrigin.Begin);
                        Aspose.Pdf.Document PdfDocument = new Aspose.Pdf.Document(document);
                        //Get the page where image needs to be added
                        Aspose.Pdf.Page Page = PdfDocument.Pages[pageNumber];

                    //Set coordinates
                    double LowerLeftX = imageLeft;
                    double LowerLeftY = imageBottom;
                    double UpperRightX = imageLeft + imageWidth;
                    double UpperRightY = imageBottom + imageHeight;

                    //Add image to Images collection of Page Resources
                    Page.Resources.Images.Add(image);


            //Using GSave operator: this operator saves current graphics state
            Page.Contents.Add(new Aspose.Pdf.Operator.GSave());

            //Create Rectangle and Matrix objects
                Aspose.Pdf.Rectangle Rectangle = new Aspose.Pdf.Rectangle(LowerLeftX, LowerLeftY, UpperRightX, UpperRightY);
                    Aspose.Pdf.Matrix matrix = new Aspose.Pdf.Matrix(new double[]
                    {
                Rectangle.URX - Rectangle.LLX,
                0,
                0,
                Rectangle.URY - Rectangle.LLY,
                Rectangle.LLX,
                Rectangle.LLY
                    });
//this is transformation matrix specified in source document
                    Matrix m = new Matrix(0.75, 0, 0, -0.75, 0, 612);
//calculate reverse matrix
                    m = m.Reverse();

//modify our matrix by reversed matrix
                    matrix = matrix.Multiply(m);

                    //Using ConcatenateMatrix (concatenate matrix) operator: defines how image must be placed
                    Page.Contents.Add(new Aspose.Pdf.Operator.ConcatenateMatrix(matrix));
  Aspose.Pdf.XImage Ximage = Page.Resources.Images[Page.Resources.Images.Count];

                    //Using Do operator: this operator draws image
                    Page.Contents.Add(new Aspose.Pdf.Operator.Do(Ximage.Name));

                    //Using GRestore operator: this operator restores graphics state
                    Page.Contents.Add(new Aspose.Pdf.Operator.GRestore());

                    PdfDocument.Save(output);
                } 

Please note that in above case you will need to analyze page contents, in order to check which transformation matrix is set after last operator.

Add operators to draw image BEFORE page contents:

public void InsertPngImageIntoDocument(Stream document, Stream image, int pageNumber, double imageHeight,
                        double imageWidth, double imageLeft, double imageBottom, ref MemoryStream output)
                {
                    if (document == null || image == null)
                        return;
                    document.Seek(0, SeekOrigin.Begin);
                    Aspose.Pdf.Document PdfDocument = new Aspose.Pdf.Document(document);
                    //Get the page where image needs to be added
                    Aspose.Pdf.Page Page = PdfDocument.Pages[pageNumber];

                    //Set coordinates
                    double LowerLeftX = imageLeft;
                    double LowerLeftY = imageBottom;
                    double UpperRightX = imageLeft + imageWidth;
                    double UpperRightY = imageBottom + imageHeight;

                    //Add image to Images collection of Page Resources
                    Page.Resources.Images.Add(image);
                    Page.Contents.Insert(1, new Aspose.Pdf.Operator.GSave());
                    //Create Rectangle and Matrix objects
                    Aspose.Pdf.Rectangle Rectangle = new Aspose.Pdf.Rectangle(LowerLeftX, LowerLeftY, UpperRightX, UpperRightY);
                    Aspose.Pdf.Matrix matrix = new Aspose.Pdf.Matrix(new double[]
                    {
                Rectangle.URX - Rectangle.LLX,
                0,
                0,
                Rectangle.URY - Rectangle.LLY,
                Rectangle.LLX,
                Rectangle.LLY
                    });


                    //Using ConcatenateMatrix (concatenate matrix) operator: defines how image must be placed
                    Page.Contents.Insert(2, new Aspose.Pdf.Operator.ConcatenateMatrix(matrix));
                    Aspose.Pdf.XImage Ximage = Page.Resources.Images[Page.Resources.Images.Count];

                    //Using Do operator: this operator draws image
                    Page.Contents.Insert(3, new Aspose.Pdf.Operator.Do(Ximage.Name));

                    //Using GRestore operator: this operator restores graphics state
                    Page.Contents.Insert(4, new Aspose.Pdf.Operator.GRestore());
                    PdfDocument.Save(output);
                }

Possible disadvantage of above approach is that, image may be hidden or overlapped by page contents.

Add q - Q (GSave - GResore) operators before and after page contents, so initial matrix (1 0 0 1 0 0 ) will be restored:

public void InsertPngImageIntoDocument(Stream document, Stream image, int pageNumber, double imageHeight,
                        double imageWidth, double imageLeft, double imageBottom, ref MemoryStream output)
                {
                    if (document == null || image == null)
                        return;
                    document.Seek(0, SeekOrigin.Begin);
                    Aspose.Pdf.Document PdfDocument = new Aspose.Pdf.Document(document);
                    //Get the page where image needs to be added
                    Aspose.Pdf.Page Page = PdfDocument.Pages[pageNumber];

                    //Set coordinates
                    double LowerLeftX = imageLeft;
                    double LowerLeftY = imageBottom;
                    double UpperRightX = imageLeft + imageWidth;
                    double UpperRightY = imageBottom + imageHeight;

                    //Add image to Images collection of Page Resources
                    Page.Resources.Images.Add(image);

                    //Add GSave and GRestore operators before and after page contents
                    Page.Contents.Insert(1, new Aspose.Pdf.Operator.GSave());
                    Page.Contents.Add(new Aspose.Pdf.Operator.GRestore());


            //Using GSave operator: this operator saves current graphics state
            Page.Contents.Add(new Aspose.Pdf.Operator.GSave());

            //Create Rectangle and Matrix objects
            Aspose.Pdf.Rectangle Rectangle = new Aspose.Pdf.Rectangle(LowerLeftX, LowerLeftY, UpperRightX, UpperRightY);
                    Aspose.Pdf.Matrix matrix = new Aspose.Pdf.Matrix(new double[]
                    {
                Rectangle.URX - Rectangle.LLX,
                0,
                0,
                Rectangle.URY - Rectangle.LLY,
                Rectangle.LLX,
                Rectangle.LLY
                    });

                    //Matrix m = new Matrix(0.75, 0, 0, -0.75, 0, 612);
                    //m = m.Reverse();
                    //matrix = matrix.Multiply(m);

                    //Using ConcatenateMatrix (concatenate matrix) operator: defines how image must be placed
                    Page.Contents.Add(new Aspose.Pdf.Operator.ConcatenateMatrix(matrix));
                    Aspose.Pdf.XImage Ximage = Page.Resources.Images[Page.Resources.Images.Count];

                    //Using Do operator: this operator draws image
                    Page.Contents.Add(new Aspose.Pdf.Operator.Do(Ximage.Name));

                    //Using GRestore operator: this operator restores graphics state
                    Page.Contents.Add(new Aspose.Pdf.Operator.GRestore());

                    PdfDocument.Save(output);
                }

Use ImageStamp:

public void InsertPngImageIntoDocument(Stream document, Stream image, int pageNumber, double imageHeight,
            double imageWidth, double imageLeft, double imageBottom, ref MemoryStream output)
        {
            if (document == null || image == null)
                return;
            document.Seek(0, SeekOrigin.Begin);
            Aspose.Pdf.Document PdfDocument = new Aspose.Pdf.Document(document);
            ImageStamp stamp = new ImageStamp(image);
            stamp.BottomMargin = imageBottom;
            stamp.LeftMargin = imageLeft;
            stamp.Width = imageWidth;
            stamp.Height = imageHeight;
            PdfDocument.Pages[1].AddStamp(stamp);
            PdfDocument.Save(output);
        }

Please use Aspose.PDF for .NET 18.3 while applying any of the above solutions and in case you still face any issue, please feel free to contact us.

Hi
We have tried your suggestions and found that they are not suitable for all documents. Please, see new DEMO that tries all solutions on another document: https://drive.google.com/file/d/1DUlfckATYmoZxEnBuuZRrfhv4KXJ8VtR/view?usp=sharing
In result of Solution 1 and Solution 2 image is not inserted at all (or is not visible); with Solution 3 it’s still rotated; with Solution 4 it’s inserted at wrong coordinates (this solution always inserts image at the bottom left corner, ImageLeft and ImageBottom are ignored).

@tak

Thanks for sharing a sample project.

We have tested the scenario in our environment and noticed that earlier suggested code snippet(s) did not generate correct output with different PDF document. We have logged an issue as PDFNET-44548 separately in our issue tracking system, with all provided details and input/output documents. We will further investigate the issue in detail and keep you posted with the rectification status. Please spare us little time.

We are sorry for the inconvenience.