
/*
 *  This file was generated by the SOM Compiler.
 *  Generated using:
 *     SOM incremental update: 2.24
 */

//********************************************************************************
//
// Lucide GBM plug-in
//
// Copyright (C) 2006-2008 Heiko Nitzsche
//
//   This software is provided 'as-is', without any express or implied
//   warranty.  In no event will the author be held liable for any damages
//   arising from the use of this software.
//
//   Permission is granted to anyone to use this software for any purpose,
//   including commercial applications, and to alter it and redistribute it
//   freely, subject to the following restrictions:
//
//   1. The origin of this software must not be misrepresented; you must not
//      claim that you wrote the original software. If you use this software
//      in a product, an acknowledgment in the product documentation would be
//      appreciated but is not required.
//   2. Altered source versions must be plainly marked as such, and must not be
//      misrepresented as being the original software.
//   3. This notice may not be removed or altered from any source distribution.
//
//********************************************************************************
//
// Supported features:
// ===================
// - document info
// - loading all formats supported by GBM
// - save loaded bitmap in any GBM supported format if it has only one page
//   (GBM does not support multipage save)
// - multipage bitmap viewing support
// - load bitmap data on demand (requested by the user)
// - scaled  rendering to a buffer
// - rotated rendering to a buffer
//
// Requires: GBM.DLL version 1.35 or higher (with multipage support)
//
//********************************************************************************
//
// History: 27-Jun-2006: Initial public beta
//          30-Jun-2006: Public beta 2
//                       Fixes for the most urgent issues
//                       - file extension list too long for file dialog (only keep the first extension)
//                       - Add error message reporting to load_file())
//          16-Jul-2006: Version 1.00 final
//                       - Added document info
//                       - Added rotated rendering
//                       - Added delayed loading of bitmap data (on user request)
//                       - Support transparent caching for fast scrolling of a scaled view
//          31-Jul-2006: Version 1.10
//                       - Save loaded bitmap in any GBM supported format if it has only one page
//                         (GBM does not support multipage save)
//                       - Remove code for rendering to a buffer, rendering is now always via HPS
//                       - Sync to latest common code shared with the Mozilla plugin
//                         updated common code
//                       - Now supports dynamic lookup of GBM.DLL either via LIBPATH
//                         or look into the directory where lugbm.dll is located.
//          16-Aug-2006  Version 1.20
//                       - Adapt makefiles to use now the patched SOM header files
//                         part of the Lucide plugindev pack.
//                       - Fix for issue that GBM.DLL is not found when LIBPATHSTRICT (or RUN!)
//                         is used. Thanks to Rich Walsh for the background info about the root cause.
//                       - Add bldlevel info
//          31-Aug-2006  Version 1.21
//                       - When reporting an extended error message after a loading error
//                         sprintf reads from an illegal address due to wrong parameter
//                         (int instead char pointer)
//          08-Sep-2006  Version 1.22
//                       - 1bpp bitmaps were shown inverted (colors swapped)
//          12-Nov-2006  Version 1.23
//                       - Improved detection of bitmap file formats
//                       - Adapt loadFile() and renderToPS() for Lucide 1.0 API
//                         (error reporting added to the API)
//          20-Jan-2007  Version 1.24
//                       - Protect getPageSize() against writing to width or height
//                         for the unusual case they're NULL.
//                       - Don't write to error/errorCode pointer if it is NULL.
//                         This is a bit strange as one would expect that they
//                         would be always valid.
//          06-Sep-2007  Version 1.30
//                       - Add support for Lucide 1.02 API extension isFileSupported()
//                         to allow file format detection via file content
//                       - Add support for creating thumbnails in EAs
//                       - Report all available extensions again as the issue with
//                         Lucide file dialog has been resolved since Lucide 1.10.
//                         So this plugin version should not be used with older Lucide!
//                       - 8bpp grayscale and true color images are now scaled with
//                         a high quality algorithm to improve appearance.
//          19-Sep-2007  Version 1.31
//                       - Fixed resampling scaler rounding issues
//          27-Jan-2008  Version 1.32
//                       - Resampling scaler now supports all grayscale bitmaps <=8bpp
//          08-Feb-2008  Version 1.33
//                       - Move HPS rendering code to common package GbmRenderer
//                         to save memory (now renders directly from cache buffer)
//                         without temporary subrectangle buffer
//                       - Allocate memory from high memory for bitmap data to
//                         stretch limit for out-of-memory errors
//                         (requires kernel with high memory support)
//                       - Custom settings definable in npgbm.cfg file
//                         (so far scaler={simple,nearestneighbor,bilinear,bell,bspline,mitchell,lanczos})
//********************************************************************************

