BCL easyPDF SDK
easyPDF SDK Usermanual
PDF Creator Programming API  |  Download Free Trial  |  Contact Us to Purchase

Using the easyPDF with JACOB

The Basics

JACOB is a free Java COM bridge that allows easyPDF SDK to be used from Java.

The entire easyPDF SDK is located under the com.bcl.easypdf namespace, which is further divided into the following parts:

It is essential that easyPDF gets initialized before any of the SDK functions are called. Please use the following line of code to initialize easyPDF:

EasyPDF.initialize();

It is good practice to uninitialize the easyPDF SDK before your application quits:

EasyPDF.uninitialize();

If you are developing a multi-threaded Java application, please read the section "Initializing Multi-Threaded Applications" below.

Note that all exceptions thrown by easyPDF SDK are of type EasyPDFException.

Initializing Multi-Threaded Applications

Initialization and uninitialization exist for the sole purpose of Win32 COM. The function EasyPDF.initialize() initializes the COM environment for the current thread. It is identical to the Win32 function CoInitialize(NULL) in C/C++. The function EasyPDF.uninitialize() uninitializes COM for the current thread. It performs exactly what CoUninitialize() does in Win32. Please consult your MSDN documentation to learn more about COM.

Although you are not required to fully understand COM in order to use it from Java, it is extremely important to follow good practices regarding the initialization of the Win32 COM subsystem. Failure to follow these guidelines may cause your application to crash or freeze, possibly immediately, or worse, at random times during execution. This has nothing to do with easyPDF per se, but the Win32 COM environment itself.

Every application that uses COM in any way must follow the following rules:

  1. The COM subsystem must be initialized before any COM objects are used in each thread. Since easyPDF is COM, proper initialization must be done for each thread that creates easyPDF classes or calls easyPDF functions. The only exception is that easyPDF enumerations (such as error codes) alone do not require initialization.
  2. Every initialization must be balanced by a corresponding uninitialization within the same thread. After COM is uninitialized, no COM objects may be used. It is considered good practice to not uninitialized until the thread exits. Failure to uninitialize a thread before it exits is at least as bad as a memory leak, or potentially worse. Failure to uninitialize before an application quits might be a little less severe, because Windows always cleans up after an application. However, in order to exit COM gracefully, a balanced uninitialization is very strongly recommended.
  3. Initialization and uninitialization are reference counted, therefore duplicate initialization is not an error, as long as it is always balanced by an uninitialization. That said, it is unnecessary to initialize each thread more than once.
  4. The first thread that initializes must be the last to uninitialize. After the first thread that initialized uninitializes, it is no longer possible to initialize COM ever again during the lifetime of the application.

The last point is probably the most important one. Once the first initialize is paired with an uninitialize, you have irreversibly and permanently closed the opportunity of ever using COM objects again until the application quits and is fully restarted.

These were the rules. As long as you obey them, you will be fine. However, we would like to suggest a set of best practices, which are very easy to follow. You are guaranteed to have no problems with COM if you do the following:

  1. The main() function of the application should start by initializing COM, and end by uninitializing it, even if the main thread never uses easyPDF at all.
  2. In addition, each thread that creates easyPDF classes and/or calls easyPDF functions should start by initializing COM, and end by uninitializing it.
  3. Uninitialize should be put in a finally block, to ensure it is never missed.
  4. easyPDF operates in single threaded apartment (STA) mode. That means easyPDF objects are not safe to be shared by multiple threads, or to be passed from one thread to another (not even to the main thread). The thread that creates the object must be the one that uses it. Of course separate threads are safe to create their own independent objects.
  5. It is also important that two threads should never operate on the same file. Make sure that file names are unique identifiers, or that each thread operates in a separate directory.

If you do not have access to the source code for the main() function to initialize/uninitialize from there, then you must find another way to initialize from the main thread. Alternatively, you could create a pilot thread. Synchronization objects must be used to ensure that the pilot thread is really the first one to call EasyPDF.initialize(). The pilot thread must then be kept alive at all cost, even if it is in permanent sleep state, until the application quits. If the pilot thread quits or uninitializes, you can no longer use easyPDF at all. Implementing this properly is much harder than simply initializing from the application's main() function.

Note that EasyPDF.initialize() is nothing more than CoInitialize(NULL). In fact, EasyPDF.initialize() is not even required, as long as you have another way of calling CoInitialize or CoInitializeEx via Java Native Interface (JNI). EasyPDF.initialize() simply uses the JACOB bridge to call CoInitialize.

