diff --git a/RenderPageToImage/RenderPage.cpp b/RenderPageToImage/RenderPage.cpp new file mode 100644 index 0000000..b49f8b6 --- /dev/null +++ b/RenderPageToImage/RenderPage.cpp @@ -0,0 +1,654 @@ +// +// Copyright (c) 2007-2024, Datalogics, Inc. All rights reserved. +// +// For complete copyright information, refer to: +// http://dev.datalogics.com/adobe-pdf-library/license-for-downloaded-pdf-samples/ +// +// The RenderPage sample program shows how to render a PDF document page to memory. +// +// For more detail see the description of the RenderPage sample program on our Developer’s site, +// http://dev.datalogics.com/adobe-pdf-library/sample-program-descriptions/c1samples#renderpage + +#include "RenderPage.h" +#include +#include +#include +#include + +#include "ASCalls.h" +#include "PDCalls.h" +#include "DLExtrasCalls.h" +#include "PERCalls.h" +#include "PEWCalls.h" + +// These are utility routines to convert Rects and Matrices between ASDouble and +// ASReal, and ASFixed. +// ASFixed was the original method of specifing "real" numbers in APDFL. It is still widely present in APDFL interfaces, though it is +// limited by both it's resolution (0.0001 typically) and it range (+- 32767). There is a full complement of methods for combining +// matrices, and transforming and comparing rectangles. Internal to APDFL, ASFixed is now seldom used. +// ASReal was introduced into APDFL later. It is implemented as a float, and it does not have the full +// complement of transform methods. It is used here because it is needed in the interface to PDPageDrawContentsToMemoryWithParams. +// ASDouble was introduced most recently. It has a full complement of transformation methods. Many interfaces to APDFL have been updated +// (Generally by the addition of "Ex" to the interface name) to provide/accept such values. +// +// However, conversion between these forms is not always supplied. These routines provided the conversions needed for this sample. +static void ASDoubleRectToASReal (ASRealRect &out, ASDoubleRect &in) +{ + out.left = (ASReal)in.left; + out.right = (ASReal)in.right; + out.top = (ASReal)in.top; + out.bottom = (ASReal)in.bottom; +} + +static void ASDoubleMatrixToASReal (ASRealMatrix &out, ASDoubleMatrix &in) +{ + out.a = (ASReal)in.a; + out.b = (ASReal)in.b; + out.c = (ASReal)in.c; + out.d = (ASReal)in.d; + out.tx = (ASReal)in.h; + out.ty = (ASReal)in.v; +} + +static void ASDoubleMatrixToASFixed(ASFixedMatrix &out, ASDoubleMatrix &in) +{ + out.a = FloatToASFixed(in.a); + out.b = FloatToASFixed(in.b); + out.c = FloatToASFixed(in.c); + out.d = FloatToASFixed(in.d); + out.h = FloatToASFixed(in.h); + out.v = FloatToASFixed(in.v); +} + + +static void ASFixedRectToASDouble (ASDoubleRect &out, ASFixedRect &in) +{ + out.left = ASFixedToFloat (in.left); + out.right = ASFixedToFloat (in.right); + out.top = ASFixedToFloat (in.top); + out.bottom = ASFixedToFloat (in.bottom); +} + +static void ASFixedMatrixToASDouble (ASDoubleMatrix &out, ASFixedMatrix &in) +{ + out.a = ASFixedToFloat (in.a); + out.b = ASFixedToFloat (in.b); + out.c = ASFixedToFloat (in.c); + out.d = ASFixedToFloat (in.d); + out.h = ASFixedToFloat (in.h); + out.v = ASFixedToFloat (in.v); +} + +static void ASDoubleToFixedRect (ASFixedRect &out, ASDoubleRect &in) +{ + out.left = FloatToASFixed (in.left); + out.right = FloatToASFixed (in.right); + out.top = FloatToASFixed (in.top); + out.bottom = FloatToASFixed (in.bottom); +} + +static void flipBytes(void *start, int len) +{ + unsigned char byteHolder; + unsigned char *pos = (unsigned char *)start, + *end = (unsigned char *)start + (len - 1); + + if (!start || len <= 1) + return; + + while (pos < end) + { + byteHolder = *end; + *end = *pos; + *pos = byteHolder; + ++pos; + --end; + } +} + + +RenderPageParams::RenderPageParams() +{ + bitspercomp = 8; + ri = AC_RelColorimetric; + colorSpace = NULL; + bVerbose = FALSE; + drawflags = 0; + smoothflags = 0; + nComps = 1; + numfilters = 0; + res = 72; + matrix = NULL; + destRect = NULL; + ocContext = NULL; +} + +RenderPageParams::~RenderPageParams() +{ +} + +void RenderPageParams::SetColorSpace(const char *colorSpace) +{ + SetColorSpace(ASAtomFromString(colorSpace)); +} + +void RenderPageParams::SetColorSpace(ASAtom atmColorSpace) +{ + static const ASAtom atmDeviceRGB = ASAtomFromString("DeviceRGB"); + static const ASAtom atmDeviceCMYK = ASAtomFromString("DeviceCMYK"); + static const ASAtom atmDeviceGray = ASAtomFromString("DeviceGray"); + static const ASAtom atmDeviceRGBA = ASAtomFromString("DeviceRGBA"); + + csAtom = atmColorSpace; + + if (csAtom == atmDeviceGray) + { + nComps = 1; + } + else if (csAtom == atmDeviceRGB) + { + nComps = 3; + } + else if (csAtom == atmDeviceCMYK) + { + nComps = 4; + } + else if (csAtom == atmDeviceRGBA) + { + nComps = 4; + } + else + { + // Not a valid/currently supported colorspace + ASRaise(genErrBadParm); + } + +} +ASAtom RenderPageParams::ColorSpaceName() const +{ + return csAtom; +} + +ASInt32 RenderPageParams::NumComps() const +{ + return nComps; +} + +void RenderPageParams::setBitsPerComponents(ASInt32 bpc) +{ + bitspercomp = bpc; + if (csAtom == ASAtomFromString("DeviceRGB") || csAtom == ASAtomFromString("DeviceCMYK") || csAtom == ASAtomFromString("DeviceRGBA")) + { + if (bitspercomp != 8) + { + // Reset to 8 + bitspercomp = 8; + } + } + else if (csAtom == ASAtomFromString("DeviceGray")) + { + if ((bitspercomp != 1) && (bitspercomp != 8) && (bitspercomp != 24)) + { + // Reset to 8 + bitspercomp = 8; + } + } +} + +ASInt32 RenderPageParams::BitsPerComponent() const +{ + return bitspercomp; +} + +void RenderPageParams::setRenderIntent(AC_RenderIntent renderIntent) +{ + ri = renderIntent; +} + +AC_RenderIntent RenderPageParams::RenderIntent() const +{ + return ri; +} + +void RenderPageParams::setResolution(double resolution) +{ + //Set resolution. +// A PDF "unit" is, by default, 1/72nd of an inch. So a resolution of 72 is one PDF "unit" per pixel. +// if no resolution is set, we will use 72 DPI as our image reslution. This sample does not attempt to +// support different horiziontal and vertical resolutions. APDFL, can easily support them by using a +// different scale factor in the scale matrix "a" (horiziontal) and "d" (vertical) members. The scale +// factors are simply (72.0 / resolution). + res = 72.0; + if (resolution > 0.0) + res = resolution; +} + +double RenderPageParams::Resolution() const +{ + return res; +} + +void RenderPageParams::setSmoothFlags(ASUns32 smoothFlags) +{ + smoothflags = smoothFlags; +} + +ASUns32 RenderPageParams::SmoothFlags() const +{ + return smoothflags; +} + +void RenderPageParams::setDrawFlags(ASUns32 drawFlags) +{ + drawflags = drawFlags; +} + +ASUns32 RenderPageParams::DrawFlags() const +{ + return drawflags; +} + +void RenderPageParams::setOCContext(PDOCContext context) +{ + ocContext = context; +} + +PDOCContext RenderPageParams::OCContext() const +{ + return ocContext; +} + +void RenderPageParams::setMatrix(ASDoubleMatrix* m) +{ + matrix = m; +} + +ASDoubleMatrix* RenderPageParams::getMatrix() +{ + return matrix; +} + +void RenderPageParams::setDestRect(ASDoubleRect* r) +{ + destRect = r; +} + +ASDoubleRect* RenderPageParams::getDestRect() +{ + return destRect; +} + +void RenderPageParams::setOutputProfile(AC_Profile profile) +{ + outputProfile = profile; +} + +AC_Profile RenderPageParams::getOutputProfile() const +{ + if (outputProfile != nullptr) + return outputProfile; + else + return NULL; +} + +void RenderPageParams::setVerbose(ASBool verbose) +{ + bVerbose = verbose; +} + +ASBool RenderPageParams::verbose() const +{ + return bVerbose; +} + +static ASBool renderpageProgressProc(float current, const char *name, ASInt32 + /* it's a PDFLFlattenProgressMarker (see PDFLPrint.h) */ stage, void *clientData) +{ + std::cout << name << " stage " << stage << ": " << current * 100 << "%" << std::endl; + return TRUE; +} + +static ASBool renderpageCancelProc(void *clientData) +{ + std::cout << "."; + return false; +} +// This both constructs the RenderPage object, and creates the page rendering. +// The rendered page can be accessed as a bitmap via the methods GetImageBuffer() and GetImageBufferSize(), or as a PDEImage, +// via the method GetPDEImage(). The PDEImage creation will be deferred until it is requested. +RenderPage::RenderPage(PDPage& pdPage, ASFixedRect* fixedUpdateRect, RenderPageParams* inParms) +{ + parms = inParms; + clock_t start_time[2] = { 0,0 }, stop_time[2] = { 0,0 }; + + ASDoubleRect updateRect; + ASFixedRectToASDouble(updateRect, *fixedUpdateRect); + + //Set resolution. + // A PDF "unit" is, by default, 1/72nd of an inch. So a resolution of 72 is one PDF "unit" per pixel. + // if no resolution is set, we will use 72 DPI as our image reslution. This sample does not attempt to + // support different horizontal and vertical resolutions. APDFL, can easily support them by using a + // different scale factor in the scale matrix "a" (horizontal) and "d" (vertical) members. The scale + // factors are simply (resolution /72.0). + double scaleFactor = parms->Resolution() / 72.0; + + //Get the colorspace atom, set the number of components per colorspace + // and store the appropriate colorspace for an output PDEImage + // This sample will raise an exception if the color space is not one of + // DeviceGray, DeviceRGB, or DeviceCMYK. APDFL supports a number of additional + // color spaces. The image created may also be conformed to a given ICC Profile. + csAtom = parms->ColorSpaceName(); + nComps = parms->NumComps(); + // initialize the output colorspace for the PDEImage we'll generate in MakePDEImage + cs = PDEColorSpaceCreateFromName(csAtom); + + //The size of each color component to be represented in the image. + bpc = parms->BitsPerComponent(); + + // Set up attributes for the PDEImage to be made by GetPDEImage + // Height and Width in pixels will be added as they are known. + memset(&attrs, 0, sizeof(PDEImageAttrs)); + attrs.flags = kPDEImageExternal; + attrs.bitsPerComponent = bpc; + + // Get the matrix that transforms user space coordinates to Image coordinates, taking into account page + // rotation. Note that page rotation is clockwise, so pdRotate90 is effectively a rotation of -90 degrees. + // Also note that Page coordinates have their origin in the lower-left while image coordinates have their + // origin in the upper-right, so the matrix must also mirror vertically. + PDRotate rotation = PDPageGetRotate(pdPage); + ASDoubleMatrix updateMatrix = { 1,0,0,1,0,0 }; + if (parms->getMatrix() != NULL) + { + ASDoubleMatrix* srcM = parms->getMatrix(); + updateMatrix.a = srcM->a; + updateMatrix.b = srcM->b; + updateMatrix.c = srcM->c; + updateMatrix.d = srcM->d; + updateMatrix.h = srcM->h; + updateMatrix.v = srcM->v; + } + else + { + ASFixedRect* fRectP = fixedUpdateRect; + ASDoubleMatrix flipMatrix = { 1, 0, 0, -1, 0, ASFixedToFloat(fRectP->top - fRectP->bottom) }; + + ASDoubleMatrix scaleMatrix = { scaleFactor, 0, 0, scaleFactor, 0, 0 }; + + switch (rotation) + { + //Note, rotation is clockwise. + case pdRotate0: + updateMatrix = { 1, 0, 0, 1, -ASFixedToFloat(fRectP->left), -ASFixedToFloat(fRectP->bottom) }; + break; + case pdRotate90: + updateMatrix = { 0, -1, 1, 0, -ASFixedToFloat(fRectP->bottom), ASFixedToFloat(fRectP->right) }; + flipMatrix.v = ASFixedToFloat(fRectP->right - fRectP->left); + break; + case pdRotate180: + updateMatrix = { -1, 0, 0, -1, ASFixedToFloat(fRectP->right), ASFixedToFloat(fRectP->top) }; + break; + case pdRotate270: + updateMatrix = { 0, 1, -1, 0, ASFixedToFloat(fRectP->top), -ASFixedToFloat(fRectP->left) }; + flipMatrix.v = ASFixedToFloat(fRectP->right - fRectP->left); + break; + } + ASDoubleMatrixConcat(&updateMatrix, &flipMatrix, &updateMatrix); + ASDoubleMatrixConcat(&updateMatrix, &scaleMatrix, &updateMatrix); + } + + // Set up the destination rectangle. + // This is a description of the image in pixels, so it will always + // have it's origin at 0,0. + ASDoubleRect doubleDestRect = { 0,0,0,0 }; + ASRealRect realDestRect; + if (parms->getDestRect() != NULL) + { + ASDoubleRect* src = parms->getDestRect(); + + doubleDestRect.left = src->left; + doubleDestRect.bottom = src->bottom; + doubleDestRect.right = src->right; + doubleDestRect.top = src->top; + } + else { + ASDoubleMatrixTransformRect(&doubleDestRect, &updateMatrix, &updateRect); + } + ASDoubleRectToASReal(realDestRect, doubleDestRect); + + assert(((ASInt32)realDestRect.left) == 0); + assert(((ASInt32)realDestRect.bottom) == 0); + + attrs.width = (ASInt32)floor(realDestRect.right + 0.5); + attrs.height = (ASInt32)floor(realDestRect.top + 0.5); + + // This is a bit clumsy, because features were added over time. The matrices and rectangles in this + // interface use ASReal as their base, rather than ASDouble. But there is not a complete set of + // concatenation and transformation methods for ASReal. So we generally generate the matrix and + // rectangle values using ASDouble, and convert to ASReal. + ASRealRect realUpdateRect; + ASRealMatrix realUpdateMatrix; + ASDoubleRectToASReal(realUpdateRect, updateRect); + ASDoubleMatrixToASReal(realUpdateMatrix, updateMatrix); + + //Allocate the buffer for storing the rendered page content + // It is important that ALL of the flags and options used in the actual draw be set the same here! + // Calling this interface with drawParms.bufferSize or drawParams.buffer equal to zero will return the size of the buffer + // needed to contain this image. This is the most certain way to get the correct buffer size. If we call this routine with a buffer + // that is not large enough to contain the image, we will not draw the image, but will simply, silently, return the size of the buffer + // needed! + + // "Best Practice" is to use PDPageDrawContentsToMemoryWithParams, as it allows + // the matrix and rects to be specified in floating point, eliminating the need + // to test for ASFixed Overflows. + PDPageDrawMParamsRec drawParams; + memset(&drawParams, 0, sizeof(PDPageDrawMParamsRec)); + drawParams.size = sizeof(PDPageDrawMParamsRec); + drawParams.csAtom = csAtom; + drawParams.bpc = bpc; + drawParams.clientOCContext = parms->OCContext(); + drawParams.iccProfile = parms->getOutputProfile(); + + // For this example we will smooth (anti-alias) all of the marks. For a given application, + // this may or may not be desireable. See the enumeration PDPageDrawSmoothFlags for the full set of options. + drawParams.smoothFlags = parms->SmoothFlags(); + + // The DoLazyErase flag is usually, if not always turned on, UseAnnotFaces will cause annotations + // in the page to be displayed, and kPDPageDsiplayOverprintPreview will display the page showing + // overprinting. The precise meaning of these flags, as well as others that may be used here, can be + // seen in the definition of PDPageDrawFlags + drawParams.flags = parms->DrawFlags(); + + drawParams.renderIntent = parms->RenderIntent(); + + drawParams.asRealDestRect = &realDestRect; // This is where the image is drawn on the resultant bitmap. + // It is generally set at 0, 0 and width/height in pixels. + drawParams.asRealUpdateRect = &realUpdateRect; // This is the portion of the document to be drawn. If omitted, + // it will be the document media box, which is generally what is wanted. + drawParams.asRealMatrix = &realUpdateMatrix; // the matrix is used to translate coordinates within the UpdateRect to pixels in the DestRect. + + drawParams.progressProc = parms->verbose() ? renderpageProgressProc : NULL; + drawParams.cancelProc = parms->verbose() ? renderpageCancelProc : NULL; + + // Additional values in this record control such features as drawing separations, + // specifiying a desired output profile, selecting optional content, and providing for + // a progress reporting callback. + + bufferSize = PDPageDrawContentsToMemoryWithParams(pdPage, &drawParams); // This call, with a NULL buffer pointer, returns needed buffer size + + // One frequent failure point in rendering images is being unable to allocate sufficient contiguous space + // for the bitmap buffer. Here, that will be indicated by a zero value for drawParams.buffer after the + // call to malloc. If the buffer size is larger than the internal limit of malloc, it may also raise an + // interupt! Catch these conditions here, and raise an out of memory error to the caller. + try + { + buffer = (char*)ASmalloc(bufferSize); + if (!buffer) + ASRaise(genErrNoMemory); + memset(buffer, 0x7F, bufferSize); + } + catch (...) + { + ASRaise(genErrNoMemory); + } + + static const ASAtom atmDeviceRGBA = ASAtomFromString("DeviceRGBA"); + if (csAtom == atmDeviceRGBA) + { + /* Initialize the buffer with the Alpha + * channel initialized to zero. It really doesn't matter + * what the RGB channels initialize too, as the alpha of zero + * will make it transparent + */ + for (ASInt32 offset = 0; offset < bufferSize; offset += 4) + { + buffer[offset + 3] = 0x00; + } + } + + // With these values in place, the next call to PDPageDrawContentsToMemoryWithParams() will fill the bitmap. + drawParams.bufferSize = bufferSize; + drawParams.buffer = buffer; + + // Render page content to the bitmap buffer + start_time[0] = clock(); + PDPageDrawContentsToMemoryWithParams(pdPage, &drawParams); + stop_time[0] = clock(); + + padded = (((nComps % 4) != 0) ? true : false); //note: this flag is so we don't remove padding more than once,max. + + if (parms->verbose()) + { + double duration = ((double)(stop_time[0] - start_time[0]) / CLOCKS_PER_SEC); + + std::cout << "\nRendering time: " << duration << " s." << std::endl; + + } +} + +RenderPage::~RenderPage() +{ + if(buffer) + ASfree (buffer); + buffer = NULL; + + PDERelease(reinterpret_cast(cs)); +} + +char * RenderPage::GetImageBuffer() +{ + return buffer; +} + +ASSize_t RenderPage::GetImageBufferSize() const +{ + return bufferSize; +} + +// This method will scale the image to fit the imageRect. +// If the ImageRect does not have the same aspect ratio as the original updateRect, +// then the image will appear distorted. +PDEImage RenderPage::GetPDEImage(ASFixedRect imageRect) +{ + // Set up the static colorspace atoms + static const ASAtom sDeviceRGB_K = ASAtomFromString("DeviceRGB"); + static const ASAtom sDeviceCMYK_K = ASAtomFromString("DeviceCMYK"); + static const ASAtom sDeviceGray_K = ASAtomFromString("DeviceGray"); + static const ASAtom sDeviceRGBA_K = ASAtomFromString("DeviceRGBA"); + + // The bitmap data generated by PDPageDrawContentsTo* uses 32-bit aligned rows. + // The PDF image operator expects, however, 8-bit aligned image rows. + // To remedy this difference, we check to see if the 32-bit aligned width + // is different from the 8-bit aligned width. If so, we fix the image data by + // stripping off the padding at the end of each row. + if (padded) + { + ASSize_t createdWidth = (((static_cast(attrs.width * bpc * nComps) + 31) / 32) * 4); + ASSize_t desiredWidth = static_cast(attrs.width * bpc * nComps) / 8; + + if (createdWidth != desiredWidth) + { + for (int row = 1; row < attrs.height; row++) + memmove(&buffer[row * desiredWidth], &buffer[row * createdWidth], desiredWidth); + bufferSize = static_cast(desiredWidth * attrs.height); + } + padded = false; + } + + //Create the image matrix using the imageRect passed in. + ASDoubleMatrix imageMatrix = { + ASFixedToFloat(imageRect.right - imageRect.left), + 0, + 0, + ASFixedToFloat(imageRect.top - imageRect.bottom), + ASFixedToFloat(imageRect.left), + ASFixedToFloat(imageRect.bottom) + }; + + PDEImage image = NULL; + if (csAtom == sDeviceRGBA_K) + { + PDEImage imageMask = NULL; + PDEColorSpace cs1 = PDEColorSpaceCreateFromName(sDeviceRGB_K); + PDEColorSpace cs2 = PDEColorSpaceCreateFromName(sDeviceGray_K); + /* Seperate the alpha info from the color info */ + ASSize_t AlphaSize = bufferSize / 4; + ASSize_t ColorSize = bufferSize - AlphaSize; + + /* Allocate alpha and color buffers */ + ASUns8* ColorBuffer = reinterpret_cast(ASmalloc(static_cast(attrs.width *attrs.height * 3))); + ASUns8* AlphaBuffer = reinterpret_cast(ASmalloc(static_cast(attrs.width * attrs.height))); + + /* Do the separation - this is not an optimized implementation */ + for (ASInt32 Index = 0; Index < AlphaSize; Index++) + { + ColorBuffer[(Index * 3) + 0] = buffer[(Index * 4) + 0]; + ColorBuffer[(Index * 3) + 1] = buffer[(Index * 4) + 1]; + ColorBuffer[(Index * 3) + 2] = buffer[(Index * 4) + 2]; + AlphaBuffer[Index] = buffer[(Index * 4) + 3]; + } + + // Create an image XObject from the bitmap buffer to embed in the output document + imageMask = PDEImageCreateEx(&attrs, + sizeof(attrs), + &imageMatrix, + 0, + cs2, + NULL, + NULL, + 0, + AlphaBuffer, + AlphaSize); + + image = PDEImageCreateEx(&attrs, + sizeof(attrs), + &imageMatrix, + 0, + cs1, + NULL, + NULL, + 0, + ColorBuffer, + ColorSize); + + PDEImageSetSMask(image, imageMask); + PDERelease((PDEObject)imageMask); + PDERelease((PDEObject)cs1); + PDERelease((PDEObject)cs2); + ASfree(ColorBuffer); + ASfree(AlphaBuffer); + } + else { // Create an image XObject from the bitmap buffer to embed in the output document + image = PDEImageCreateEx(&attrs, + sizeof(attrs), + &imageMatrix, + 0, + cs, + NULL, + NULL, + 0, + reinterpret_cast(buffer), + bufferSize); + } + + return image; +} diff --git a/RenderPageToImage/RenderPage.h b/RenderPageToImage/RenderPage.h new file mode 100644 index 0000000..5b36b3e --- /dev/null +++ b/RenderPageToImage/RenderPage.h @@ -0,0 +1,98 @@ +// +// Copyright (c) 2007-2024, Datalogics, Inc. All rights reserved. +// +// For complete copyright information, refer to: +// http://dev.datalogics.com/adobe-pdf-library/license-for-downloaded-pdf-samples/ +// +// Sample: RenderPage +// +// This file contains declarations for the RenderPage class. +// + +#include +#include + +#include "PDFLExpT.h" +#include "AcroColorExpT.h" + + +class RenderPageParams +{ +private: + ASAtom csAtom{ ASAtomNull }; + ASInt32 nComps, bitspercomp; + char* colorSpace; + ASDouble res; + ASUns32 smoothflags, drawflags; + PDOCContext ocContext; + int numfilters; + AC_RenderIntent ri; + ASBool bVerbose; + ASDoubleMatrix* matrix; + ASDoubleRect* destRect; + AC_Profile outputProfile{ nullptr }; + +public: + RenderPageParams(); + ~RenderPageParams(); + + void SetColorSpace(const char *colorSpace); + void SetColorSpace(ASAtom atmColorSpace); + ASAtom ColorSpaceName() const; + ASInt32 NumComps() const; + + void setBitsPerComponents(ASInt32 bpc); + ASInt32 BitsPerComponent() const; + + void setResolution(double resolution); + double Resolution() const; + + void setSmoothFlags(ASUns32 smoothFlags); + ASUns32 SmoothFlags() const; + + void setDrawFlags(ASUns32 drawFlags); + ASUns32 DrawFlags() const; + + void setOCContext(PDOCContext ocContext); + PDOCContext OCContext() const; + + void setRenderIntent(AC_RenderIntent ri); + AC_RenderIntent RenderIntent() const; + + void setVerbose(ASBool verbose); + ASBool verbose() const; + + void setMatrix(ASDoubleMatrix* m); + ASDoubleMatrix* getMatrix(); + + void setDestRect(ASDoubleRect* r); + ASDoubleRect* getDestRect(); + + void setOutputProfile(AC_Profile profile); + AC_Profile getOutputProfile() const; +}; + +class RenderPage +{ +private: + RenderPageParams* parms; + PDPage pdPage; + PDEImageAttrs attrs; + PDEColorSpace cs; + ASAtom csAtom; + ASInt32 nComps; + ASSize_t bufferSize; + ASInt32 bpc; + ASUns32 smoothFlags; + char* buffer; + bool padded; + +public: + RenderPage(PDPage &pdPage, ASFixedRect* updateRect, RenderPageParams* parms); + + ~RenderPage(); + + char* GetImageBuffer(); + ASSize_t GetImageBufferSize() const; + PDEImage GetPDEImage(ASFixedRect ImageRect); +}; diff --git a/RenderPageToImage/RenderPageToImage.vcxproj b/RenderPageToImage/RenderPageToImage.vcxproj new file mode 100644 index 0000000..df04e9a --- /dev/null +++ b/RenderPageToImage/RenderPageToImage.vcxproj @@ -0,0 +1,175 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {65FB48A9-238C-4B34-ADCB-FF6E549F0B47} + RenderPageToImage + Win32Proj + RenderPageToImage + + + + Application + false + v143 + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + Unicode + + + Application + true + v143 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>12.0.21005.1 + + + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + + NotUsing + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG;DEBUG;_WIN32;WIN32;WIN_ENV;WIN_PLATFORM;PRODUCT="HFTLibrary.h";PI_ACROCOLOR_VERSION=AcroColorHFT_VERSION_6;%(PreprocessorDefinitions) + true + ..\..\..\Include\Headers;..\..\_Common;..\..\_Common;%(AdditionalIncludeDirectories) + + + + Console + true + ..\..\..\Binaries + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;DL150PDFL.lib;%(AdditionalDependencies) + DL150pdfl.dll + + + + + NotUsing + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;_CONSOLE;_DEBUG;DEBUG;_WIN64;WIN64;WIN_ENV;WIN_PLATFORM;PRODUCT="HFTLibrary.h";PI_ACROCOLOR_VERSION=AcroColorHFT_VERSION_6;%(PreprocessorDefinitions) + true + ..\..\..\Include\Headers;..\..\_Common;..\..\_Common;%(AdditionalIncludeDirectories) + + + + Console + true + ..\..\..\Binaries + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;DL180PDFL.lib;%(AdditionalDependencies) + DL180pdfl.dll + + + + + NotUsing + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN32;WIN32;WIN_ENV;WIN_PLATFORM;PRODUCT="HFTLibrary.h";PI_ACROCOLOR_VERSION=AcroColorHFT_VERSION_6;%(PreprocessorDefinitions) + true + ..\..\..\Include\Headers;..\..\_Common;..\..\_Common;%(AdditionalIncludeDirectories) + + + + Console + true + ..\..\..\Binaries + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;DL150PDFL.lib;%(AdditionalDependencies) + DL150pdfl.dll + + + + + NotUsing + Level3 + Disabled + _CRT_SECURE_NO_WARNINGS;_CONSOLE;WIN64;WIN64;WIN_ENV;WIN_PLATFORM;PRODUCT="HFTLibrary.h";PI_ACROCOLOR_VERSION=AcroColorHFT_VERSION_6;%(PreprocessorDefinitions) + true + ..\..\..\Include\Headers;..\..\_Common;..\..\_Common;%(AdditionalIncludeDirectories) + + + + Console + true + ..\..\..\Binaries + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;DL150PDFL.lib;%(AdditionalDependencies) + DL150pdfl.dll + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RenderPageToImage/RenderPageToImage_64Bit.sln b/RenderPageToImage/RenderPageToImage_64Bit.sln new file mode 100644 index 0000000..82b8e0b --- /dev/null +++ b/RenderPageToImage/RenderPageToImage_64Bit.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderPageToImage", "RenderPageToImage.vcxproj", "{65FB48A9-238C-4B34-ADCB-FF6E549F0B47}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {65FB48A9-238C-4B34-ADCB-FF6E549F0B47}.Debug|x64.ActiveCfg = Debug|x64 + {65FB48A9-238C-4B34-ADCB-FF6E549F0B47}.Debug|x64.Build.0 = Debug|x64 + {65FB48A9-238C-4B34-ADCB-FF6E549F0B47}.Release|x64.ActiveCfg = Release|x64 + {65FB48A9-238C-4B34-ADCB-FF6E549F0B47}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/RenderPageToImage/mainproc.cpp b/RenderPageToImage/mainproc.cpp new file mode 100644 index 0000000..d451532 --- /dev/null +++ b/RenderPageToImage/mainproc.cpp @@ -0,0 +1,373 @@ +// +// Copyright (c) 2007-2024, Datalogics, Inc. All rights reserved. +// +// For complete copyright information, refer to: +// http://dev.datalogics.com/adobe-pdf-library/license-for-downloaded-pdf-samples/ +// +// Sample: RenderPageToImage - Demonstrates the process of rasterizing an area of a PDF page +// and saving it to an Image File +// + +#include "PERCalls.h" +#include "DLExtrasCalls.h" +#include "AcroColorCalls.h" +#include "APDFLDoc.h" +#include "InitializeLibrary.h" + +#include +#include +#include +#include + +#include "RenderPage.h" + +#define DIR_LOC "../../../../Resources/Sample_Input/" +#define DEF_INPUT "RenderPage.pdf" +constexpr auto DEF_OUTPUT = "RenderPageToImage-out.png"; + +constexpr auto RESOLUTION = 300.0; // Other common choices might be 72.0, 150.0, 200.0, 300.0, or 600.0; +constexpr auto COLORSPACE = "DeviceRGB"; // Typically this, DeviceGray or DeviceCMYK; +constexpr auto BPC = 8; // This must be 8 for DeviceRGB and DeviceCYMK, ; + // 1, 8, or 24 for DeviceGray + +static std::vector ReadFromFile(const char* path) +{ + std::vector ret; + + if (path && strlen(path)) + { + std::ifstream file(path, std::ios::binary); + if (file.is_open()) + { + ret.assign((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + } + } + return ret; +} + + +int main(int argc, char** argv) +{ + ASErrorCode errCode = 0; + + // Initialize the library + APDFLib libInit; + + // If library in initialization failed. + if (libInit.isValid() == false) + { + errCode = libInit.getInitError(); + std::cout << "Initialization failed with code " << errCode << std::endl; + return libInit.getInitError(); + } + + int curArg = 1; + RenderPageParams parms; + parms.setVerbose(FALSE); + parms.SetColorSpace(ASAtomFromString(COLORSPACE)); + parms.setResolution(RESOLUTION); + parms.setBitsPerComponents(BPC); + parms.setRenderIntent(AC_Perceptual); + ASFixedRect fCropRect = { fixedZero,fixedZero,fixedZero,fixedZero }; + ASBool bUseSpecifiedRect = FALSE; + ASDoubleMatrix specifiedMatrix = { 1,0,0,1,0,0 }; + ASDoubleRect specifiedDest = { 0,0,0,0 }; + ASUns32 smoothFlags = kPDPageDrawSmoothText | kPDPageDrawSmoothLineArt | kPDPageDrawSmoothImage; + ASUns32 drawFlags = kPDPageDoLazyErase | kPDPageUseAnnotFaces; + ASText layerName = ASTextNew(); //initialized to an empty string/ + int pageNum = 0; + AC_Profile outputProfile{ nullptr }; + + while (argc > curArg) + { + if (strcmp(argv[curArg], "-relax") == 0) + { + PDPrefSetAllowRelaxedSyntax(true); + } + else if (strcmp(argv[curArg], "-verbose") == 0) + { + parms.setVerbose(TRUE); + } + else if (strcmp(argv[curArg], "-quiet") == 0) + { + parms.setVerbose(FALSE); + } + else if (strcmp(argv[curArg], "-xfa") == 0) + { + PDPrefSetAllowOpeningXFA(true); + } + else if (strcmp(argv[curArg], "-blackpointcompensation") == 0) + { + if (!PDPrefGetBlackPointCompensation()) + PDPrefSetBlackPointCompensation(true); + } + else if (strcmp(argv[curArg], "-noblackpointcompensation") == 0) + { + if (PDPrefGetBlackPointCompensation()) + PDPrefSetBlackPointCompensation(false); + } + else if (strcmp(argv[curArg], "-memtempfiles") == 0) + { + ASRamFileSysSetLimitKB(0); + ASSetTempFileSys(ASGetRamFileSys()); + } + else if (strcmp(argv[curArg], "-pg") == 0) + { + pageNum = atoi(argv[++curArg]); + } + else if (strcmp(argv[curArg], "-bpc") == 0) + { + parms.setBitsPerComponents(atoi(argv[++curArg])); + } + else if (strcmp(argv[curArg], "-res") == 0) + { + parms.setResolution(atof(argv[++curArg])); + } + else if (strcmp(argv[curArg], "-rgb") == 0) + { + parms.SetColorSpace(ASAtomFromString("DeviceRGB")); + } + else if (strcmp(argv[curArg], "-cmyk") == 0) + { + parms.SetColorSpace(ASAtomFromString("DeviceCMYK")); + } + else if (strcmp(argv[curArg], "-gray") == 0) + { + parms.SetColorSpace(ASAtomFromString("DeviceGray")); + } + else if (strcmp(argv[curArg], "-rgba") == 0) //experimental + { + parms.SetColorSpace(ASAtomFromString("DeviceRGBA")); + } + else if (strcmp(argv[curArg], "-grayworkingprofile") == 0) + { + std::vector ProfBuf; + ProfBuf = ReadFromFile(argv[++curArg]); + if (ProfBuf.size()) + { + PDPrefSetWorkingGray(&ProfBuf[0], static_cast(ProfBuf.size())); + } + } + else if (strcmp(argv[curArg], "-rgbworkingprofile") == 0) + { + std::vector ProfBuf; + ProfBuf = ReadFromFile(argv[++curArg]); + if (ProfBuf.size()) + { + PDPrefSetWorkingRGB(&ProfBuf[0], static_cast(ProfBuf.size())); + } + } + else if (strcmp(argv[curArg], "-cmykworkingprofile") == 0) + { + std::vector ProfBuf; + ProfBuf = ReadFromFile(argv[++curArg]); + if (ProfBuf.size()) + { + PDPrefSetWorkingCMYK(&ProfBuf[0], static_cast(ProfBuf.size())); + } + } + else if (strcmp(argv[curArg], "-targetprofile") == 0) + { + std::vector ProfBuf; + ProfBuf = ReadFromFile(argv[++curArg]); + if (ProfBuf.size()) + { + ACMakeBufferProfile(&outputProfile, &ProfBuf[0], static_cast(ProfBuf.size())); + } + } + else if (strcmp(argv[curArg], "-layer") == 0) //experimental + { + ASTextDestroy(layerName); + layerName = ASTextFromUnicode(reinterpret_cast(argv[++curArg]), kUTF8); + } + else if (strcmp(argv[curArg], "-nosmoothtext") == 0) + { + smoothFlags &= ~kPDPageDrawSmoothText; + } + else if (strcmp(argv[curArg], "-nosmoothlineart") == 0) + { + smoothFlags &= ~kPDPageDrawSmoothLineArt; + } + else if (strcmp(argv[curArg], "-nosmoothimage") == 0) + { + smoothFlags &= ~kPDPageDrawSmoothImage; + } + else if (strcmp(argv[curArg], "-ddrsmoothtext") == 0) + { + smoothFlags |= kPDPageDrawSmoothAATextDDR; + } + else if (strcmp(argv[curArg], "-smoothbicubic") == 0) //Note: needs to be combined with -antialias and -nosmoothimage + { + smoothFlags |= kPDPageImageResampleBicubic; + } + else if (strcmp(argv[curArg], "-smoothlinear") == 0) //Note: needs to be combined with -antialias and -nosmoothimage + { + smoothFlags |= kPDPageImageResampleLinear; // effectively equivalent to kPDPageDrawSmoothImage when combined with kPDPageImageAntiAlias + } + else if (strcmp(argv[curArg], "-antialias") == 0) //Note: needs to be combined -nosmoothimage and either -smoothbicubic or -smoothlinear + { + smoothFlags |= kPDPageImageAntiAlias; + } + else if (strcmp(argv[curArg], "-noannotfaces") == 0) + { + drawFlags &= ~kPDPageUseAnnotFaces; + } + else if (strcmp(argv[curArg], "-nolazyerase") == 0) + { + drawFlags &= ~kPDPageDoLazyErase; + } + else if (strcmp(argv[curArg], "-overprintpreview") == 0) + { + drawFlags |= kPDPageDisplayOverPrintPreview; + } + else if (strcmp(argv[curArg], "-abscolmetric") == 0) + { + parms.setRenderIntent(AC_AbsColorimetric); + } + else if (strcmp(argv[curArg], "-relcolmetric") == 0) + { + parms.setRenderIntent(AC_RelColorimetric); + } + else if (strcmp(argv[curArg], "-saturation") == 0) + { + parms.setRenderIntent(AC_Saturation); + } + else if (strcmp(argv[curArg], "-profileintent") == 0) + { + parms.setRenderIntent(AC_UseProfileIntent); + } + else if (strcmp(argv[curArg], "-gstateintent") == 0) + { + parms.setRenderIntent(AC_UseGStateIntent); + } + else if (strcmp(argv[curArg], "-rect") == 0) + { + bUseSpecifiedRect = TRUE; + fCropRect.left = FloatToASFixed(atof(argv[++curArg])); + fCropRect.bottom = FloatToASFixed(atof(argv[++curArg])); + fCropRect.right = FloatToASFixed(atof(argv[++curArg])); + fCropRect.top = FloatToASFixed(atof(argv[++curArg])); + } + else if (strcmp(argv[curArg], "-dest") == 0) + { + specifiedDest.left = atof(argv[++curArg]); + specifiedDest.bottom = atof(argv[++curArg]); + specifiedDest.right = atof(argv[++curArg]); + specifiedDest.top = atof(argv[++curArg]); + parms.setDestRect(&specifiedDest); + } + else if (strcmp(argv[curArg], "-matrix") == 0) + { + specifiedMatrix.a = atof(argv[++curArg]); + specifiedMatrix.b = atof(argv[++curArg]); + specifiedMatrix.c = atof(argv[++curArg]); + specifiedMatrix.d = atof(argv[++curArg]); + specifiedMatrix.h = atof(argv[++curArg]); + specifiedMatrix.v = atof(argv[++curArg]); + parms.setMatrix(&specifiedMatrix); + } + else + break; + ++curArg; + } + + std::string csInputFileName(argc > curArg ? argv[curArg] : DIR_LOC DEF_INPUT); + ++curArg; + std::string csOutputFileName(argc > curArg ? argv[curArg] : DEF_OUTPUT); + + if (parms.verbose()) + { + std::cout << "Rendering " << csInputFileName.c_str() << " to " << csOutputFileName.c_str() + << " with " << std::endl << " Resolution of " << parms.Resolution() << ", Colorspace " + << ASAtomGetString(parms.ColorSpaceName()) << ", and BPC " << parms.BitsPerComponent() << std::endl; + } + DURING + + // Open the input document and acquire the desired page + APDFLDoc inDoc(csInputFileName.c_str(), true); + PDPage pdPage = inDoc.getPage(pageNum); + + if (!bUseSpecifiedRect) + PDPageGetCropBox(pdPage, &fCropRect); + + ASFixedRect fOutRect; + PDRotate rotation = PDPageGetRotate(pdPage); + if (rotation == pdRotate90 || rotation == pdRotate270) + { + //if the source page is rotated perpendicular, then swap the dimensions for the output rect. + //ASFixedRect Members are: left,top,right,bottom + fOutRect = { fCropRect.bottom, fCropRect.right, fCropRect.top, fCropRect.left }; + } + else + fOutRect = fCropRect; + + if (parms.verbose()) + std::cout << "Rendering page " << pageNum << " area: " << ((fOutRect.right - fOutRect.left) * 0.125 / fixedNine) << " * " << ((fOutRect.top - fOutRect.bottom) * 0.125 / fixedNine) << " inches." << std::endl; + + + //if specified, only render the optional content for a particular layer (along with non-optional content), otherwise use the default currently visible layers. + PDOCContext curContext = PDDocGetOCContext(inDoc.getPDDoc()); + if (!ASTextIsEmpty(layerName)) + { + PDOCG* ocgs = PDPageGetOCGs(pdPage); + int limit = PDDocGetNumOCGs(inDoc.getPDDoc()); + int n = 0; + std::cout << "looking for: [" << reinterpret_cast(ASTextGetUnicodeCopy(layerName, kUTF8)) << "]" << std::endl; + while (n < limit && ocgs[n] != NULL) + { + std::cout << "layer: [" << reinterpret_cast(ASTextGetUnicodeCopy(PDOCGGetName(ocgs[n]), kUTF8)) << "]\n" << std::endl; + if (ASTextCmp(layerName, PDOCGGetName(ocgs[n])) == 0) + { + curContext = PDOCContextNew(kOCCInit_ON, NULL, NULL, inDoc.getPDDoc()); + PDOCG layers[2] = { NULL,ocgs[n] }; + ASBool state = true; + PDOCContextSetOCGStates(curContext, layers, &state); + } + ++n; + } + } + + parms.setOCContext(curContext); + parms.setDrawFlags(drawFlags); + parms.setSmoothFlags(smoothFlags); + parms.setOutputProfile(outputProfile); + + // Construction of the drawPage object does all the work to rasterize the page + RenderPage drawPage(pdPage, &fCropRect, &parms); + + DLPDEImageExportParams exportParams = DLPDEImageGetExportParams(); + exportParams.ExportHorizontalDPI = exportParams.ExportVerticalDPI = parms.Resolution(); + + ASPathName outPath; + ASText textToCreatePath = NULL; // Text object to create ASPathName + // Determine size of wchar_t on system and get the ASText + if (sizeof(wchar_t) == 2) + textToCreatePath = ASTextFromUnicode(reinterpret_cast(csOutputFileName.c_str()), kUTF16HostEndian); + else + textToCreatePath = ASTextFromUnicode(reinterpret_cast(csOutputFileName.c_str()), kUTF32HostEndian); + + outPath = ASFileSysCreatePathFromDIPathText(NULL, textToCreatePath, NULL); + + // The call to GetPDEImage synthesizes a PDEImage object from the rasterized PDF page + // created in the constructor, suitable for extracting to an image file. + PDEImage pageImage = drawPage.GetPDEImage(fOutRect); + + DLExportPDEImage(pageImage, outPath, ExportType_PNG, exportParams); + + // clean up + PDPageRelease(pdPage); + ASTextDestroy(textToCreatePath); + ASFileSysReleasePath(NULL, outPath); + PDERelease(reinterpret_cast(pageImage)); + + HANDLER + errCode = ERRORCODE; + libInit.displayError(errCode); + END_HANDLER + + if (outputProfile != nullptr) + ACUnReferenceProfile(outputProfile); + + return errCode; +}