0

I have a PDF file that I converted into an image. I Was able to write on top of the image but when I tried to save the shapes/lines into the pdf, the point are not their place and the shapes are inverted.

This is related to this question and it the first part was already answered. Now the second problem was when I try to scale the image. the points are not in their places.

I found out that it is not working when the scale is changed.. I updated the code and added the Translate values to the moveTo and lineTo as a parameter and it somehow worked when the scale is on default which is 72/200f.

Thank you Tilman Hausherr for your patience..

NOTE : Hold CTRL then use mouse wheel to zoom in/out. This event can change the scale and thus messing up with the points of my shape.

package pdfwriter;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.util.Matrix;

public class SO57387803DrawShapesInPDF {

    private static class MyImagePanel extends JPanel {

        final BufferedImage image;
        //final float scale = .38f;
        final int DPI = 200;
        float scale = 72f / DPI;

        //NEW
        double affineX;
        double affineY;

        AffineTransform atg;
        Point start = new Point();
        Point end = new Point();
        boolean isNewLine = true;
        static ArrayList<Line2D> lines = new ArrayList<>();
        static PDDocument document;

        public MyImagePanel() throws IOException {
            document = PDDocument.load(new File("c:\\users\\john ebarita\\documents\\plan.pdf"));
            PDFRenderer renderer = new PDFRenderer(document);

            image = renderer.renderImageWithDPI(0, DPI, ImageType.RGB);

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseReleased(MouseEvent e) {
                    if (end != start) {
                        Line2D line = new Line2D.Float(start.x, start.y, end.x, end.y);

                        lines.add(line);
                        Point2D p = calcCoordinates(e);

                        start = new Point();
                        start.x = (int) p.getX();
                        start.y = (int) p.getY();

                        repaint();
                    }

                }
            });
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    Point2D p = calcCoordinates(e);
                    end = new Point();