Naming Conventions

We tried to make the Java API as close to the native COM API as possible. As a result, the Java class, enumeration and function names are all identical to the COM API names. That means the following:

Parameter Types

We tried to make the Java API strongly typed whenever it was possible. That means an integer parameter is of type int, a string parameter is of type String, and so on. However, since COM itself is not always strongly typed, the easyPDF Java API contains some loosely typed parameters.

  1. Optional input parameters are always passed as Object.

    You can simply pass a value, and it gets implicitly converted into Object. For example, Password is an optional parameter in IPDFProcessor.GetPageCount:

    processor.GetPageCount("c:\\test\\input.doc", "password");
    

    Simply pass null to use the default value for an optional parameter.

  2. Variant input parameters are always passed as Object.

    Variants are variable type parameters. You can simply pass a value, and it gets implicitly converted into Object. For example, IFormField.Item's Index parameter may be either an integer or a string:

    IFormField field1 = document.getFormFields().getItem(0);
    IFormField field2 = document.getFormFields().getItem("Name");
    
  3. Input stream parameters are always passed as Object.

    A stream is an array of bytes with a type of byte[]. It gets implicitly converted into Object when passed into a function that accepts a stream. Example:

    File inputFile = new File("c:\\test\\input.doc");
    FileInputStream inputFileStream = new FileInputStream(inputFile.getCanonicalPath());
    byte[] inputStream = new byte[(int)inputFile.length()];
    inputFileStream.read(inputStream);
    byte[] outputStream = printjob.PrintOut3(inputStream, ".xls").toSafeArray().toByteArray();
    
  4. Output stream are always returned as Variant.

    Variant is a JACOB-specific class designed as a wrapper around COM Variants. The Variant can be converted into byte[] by calling .toSafeArray().toByteArray() on it. For example:

    File inputFile = new File("c:\\test\\input.doc");
    FileInputStream inputFileStream = new FileInputStream(inputFile.getCanonicalPath());
    byte[] inputStream = new byte[(int)inputFile.length()];
    inputFileStream.read(inputStream);
    byte[] outputStream = printjob.PrintOut3(inputStream, ".xls").toSafeArray().toByteArray();
    

    Note that for the Variant class you need to import com.jacob.com.*.

    Here is a list of functions that return streams: IPrintJob.PrintOut2, IPrintJob.PrintOut3, IPDFProcessor.MergeMem, IPDFProcessor.MergeBatchMem, IPDFProcessorHandle.CloseMem, IPDFProcessorHandle.CloseMemEncrypt, IPDFProcessorHandle.CloseMemDecrypt.

  5. Functions that return multiple output values always use Variant parameters.

    Parameters receiving an output from a function are called [out] parameters in COM.

    Variant is a JACOB-specific class designed as a wrapper around COM Variants, which can handle [out] parameters as well. For example, IPDFProcessor.GetPageSizeVar returns both the page width and height at the same time:

    Variant varWidth = new Variant(new Double(0.0), true);
    Variant varHeight = new Variant(new Double(0.0), true);
    processor.GetPageSizeVar(inputFileName1, 0, varWidth, varHeight);
    System.out.println(varWidth.getDouble());
    System.out.println(varHeight.getDouble());
    

    First the Variant object must be created and initialized with the appropriate type. Since GetPageSizeVar returns Double, we are passing a new Double variable to Variant. The second parameter to the Variant constructor should be true, indicating a by-reference type of variant, which is necessary for [out] parameters. After GetPageSizeVar succeeds, the getDouble() method is used to convert the variant into a Double.

    If an [out] parameter returns a String, then you have to pass a new String to the constructor of Variant, and use getString() to convert the result into a String.

    If an [out] parameter returns an integer, then you have to pass a new Integer to the constructor of Variant, and use getInt() to convert the result into an integer.

    Note that for the Variant class you need to import com.jacob.com.*.

    Here is a list of functions with [out] parameters: IPDFProcessor.GetVersionNumber, IPDFProcessor.GetDocumentInfo, IPDFProcessor.GetPageSizeVar, IPDFProcessor.GetDigitalSignatureInformation, IPDFProcessorHandle.GetDocumentInfo, IPDFProcessorHandle.GetPageSizeVar.