#ifdef __IBMCPP__
  #pragma strings( readonly )
#endif

#include "lugbm.hpp"

#ifndef SOM_Module_lugbm_Source
#define SOM_Module_lugbm_Source
#endif
#define LuGbmDocument_Class_Source

#include "lugbm.xih"

#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "os2defs.h"
#include "WGbmDocument.hpp"
#include "gbm.h"

// ---------------------------------------------------------

#define PLUGIN_VERSION    "1.33"

// ---------------------------------------------------------

static HMODULE DLLInstance;
static char * supportedExtensions    = NULL;
static char * pluginDescription      = NULL;
static GbmAccessor * pGbmAccessor    = NULL;
static char fullCfgPath[_MAX_PATH+1] = { 0 };

// ---------------------------------------------------------

// Init / Terminate DLL

static unsigned libInitialize(unsigned termination)
{
    if ( termination )
    {
        delete pGbmAccessor;
        pGbmAccessor = NULL;

        delete [] pluginDescription;
        pluginDescription = NULL;

        /* DLL is detaching from process */
        free(supportedExtensions);
        supportedExtensions = NULL;

        memset(fullCfgPath, 0, sizeof(fullCfgPath));
    }
    else
    {
        /* DLL is attaching to process */

        char fullDllName[_MAX_PATH+1] = { 0 };
        if (DosQueryModuleName(DLLInstance, sizeof(fullDllName)-1, fullDllName) != 0)
        {
            return 0;
        }
        // extract drive:path
        char drive[_MAX_DRIVE+1] = { 0 };
        char dir[_MAX_DIR+1]     = { 0 };

        _splitpath(fullDllName, drive, dir, NULL       , NULL);
        _makepath (fullDllName, drive, dir, "GBM.DLL"  , NULL);
        _makepath (fullCfgPath, drive, dir, "LUGBM.CFG", NULL);

        // try to load via LIBPATH lookup
        try
        {
            pGbmAccessor = new GbmAccessor();
        }
        catch(...)
        {
        }
        if (pGbmAccessor == NULL)
        {
            // try to load via absolute path of LUGBM.DLL (plugin DLL)
            try
            {
                pGbmAccessor = new GbmAccessor(fullDllName);
            }
            catch(...)
            {
            }
            if (pGbmAccessor == NULL)
            {
                return 0;
            }
        }

        /* get all supported extensions from GBM */

        int n_ft = 0;
        pGbmAccessor->Gbm_query_n_filetypes(&n_ft);

        // we assume here that none of the import filters has more than 10 extensions with 3 chars
        int extensions_length = 10 * 3 * n_ft + 1;

        supportedExtensions = (char *) malloc(extensions_length);
        if (supportedExtensions == NULL)
        {
            delete pGbmAccessor;
            pGbmAccessor = NULL;
            return 0;
        }
        strcpy(supportedExtensions, "");

        for (int ft = 0; ft < n_ft; ft++)
        {
            GBMFT gbmft;

            pGbmAccessor->Gbm_query_filetype(ft, &gbmft);

            // get all extensions
            char * extTok = strtok(gbmft.extensions, " ");

#if 0 // Limit the number of reported extension to only the first one (most often used one)
      // until the file dialog extension list issue with the limited filename entry field
      // length is solved.

            if (extTok != NULL)
            {
                BOOLEAN tokenAdded = FALSE;

                // ignore IAX as for it the user would have to specify the size explicitly (no header))
                if (strcmp(extTok, "IAX") != 0)
                {
                    strcat(supportedExtensions, extTok);
                    tokenAdded = TRUE;
                }
                if ((ft + 1 < n_ft) && tokenAdded)
                {
                    strcat(supportedExtensions, ";");
                }
            }
#else
            while (extTok != NULL)
            {
                BOOLEAN tokenAdded = FALSE;

                if (strlen(supportedExtensions) + strlen(extTok) + 1 > extensions_length)
                {
                    // resize buffer
                    extensions_length += 10;
                    supportedExtensions = (char *) realloc(supportedExtensions, extensions_length);
                    if (supportedExtensions == NULL)
                    {
                        delete pGbmAccessor;
                        pGbmAccessor = NULL;
                        return 0;
                    }
                }
                // ignore IAX as for it the user would have to specify the size explicitly (no header))
                if (strcmp(extTok, "IAX") != 0)
                {
                    strcat(supportedExtensions, extTok);
                    tokenAdded = TRUE;
                }
                extTok = strtok(NULL, " ");
                if (tokenAdded)
                {
                    if (ft + 1 < n_ft)
                    {
                        strcat(supportedExtensions, ";");
                    }
                    else
                    {
                        if (extTok != NULL)
                        {
                            strcat(supportedExtensions, ";");
                        }
                    }
                }
            }
#endif
        }

        // create the plugin description string
        const char * descFormat = "GBM plugin ("PLUGIN_VERSION"), based on Generalised Bitmap Module (GBM.DLL: %.2f)";

        pluginDescription = new char[strlen(descFormat) + 20 + 1];
        sprintf(pluginDescription, descFormat, ((float)pGbmAccessor->Gbm_version())/100.0);
    }

    return( 1 );
}