                    end.x = (int) p.getX();
                    end.y = (int) p.getY();
                    repaint();
                }
            });
            addMouseWheelListener(new MouseWheelListener() {
                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {

                    int rotation = e.getWheelRotation();

                    //Zoom in/out on CTRL
                    if (e.isControlDown()) {

                        if (rotation < 0) {
                            scale -= .05;
                        } else {
                            scale += .05;
                        }
                        if (scale < .2) {
//                            IMAGE_SCALE = (float) .2;
                        } else if (scale > 4) {
                            scale = 4;
                        }
                        revalidate();
                        repaint();
                    }
                }
            });
        }

        private Point2D calcCoordinates(MouseEvent e) {
            Point p = new Point(e.getX(), e.getY());
            try {
                return atg.inverseTransform(p, null);
            } catch (NoninvertibleTransformException ex) {
                return p;
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2D = (Graphics2D) g.create();

//          double  affineX = (getWidth() - scale * image.getWidth()) / 2;
//          double affineY = (getHeight() - scale * image.getHeight()) / 2;
//          updated
            affineX = (getWidth() - scale * image.getWidth()) / 2;
            affineY = (getHeight() - scale * image.getHeight()) / 2;

            AffineTransform at = new AffineTransform();
            at.translate(affineX, affineY);
            at.scale(scale, scale);

            AffineTransform atf = g2D.getTransform();
            atf.concatenate(at);
            atg = (AffineTransform) at.clone();

            g2D.setTransform(atf);

            g2D.drawImage(image, 0, 0, this);
            try {
                g2D.drawLine(start.x, start.y, end.x, end.y);
            } catch (NullPointerException e) {

            }

            for (Line2D l : lines) {
                g2D.draw(l);
            }

            g2D.dispose();
        }

        public void save() {

            try {
                PDPage page = document.getPage(0);

                PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true, true);
                contentStream.transform(Matrix.getRotateInstance(Math.toRadians(270), 0, page.getMediaBox().getHeight()));
                for (Line2D l : lines) {
                    Point2D p1 = new Point2D.Double(l.getX1(), l.getY1());
                    Point2D p2 = new Point2D.Double(l.getX2(), l.getY2());

                    p1 = atg.transform(p1, null);
                    p2 = atg.transform(p2, null);

//                    contentStream.moveTo((float) (p1.getX()), page.getMediaBox().getWidth() - (float) (p1.getY() ));
//                    contentStream.lineTo((float) (p2.getX()), page.getMediaBox().getWidth() - (float) (p2.getY() ));
                    //UPATED
                    contentStream.moveTo((float) (p1.getX() - affineX), page.getMediaBox().getWidth() - (float) (p1.getY() - affineY));
                    contentStream.lineTo((float) (p2.getX() - affineX), page.getMediaBox().getWidth() - (float) (p2.getY() - affineY));

                    contentStream.stroke();
                }

                contentStream.close();

                document.save(new File("c:\\users\\john ebarita\\documents\\saved.pdf"));
                document.close();
                System.out.println("file saved");
            } catch (IOException ex) {
//                Logger.getLogger(SO57387803DrawShapesInPDF.class.getName()).log(Level.SEVERE, null, ex);
                ex.printStackTrace();
            }

        }

        @Override
        public Dimension getPreferredSize() {
            int width = (int) (scale * image.getWidth());
            int height = (int) (scale * image.getHeight());
            return new Dimension(width, height);
        }
    }

    public static void main(String[] args) {

//        System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
        JFrame frame = new JFrame("PDF");
        frame.setSize(1500, 1200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        // MyImagePanel imagePanel = null;
        MyImagePanel imagePanel;
        try {
            imagePanel = new MyImagePanel();
        } catch (IOException ex) {
            ex.printStackTrace();
//            Logger.getLogger(SO57387803DrawShapesInPDF.class
//                    .getName()).log(Level.SEVERE, null, ex);
            return; // or there would be an uninitialized variable
        }

        JButton btn = new JButton("Save");

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                imagePanel.save();
                //MyImagePanel.save();
            }
        });

        btn.setBounds(10, 0, 70, 30);

        frame.add(btn);
        JPanel pnl = new JPanel();
        pnl.add(imagePanel);
        pnl.setBounds(0, 100, 1500, 1200);
        frame.add(pnl);
        frame.revalidate();
        frame.repaint();
        frame.setVisible(true);
    }

}
John
  • 45
  • 8
  • I don't see any difference regardless of the dpi, maybe it depends on the PDF file and that is why I've often told to share a link to your file (or to a public file that has the same effect). I also see that you are doing a rotation, this will of course only work nicely with a rotated file. – Tilman Hausherr Aug 15 '19 at 03:36
  • Sir have you tried this code?.. I added a `mouseWheelListener` that zoom in/out of the image. Therefore the scale is changed and messes up with the points. And the page that I am using is in 270 rotation. – John Aug 15 '19 at 07:33
  • moving the mouse wheel has no visible effect for me, nor does your text mention that one should use it and what to expect. – Tilman Hausherr Aug 15 '19 at 08:24
  • I'm sorry. I forgot to add mention it in my question. You need to hold the CTRL key before moving the mouse wheel for it to zoom. – John Aug 15 '19 at 23:36
  • OK, got it. If you change the scale, then you need to change DPI as well. Or you render the PDF page with `renderImage`, but I suspect that the scale parameter needs to be `1f / scale`. – Tilman Hausherr Aug 16 '19 at 08:12
  • What really is the significance of `72/200f` or `72/DPI` ? for what I know , it is only used to give a scale that is used to make the image appear relative to the `container` size which is `MyImagePanel`. – John Aug 17 '19 at 00:48
  • I don`t understand why I need to change the DPI every time I change the scale or I am not getting what you mean? Also I cannot afford to use `renderImage` because I need the `AffineTransform` set on the `graphics` itself. Thank you. – John Aug 17 '19 at 00:51
  • And one more thing , I know its off the topic but is it possible to extract line segments on a pdf using pdfbox? Like table borders and lines from pdf and I want to have there coordinates. – John Aug 17 '19 at 00:55
  • The image is displayed in a specific resolution (you start with 200 dots per inch), so when you click on such an image the position is the position of a bigger image than 1:1 which is 72 dpi per PDF standard (1 PDF page unit = 1/72 inch), thus the need to adjust. Re extracting the line segments see here: https://stackoverflow.com/questions/38931422/pdfbox-2-0-2-calling-of-pagedrawer-processpage-method-caught-exceptions – Tilman Hausherr Aug 17 '19 at 04:20
  • If you can't use "renderImage", then recalculate the dpi if you change the scale - it makes no sense to "zoom" on the same image, it will be bigger but without more details. – Tilman Hausherr Aug 17 '19 at 04:24
  • And btw I tried with a 270 rotated image - it is not "inverted". It is just the scale that doesn't match. – Tilman Hausherr Aug 17 '19 at 04:25
  • Yes the image is not inverted but the scale is doesn't match. And now I am using `drawRenderedImage()`. Any Idea on how the match the scale? I also want to try to change the DPI but I don't where to start. Do I have to call `renderer.renderImageWithDPI();` again? – John Aug 19 '19 at 03:01
  • 1
    Finally got it. – John Aug 19 '19 at 05:29

1 Answers1

0

After a number of trial and error I think I got the answer to my problem with the scale and points.

I started off with 72/200f scale.So what I did was I made that the did was after transforming the points I divide with the currentScale/firstScale, then divide it to the transformed points;

Thank you Tilman Hausherr for the helping me.

  p1 = atg.transform(p1, null);
  p2 = atg.transform(p2, null);

 contentStream.moveTo((float) (p1.getX() / (scale / .36)), (float) page.getMediaBox().getWidth() - p1.getY() / (scale / .36)));
 contentStream.lineTo((float) (p2.getX() / (scale / .36)), (float) (page.getMediaBox().getWidth() - p2.getY() / (scale / .36)));

Full Code;

package pdfwriter;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.util.Matrix;

public class SO57387803DrawShapesInPDF {

    private static class MyImagePanel extends JPanel {

        final BufferedImage image;
        //final float scale = .38f;
        final int DPI = 200;
        float scale = 72f / DPI;

        //NEW
        double affineX;
        double affineY;

        AffineTransform atg;
        Point start = new Point();
        Point end = new Point();
        boolean isNewLine = true;
        static ArrayList<Line2D> lines = new ArrayList<>();
        static PDDocument document;

        public MyImagePanel() throws IOException {
            document = PDDocument.load(new File("plan.pdf"));
            PDFRenderer renderer = new PDFRenderer(document);

            image = renderer.renderImageWithDPI(0, DPI, ImageType.RGB);

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseReleased(MouseEvent e) {
                    if (end != start) {
                        Line2D line = new Line2D.Float(start.x, start.y, end.x, end.y);

                        lines.add(line);
                        Point2D p = calcCoordinates(e);

                        start = new Point();
                        start.x = (int) p.getX();
                        start.y = (int) p.getY();

                        repaint();
                    }

                }
            });
            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    Point2D p = calcCoordinates(e);
                    end = new Point();

                    end.x = (int) p.getX();
                    end.y = (int) p.getY();
                    repaint();
                }
            });
            addMouseWheelListener(new MouseWheelListener() {
                @Override
                public void mouseWheelMoved(MouseWheelEvent e) {

                    int rotation = e.getWheelRotation();

                    //Zoom in/out on CTRL
                    if (e.isControlDown()) {

                        if (rotation < 0) {
                            scale -= .05;
                        } else {
                            scale += .05;
                        }
                        if (scale < .2) {
                            scale = .2f;
                        } else if (scale > 4) {
                            scale = 4;
                        }
                        revalidate();
                        repaint();
                    }
                }
            });
        }

        private Point2D calcCoordinates(MouseEvent e) {
            Point p = new Point(e.getX(), e.getY());
            try {
                return atg.inverseTransform(p, null);
            } catch (NoninvertibleTransformException ex) {
                return p;
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2D = (Graphics2D) g.create();

            //          double  affineX = (getWidth() - scale * image.getWidth()) / 2;
            //          double affineY = (getHeight() - scale * image.getHeight()) / 2;
            //          updated
            affineX = (getWidth() - scale * image.getWidth()) / 2;
            affineY = (getHeight() - scale * image.getHeight()) / 2;

            AffineTransform at = new AffineTransform();
            at.translate(affineX, affineY);
            at.scale(scale, scale);

            AffineTransform atf = g2D.getTransform();
            atf.concatenate(at);
            atg = (AffineTransform) at.clone();

            g2D.setTransform(atf);
            g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2D.drawImage(image, 0, 0, this);
            try {
                g2D.drawLine(start.x, start.y, end.x, end.y);
            } catch (NullPointerException e) {

            }

            for (Line2D l : lines) {
                g2D.draw(l);
            }

            g2D.dispose();
        }

        public void save() {

            try {
                PDPage page = document.getPage(0);

                PDPageContentStream contentStream = new PDPageContentStream(document, page, true, true, true);
                contentStream.transform(Matrix.getRotateInstance(Math.toRadians(270), 0, page.getMediaBox().getHeight()));
                for (Line2D l : lines) {
                    Point2D p1 = new Point((int) l.getX1(), (int) l.getY1());
                    Point2D p2 = new Point((int) l.getX2(), (int) l.getY2());

                    p1 = atg.transform(p1, null);
                    p2 = atg.transform(p2, null);

                    //UPATED
                    System.out.println("Points");
                    System.out.println(p1);
                    System.out.println(p2);
                    System.out.println("Scale " + scale);

                    //0.36 is the default scale or 72/200f 
                    contentStream.moveTo((float) (p1.getX() / (scale / .36)), (float) (page.getMediaBox().getWidth() - p1.getY() / (scale / .36)));

                    contentStream.lineTo((float) (p2.getX() / (scale / .36)), (float) (page.getMediaBox().getWidth() - p2.getY() / (scale / .36)));

                    contentStream.stroke();
                }

                contentStream.close();

                document.save(new File("c:\\users\\john ebarita\\documents\\saved.pdf"));
                document.close();
                System.out.println("file saved");
            } catch (IOException ex) {
                //                Logger.getLogger(SO57387803DrawShapesInPDF.class.getName()).log(Level.SEVERE, null, ex);
                ex.printStackTrace();
            }

        }

        @Override
        public Dimension getPreferredSize() {
            int width = (int) (scale * image.getWidth());
            int height = (int) (scale * image.getHeight());
            return new Dimension(width, height);
        }
    }

    public static void main(String[] args) {

        //        System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
        JFrame frame = new JFrame("PDF");
        frame.setSize(1500, 1200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        // MyImagePanel imagePanel = null;
        MyImagePanel imagePanel;
        try {
            imagePanel = new MyImagePanel();
        } catch (IOException ex) {
            ex.printStackTrace();
            //            Logger.getLogger(SO57387803DrawShapesInPDF.class
            //                    .getName()).log(Level.SEVERE, null, ex);
            return; // or there would be an uninitialized variable
        }

        JButton btn = new JButton("Save");

        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                imagePanel.save();
                //MyImagePanel.save();
            }
        });

        btn.setBounds(10, 0, 70, 30);

        frame.add(btn);
        JPanel pnl = new JPanel();
        pnl.add(imagePanel);
        pnl.setBounds(0, 100, 1500, 1200);
        frame.add(pnl);
        frame.revalidate();
        frame.repaint();
        frame.setVisible(true);
    }

}
John
  • 45
  • 8