1

I'm trying to get my Jframe to match my image dimensions exactly, so that when i attempt to get the Rectangle2D co-ordinates of an area via drawing a rectangle, it's give me the true co-ordinates of where it would appear on the actual image.

The objective with this solution is to convert a PDF to a image, identify a particular area using the visual mapper and then use PDFBox (PDFTextStripperbyArea) to extract against this area.

The co-ordinates being given by the below code is not extracting the required area's.

This is the code:

    public class PDFVisualMapper extends JFrame {

    BufferedImage image = null;

    public static void main(String[] args) throws IOException {
        new PDFVisualMapper();
    }

    public PDFVisualMapper() throws IOException {
        this.setSize(1700, 2200);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(new PaintSurface(), BorderLayout.CENTER);
        this.setVisible(true);
    }

    private class PaintSurface extends JComponent {
        ArrayList<Shape> shapes = new ArrayList<Shape>();

        Point startDrag, endDrag;

        public PaintSurface() throws IOException {
            image = ImageIO.read(new File("C:\\Users\\Rusty\\Desktop\\temp\\Test_PDF-1.png"));
            if ( image != null ) {
                Dimension size = new Dimension(image.getWidth(null), image.getHeight(null));
                setPreferredSize(size);
                setMinimumSize(size);
                setMaximumSize(size);
                setSize(size);
                setLayout(null);
            }

            this.addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    startDrag = new Point(e.getX(), e.getY());
                    endDrag = startDrag;
                    repaint();
                }

                public void mouseReleased(MouseEvent e) {
                    Shape r = makeRectangle(startDrag.x, startDrag.y, e.getX(), e.getY());
                    shapes.add(r);
                    startDrag = null;
                    endDrag = null;
                    repaint();
                }
            });

            this.addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseDragged(MouseEvent e) {
                    endDrag = new Point(e.getX(), e.getY());
                    repaint();
                }
            });
        }

        private void paintBackground(Graphics2D g2) {
            g2.setPaint(Color.LIGHT_GRAY);
            for (int i = 0; i < getSize().width; i += 10) {
                Shape line = new Line2D.Float(i, 0, i, getSize().height);
                g2.draw(line);
            }

            for (int i = 0; i < getSize().height; i += 10) {
                Shape line = new Line2D.Float(0, i, getSize().width, i);
                g2.draw(line);
            }

        }

        public void paint(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
             paintBackground(g2);
            Color[] colors = { Color.YELLOW, Color.MAGENTA, Color.CYAN, Color.RED, Color.BLUE, Color.PINK };
            int colorIndex = 0;
             g2.drawImage(image, null, 0, 0);

            g2.setStroke(new BasicStroke(2));
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.50f));

            for (Shape s : shapes) {
                g2.setPaint(Color.BLACK);
                g2.draw(s);
                g2.setPaint(colors[(colorIndex++) % 6]);
                g2.fill(s);
            }

            if (startDrag != null && endDrag != null) {
                g2.setPaint(Color.LIGHT_GRAY);
                Shape r = makeRectangle(startDrag.x, startDrag.y, endDrag.x, endDrag.y);
                g2.draw(r);
                System.out.println(r.getBounds2D());
            }
        }
    }

    private Rectangle2D.Float makeRectangle(int x1, int y1, int x2, int y2) {
        return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
    }

}

Can anybody help?

Ansharja
  • 1,237
  • 1
  • 14
  • 37
