1

I've tried to paint component to PDF. I've got itextpdf 4.2 and everything works perfectly. But this works only if I make visible the frame that I've tried to render.

The similar question that I've found is How to paint an invisible JFrame elsewhere? that has the same issue, but the solution wasn't provided in answer.

A little of code. I've created a JFrame and insert main view that I want to render.

JFrame jframe = new ShowingFrame();
jframe.setPreferredSize(new Dimension(PDFHelper.getOriginalWidth().intValue(), PDFHelper.getOriginalHeight().intValue()));
jframe.setMinimumSize(new Dimension(PDFHelper.getOriginalWidth().intValue(), PDFHelper.getOriginalHeight().intValue()));
jframe.add(view);
jframe.setUndecorated(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setState(Frame.ICONIFIED);
jframe.setState(Frame.NORMAL);
//jframe.setVisible(true);

If I uncomment jframe.setVisible(true) than everything works. But user will see this frame that I want to avoid.

So the question is: How to paint hidden control?

Inside Swing Component.java class all paint methods first check if the component is visible:



    public void paint(Graphics g) {
        if (isShowing()) {
            // rest of the code...
        }
    }


I've tried to create inherit class ShowingFrame extends JFrame, that override isShowing and always return true. But this not helps to.

Community
  • 1
  • 1
WerWolf
  • 124
  • 1
  • 9
  • For a whole `JFrame`, this might be difficult. But when it is sufficient to paint the *content pane* of the frame, this should be possible with http://docs.oracle.com/javase/8/docs/api/javax/swing/SwingUtilities.html#paintComponent-java.awt.Graphics-java.awt.Component-java.awt.Container-int-int-int-int- (note that the method has some caveats - e.g. it changes the component bounds - but painting non-visible components is the actual purpose of this method) – Marco13 Jun 30 '14 at 15:10
  • Thanks, that was looked as working solution, but **paintComponent** supports only lightweight components – WerWolf Jun 30 '14 at 15:38

3 Answers3

2

Swing (and the Java Graphics API) is optimized to stop rendering as soon as possible.

So the solution is to create a BufferedImage, get the Graphics instance from it and then call component.paint(g); with it.

Now you have a tab component. Try to get the content of the tab instead of rendering the tab itself. If that doesn't work, you can try to clone the tree of children, create a new JPanel, attach the children and render the result. But cloning can become tedious if the models don't behave well.

See this question for some code: Swing: Obtain Image of JFrame

Community
  • 1
  • 1
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • 1
    I'm getting the Graphics of PdfTemplate (class from itextpdf library), but component.paint(g) doesn't work because component is invisible. Looks like I've missed something. – WerWolf Jun 30 '14 at 15:46
  • My guess is that the parent component isn't visible, so Java doesn't even bother to ask the children. You may have to clone the Swing components in the tab to get what you want. – Aaron Digulla Jun 30 '14 at 15:48
  • 1
    @WerWolf: A SWAG: what if you render it first by calling `pack()` on the JFrame before trying to extract an image? – Hovercraft Full Of Eels Jun 30 '14 at 15:50
  • Thansk @HovercraftFullOfEels, pack has helped for **BufferedImage** **Graphics**. But haven't helped for **PdfTemplate** **Graphics**. That at leas some result! Thank you! – WerWolf Jun 30 '14 at 16:09
  • @WerWolf: can you create a PdfTemplate graphics from a BufferedImage? If so, then there you go. If not then you can certainly change a BufferedImage into a image file of one of several standard formats, and then use that to create your PdfTemplate graphics. – Hovercraft Full Of Eels Jun 30 '14 at 16:14
  • @HovercraftFullOfEels I've done it. So Render to PdfGraphics works only if previously I render into BufferedImage graphics. `component.paint(big); component.paint(g2d);` While just `component.paing(g2d)` doesn't. Even if call it twice (yes I've tested it as well). – WerWolf Jun 30 '14 at 17:08
  • @WerWolf: Can you please post the working code (answer your own question) so other people can see how you did it? – Aaron Digulla Jul 01 '14 at 09:39
  • @AaronDigulla of course I'll do it, after it will works how it should. But I didn't find proper solution. Now with rendering twice in BufferedImage and then into Pdf doesn't look as a good one. – WerWolf Jul 01 '14 at 11:41
1

Why do you want to paint something that is not visible? Your computer does not want to waste CPU cycles rendering graphics that can't be seen. In fact, there is a lot of computations done to see what parts of each window are visible and only paint the visible parts (the so called clip window).

If you want to paint something so you can use it later or save it you can always create a BufferedImage of the size you want and paint to that.

dkatzel
  • 31,188
  • 3
  • 63
  • 67
  • So, I have a tab control. And I want to generate each tab into PDF (each tab on different page). So all tab items, except the selected one, are invisible. And I can't render it. That is why I've try to render invisible elements. – WerWolf Jun 30 '14 at 15:34
  • @WerWolf render each tab into a BufferedImage. In your `paint` methods, you can draw the image to the `Graphics` parameter and should also be able to re-use the image to save each image as a pdf page. – dkatzel Jun 30 '14 at 15:37
  • So this is the question, how can I do it? – WerWolf Jun 30 '14 at 15:40
  • I can create a Graphics of **BufferedImage**. And I creates **Graphics** of **PdfTemplate** in the same way. But **component.paintAll(g2d)** render empty PDF page, because tab item is not visible. – WerWolf Jun 30 '14 at 15:44
  • @WerWolf you don't call `component.paintAll()` directly. Once you have a graphics object from your pdfLibrary. calling `pdfGraphics.drawImage()` should work. – dkatzel Jun 30 '14 at 15:54
  • But I can't render into image as I can't render in pdfGraphics. – WerWolf Jun 30 '14 at 16:10
1

If I uncomment jframe.setVisible(true) than everything works. But user will see this frame that I want to avoid.

You can set the frame location so that it is not visible on the screen. Maybe something like:

frame.pack();
Dimension d = frame.getSize();
frame.setLocation(-d.witdh, 0);
camickr
  • 321,443
  • 19
  • 166
  • 288
  • It will be seen on the second monitor in this case. But the next issue is that under applet it displays security risk window. – WerWolf Jun 30 '14 at 18:00
  • *"It will be seen on the second monitor in this case."* You tried it? If so, set the location to -5000,-5000. I would not expect *any* multi-monitor system to display that. – Andrew Thompson Jul 01 '14 at 03:00