// ---------------------------------------------------------

#ifdef __IBMCPP__

/* _CRT_init is the C run-time environment initialization function.         */
/* It will return 0 to indicate success and -1 to indicate failure.         */
extern "C" int _CRT_init(void);

/* _CRT_term is the C run-time environment termination function.            */
/* It only needs to be called when the C run-time functions are statically  */
/* linked.                                                                  */
extern "C" void _CRT_term(void);

/* __ctordtorInit is the C++ run-time environment initialization function.  */
/* It is required to initialize static objects and destructors              */
extern "C" void __ctordtorInit(void);

/* __ctordtorTerm is the C++ run-time environment termination function.  */
/* It is required to destroy static objects and destructors              */
extern "C" void __ctordtorTerm(void);

/* _DLL_InitTerm() - called by the loader for DLL initialization/termination  */
/* This function must return a non-zero value if successful and a zero value  */
/* if unsuccessful.                                                           */

extern "C" unsigned long _System _DLL_InitTerm( unsigned long hModule, unsigned long termination )
{
   DLLInstance = (HMODULE) hModule;

   switch(termination)
   {
       case 0:
           if (_CRT_init() == -1)
           {
               return 0;
           }
           __ctordtorInit();

         // init our DLL
         if (libInitialize(0) == 0)
         {
             return 0;
         }
         break;

      case 1:
         // terminate our DLL
         if (libInitialize(1) == 0)
         {
             return 0;
         }

         __ctordtorTerm();
         _CRT_term();
         break;

      default:
         return 0;
   }

   /* Indicate success.  Non-zero means success!!! */
   return 1;
}

#endif // ifdef __IBMCPP__

// -------------------------------------------------------
// -------------------------------------------------------

#ifdef __WATCOM_CPLUSPLUS__

extern "C" unsigned _System LibMain( unsigned hmod, unsigned termination )
{
   DLLInstance = (HMODULE) hmod;

    if ( termination )
    {
        // terminate our DLL
        if (libInitialize(1) == 0)
        {
            return 0;
        }
    }
    else
    {
        /* DLL is attaching to process */

        // init our DLL
        if (libInitialize(0) == 0)
        {
            return 0;
        }
    }

    return( 1 );
}
#endif // ifdef __WATCOM_CPLUSPLUS__

// ---------------------------------------------------------

extern "C" LuDocument * _System createObject()
{
    return new LuGbmDocument();
}

// ---------------------------------------------------------

extern "C" char * _System getSupportedExtensions()
{
    return supportedExtensions;
}

// ---------------------------------------------------------

extern "C" char * _System getDescription()
{
    return pluginDescription;
}

// ---------------------------------------------------------

