BFO PDF Library 2.15 - but what happened to 2.14.1?

Our version numbering scheme is a very common one - changing the second part of the number (for example from 2.10 to 2.11) is required when there's a change that alters the API in some way, and on reflection our 2.14.1 release did just that. So we've renumbered it, and this article is to explain why.

Page list

Each PDF has a list of pages, and a page can only belong to one PDF at a time. This is fairly self-evident, but it has some implications for the API when you're moving pages between documents. In 2.15 we enforce this by deleting the page from the old PDF's list when we add it to the new PDF. For example:

PDF pdf1 = new PDF(new PDFReader(new File("singlepage.pdf")));
PDF pdf2 = new PDF();
List list1 = pdf1.getPages();
List list2 = pdf2.getPages();
assert list2.size() == 1;
list1.add(list2.get(0));
assert list1.size() == 0;
assert list2.size() == 0;

The last line is the significant one - the page is moved, not copied. Prior to 2.15 it was copied, which led to some confusing situations:

pdf2.getPages().add(pdf1.getPage(0));
assert pdf1.getPage(0).getPDF() == pdf1;   // fails?!?!

Where you might come unstuck with this change is if you're moving pages between documents and relying on the old behaviour. For instance, the following code snippets would work in 2.14 but fail in the latest release:

for (int i=0;i<pdf1.getNumberOfPages();i++) {
    pdf2.getPages().add(pdf1.getPage(i));
}

List pages = pdf1.getPages();
for (int i=0;i<pages.size();i++) {
    pdf2.getPages().add(pages.get(i));
}

Both of these will eventually give an ArrayIndexOutOfBoundsException as the pages are removed from the first PDF's list. There are several other approaches which will work and which we'd recommend you use instead..

// Will work, but not terribly intuitive.
while (pdf1.getNumberOfPages() > 0) {
    pdf2.getPages().add(pdf1.getPage(0));
}

// Simpler option: copy the list
List pages = new ArrayList(pdf1.getPages());
for (int i=0;i<pages.size();i++) {
    pdf2.getPages().add(pages.get(i));
}

// Iterators are always safe
for (Iterator i = pdf1.getPages().iterator();i.hasNext();) {
    pdf2.getPages().add(i.next());
}

// Best option by far
pdf2.getPages().addAll(pdf1.getPages());

The above change in behaviour is why we renumbered the release - although technically our API hasn't changed, this is a breaking behaviour for some and you may want to check your code before upgrading..