Rusty Shackleford
  • 337
  • 1
  • 6
  • 18
  • What is the desired result ? A JFrame that is the same size as the image ? – c0der Oct 22 '17 at 15:26
  • Thats correct yes, the same dimensions. In theory, if the frame and the image are the same size, the area co-ordinate's extraction given by this method should match the actual images area co-ordinates – Rusty Shackleford Oct 22 '17 at 15:31
  • [Don't use `setPreferredSize()` when you really mean to override `getPreferredSize()`](http://stackoverflow.com/q/7229226/230513); when you then `pack()` the enclosing `Window`, it will be the right size. – trashgod Oct 22 '17 at 15:33
  • @trashgod, how would i implement that in the above code? I'm new to Jframe, so it's a bit confusing to me at the moment – Rusty Shackleford Oct 22 '17 at 15:34
  • The example below does this implicitly, as the label's preferred size is that of the `ImageIcon`; `pack()` works accordingly. – trashgod Oct 22 '17 at 15:45
  • I've tried this and it's looking good, but the jframe is bigger than my screen. Is there anyway i can dynamically resize the jframe so that the image still fits if i make it smaller? – Rusty Shackleford Oct 22 '17 at 15:46

2 Answers2

2

This might be simpler: using a JLabel within the contentpane, using FlowLayout:

import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class WarpImage {

    public static void main(String[] args) throws IOException {

        displayImage();
    }

    private static void displayImage() throws IOException{

        URL url = new URL("http://www.digitalphotoartistry.com/rose1.jpg");
        BufferedImage image = ImageIO.read(url);
        ImageIcon icon= new ImageIcon(image);
        JFrame frame=new JFrame(); 
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());
        JLabel lbl= new JLabel();
        lbl.setIcon(icon);
        frame.add(lbl);
        frame.pack();
        //check size :
        Rectangle bounds = lbl.getBounds();
        System.out.println(bounds.getWidth() +"-"+ bounds.getHeight());
        frame.setVisible(true);
    }
}
c0der
  • 18,467
  • 6
  • 33
  • 65
  • I've tried this and it's looking good, but the jframe is bigger than my screen. Is there anyway i can dynamically resize the jframe so that the image still fits if i make it smaller? – Rusty Shackleford Oct 22 '17 at 15:42
  • As suggested [here](https://stackoverflow.com/q/7229226/230513) you can call `setSize()` to enlarge the frame _after_ you `pack()` it. – trashgod Oct 22 '17 at 15:46
  • @RustyShackleford did you want the `JFrame` smaller than the image ? – c0der Oct 22 '17 at 15:49
  • I just want to be able to see the entire image so i can identify areas for extractions. I understand this Jframe fits the image exactly, which is what i did ask in my original question, but i need to be able to see all of the image. Basically, i am trying to achieve the solution discussed here > https://dzone.com/articles/visual-mapper-for-pdf-data-extraction – Rusty Shackleford Oct 22 '17 at 15:52
  • 1
    If the question was answered: consider a new post for other questions. – c0der Oct 22 '17 at 15:56
  • @trashgod why use `setSize()` at all ? What is the additional benefit here ? – c0der Oct 22 '17 at 15:59
  • Ok i will - Thinking about it though, could the jframe be scrollable? if that was the case, i could get at the parts of the image jframe is currently not displaying – Rusty Shackleford Oct 22 '17 at 16:20
  • Yes, you can warp the label with [JScrollPane](https://docs.oracle.com/javase/7/docs/api/javax/swing/JScrollPane.html) – c0der Oct 22 '17 at 16:24
  • how would i implement that into your above solution? – Rusty Shackleford Oct 22 '17 at 16:26
  • If the question was answered: consider a new post for other questions. – c0der Oct 22 '17 at 16:27
  • https://stackoverflow.com/questions/46876708/dynamically-resize-jframe-image-or-scroll – Rusty Shackleford Oct 22 '17 at 16:48
  • @coder: no special benefit; it's just relatively safe to _enlarge_ after `pack()`; counter-example [here](https://stackoverflow.com/a/37801762/230513). – trashgod Oct 22 '17 at 22:36
1

I'm trying to get my Jframe to match my image dimensions exactly, so that when i attempt to get the Rectangle2D co-ordinates of an area via drawing a rectangle, it's give me the true co-ordinates of where it would appear on the actual image.

Then you paint the image yourself. Why? Because components like JLabel have their own internal layout mechanics which provide no way for you to determine the offset of the image within in, if the image is to large or to small for the component size.

Something like this, for example:

public class ImagePane extends JPanel {

    private BufferedImage img;

    public ImagePane(BufferedImage img) {
        this.img = img;
    }

    @Override
    public Dimension getPreferredSize() {
        return img == null ? new Dimension(0, 0) : new Dimension(img.getWidth(), img.getHeight());
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (img != null) {
            Graphics2D g2 = (Graphics2D) g.create();
            g2.drawImage(img, 0, 0, this);
            g2.dispose();
        }
    }
}

This places the image in the top left corner of the component, so if it's resized for some reason, the image will always be in the top left position. To be frank, it wouldn't be hard to generate an offset to allow the image to be centred, this could then be used by decedents of the component to calculate the offset required to adjust their own output as needed

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366