extern "C" BOOL _System isFileSupported(PCSZ filename)
{
    WGbmDocument *wGbmDoc = NULL;
    try
    {
        wGbmDoc = new WGbmDocument(*pGbmAccessor);
    }
    catch(...)
    {
    }
    if (wGbmDoc == NULL)
    {
        return FALSE;
    }

    const GbmDocument & gbmDocument(wGbmDoc->getGbmDocument());

    // check the file content rather than the extension only
    const BOOL isSupportedFmt = gbmDocument.isSupportedFormat(filename, NULL)
                                ? TRUE
                                : FALSE;
    delete wGbmDoc;
    wGbmDoc = NULL;

    return isSupportedFmt;
}

// ---------------------------------------------------------
// ---------------------------------------------------------

// Sets the error code to the provided pointer if the
// pointer is valid.
static void setErrorCode(const long  errorCodeToSet,
                               long *errorCode)
{
    if (errorCode != NULL)
    {
        *errorCode = errorCodeToSet;
    }
}

// ---------------------------------------------------------

static void createErrorMessage(const GbmAccessor  & gbmAccessor,
                               const GbmException & ex,
                               string             * error)
{
    if (error != NULL)
    {
        // extract error message
        const char * exMessage       = ex.getErrorMessage();
        const int    exMessageLength = strlen(exMessage);

        if (exMessageLength == 0)
        {
            const char * gbmErrorMsg = gbmAccessor.Gbm_err(ex.getErrorId());

            *error = (char *) SOMMalloc(strlen(gbmErrorMsg) + 1);
            strcpy(*error, gbmErrorMsg);
        }
        else
        {
            const char * gbmErrorMsg = gbmAccessor.Gbm_err(ex.getErrorId());

            // format the error string: user_message:\n(gbm_error)

            *error = (char *) SOMMalloc(strlen(gbmErrorMsg) + exMessageLength + 3 + 1);
            sprintf(*error, "%s: %s", exMessage, gbmErrorMsg);
        }
    }
}

// ---------------------------------------------------------

/**
 * loadFile
 * filename: filename of the file to load
 * password: password to unlock the file with, or NULL
 * errorCode: Return location for an error code, or NULL
 * error: Return location for an error, or NULL
 *
 * Loads the specified file.
 *
 * Return value: TRUE if file loaded, FALSE otherwise.
 * Default return value: FALSE
 *
 * Note: error string allocated using SOMMalloc(),
 *       use SOMFree() to free.
 **/

SOM_Scope boolean  SOMLINK loadFile(LuGbmDocument *somSelf ,
                                    Environment   *ev      , string  filename,
                                    string         password, long   *errorCode,
                                    string        *error)
{
    if (somSelf == NULL)
    {
        DosBeep(500,50);
        return FALSE;
    }
    LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
    WGbmDocument      *d       = (WGbmDocument *)somThis->data;

    if (error != NULL)
    {
        *error = NULL;
    }
    try
    {
        d->setDocumentFile(filename);
    }
    catch(GbmException & ex)
    {
        switch(ex.getErrorId())
        {
            case GBM_ERR_MEM:
                setErrorCode(LU_LDERR_OUT_OF_MEMORY, errorCode);
                break;

            case GBM_ERR_NOT_FOUND:
                setErrorCode(LU_LDERR_OPEN_ERROR, errorCode);
                break;

            case GBM_ERR_READ:
            case GBM_ERR_BAD_OPTION:
            case GBM_ERR_BAD_ARG:
                setErrorCode(LU_LDERR_READ_ERROR, errorCode);
                break;

            case GBM_ERR_NOT_SUPP:
            case GBM_ERR_BAD_MAGIC:
            case GBM_ERR_BAD_SIZE:
                setErrorCode(LU_LDERR_WRONG_FORMAT, errorCode);
                break;

            default:
                // extract error message
                setErrorCode(LU_LDERR_CUSTOM, errorCode);
                createErrorMessage(d->getGbmAccessor(), ex, error);
                break;
        }
        return FALSE;
    }
    setErrorCode(LU_LDERR_NO_ERROR, errorCode);
    return TRUE;
}

// ---------------------------------------------------------

/**
 * getBpp
 *
 * Return value: Number of bytes per pixel used for draw.
 **/

