Click here to Skip to main content
15,883,839 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
INTRODUCTION AND RELEVANT INFORMATION:

I am trying to bypass another problem in my application by trying to do printing/print preview on my own.

I am trying to create a table that would look like in this picture[^].

I am using C++ and WinAPI, on WindowsXP SP3. I work in MS Visual Studio 2008.

I do not have a printer, so I am testing the results by printing to MS OneNote and XPS file.

PROBLEM:

Text is obtained from database and is of variable length. Since it might not fit into the original cell, I will need to expand the cell and fit the text appropriately, like in the above image.

SIDE EFFECT:

The result of my testing code gives inconsistent results regarding font size.

In OneNote the print result[^] seems fine, however, in XPS it looks different[^].

MY EFFORTS TO SOLVE THIS TASK:

I have checked MSDN documentation to get started. So far I am able to successfully draw text and lines on a printing surface.

I have used DrawTextEx [^] to perform word breaking ( by using flag DT_WORDBREAK ).

To obtain the size of the printing area I have used GetDeviceCaps[^], and to obtain printer device context I have used print property sheet[^].

QUESTIONS:

IMPORTANT REMARKS:

If the following questions are considered too broad please leave a comment and I will edit my post. I still believe that my mistakes are minor and can be explained in a single post.

1. Can you explain me how to adjust cells so the entire string can fit ?

2. Why is my font inconsistently drawn ?

As always, here are the instructions for creating SSCCE :

1) In Visual Studio, create default Win32 project.

2) in stdafx.h file comment out #define WIN32_LEAN_AND_MEAN so print property sheet can work properly.

3) In stdafx.h add the following, below #include <windows.h> line :

C++
#include <windowsx.h>
#include <commctrl.h>

#pragma comment( linker, "/manifestdependency:\"type='win32' \
    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
    language='*'\"")


