Skip to Content

Can APDFL and Modern C++ Run in the Same Program?

Estimated Reading Time: 8 Minutes
Overview

Yes. The Adobe PDF Library (APDFL) Adobe C/C++ interface and the Modern C++ interface can be used in the same application. Datalogics Engineering has confirmed that this use is expected to work. This allows applications to retain existing Adobe C/C++ workflows while introducing Modern C++ for new or migrated functionality.

Using both interfaces together does not provide direct object interoperability. Objects created through one interface are not automatically available through the other. To transfer document content between interfaces, save the document and reopen it through the other interface.

The investigation described below was performed on Windows x64 only, using Modern C++ SDK 21.5.0 and APDFL 21.0.0P1f. Other platforms and toolchains were not tested.

 

What Did the Investigation Demonstrate?

The test program successfully used both interfaces with either initialization order, with documents open through both interfaces at the same time, and with calls interleaved between them.

The investigation reported twelve successful runs covering six execution modes and two header inclusion orders:

  • Both interfaces worked independently as baseline tests.
  • Including the Modern C++ headers before or after the Adobe C/C++ headers compiled successfully.
  • Initializing either interface first allowed both interfaces to operate.
  • Destroying the Modern C++ Library left an existing legacy PDDoc usable.
  • Calling PDFLTermHFT() left an existing Modern C++ document usable while its Library remained active.
  • Legacy operations on the main thread and Modern C++ operations on a worker thread completed 200 iterations each using separate documents.

These tests covered the configurations and operations described in this article. The observed initialization and termination behavior should not be treated as a guarantee for every configuration.

 

Why Do the Interfaces Coexist?

The Modern C++ interface provides an object-oriented interface to the same underlying APDFL engine used by Adobe C/C++. It does not introduce a separate PDF engine.

In the tested Windows configuration, datalogics_interface_api.dll depends on DL210pdfl.dll. The legacy application also accesses that core library through its Host Function Table (HFT) initialization.

Each interface maintains its own client-side initialization state while accessing the shared core. The investigation attributed the successful teardown tests to reference-counted initialization, allowing the core to remain available while the other interface still required it.

Applications should continue to balance initialization and termination and release each interface’s documents before terminating that interface.

 

What Can Be Shared Between the Interfaces?

The interfaces can operate in the same process, but they do not currently provide direct interoperability between their API objects.

For example, creating an annotation through Modern C++ does not provide a corresponding Adobe C/C++ object handle that the application can immediately use. Likewise, the tested Modern C++ public API does not expose or accept legacy PDDoc, CosDoc, or CosObj handles.

To work with the resulting content through the other interface:

  1. Save the document using the interface that modified it.
  2. Open the saved document through the other interface.
  3. Access the content using that interface’s API.

A file provides a straightforward transfer method. Memory-based serialization can also avoid a temporary file when implemented using the appropriate APIs on both sides.

 

Set Up the Test Program on Windows

The complete mixtest.cpp uses both SDK interfaces in one C++ executable. It is not intended to build against either SDK alone.

The investigation used the following configuration:

Component Tested configuration
Modern C++ SDK 21.5.0
Adobe C/C++ SDK APDFL 21.0.0P1f
APDFL core DLL DL210pdfl.dll, file version 21.0.0.5
Compiler Visual Studio 2022, MSVC 14.44.35207
Compiler options /MD /EHsc /std:c++17

A supplied Modern C++ sample project is a useful starting point because it already configures the Modern C++ compiler settings and dependencies. Add the Adobe C/C++ headers, initialization sources, import library, and required preprocessor definitions to that project.

An existing Adobe C/C++ application can also serve as the starting project by adding the Modern C++ headers, import library, and deployment dependencies.

To build the complete test program directly, follow the steps below.

1. Update the Paths in mixtest.cpp

The supplied source contains paths specific to the investigation machine. Update these constants for your installation:

Constant Required location
kPdf An existing input PDF
kLib The directory containing the runtime binaries
kFontDir The Resources/Font directory
kCMapDir The Resources/CMap directory
kUniDir The Resources/Unicode directory

Use a one-page PDF for the threads test. That mode checks that the Modern C++ page count equals one.

2. Configure the Build Environment

Open an x64 Native Tools Command Prompt for Visual Studio 2022. The following commands use Command Prompt syntax.

Set the SDK locations, adjusting these example paths:

set "APDFL=C:\Datalogics\APDFL21.0.0P1f_x64"
set "MCPP=C:\Datalogics\ModernCppSDK-21.5.0"

Change to the directory containing mixtest.cpp.

The build uses:

  • Adobe C/C++ headers from %APDFL%\CPlusPlus\Include\Headers.
  • Modern C++ headers from %MCPP%\include.
  • Legacy initialization sources PDFLInitCommon.c and PDFLInitHFT.c.
  • Import libraries DL210PDFL.lib and datalogics_interface_api.lib.

Ensure that the Visual Studio C++ tools and a compatible Windows SDK are installed and available in the command prompt.

3. Build the Executable

The investigation used the following build command:

cl /nologo /EHsc /MD /std:c++17 /Fe:mixtest.exe ^
   /D_CRT_SECURE_NO_WARNINGS /D_CONSOLE /DWIN64 /DWIN_ENV /DWIN_PLATFORM ^
   /DPRODUCT=\"HFTLibrary.h\" ^
   /DPI_ACROCOLOR_VERSION=AcroColorHFT_VERSION_6 ^
   /I"%APDFL%\CPlusPlus\Include\Headers" /I"%MCPP%\include" ^
   mixtest.cpp ^
   "%APDFL%\CPlusPlus\Include\Source\PDFLInitCommon.c" ^
   "%APDFL%\CPlusPlus\Include\Source\PDFLInitHFT.c" ^
   /link ^
   /LIBPATH:"%APDFL%\CPlusPlus\Binaries" ^
   /LIBPATH:"%MCPP%\lib" ^
   DL210PDFL.lib datalogics_interface_api.lib

This builds the Release-library configuration with the legacy headers included first.

To test the reverse header order, repeat the command with /DMODERN_FIRST and change the output name to /Fe:mixtest_mf.exe.

For a Visual Studio project, carry over the corresponding settings. Use the appropriate Modern C++ import library for the selected configuration; the Debug library is datalogics_interface_apid.lib.

4. Configure Runtime Discovery and Run

Make the matching runtime binaries available before starting the executable. For the directory layout above:

set "PATH=%MCPP%\lib;%PATH%"

Ensure that kLib points to the intended runtime directory. Also check for another copy of DL210pdfl.dll beside the executable, which could cause a different core to load.

Deploy a consistent set of core binaries, plug-ins, and resources rather than relying on components from different SDK installations.

5. Run the Tests

Run each mode separately:

mixtest.exe legacy
mixtest.exe modern
mixtest.exe legacy-first
mixtest.exe modern-first
mixtest.exe threads
mixtest.exe longjmp
Mode Purpose
legacy Exercises Adobe C/C++ alone
modern Exercises Modern C++ alone
legacy-first Initializes legacy first, interleaves calls, and checks legacy use after Modern C++ teardown
modern-first Initializes Modern C++ first and checks modern use after legacy termination
threads Runs legacy and modern operations concurrently on separate documents
longjmp Examines destructor behavior during legacy error handling on the tested toolchain

Repeat the commands with mixtest_mf.exe to test the alternate header order.

Review the console output as well as the exit status. Some test helpers report errors without propagating a failing process exit code. The program also reports loaded DLL paths, which help identify unintended runtime selection.

 

Code Example

The following excerpt illustrates the legacy-first workflow. It uses the legacy_* helpers and kPdf definition from the complete mixtest.cpp; the excerpt is not a standalone source file.

Both interfaces open their own document objects. The example demonstrates coexistence without passing document handles between interfaces.

#include "PDFLCalls.h"
#include "ASCalls.h"
#include "PDCalls.h"
#include "CosCalls.h"
#include <datalogics_interface/datalogics_interface.hpp>

#include <exception>
#include <iostream>

namespace dl = datalogics_interface;

int main()
{
    if (legacy_init() != 0)
        return 1;

    legacy_open();
    int result = 0;

    try
    {
        dl::Library library;
        dl::Document document(kPdf);

        std::cout << "Modern C++ pages: "
                  << document.get_num_pages() << '\n';

        legacy_use("while-modern-live");

        std::cout << "Modern C++ pages after legacy call: "
                  << document.get_num_pages() << '\n';
    }
    catch (const std::exception& error)
    {
        std::cerr << error.what() << '\n';
        result = 1;
    }

    // Modern C++ objects have left scope.
    // The legacy document remains available.
    legacy_use("after-modern-teardown");

    legacy_close();
    legacy_term();
    return result;
}

The complete test program contains the resource configuration, legacy initialization and error-handling helpers, and all six test modes.

 

Additional Integration Considerations

Verify the loaded runtime. In the tested configuration, both interfaces used the same loaded DL210pdfl.dll. Linking against two SDK directories does not establish a separate core for each interface. Check the resolved DLL paths when diagnosing unexpected behavior.

Keep error handling within the appropriate interface. Legacy calls use DURING / HANDLER, while Modern C++ uses C++ exceptions. The longjmp test observed C++ destructors running under MSVC x64 with /EHsc. Treat this as a toolchain-specific observation. Avoid constructing objects with nontrivial destructors inside legacy error blocks, and do not rely on that cleanup behavior in portable code.

Follow each interface’s threading rules. Each thread using Modern C++ requires its own Library, which must outlive the objects it supports. The concurrency test used separate documents on separate threads; it did not validate sharing document objects between threads or interfaces.

 

How to Get Additional Help

Contact Datalogics Tech Support at tech_support@datalogics.com.

For broader migration guidance, see Migrating from Adobe C/C++ to the APDFL Modern C++ SDK.

Can APDFL and Modern C++ Run in the Same Program?
  • Attached Files
  • mixtest.cpp (8.43 KB) 4
  • COMMENT