SOM_Scope short  SOMLINK getBpp(LuGbmDocument *somSelf, Environment *ev)
{
    return 3;
}

// ---------------------------------------------------------

/**
 * isScalable
 *
 * (See renderPageToPixbuf(), scale)
 *
 * Return value: TRUE if document scalable, FALSE otherwise.
 **/

SOM_Scope boolean  SOMLINK isScalable(LuGbmDocument *somSelf,
                                      Environment   *ev)
{
    return TRUE;
}

/**
 * isCreateFileThumbnail
 *
 * If this method returns TRUE, then GUI may create thumbnail for
 * this file and write it into EAs.
 *
 * Default return value: FALSE
 **/
SOM_Scope boolean SOMLINK isCreateFileThumbnail(LuGbmDocument *somSelf,
                                                Environment   *ev)
{
    return TRUE;
}

// ---------------------------------------------------------

/**
 * isRotable
 *
 * (See renderPageToPixbuf(), rotation)
 *
 * Return value: TRUE if document can be rotated, FALSE otherwise.
 **/

SOM_Scope boolean  SOMLINK isRotable(LuGbmDocument *somSelf,
                                     Environment   *ev)
{
    return TRUE;
}

// ---------------------------------------------------------

/**
 * isRenderIntoPS
 *
 * See renderPageToPS()
 *
 * Return value: TRUE if document will render directly into
 *               presentation space.
 **/
SOM_Scope boolean  SOMLINK isRenderIntoPS(LuGbmDocument *somSelf,
                                          Environment   *ev)
{
    return TRUE;
}

// ---------------------------------------------------------

/**
 * getPageCount
 *
 * Returns the number of pages in a loaded document.
 *
 * Return value: Number of pages
 **/

SOM_Scope long  SOMLINK getPageCount(LuGbmDocument *somSelf,
                                     Environment   *ev)
{
    if (somSelf == NULL)
    {
        DosBeep(500,50);
        return 0;
    }
    const LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
          WGbmDocument      *d       = (WGbmDocument *)somThis->data;

    return d->getGbmDocument().getNumberOfPages();
}

// ---------------------------------------------------------

/**
 * getPageSize
 * pagenum: Page number
 * width: return location for the width of page
 *        (NULL if width not needed)
 * height: return location for the height of page
 *         (NULL if height not needed)
 *
 * Gets the size of specified page.
 **/

SOM_Scope void  SOMLINK getPageSize(LuGbmDocument *somSelf,
                                    Environment   *ev     , long     pagenum,
                                    double        *width  , double * height)
{
    if (somSelf == NULL)
    {
        DosBeep(500,50);
        return;
    }
    const LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
          WGbmDocument      *d       = (WGbmDocument *)somThis->data;

    double w = 0.0, h = 0.0;

    try
    {
        GbmDocumentPage & page(d->getGbmDocument().getPage(pagenum));

        if (d->getPageRotation(pagenum) % 180)
        {
            w = page.getHeight();
            h = page.getWidth();
        }
        else
        {
            w = page.getWidth();
            h = page.getHeight();
        }
    }
    catch(GbmException & ex)
    {
    }
    if (width != NULL)
    {
        *width = w;
    }
    if (height != NULL)
    {
        *height = h;
    }
}

// ---------------------------------------------------------

/**
 * getDocumentInfo
 *
 * Return value: a new LuDocumentInfo.
 **/

SOM_Scope LuDocumentInfo*  SOMLINK getDocumentInfo(LuGbmDocument *somSelf,
                                                   Environment   *ev)
{
    if (somSelf == NULL)
    {
        DosBeep(500,50);
        return NULL;
    }
    LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
    WGbmDocument      *d       = (WGbmDocument *)somThis->data;
    GbmDocument       &doc(d->getGbmDocument());

    LuDocumentInfo *info = (LuDocumentInfo *)SOMMalloc( sizeof( LuDocumentInfo ) );
    memset( info, 0, sizeof( LuDocumentInfo ) );

    // set long format description
    const char * formatDescription = doc.getLongDescription();
    info->format = (char *) SOMMalloc( strlen(formatDescription) + 1 );
    strcpy(info->format, formatDescription);
    info->fields_mask |= LU_DOCUMENT_INFO_FORMAT;

    // set number of pages
    info->n_pages = doc.getNumberOfPages();
    info->fields_mask |= LU_DOCUMENT_INFO_N_PAGES;

    return info;
}