4) Add the following function above window procedure :
C++
// hWnd is the window that owns the property sheet.
HRESULT DisplayPrintPropertySheet(HWND hWnd)
{
    HRESULT hResult;
    PRINTDLGEX pdx = {0};
    LPPRINTPAGERANGE pPageRanges = NULL;

    // Allocate an array of PRINTPAGERANGE structures.
    pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));

    if (!pPageRanges)
        return E_OUTOFMEMORY;

    //  Initialize the PRINTDLGEX structure.
    pdx.lStructSize = sizeof(PRINTDLGEX);
    pdx.hwndOwner = hWnd;
    pdx.hDevMode = NULL;
    pdx.hDevNames = NULL;
    pdx.hDC = NULL;
    pdx.Flags = PD_RETURNDC;
    pdx.Flags2 = 0;
    pdx.ExclusionFlags = 0;
    pdx.nPageRanges = 0;
    pdx.nMaxPageRanges = 10;
    pdx.lpPageRanges = pPageRanges;
    pdx.nMinPage = 1;
    pdx.nMaxPage = 1000;
    pdx.nCopies = 1;
    pdx.hInstance = 0;
    pdx.lpPrintTemplateName = NULL;
    pdx.lpCallback = NULL;
    pdx.nPropertyPages = 0;
    pdx.lphPropertyPages = NULL;
    pdx.nStartPage = START_PAGE_GENERAL;
    pdx.dwResultAction = 0;

    //  Invoke the Print property sheet.

    hResult = PrintDlgEx(&pdx);

    if ( ( hResult == S_OK ) 
        && ( pdx.dwResultAction == PD_RESULT_PRINT ) )
    {

        // User clicked the Print button, 
        // so use the DC and other information returned in the 
        // PRINTDLGEX structure to print the document.

        DOCINFO diDocInfo = {0};
        diDocInfo.cbSize = sizeof( DOCINFO ); 
        diDocInfo.lpszDocName = L"Testing printing...";

        //******************** initialize testing font *****************//

        HFONT font, oldFont; 

        long lfHeight = -MulDiv( 14, GetDeviceCaps( pdx.hDC, LOGPIXELSY), 72 );

        font = CreateFont( lfHeight, 
            0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE, 0, 0, 0, 
            0, 0, L"Microsoft Sans Serif" );

        oldFont = SelectFont( pdx.hDC, font );

        SetBkMode( pdx.hDC, TRANSPARENT );

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

        //******************** end of initialization ******************//

        if( StartDoc( pdx.hDC, &diDocInfo ) > 0 )
        {
            if( StartPage( pdx.hDC ) > 0 )
            {
                // get paper dimensions
                int pageWidth, pageHeight;

                pageWidth = GetDeviceCaps( pdx.hDC, HORZRES );
                pageHeight = GetDeviceCaps( pdx.hDC, VERTRES );

                /************ draw a testing grid ***************/

                // draw vertical lines of the grid
                for( int i = 0; i < pageWidth; i += pageWidth / 4 )
                {
                     MoveToEx( pdx.hDC, i, 0, NULL );
                     LineTo( pdx.hDC, i, pageHeight );
                }

                // draw horizontal lines of the grid
                for( int j = 0; j < pageHeight; j += pageWidth / 10 )
                {
                     MoveToEx( pdx.hDC, 0, j, NULL );
                     LineTo( pdx.hDC, pageWidth, j );
                }

                /************************************************/

                // test rectangle for drawing the text
                RECT r;
                r.left = 0;
                r.top = 0;
                r.right = 550;
                r.bottom = 100;

                // fill rectangle with light gray brush
                // so we can see if text is properly drawn

                FillRect( pdx.hDC, &r, 
                    (HBRUSH)GetStockObject(LTGRAY_BRUSH) );

                // draw text in test rectangle 
                if( 0 == DrawTextEx( pdx.hDC, 
                     L"This is test string!", 
                     wcslen( L"This is test string!" ), 
                     &r, 
                     DT_CENTER | DT_WORDBREAK | DT_NOCLIP, NULL ) )
                     // for now pop a message box saying something went wrong
                     MessageBox( hWnd, L"DrawText failed!", L"Error", MB_OK );

                if( EndPage( pdx.hDC ) < 0 )
                     // for now pop a message box saying something went wrong
                     MessageBox( hWnd, L"EndDoc failed!", L"Error", MB_OK );
            }

            EndDoc( pdx.hDC );

            SelectFont( pdx.hDC, oldFont );
            DeleteFont( font );
        }
    }

    if (pdx.hDevMode != NULL) 
        GlobalFree(pdx.hDevMode); 

    if (pdx.hDevNames != NULL) 
        GlobalFree(pdx.hDevNames); 

    if (pdx.lpPageRanges != NULL)
        GlobalFree(pPageRanges);

    if (pdx.hDC != NULL) 
        DeleteDC(pdx.hDC);

    return hResult;
}


5) In WM_COMMAND handler, modify case IDM_ABOUT like this :

C++
case IDM_ABOUT:   // test our printing here
    {
        if( FAILED( DisplayPrintPropertySheet( hWnd ) ) )
            MessageBox( hWnd, 
                L"Can't display print property sheet!", 
                L"Error", MB_OK );
    }
    //DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
    break;



EDITED on June, 8th 2014 :

After the block if ( ( hResult == S_OK ) && ( pdx.dwResultAction == PD_RESULT_PRINT ) ) in the submitted SSCCE I have added the following for testing purposes :

C++
int xDpi = GetDeviceCaps( pdx.hDC, LOGPIXELSX ),
    yDpi = GetDeviceCaps( pdx.hDC, LOGPIXELSY );

int mapMode = GetMapMode( pdx.hDC );

wchar_t displayDPI[50];
swprintf_s( displayDPI, 50, L" xDPI = %s , yDPI = %s", xDpi, yDpi );
MessageBox( hWnd, displayDPI, L"", MB_OK );

