Drawing Objects Within Layers

Within a StartPage/EndPage sequence of calls, calling SetLayer with a valid layer title will start a new layer and place all subsequent drawing instructions within that layer. Calling SetLayer with an empty string will end the last layer.

If multiple layers are nested, multiple calls to SetLayer with an empty string are needed to close all layers. If all layers are not closed at the call to EndPage, the PDF printer will automatically close all open layers. for example:

 

// start parent layer
SetLayer( hDC, L"Blue Layer" );
SetTextColor( hDC, RGB(0, 0, 255) );
TextOut( hDC, 200, yPos, buf, lstrlen(buf) );
yPos += 200;
 
// start blue sub-layer 1
SetLayer( hDC, L"Blue Layer - 1" );
SetTextColor( hDC, RGB(0, 0, 128) );
TextOut( hDC, 800, yPos, "Blue Layer - 1", lstrlen("Blue Layer - 1") );
yPos += 200;
SetLayer( hDC, L"" ); // close blue sub-layer 1
SetLayer( hDC, L"" ); // close blue parent layer

 

The sequence of opening and closing layers should be consistent with the order string for the PDF viewer to show or hide layers with their sub-layers in a consistent way.

 

Example

#include <Windows.h>

#include <windowsx.h>

#include <iostream>

#include <tchar.h>

#define LAYERS

 

using namespace std;

 

#define    ESCAPE_SETALPHA        247

#define    ESCAPE_SETLAYER        248

#define    ESCAPE_SETMARKEDCONTENT        246

#define    ESCAPE_SETBOOKMARK        250        // set bookmark on current drawing operation

#define    ESCAPE_SETHYPERLINK        252        // set hyperlink on current drawing operation

 

void SetLayer( HDC hDC, LPWSTR LayerInfo )

{

#ifdef LAYERS

    switch ( ExtEscape( hDC, ESCAPE_SETLAYER, (int)((wcslen(LayerInfo) + 1) * sizeof(LayerInfo[0])), (LPCSTR)LayerInfo, 0, NULL ) )

    {

    default:

        // positive return indicates success

    case 0:

        // no error

        return;

 

    case -1:

        // error occured, closing a layer when none was open

        break;

 

    case -2:

        // memory allocation error (low memory or invalid string)

        break;

    }

#endif

}

 

void SetMarkedContent( HDC hDC, LPSTR LayerInfo )

{

    switch ( ExtEscape( hDC, ESCAPE_SETMARKEDCONTENT, (int)((strlen(LayerInfo) + 1) * sizeof(LayerInfo[0])), (LPCSTR)LayerInfo, 0, NULL ) )

    {

    default:

        // 0 or positive return indicates success

        return;

 

    case -1:

        // error occured, closing a layer when none was open        

        break;

 

    case -2:

        // memory allocation error (low memory or invalid string)        

        break;

    }

}

 

// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // /

long SetAlphaLevel( HDC hdc, int lStrokeAlpha, int lFillAlpha )

// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // /

{

#pragma pack( push, 1 )

    struct

    {

        int    lStrokeAlpha;

        int    lFillAlpha;

    } Alpha;

 

    Alpha.lStrokeAlpha = lStrokeAlpha;

    Alpha.lFillAlpha = lFillAlpha;

 

    return ExtEscape( hdc, ESCAPE_SETALPHA, sizeof(Alpha), (LPCSTR)&Alpha, 0, NULL );

#pragma pack( pop )

}

 

long SetBookmark(HDC hdc, long lParent, LPCTSTR szTitle)

{

#pragma pack( push, 1 )

    struct

    {

        long    lParent;

        char    szTitle[255];

    } Bookmark;

 

    Bookmark.lParent = lParent;

    lstrcpyn( Bookmark.szTitle, szTitle, 254 );

 

    return ExtEscape( hdc, ESCAPE_SETBOOKMARK, sizeof( DWORD ) + lstrlen( Bookmark.szTitle ) + 1,

                        (LPCSTR)&Bookmark, 0, NULL );

#pragma pack( pop )

}

 

long SetHyperLink(HDC hdc, LPCTSTR szURL)

{

    return ExtEscape( hdc, ESCAPE_SETHYPERLINK, lstrlen( szURL ) + 1,

                        szURL, 0, NULL );

}

 

int _tmain(int argc, _TCHAR* argv[])

