New features in PDF Library 2.12

What have we been up to?

Yesterday we released our first PDF Library for a few months, version 2.12, so it's a good to give a bit of a summary of the changes

Viewer Event model

The first one of note is the new viewer event model, which results from us fixing a bug of our own creation. The Viewer and the core API communicate largely by events - whenever the viewer changes the PDF (say by adding an annotation) the core API fires a PropertyChangeEvent which is listened for by other sections of the viewer to update its state to reflect the change.

All of these changes happen in order on a single thread, but we do have other threads that work in the background (painting thumbnails, for example), and these are notified by listeners on the main thread.

The problem comes when we have multiple changes in one action - the listener is notified on the first change, and any background threads may get the state halfway between the two changes. You can avoid this with heavy use of synchronizing, but with multiple objects being updated in a single action (and multiple objects being repainted) this is a complex, deadlock-prone strategy.

The solution is to queue up the PropertyChange events until after the first event has completed and the changes committed. The AWT already has such an EventQueue, but the PropertyChangeEvent isn't an AWTEvent and so can't be queued directly, so we fire them from within the dispatch method of an ActiveEvent - the same approach you take when you call SwingUtilities.invokeLater. It's not rocket science, just a simple change: obvious in hindsight but not necessarily when you're growing an increasingly complex API.

This change is large but subtle, and while we think it's a considerably improvement over the previous builds - a lot of the complex synchronization has gone, for one - it means the event listeners will potentially fire in a different order than previous releases. For this reason we've tagged this build as 2.12 rather than 2.11.26 - the API hasn't changed, but if you've written custom code for this build you should verify it works with the new build before rolling out.

Closing Documents in the Viewer

The next one is another simple change which may impact some customers. When you close a PDF in the viewer, we close our DocumentPanel which takes with it the reference to the PDF. Eventually both will be finalized by the garbage collector. If the PDF was opened from a File (as they usually are in the viewer) then the file handle is kept open for the life of the PDF too - we don't load the entire file into memory unless in this case. This will be closed on finalization too, but the problem comes when the viewer is part of a larger workflow which involves deleting the file after display: on Windows, an open file cannot be deleted.

There are cases where the PDF needs to be kept open after its panel has closed - when you concatenate two files in the viewer, for example, the PDF object has references to both source files until it's saved. However if you're not doing this and need the file to be closed immediately then you can set the org.faceless.pdf2.viewer2.EarlyClose system property to close the file when its panel is closed.

Thumbnail Actions

The Thumbnail panel in the viewer has been rewritten in this build and it's now possible to add your own custom actions to the panel. Perhaps you want to stamp a watermark on selected pages? You can now add a new feature to the viewer that implements ThumbnailSelectionAction, which could be as simple as this:

class Watermark extends ViewerFeature implements ThumbnailPanel.ThumbnailSelectionAction {
  Watermark() {
    super("Watermark");
  }
  public Action getAction(final ThumbnailPanel.View view) {
    return new AbstractAction() {
      public void actionPerformed(ActionEvent event) {
        DocumentPanel docpanel = view.getDocumentPanel();
        List pages = view.getSelectedPages();
        for (int i=0;i<pages.size();i++) {
          PDFPage page = (PDFPage)pages.get(i);
          // Watermark your pages here
        }
      }
    };
  }
}

Those are some of the highlights, but there are other reasons to upgrade.