A look at ImageGear C++ Samples


03/07/2017

Paul Pauquette, Accusoft Senior Software Engineer

Welcome

You've downloaded the ImageGear imaging SDK and you're trying to learn what you can do with it . What better place to start than in our samples? In ImageGear for C and C++ v19, we've added new easier-to-use samples that demonstrate a specific subset of functionality.

In addition to our Legacy samples, which you will find aptly enough in a folder named "Legacy," you will see a VS2010 and VS2015 folder with samples for each environment. However, these samples should be able to easily open and convert to any version of Visual Studio between 2010 and 2015. As you can see below, you should be able to find the topic that interests you, everything from Annotations and CAD to PDF and Processing.

imagegear c++ samples

In this article we'll be examining the structure of our new samples using the SavePDFtoRasterImage sample in the PDF folder.

save pdf to raster image

SavePDFtoRasterImage

The SavePDFtoRasterImage sample is a fairly straightforward sample that shows how to save the first page of a PDF document to several different types of raster images. Most of these new samples follow a fairly standard format with minimal error processing to get to the meat of the functionality. The exceptions to this practice are the samples in the ErrorHandling folder, which contains a more detailed explanation of how to handle errors that can occur during processing.

Let's dig in!

One of the first things you notice when you open up the SavePDFtoRasteImage sample is the Solution Explorer.

solution explorer

You'll see one C++ file that has the same name as the sample. In this example, that's the SavePDFtoRasterImage.cpp file. This file contains all of the functionality of the sample. You'll also notice two filters, "ImageGear 32-bit files" and "ImageGear 64-bit files." If you expand these filters, you'll see the libraries that are needed to build for 32-bit or 64-bit; the only thing that is different is the location to the 32-bit and 64-bit versions. Each solution platform includes the correct version for that platform and excludes the other.

expand solution explorer

Each sample lets you build 32-bit and 64-bit Debug and Release to the "ImageGear for C and C++ v19\Build\Bin\x86" and "ImageGear for C and C++ v19\Build\Bin\x64" folders, respectively. Now that we have the scaffolding set up, let's start drilling down into the sample.

Top

Each sample's C++ file starts with the comment section that shows the name of the file, a copyright statement, the required headers for the sample, a namespace identifier, and then finally a comment section that describes the purpose of this sample. You'll notice in the headers that they are generally split into two sections: the first section includes standard common header files, and the second section includes the ImageGear specific header files. In this case this includes the standard "gear.h" that is required for all ImageGear samples and then the "i_PDF.h" header file for the PDF component. The "std" namespace is set mainly for the use of the string class.

// SavePDFtoRasterImage.cpp : Defines the entry point for the console application.
//
// Copyright Accusoft Corporation, Tampa Florida. All Rights Reserved.

#include 
#include 
#include 
#include 
#include 

#include "gear.h"
#include "i_PDF.h"

using namespace std;

// This sample illustrates how to read the first page of a PDF
// document and save as a raster image.
// Note: The samples in the ErrorHandling folder demonstrate error handling.
Initialize and Terminate

Some of the samples, including this one, have Initialize and Terminate methods that show what additional components need to be attached and/or initialized. You may also see in this section additional settings that may need to be set for the sample, like setting up different filter properties. The SavePDFtoRasterImage sample attaches and initializes the PDF component with the Initialize method. In the Terminate method, the PDF component needs to be cleaned up by calling the IG_PDF_terminate function. If you read the ImageGear help for the IG_PDF_initialize function, you will see that IG_PDF_initialize and IG_PDF_terminate should be called once for each thread of a multi-threaded application. Since this sample is a single-threaded sample, we do it once at the start and end of the sample, respectively.

bool Initialize( )
{
    // Attach and initialize the PDF component.
    if ((IG_comm_comp_attach("PDF") != IGE_SUCCESS) || (IG_PDF_initialize(NULL) != IGE_SUCCESS))
    {
        // This error usually occurs because a DLL or the Resource directory cannot be found.
        cout << "PDF component error." << endl;
        return false;
    }
    return true;
}

void Terminate( )
{
    // Terminate the PDF component.
    IG_PDF_terminate();
}

The next section usually contains one or more methods that are the meat of the sample. These are methods that will perform the behavior we're demonstrating. In this example, it is the SavePdfToRasterImages method that does the conversion from a vector PDF page to the multiple saves to different raster image formats. We'll skip over this method for now and come back to analyze it later. Let's fast-forward to the main function and some helper methods.

Simple File Checking

As you see, there really isn't a lot of error checking in this sample. However, we do want to make sure that the file to process exists and is valid for this sample. In this case, we check that it is a PDF file. We do that by loading in the file and the making sure the image handle we have is valid using the IG_image_is_valid function. Then we check to make sure it is a PDF with the built-in IG_image_is_PDF function.

static bool FileExists(const string& filePath)
{
    struct stat buffer;
    return (stat(filePath.c_str(), &buffer) == 0);
}

