Open PDFs from Custom Data Providers in Java

This guide explains how to open a PDF document from a custom data provider.

PdfDocument expects a DataProvider to read from. You can implement an InputStreamDataProvider (which is a DataProvider) to provide data from any custom source.

It’s also useful to extend a WritableDataProvider in the same class to support writing back to the custom data source.

For example, here’s a CustomProvider that takes a byte array as the input data on construction:

// A custom data provider that extends `InputStreamDataProvider` and implements `WritableDataProvider`
// so you can read and write to a data block, `pdfData`.
class CustomProvider extends InputStreamDataProvider implements WritableDataProvider {
    private List<Byte> pdfData = new ArrayList<>();

    CustomProvider(@NotNull final byte[] data) {
        for (byte value : data) {
            pdfData.add(value);
        }
    }

    @NotNull
    @Override
    protected InputStream openInputStream() {
        byte[] data = new byte[pdfData.size()];
        for (int i = 0; i < pdfData.size(); i++) {
            data[i] = pdfData.get(i);
        }
        return new ByteArrayInputStream(data);
    }

    @Override
    public long getSize() {
        return pdfData.size();
    }

    @Nullable
    @Override
    public String getTitle() {
        return "Fake-Title";
    }

    @Override
    public boolean canWrite() {
        // You're allowing writing.
        return true;
    }

    @Override
    public boolean startWrite(WriteMode writeMode) {
        // Remove the current data if you're rewriting from scratch.
        if (writeMode == WriteMode.REWRITE) {
            pdfData.clear();
        }

        return true;
    }

    @Override
    public boolean write(byte[] bytes) {
        for (byte value : bytes) {
            pdfData.add(value);
        }
        return true;
    }

    @Override
    public boolean finishWrite() {
        // Nothing to do.
        return true;
    }

    @Override
    public boolean supportsAppending() {
        // You can append onto the string if you want to.
        return true;
    }
}

Here’s an example of loading a PdfDocument using the CustomProvider provider above by reading the bytes from a PDF file:

{
    FileInputStream fileInputStream = new FileInputStream(new File("document.pdf"));
    int numberOfBytesToRead = fileInputStream.available();
    byte[] buffer = new byte[numberOfBytesToRead];
    fileInputStream.read(buffer, 0, numberOfBytesToRead);
    PdfDocument document = PdfDocument.open(new CustomProvider(buffer));
}

Another example of this can be found in the ReadWriteCustomProviderExample Catalog example.