0

It turned out that it was me that was thinking in the wrong coordinate system. It always worked as intended but I was drawing the rectangles basically off-screen. See my other stackoverflow question.

Note: I was asking for deletion of this question but the moderator decided this question should stay.


I am having a PdfPage extends JPanel that represents a PDF-page which gets drawn as an Image. First I load the images (rendered with PDFBox) and add it to a simple pager I wrote. Then I'm adding Highlight objects to each PdfPage using PdfPage.add(JComponent component) where each Highlight is supposed to annotate an error in my document.

My problem is that only the last added Highlight gets painted and the others are invisible ..

public DatasheetReviserRenderer(File file, List<DatasheetError> datasheetErrorList, int resolution) {

    // ..
    this.pdfViewer = new PdfViewer(file, resolution);

    PdfPage[] pdfPages = this.pdfViewer.getPdfPages();

    List<Highlight> highlights = new ArrayList<Highlight>();

    for (DatasheetError datasheetError : datasheetErrorList) {

        int pageNumber = datasheetError.getPage();
        Highlight highlight = createErrorHighlight(datasheetError);
        highlights.add(highlight);

        PdfPage pdfPage = pdfPages[pageNumber];         
        pdfPage.add(highlight); 
    }

    this.pdfViewer.setVisible(true);
}

private Highlight createErrorHighlight(DatasheetError datasheetError) {
    // ..
    return new Highlight(rectangle, new Color(0.5f, 0.1f,0.3f, 0.4f));
}

This is how PdfPage.java looks like:

public class PdfPage extends JPanel {
    private static final long serialVersionUID = 7756137054877582063L;

    final Image pageImage;

    public PdfPage(Image pageImage) {
        super(new BorderLayout());
        // ..
    }

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

    private void paintPdfPage(Graphics g) {
        // just draws pageImage
    }
}

And here is Highlight.java:

public class Highlight extends JComponent {
    private static final long serialVersionUID = -5376556610591196188L;

    /** The rectangle that represents the highlight. */
    private Rectangle rectangle;

    /** Border is invisible per default. */
    private Color borderColor = new Color(0, 0, 0, 0);

    /** The highlight color. */
    private Color fillColor;

    public Highlight(Rectangle rectangle, Color fillColor) {
        this.setBounds(rectangle);
        this.rectangle = rectangle;
        this.fillColor = fillColor; 
    }

    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g);

        g.setColor(this.fillColor);
        g.fillRect(this.rectangle.x, this.rectangle.y, this.rectangle.width, this.rectangle.height);

        g.setColor(this.borderColor);
        g.drawRect(this.rectangle.x, this.rectangle.y, this.rectangle.width, this.rectangle.height);
    }

}

Why aren't all Highlight objects painted?

Community
  • 1
  • 1
Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
  • Is the `paintComponent()` method from `Highlight` ever called? Could it be that the highlights are painted but then covered by the PDF image? – Eric Leibenguth Aug 04 '15 at 15:40
  • @EricLeibenguth Yes, it is getting called. Wouldn't the image overpaint *every* `Highlight`? Because the last one added gets displayed. – Stefan Falk Aug 04 '15 at 15:41
  • Yes, that's odd... Any chance that the highlights somehow get painted at the same coordinates? Not exactly a solution, but maybe instead of making `Highlight` extend `JComponent`, make it a simple POJO, add them to an `ArrayList` in `PdfPage`. Then you can paint them directly from the `PdfPage` `paintComponent()` method. At best it solves the problem, and at worst it makes debugging easier... – Eric Leibenguth Aug 04 '15 at 15:50
  • @EricLeibenguth Well I debugged it by getting `(PdfPage) this.getParent)` in `Highlights.paintComponent()`. What I can see is that `PdfPage.paintComponent()` gets called first and then the according `Highlight.paintComponent()` methods.. – Stefan Falk Aug 04 '15 at 16:01

1 Answers1

1

The culprit is:

super(new BorderLayout());

Every time you add a component to PdfPage, it replaces the component in the center, discardind the previous adds.

If you leave it as:

super();

It will work with a FlowLayout, where you can have any number of components. However, the Highlight components will have a size of 0x0 (because you never give them a size), so anything you paint will not be visible either.

There are two ways out of this:

  1. As commented, make Highlight a POJO, and paint it from PdfPage.paint() directly.
  2. Use a null layout and give your components hard-coded dimensions and bounds (not a clean solution though).
Eric Leibenguth
  • 4,167
  • 3
  • 24
  • 51
  • *Changing it to super(); results in no Highlight getting painted.* Yes, because as mentioned, that gets you a **`FlowLayout`**. Try with a **`null`** layout (`setLayout(null)`). *the last added Highlight gets painted correctly so it can't have a size of 0x0*: The last Highlight actually fills the entire space because of the border layout (which anyway overrides any dimension you set). It appears to be at the correct location, because your are manually drawing the rectangle inside this entire space. – Eric Leibenguth Aug 04 '15 at 16:59
  • Okay I really don't understand why it is not getting painted if I just set it to `super()` in `PdfPage` and call `setBounds(rectangle)` in `Highlight`. I don't want `PdfPage` to know anything about `Highlight`. `PdfPage` is supposed to be just a `JPanel` that renders an image. It should not render anything else by explicitly overriding `paint()` or something. I want it to paint anything that it gets by `JPanel.add()`. Imho it's strange that it's not working the way I'm doing it already .. :/ – Stefan Falk Aug 04 '15 at 17:23
  • I have created an `interface Drawable` that gets now implemented by `Highlight`. `PdfPage` not has a `List` and offers add/remove functionality. I thought swing would handle stuff like that for me but alright.. it works that way. – Stefan Falk Aug 04 '15 at 17:31
  • 1
    Just to clarify: `super()` creates a **`FlowLayout`** which **does not care about your bounds**. It will completely override them (just like `BorderLayout`). If you want to place components at given coordinates, you must use a `null` layout (`super(null)`), just as described in https://docs.oracle.com/javase/tutorial/uiswing/layout/none.html – Eric Leibenguth Aug 04 '15 at 17:40
  • I noticed that this approach now gives me troubles with MouseAdapter as no events are delegated to that object :/ – Stefan Falk Aug 04 '15 at 18:37
  • Then, either go for a `null` layout or add a `delegateMouseEvent()` method to the `drawable` interface (with `MouseAdapter` on the `PdfPanel`). Note: If you go for `null` layout, be carefull with the coordinate system (`Highlight.paintComponent()` paints relatively to the highlight's coordinate system, which is different from the parent's coordinate system) – Eric Leibenguth Aug 04 '15 at 19:48
  • A similar problem is examined [here](http://stackoverflow.com/q/30361149/230513). – trashgod Aug 04 '15 at 21:38