static bool FileIsPDF(const string& filePath)
{
    bool isPDF = false;
    HIGEAR image = 0;
    IG_load_file((LPSTR)filePath.c_str(), &image);
    if (IG_image_is_valid(image))
    {
        isPDF = (IG_image_is_PDF(image) != FALSE);
        IG_image_delete(image);
        image = 0;
    }
    return isPDF;
}
Main

You'll note in the "main" function below that the first thing we do again is to output what this sample is demonstrating to the screen. After that, there is usually some simple argument parsing to get the name of a file to process in the sample. Each sample is designed to work with a default image we provide so that you already have what you need to begin stepping into the sample.

int main(int argc, char* argv[])
{
    string fileIn;

    cout << "This sample demonstrates loading the first page from a PDF file," << endl
        << "rasterizing the page, and saving to various raster formats." << endl;

    // Get the file to process.
    if (argc > 1)
    {
        fileIn = argv[1];
    }
    else
    {
        // Use a default image shipped with ImageGear.
        string defaultImage = "\\Documents\\Accusoft\\Common\\Images\\single-page.pdf";
        size_t len = 0;
        char documentDir[MAX_PATH + 1];
        getenv_s(&len, documentDir, sizeof(documentDir), "PUBLIC");
        fileIn = documentDir + defaultImage;
    }

    if (FileExists(fileIn) == false)
    {
        cout << "File '" << fileIn << "' does not exist." << endl;
        return EXIT_FAILURE;
    }

    if (Initialize( ) == false)
    {
        cout << "Unable to initialize the PDF library." << endl;
        return EXIT_FAILURE;
    }

    if (FileIsPDF(fileIn))
    {
        // Process and save the file.
        SavePdfToRasterImages(fileIn);
    }
    else
    {
        cout << "File '" << fileIn << "' is not a PDF." << endl;
        Terminate();
        return EXIT_FAILURE;
    }

    Terminate();
    return EXIT_SUCCESS;
}

After the input file is checked to make sure it exists, we call the previously shown Initialize() method to initialize the PDF component. Once ImageGear's PDF component is initialized, we call the FileIsPDF(...) check to make sure the file is a valid PDF, and if it is, we call the SavePdfToRasterImage(...) method to actually do the work that the sample demonstrates. Finally, no matter if we exit successfully or not, we call the Terminate method if the Initialize method was called.

SavePdfToRasterImage

The SavePdfToRasterImage method is where the real work of this sample occurs. Below is the full function. We'll break this apart into specific sections next.

void SavePdfToRasterImages(const string& fileIn)
{
    // Load the multi-page document.
    HMIGEAR document = 0;
    IG_mpi_create(&document, 0);
    IG_mpi_file_open((LPSTR)fileIn.c_str(), document, IG_FORMAT_PDF, IG_MP_OPENMODE_READONLY);

    // Get a handle to the first page.
    const UINT firstPage = 0;
    HIGEAR page = 0;
    IG_mpi_page_get(document, firstPage, &page);

    // Set the resolution of the PDF filter to 100 dots per inch.
    // This specifies the resolution used in the conversion to a raster image.
    AT_UINT resolution = 100;
    IG_fltr_ctrl_set(IG_FORMAT_PDF, "RESOLUTION_X", (LPVOID)resolution, sizeof(resolution));
    IG_fltr_ctrl_set(IG_FORMAT_PDF, "RESOLUTION_Y", (LPVOID)resolution, sizeof(resolution));

    // Rasterize the loaded page.
    HIGEAR rasterPage = 0;
    IG_vector_data_to_dib(page, &rasterPage);

    // Inform the user that the output will be located in the current working directory.
    char currentDir[MAX_PATH + 1];
    _getcwd(currentDir, sizeof(currentDir));
    cout << "The raster output images will be placed in the folder: " << currentDir << endl;

    // Save the loaded page as a raster image.
    IG_fltr_save_file(rasterPage, "PdfToRaster.bmp", IG_SAVE_BMP_UNCOMP, firstPage, TRUE);

    // If the FormatType is set to IG_SAVE_UNKNOWN, ImageGear will check the file's
    // extension and save the image accordingly.
    IG_fltr_save_file(rasterPage, "PdfToRaster.png", IG_SAVE_UNKNOWN, firstPage, TRUE);
    IG_fltr_save_file(rasterPage, "PdfToRaster.jpg", IG_SAVE_UNKNOWN, firstPage, TRUE);

    // Cleanup the raster page.
    IG_image_delete(rasterPage);

    // Cleanup the document and its pages.
    IG_mpi_delete(document);
}
>Load the multi-page document