// ---------------------------------------------------------

/**
 * isSaveable
 *
 * Return value: TRUE if document can be saved in same format
 *               as original, FALSE otherwise.
 **/

SOM_Scope boolean  SOMLINK isSaveable(LuGbmDocument *somSelf,
                                      Environment   *ev)
{
    if (somSelf == NULL)
    {
        DosBeep(500,50);
        return FALSE;
    }
    LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
    WGbmDocument      *d       = (WGbmDocument *)somThis->data;
    GbmDocument       &doc(d->getGbmDocument());

    return (doc.getNumberOfPages() == 1) ? TRUE : FALSE;
}

// ---------------------------------------------------------

/**
 * saveAs
 * filename: name of file to save
 *
 * Saves document.
 *
 * Return value: TRUE, if the document was successfully saved,
 *               FALSE otherwise.
 **/

SOM_Scope boolean  SOMLINK saveAs(LuGbmDocument *somSelf,  Environment *ev,
                                  string filename)
{
    if (somSelf == NULL)
    {
        DosBeep(500,50);
        return FALSE;
    }
    LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
    WGbmDocument      *d       = (WGbmDocument *)somThis->data;

    try
    {
        d->writeDocumentPageToFile(filename, 0);
    }
    catch(GbmException & ex)
    {
        return FALSE;
    }
    return TRUE;
}

// ---------------------------------------------------------

/**
 * renderPageToPixbuf
 * pagenum: the page to render from
 * src_x: x coordinate of upper left corner
 * src_y: y coordinate of upper left corner
 * src_width: width of rectangle to render
 * src_height: height of rectangle to render
 * scale: scale specified as pixels per point
 *        (if isScalable() is FALSE, scale ignored and assumed 1.0)
 * rotation: rotate the document by the specified degree
 *           allowed values is 0, 90, 180, 270
 *        (if isRotable() is FALSE, rotation ignored and assumed 0)
 * pixbuf: pixbuf to render into
 * errorCode: Return location for an error/warning code, or NULL
 * error: Return location for an error/warning, or NULL
 *
 * First scale the document to match the specified pixels per point,
 * then render the rectangle given by the upper left corner at
 * (src_x, src_y) and src_width and src_height.
 **/

/* Note: Implemented only to support thumbnail creation.
 *       This function is not thought to be used for fast rendering
 *       to the Lucide window, renderPageToPS() is optimized for this.
 */