{

    int        ret = 0;

    HDC        hDC = NULL;

    HFONT    hfnt = NULL;

    LPDEVMODE    devmode = NULL;

    int            PageCount = 2;

 

    cout << "Press ENTER to continue..." << endl;

    cin.get();

 

    while( TRUE )

    {

        if ( argc < 2 )

        {

            cout << "Usage: LayerSample \"Printer Name\" PageCount\n";

            ret = -1;

            break;

        }

 

        // create device context from printer

        hDC = CreateDC( NULL, argv[1], NULL, NULL );

        if ( hDC == NULL )

        {

            cout << "Failed to retrieve printer DC for printer " << argv[1] << "\n";

            ret = -2;

            break;

        }

 

        // get information about the printer

        HANDLE    hPrinter = NULL;

        DWORD    dw = 0;

 

        if ( !OpenPrinter( argv[1], &hPrinter, NULL )

             || hPrinter == NULL

             )

        {

            ret = -3;

            break;

        }

 

        // get default devmode

        if ( (dw = DocumentProperties( NULL, hPrinter, argv[1], 

                                                NULL, NULL, 0 )) <= 0 )

        {

            ret = -4;

            break;

        }

 

        devmode = (LPDEVMODE)GlobalAllocPtr( GHND, dw );

        DocumentProperties( NULL, hPrinter, argv[1], devmode, NULL, DM_OUT_BUFFER );

        ClosePrinter( hPrinter );

 

        // read the page count from the command line

        if ( argc > 2 && argv[2][0] )

        {

            PageCount = atol( argv[2] );

        }

 

        // start new document

        DOCINFO        di;

        int            nPage = 1;

        memset( &di, 0, sizeof(di) );

        di.cbSize = sizeof(di);

        di.lpszDocName = _T("Layered PDF");

 

        // check if PDF printer supports layers

        CHAR    technology[4];

        int        escape = ESCAPE_SETLAYER;

        ExtEscape( hDC, GETTECHNOLOGY, 0, NULL, sizeof(technology), (LPSTR)technology );

        // the technology should be PDF

        if ( lstrcmp(technology, "PDF") )

        {

            MessageBox( 0, "Not an Amyuni PDF driver", "Error", MB_ICONERROR );

        }

 

        // and support the SETLAYER escape

        if ( !ExtEscape( hDC, QUERYESCSUPPORT, sizeof(escape), (LPCSTR)&escape, 0, NULL ) )    

        {

            MessageBox( 0, "Not an Amyuni PDF driver", "Error", MB_ICONERROR );

        }

 

        // trigger PDF-1.5 header

        SetLayer( hDC, L"" );

 

        ret = StartDoc( hDC, &di );

        if ( ret < 0 )

        {

            cout << "StartDoc failed\n";

            break;

        }

 

        for ( nPage = 1; nPage <= PageCount; nPage++ )

        {

            cerr << "********** Printing page " << nPage << " **********\n";

 

            ret = StartPage( hDC );

            if ( ret < 0 )

            {

                cout << "StartPage failed\n";

                // fall through

            }

 

            LOGFONT    lf;

            HFONT    oldFont;

            CHAR    buf[255];

            int        yPos = 200;

            memset( &lf, 0, sizeof(lf) );

            lstrcpy( lf.lfFaceName, "Arial" );

            lf.lfHeight = -48;

            hfnt = CreateFontIndirect( &lf );

            oldFont = (HFONT)SelectObject( hDC, hfnt );

            SetBkMode( hDC, TRANSPARENT );

 

            // set a bookmark on page 1

            wsprintf( buf, "Bookmark %d", nPage );

            SetBookmark(hDC, 0, buf);

 

            SetLayer( hDC, L"Blue Layer" );

            SetTextColor( hDC, RGB(0, 0, 255) );

            wsprintf( buf, "Page %d", nPage );

            TextOut( hDC, 200, yPos, buf, lstrlen(buf) );

            yPos += 200;

 

            SetLayer( hDC, L"Blue Layer - 1" );

            SetTextColor( hDC, RGB(0, 0, 128) );

            TextOut( hDC, 800, yPos, "Blue Layer - 1", lstrlen("Blue Layer - 1") );

            yPos += 200;

            SetLayer( hDC, L"" );    // close blue sub-layer 1

 

            if ( nPage == 1 )

                // add a hyperlink to our web site

                SetHyperLink(hDC, "http:// www.amyuni.com");

            else

                // set a hyperlink to page 1

                SetHyperLink(hDC, "#Bookmark 1");

 

            SetLayer( hDC, L"Green Layer\t1" );

            SetTextColor( hDC, RGB(0, 128, 0) );

            TextOut( hDC, 800, yPos, "Green Sub Layer", lstrlen("Green Sub Layer") );

            yPos += 200;

            SetLayer( hDC, L"" );    // close blue sub-layer 2

 

            SetLayer( hDC, L"" );    // close blue layer

 

            SetLayer( hDC, L"Red Layer" );

            SetTextColor( hDC, RGB(255, 0, 0) );

            TextOut( hDC, 200, yPos, buf, lstrlen(buf) );

            yPos += 200;

            SetLayer( hDC, L"" );    // close red layer

 

            SetLayer( hDC, L"Green Layer" );

            SetMarkedContent( hDC, "Figure" );            

            SetAlphaLevel( hDC, 100, 20 );

 

            // draw a green rectangle to show the effects of Alpha levels

            HBRUSH    hbr = CreateSolidBrush( RGB(0, 200, 0) );

            RECT    rc = { 200, yPos - 800, 2000, yPos - 200 };

            FillRect( hDC, &rc, hbr );

            DeleteObject( hbr );

 

            yPos += 200;

 

            // restore the alpha level

            SetAlphaLevel( hDC, 100, 100 );

 

            SetMarkedContent( hDC, "" ); // close marked content

            SetLayer( hDC, L"" );    // close green layer

 

            cout << "Printed one single line\n";

 

            SelectObject( hDC, oldFont );

 

            ret = EndPage( hDC );

            if ( ret < 0 )

            {

                cout << "EndPage failed\n";

                // fall through

            }

        }

 

        // set the order and radio/button groups of layers

        SetLayer( hDC, L"/Order[(Blue Layer)[(Blue Layer - 1)(Green Layer\t1)](Red Layer)(Green Layer)]" \

                       L"/RBGroups[[(Red Layer)(Green Layer)]]/OFF[(Red Layer)]/ON[(Green Layer)]"

                       );

 

        ret = EndDoc( hDC );

        if ( ret < 0 )

        {

            cout << "EndDoc failed\n";

            break;

        }

 

        break;

    }

 

    if ( hfnt != NULL )

    {

        DeleteObject( hfnt );

    }

 

    if ( hDC != NULL )

    {

        DeleteDC( hDC );

    }

 

    if ( devmode != NULL )

    {

        GlobalFreePtr( devmode );

    }

 

    if ( ret < 0 )

    {

        cerr << "Error code " << ret << ", GetLastError returned " << GetLastError() << "\n";

    }

 

    cout << "Press ENTER to continue..." << endl;

    cin.get();

    return ret;

}