The first thing that happens is that we need to load the multi-page document. This is done with the IG_mpi_create function to create and initialize a multi-page image. Then the IG_mpi_file_open function is called to associate the external PDF file with the multi-page image. Once the multi-page image is initialized and opened, we can get a handle to the first page using the IG_mpi_page_get function. We now have a handle to the first page of a PDF document in the HIGEAR page handle.

   // Load the multi-page document.
    HMIGEAR document = 0;
    IG_mpi_create(&document, 0);
    IG_mpi_file_open((LPSTR)fileIn.c_str(), document, IG_FORMAT_PDF, IG_MP_OPENMODE_READONLY);

    // Get a handle to the first page.
    const UINT firstPage = 0;
    HIGEAR page = 0;
    IG_mpi_page_get(document, firstPage, &page);
Vector to Raster

The HIGEAR page object is now a PDF vector image. Since we want to go to a raster image for this sample, we need to convert from a vector image to a raster image using the function IG_vector_data_to_dib. Before we do that, we want to specify what the resolution of the raster image should be. We use the IG_fltr_ctrl_set function and specify the RESOLUTION_X and RESOLUTION_Y values when converting from vector to raster for the PDF format. Then we're ready to convert to a raster format with IG_vector_data_to_dib to get the new HIGEAR rasterPage image.

// Set the resolution of the PDF filter to 100 dots per inch.
    // This specifies the resolution used in the conversion to a raster image.
    AT_UINT resolution = 100;
    IG_fltr_ctrl_set(IG_FORMAT_PDF, "RESOLUTION_X", (LPVOID)resolution, sizeof(resolution));
    IG_fltr_ctrl_set(IG_FORMAT_PDF, "RESOLUTION_Y", (LPVOID)resolution, sizeof(resolution));

    // Rasterize the loaded page.
    HIGEAR rasterPage = 0;
    IG_vector_data_to_dib(page, &rasterPage);
Saving

At this point we have a raster image in memory and we want to save it to file in a couple of different formats to show how that it is possible. The first thing this sample, as well as the other new samples, does is to let you know where the output image will be located. If you're running this from Visual Studio, as we expect you will most likely do, the output will be in the sample's project folder. If you run this sample directly from the x86 or x64 directory in the "ImageGear for C and C++ v19\Build\Bin" directory, then the output files will be placed in that x86 or x64 folder.

  // Inform the user that the output will be located in the current working directory.
    char currentDir[MAX_PATH + 1];
    _getcwd(currentDir, sizeof(currentDir));
    cout << "The raster output images will be placed in the folder: " << currentDir << endl;

    // Save the loaded page as a raster image.
    IG_fltr_save_file(rasterPage, "PdfToRaster.bmp", IG_SAVE_BMP_UNCOMP, firstPage, TRUE);

    // If the FormatType is set to IG_SAVE_UNKNOWN, ImageGear will check the file's
    // extension and save the image accordingly.
    IG_fltr_save_file(rasterPage, "PdfToRaster.png", IG_SAVE_UNKNOWN, firstPage, TRUE);
    IG_fltr_save_file(rasterPage, "PdfToRaster.jpg", IG_SAVE_UNKNOWN, firstPage, TRUE);

At this point we have a raster image loaded in the rasterPage handle, so we can call the IG_fltr_save_file to save to the desired file format. You'll notice in the first example that when we save to BMP, we actually set the format type, whereas when we save to PNG and JPEG we set the format type to IG_SAVE_UNKNOWN, and the save routine uses the extension to determine the format type. Now we could've used the IG_save_file function in this method, but we chose to use the IG_fltr_save_file function, since it is an extended version of the function. We did this to make it easy for you to modify the sample to save to other file formats that support multiple pages. This sample also does not show some of the additional parameters you can set for these file formats. To learn more about the filter parameters, perhaps one that allows a thumbnail image for a JPEG for instance, then I suggest you look at the FormatConversionAndCompression sample in the Conversion folder, which shows you how to set some of those filter parameters.

Clean-up

And now that we have processed the PDF page and saved the raster image, it is important to clean up so that no leaks occur.

 // Cleanup the raster page.
    IG_image_delete(rasterPage);

    // Cleanup the document and its pages.
    IG_mpi_delete(document);

We delete the raster image with a call to the IG_image_delete function. Then we delete the PDF document and its corresponding pages with an IG_mpi_delete on the PDF document. Since the HIGEAR page variable is a handle to a page in the PDF document, there is no need to delete it. When you delete the PDF document, any pages will be cleaned-up as well.

Conclusion

We hope you've seen how easy it is to navigate one of the new samples that are provided in ImageGear for C and C++ v19. Most of the samples follow this pattern, though the complexity varies depending on their functionality. Each sample will help to show you:

  • What components you need to attach and initialize for the demonstrated functionality
  • Which operations are necessary to perform the desired task
  • The proper way to create, load, save, and clean-up images.

If you have a question about one of the samples or have extended a sample, but it's not doing what you want, then please contact support to help you on your way. Also if you have an idea for a new sample, you can contact support so that we may provide this in a future release of ImageGear for C and C++.


Paul Pauquette is a Senior Software Engineer in the SDK division. He joined the company in 1998 and has over 20 years of experience in software development. He has worked on many of the different SDKs provided by Accusoft.

Join the discussion.