SOM_Scope boolean  SOMLINK renderPageToPixbuf(LuGbmDocument *somSelf,
                                              Environment   *ev,
                                              long           pagenum   , long       src_x,
                                              long           src_y     , long       src_width,
                                              long           src_height, double     scale,
                                              long           rotation  , LuPixbuf * pixbuf,
                                              long          *errorCode , string    *error)
{
    if (somSelf == NULL)
    {
        setErrorCode(LU_RERR_CUSTOM, errorCode);
        if (error != NULL)
        {
            static const char * msg = "SOM document is NULL";
            *error = (char *) SOMMalloc(strlen(msg) + 1);
            strcpy(*error, msg);
        }
        DosBeep(500,50);
        return FALSE;
    }
    const LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
          WGbmDocument      *d       = (WGbmDocument *)somThis->data;
          GbmDocument       &doc(d->getGbmDocument());

    if (error != NULL)
    {
        *error = NULL;
    }

    if ((scale < 0.0) || (pagenum < 0) ||
        (src_y < 0)   || (src_x < 0)   || (src_width <= 0) || (src_height <= 0))
    {
        setErrorCode(LU_RERR_CUSTOM, errorCode);
        if (error != NULL)
        {
            static const char * msg = "Illegal render request";
            *error = (char *) SOMMalloc(strlen(msg) + 1);
            strcpy(*error, msg);
        }
        return FALSE;
    }

    GbmRenderer & renderer(doc.getRenderer());
    const GbmRenderer::ScalerType oldScalerType(renderer.getScalerType());

    try
    {
        if ((pagenum < 0) || (pagenum >= doc.getNumberOfPages()))
        {
            setErrorCode(LU_RERR_CUSTOM, errorCode);
            if (error != NULL)
            {
                static const char * msg = "Illegal render request";
                *error = (char *) SOMMalloc(strlen(msg) + 1);
                strcpy(*error, msg);
            }
            return FALSE;
        }

        // set the rotation (use the wrapper document to track lats rotation angle)
        d->setPageRotation(pagenum, rotation);

        GbmDocumentPage & page(doc.getPage(pagenum));

        // render upsampled to 24bpp

        renderer.setRenderPage(&page);

        const long requiredBufLength =
          renderer.calcRenderBufferSize(src_x    , src_y,
                                        src_width, src_height,
                                        scale    , scale);
        if (requiredBufLength > pixbuf->getDataLen(ev))
        {
            setErrorCode(LU_RERR_CUSTOM, errorCode);
            if (error != NULL)
            {
                static const char * msg = "Illegal render request: Buffer too small";
                *error = (char *) SOMMalloc(strlen(msg) + 1);
                strcpy(*error, msg);
            }
            return FALSE;
        }

        byte *drawBuffer = (byte *)pixbuf->getDataPtr( ev );
        if (drawBuffer == NULL)
        {
            setErrorCode(LU_RERR_OUT_OF_MEMORY, errorCode);
            return FALSE;
        }

        // use high quality resampling scaler
        renderer.setScalerType(GbmRenderer::ScalerType_MITCHELL);

        GbmRenderer::BMPHEADER bmpHeader;
        renderer.renderToBuffer24bpp(src_x     , src_y,
                                     src_width , src_height,
                                     scale     , scale,
                                     drawBuffer, &bmpHeader);

        // restore previous scaler type
        renderer.setScalerType(oldScalerType);

        drawBuffer = NULL;
    }
    catch(GbmException & ex)
    {
        // restore previous scaler type
        renderer.setScalerType(oldScalerType);

        switch(ex.getErrorId())
        {
            case GBM_ERR_MEM:
                setErrorCode(LU_RERR_OUT_OF_MEMORY, errorCode);
                break;

            case GBM_ERR_NOT_FOUND:
            case GBM_ERR_READ:
            case GBM_ERR_BAD_OPTION:
            case GBM_ERR_BAD_ARG:
            case GBM_ERR_NOT_SUPP:
            case GBM_ERR_BAD_MAGIC:
            case GBM_ERR_BAD_SIZE:
                setErrorCode(LU_RERR_CORRUPTED_PAGE_DATA, errorCode);
                break;

            default:
                // extract error message
                setErrorCode(LU_RERR_CUSTOM, errorCode);
                createErrorMessage(d->getGbmAccessor(), ex, error);
                break;
        }
        return FALSE;
    }
    setErrorCode(LU_RERR_NO_ERROR, errorCode);
    return TRUE;
}

// ---------------------------------------------------------

/**
 * renderPageToPS
 * pagenum: the page to render from
 * src_x: x coordinate of upper left corner
 * src_y: y coordinate of upper left corner
 * src_width: width of rectangle to render
 * src_height: height of rectangle to render
 * scale: scale specified as pixels per point
 *        (if isScalable() is FALSE, scale ignored and assumed 1.0)
 * rotation: rotate the document by the specified degree
 *           allowed values is 0, 90, 180, 270
 *        (if isRotable() is FALSE, rotation ignored and assumed 0)
 * hps: handle of presentation space to render into
 * rect: pointer to RECTL structure, defines render area on HPS
 * errorCode: Return location for an error/warning code, or NULL
 * error: Return location for an error/warning, or NULL
 *
 * First scale the document to match the specified pixels per point,
 * then render the rectangle given by the upper left corner at
 * (src_x, src_y) and src_width and src_height.
 **/

