Coordinates of a JTextPane to make a Screenshot in Java
Asked Answered
J

2

7

I hope some one can help me, this is what I want to do.

I have a JTextPane and I want to take a screenshot to that specific JTextPane coordinates and size, so far I can do a screenshot with the size of the JTextPane but I can't get the specific coordinates my screenshots always gets the (0,0) coordinates.

This is my method:

void capturaPantalla () 
{
    try
    {
        int x = txtCodigo.getX();
        int y = txtCodigo.getY();

        Rectangle areaCaptura = new Rectangle(x, y, txtCodigo.getWidth(), txtCodigo.getHeight());

        BufferedImage capturaPantalla = new Robot().createScreenCapture(areaCaptura);

        File ruta = new File("P:\\captura.png");

        ImageIO.write(capturaPantalla, "png", ruta);

        JOptionPane.showMessageDialog(null, "Codigo de barras guardado!");      
    }

    catch (IOException ioe) 
    {
        System.out.println(ioe);
    }     

    catch(AWTException ex)
    {
        System.out.println(ex);
    }
} 
Jameson answered 13/6, 2015 at 3:11 Comment(0)
J
8

When you call getX() and getY() on any Swing component, you get the x and y relative to the component's container, not the screen. Instead you want the location of the component relative to the screen and get position based on that via getLocationOnScreen()

Point p = txtCodigo.getLocationOnScreen();
int x = p.x;
int y = p.y;

As per MadProgrammer's comment, you could simply call printAll(Graphics g) on your txtCodigo component, passing in a Graphics object obtained from a properly sized BufferedImage and forgo use of a Robot.

Dimension d = txtCodigo.getSize();
BufferedImage img = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
txtCodigo.printAll(g);
g.dispose();
// use ImageIO to write BufferedImage to file

To compare to the two methods:

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.*;

@SuppressWarnings("serial")
public class RobotVsPrintAll extends JPanel {
   private static final int WORDS = 400;
   private JTextArea textArea = new JTextArea(20, 40);
   private JScrollPane scrollPane = new JScrollPane(textArea);
   private Random random = new Random();

   public RobotVsPrintAll() {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < WORDS; i++) {
         int wordLength = random.nextInt(4) + 4;
         for (int j = 0; j < wordLength; j++) {
            char myChar = (char) (random.nextInt('z' - 'a' + 1) + 'a');
            sb.append(myChar);
         }
         sb.append(" ");
      }
      textArea.setText(sb.toString());

      textArea.setLineWrap(true);
      textArea.setWrapStyleWord(true);
      scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

      JButton robot1Btn = new JButton(new Robot1Action("Robot 1"));
      JButton robot2Btn = new JButton(new Robot2Action("Robot 2"));
      JButton printAllBtn = new JButton(new PrintAllAction("Print All"));

      JPanel btnPanel = new JPanel();
      btnPanel.add(robot1Btn);
      btnPanel.add(robot2Btn);
      btnPanel.add(printAllBtn);

      setLayout(new BorderLayout());
      add(scrollPane, BorderLayout.CENTER);
      add(btnPanel, BorderLayout.PAGE_END);
   }

   private void displayImg(BufferedImage img) {
      ImageIcon icon = new ImageIcon(img);
      JOptionPane.showMessageDialog(this, icon, "Display Image", 
            JOptionPane.PLAIN_MESSAGE);
   }

   private class Robot1Action extends AbstractAction {
      public Robot1Action(String name) {
         super(name);
         int mnemonic = (int) name.charAt(0);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         try {
            Component comp = scrollPane.getViewport();
            Point p = comp.getLocationOnScreen();
            Dimension d = comp.getSize();

            Robot robot = new Robot();

            Rectangle screenRect = new Rectangle(p.x, p.y, d.width, d.height);
            BufferedImage img = robot.createScreenCapture(screenRect);
            displayImg(img);

         } catch (AWTException e1) {
            e1.printStackTrace();
         }

      }

   }

   private class Robot2Action extends AbstractAction {
      public Robot2Action(String name) {
         super(name);
         int mnemonic = (int) name.charAt(0);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         try {
            Component comp = textArea;
            Point p = comp.getLocationOnScreen();
            Dimension d = comp.getSize();

            Robot robot = new Robot();

            Rectangle screenRect = new Rectangle(p.x, p.y, d.width, d.height);
            BufferedImage img = robot.createScreenCapture(screenRect);
            displayImg(img);

         } catch (AWTException e1) {
            e1.printStackTrace();
         }

      }

   }

   private class PrintAllAction extends AbstractAction {
      public PrintAllAction(String name) {
         super(name);
         int mnemonic = (int) name.charAt(0);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      public void actionPerformed(ActionEvent e) {
         Dimension d = textArea.getSize();
         BufferedImage img = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
         Graphics g = img.getGraphics();
         textArea.printAll(g);
         g.dispose();
         displayImg(img);
      }
   }

   private static void createAndShowGui() {
      RobotVsPrintAll mainPanel = new RobotVsPrintAll();

      JFrame frame = new JFrame("Robot Vs PrintAll");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

If you print the text component with printAll, you get the entire text component, even the parts that are not displayed in the JScrollPane's viewport.

Jalousie answered 13/6, 2015 at 3:17 Comment(10)
Just about to submit my answer, you beat me to it!Savadove
@ChristopherSmith: sorry for that!Jalousie
You can also use printAll to paint the contents to a BufferedImage, but I'm just been picky ;)Lashawna
@MadProgrammer: true, probably a better solution -- post it -- a good demonstration of an XY Problem type question, where the better solution is not to answer the actual question.Jalousie
Would doing one or the other have an actual effect on the output image? Even slightly?Savadove
@ChristopherSmith: I really don't know, but it would be curious to explore. One thing that I wonder about is what if the text component is held within a JScrollPane. With the Robot, you'd only get what you see, but perhaps with the printAll called on the text component and not the JScrollPane, you'd see more of the complete component -- I'm not sure.Jalousie
@ChristopherSmith: please see test code comparing methods. If you print the text component with printAll, you get the entire text component, even the parts that are not displayed in the JScrollPane's viewport.Jalousie
@ChristopherSmith It shouldn't effect the resulting image, but printing the image allows you to control the Graphics context, so, for example, you could add in different RenderingHints based on your needs, it will also print the whole JTextPane instead of what is available within the JScrollPane. Personally, I also found that Robot is a "little" impreciseLashawna
@HovercraftFullOfEels That is a very nice example, on Linux, Robot 2 gives me a black spot and stretched out text. hichris.com/Robot_2.pngSavadove
@Hovercraft Full Of Eels Thank You very much!, that totally worked and no more black screenshots...just right what I need it..Jameson
M
4

You can use the Screen Image class which does all the work for you. All you do is specify the component you want to capture.

The code would be:

BufferedImage bi = ScreenImage.createImage( component );

And you can save the image to a file using:

ScreenImage.writeImage(bi, "imageName.jpg");

This class will use the painting method of the Swing component which is more efficient than using a Robot.

Magisterial answered 13/6, 2015 at 5:7 Comment(1)
@HovercraftFullOfEels, thanks, this was one of my first postings 6 years ago. I'm surprised you haven't seen it before :)Magisterial

© 2022 - 2024 — McMap. All rights reserved.