switch( mapMode )
{
case MM_ANISOTROPIC:
    MessageBox( hWnd, L"MM_ANISOTROPIC", L"", MB_OK );
    break;
case MM_HIENGLISH:
    MessageBox( hWnd, L"MM_HIENGLISH", L"", MB_OK );
    break;
case MM_HIMETRIC:
    MessageBox( hWnd, L"MM_HIMETRIC", L"", MB_OK );
    break;
case MM_ISOTROPIC:
    MessageBox( hWnd, L"MM_ISOTROPIC", L"", MB_OK );
    break;
case MM_LOENGLISH:
    MessageBox( hWnd, L"MM_LOENGLISH", L"", MB_OK );
    break;
case MM_LOMETRIC:
    MessageBox( hWnd, L"MM_LOMETRIC", L"", MB_OK );
    break;
case MM_TEXT:
    MessageBox( hWnd, L"MM_TEXT", L"", MB_OK );
    break;
case MM_TWIPS:
    MessageBox( hWnd, L"MM_TWIPS", L"", MB_OK );
    break;
default:
    MessageBeep(0);
    break;
}


In both cases mapping mode was the same ( MM_TEXT ) but for XPS I got xDPI = 600 , yDPI = 600 in the MessageBox while OneNote had xDPI = 300 , yDPI = 300.

This leads to the conclusion that with the same characteristics virtual printers will reproduce the same result. This also explains why OneNote printed properly into XPS when I tested it, and why my application failed. To solve this problem I need to find DPI aware solution...

EDITED on June, 9th 2014 :

Using GDI+ to create font and draw text I was able to get consistent results ( DPI is no longer a problem ). Still, if anyone knows how to achieve the same result using only GDI I would be still interested.

The only thing left for me is to draw a proper grid so the text can fit into cells properly.

EDITED on June, 10th 2014 :

After carefully reading through this MSDN link[^] I was able to alter the font creating code to achieve ( in my opinion ) stable results :
C++
font = CreateFont( 
    // DPI aware, thanks to the below equation ( or so it seems... )
    lfHeight / ( GetDeviceCaps( pdx.hDC, LOGPIXELSY ) / 96 ), 
    0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE, 0, 0, 0,      // remained the same
    0, 0, L"Microsoft Sans Serif" );                    // remained the same

Just to be safe, I will try to stick with GDI+ but will update this post with the testing results when GDI and the mentioned equation is used in case someone else stumbles upon the same problem. I just hope it will save that persons time...
Posted
Updated 10-Jun-14 9:13am
v5
Comments
Richard MacCutchan 11-Jun-14 4:34am    
Since you seem to have discovered and solved all the problems, it may be a good idea to write this up as a Tip and publish it.
AlwaysLearningNewStuff 11-Jun-14 16:31pm    
I haven't found a way to properly adjust cell rectangles so I can properly position text.

I believe that I will be able to solve this too, so if you could explain me what Tip is and how to write it I would appreciate it ( I am always for helping others as much as I can... ).
Richard MacCutchan 12-Jun-14 3:22am    
Tips & Tricks, are just short articles showing how to overcome some issue or problem that many developers may face from time to time. See http://www.codeproject.com/info/Submit.aspx and http://www.codeproject.com/KB/FAQs/ArticleFAQ.aspx for details on how to write up and submit.
AlwaysLearningNewStuff 12-Jun-14 10:00am    
Thank you. I will do that once I figure out how to adjust GDI equation to have proper size of the font. Currently, font size is way too small, although the results seem DPI consistent. For now, the safest seems to just use GDI+.
Richard MacCutchan 12-Jun-14 11:13am    
I use the following expression to get the font size, which is based on the use of points, each of which is 1/72 of an inch.

int points = MulDiv(10, GetDeviceCaps(hDC, LOGPIXELSY), 72);
hFont = CreateFont(-points, 0, 0, 0, FW_NORMAL, 0, 0, 0, 0, 0, 0, 0, 0, _T("Courier New"));

See http://msdn.microsoft.com/en-gb/library/windows/desktop/dd183499(v=vs.85).aspx for more details.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900