SOM_Scope boolean  SOMLINK renderPageToPS(LuGbmDocument *somSelf  ,
                                          Environment   *ev       , long       pagenum,
                                          long           src_x    , long       src_y,
                                          long           src_width, long       src_height,
                                          double         scale    , long       rotation,
                                          unsigned long  hps      , somMToken  rect,
                                          long          *errorCode, string    *error)
{
    if (somSelf == NULL)
    {
        setErrorCode(LU_RERR_CUSTOM, errorCode);
        if (error != NULL)
        {
            static const char * msg = "SOM document is NULL";
            *error = (char *) SOMMalloc(strlen(msg) + 1);
            strcpy(*error, msg);
        }
        DosBeep(500,50);
        return FALSE;
    }
    const LuGbmDocumentData *somThis = LuGbmDocumentGetData(somSelf);
          WGbmDocument      *d       = (WGbmDocument *)somThis->data;
          GbmDocument       &doc(d->getGbmDocument());

    if (error != NULL)
    {
        *error = NULL;
    }

    if ((scale < 0.0) || (pagenum < 0) ||
        (src_y < 0)   || (src_x < 0)   || (src_width <= 0) || (src_height <= 0))
    {
        setErrorCode(LU_RERR_CUSTOM, errorCode);
        if (error != NULL)
        {
            static const char * msg = "Illegal render request";
            *error = (char *) SOMMalloc(strlen(msg) + 1);
            strcpy(*error, msg);
        }
        return FALSE;
    }

    try
    {
        if ((pagenum < 0) || (pagenum >= doc.getNumberOfPages()))
        {
            setErrorCode(LU_RERR_CUSTOM, errorCode);
            if (error != NULL)
            {
                static const char * msg = "Illegal render request";
                *error = (char *) SOMMalloc(strlen(msg) + 1);
                strcpy(*error, msg);
            }
            return FALSE;
        }

        // set the rotation (use the wrapper document to track last rotation angle)
        d->setPageRotation(pagenum, rotation);

        GbmDocumentPage & page(doc.getPage(pagenum));
        GbmRenderer     & renderer(doc.getRenderer());

        renderer.setRenderPage(&page);

        const PRECTL renderArea = (const PRECTL) rect;
        renderer.renderToHPS(src_x, src_y,   src_width, src_height,
                             scale, scale, *renderArea, hps);
    }
    catch(GbmException & ex)
    {
        switch(ex.getErrorId())
        {
            case GBM_ERR_MEM:
                setErrorCode(LU_RERR_OUT_OF_MEMORY, errorCode);
                break;

            case GBM_ERR_NOT_FOUND:
            case GBM_ERR_READ:
            case GBM_ERR_BAD_OPTION:
            case GBM_ERR_BAD_ARG:
            case GBM_ERR_NOT_SUPP:
            case GBM_ERR_BAD_MAGIC:
            case GBM_ERR_BAD_SIZE:
                setErrorCode(LU_RERR_CORRUPTED_PAGE_DATA, errorCode);
                break;

            default:
                // extract error message
                setErrorCode(LU_RERR_CUSTOM, errorCode);
                createErrorMessage(d->getGbmAccessor(), ex, error);
                break;
        }
        return FALSE;
    }
    setErrorCode(LU_RERR_NO_ERROR, errorCode);
    return TRUE;
}

// ---------------------------------------------------------
// ---------------------------------------------------------

SOM_Scope void SOMLINK somDefaultInit(LuGbmDocument * somSelf,
                                      som3InitCtrl  * ctrl)
{
    LuGbmDocumentData *somThis;
    somInitCtrl        globalCtrl;
    somBooleanVector   myMask;
    LuGbmDocument_BeginInitializer_somDefaultInit;
    LuGbmDocument_Init_LuDocument_somDefaultInit(somSelf, ctrl);

    // local LuGbmDocument initialization code
    WGbmDocument *d = new WGbmDocument(*pGbmAccessor);
    d->setConfigurationFile(fullCfgPath);
    somThis->data = d;
}

// ---------------------------------------------------------

SOM_Scope void SOMLINK somDestruct(LuGbmDocument *somSelf, octet doFree,
                                   som3DestructCtrl* ctrl)
{
    LuGbmDocumentData *somThis;
    somDestructCtrl    globalCtrl;
    somBooleanVector   myMask;
    LuGbmDocument_BeginDestructor;

    // local LuGbmDocument deinitialization code
    WGbmDocument *d = (WGbmDocument *)somThis->data;

    delete d;
    d = NULL;

    // end of local LuGbmDocument deinitialization code

    LuGbmDocument_EndDestructor;
}


