预期和实际打印结果不匹配

时间:2014-06-07 14:52:25

标签: c++ winapi printing

引言及相关资料:

我试图通过尝试自己进行打印/打印预览来绕过我的应用程序中的另一个问题。

我正在尝试创建一个如下图所示的表格:

enter image description here

我在WindowsXP SP3上使用C++WinAPI。我在MS Visual Studio 2008中工作。

我没有打印机,所以我通过打印来测试结果 MS OneNote XPS file

问题:

文本从数据库获取,长度可变。由于它可能不适合原始单元格,我需要扩展单元格并适当地拟合文本,如上图所示。

副作用:

我的测试代码的结果给出了有关字体大小的不一致结果。

OneNote 中,打印结果似乎很好:

enter image description here

但是,在 XPS 中它看起来不同:

enter image description here

我努力解决这个问题:

我已查看 MSDN文档以开始使用。到目前为止,我能够在打印面上成功绘制文本和线条。

我使用DrawTextEx来执行分词(使用标记DT_WORDBREAK)。

要获得我使用GetDeviceCaps的打印区域的大小,并且为了获得打印机设备上下文,我使用了print property sheet

问题:

重要提示:

如果以下问题被认为过于宽泛,请发表评论,我将编辑我的帖子。我仍然认为我的错误很小,可以在一个帖子中解释。

<击> 1。你能解释一下如何调整单元格以使整个字符串适合吗?

  1. 为什么我的字体不一致?
  2. 与往常一样,以下是创建SSCCE的说明:

    1)在Visual Studio中,创建默认的Win32项目。

    2)在stdafx.h文件注释 #define WIN32_LEAN_AND_MEAN中,以便打印属性表可以正常工作。

    3)在stdafx.h中添加以下#include <windows.h>行:

    #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)在窗口过程上面添加以下功能:

    // 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.
    
            /***************** IMPORTANT INFO : ********************/
            /****** I have added additional test code here *********/
            /**** please refer to the edited part of this post *****/
            /***************** at the very bottom !! ***************/
    
            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)在WM_COMMAND处理程序中,修改case IDM_ABOUT,如下所示:

    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;
    

    于2014年6月8日编辑:

    在提交的 SSCCE 中阻止if ( ( hResult == S_OK ) && ( pdx.dwResultAction == PD_RESULT_PRINT ) )后,我已添加以下内容用于测试目的:

    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;
    }
    

    在这两种情况下,映射模式都是相同的(MM_TEXT)但是对于 XPS 我在xDPI = 600 , yDPI = 600中得到MessageBox OneNote xDPI = 300 , yDPI = 300

    这导致了成员* Carey Gregory *的评论是正确的结论 - &gt;具有相同特征的虚拟打印机将重现相同的结果。这也解释了为什么 OneNote 在我测试时正确打印到 XPS 中,以及为什么我的应用程序失败了。要解决这个问题,我需要找到DPI感知解决方案...

    于2014年6月9日编辑:

    使用GDI+创建字体和绘制文本我能够获得一致的结果(DPI不再是问题)。但是,如果有人知道如何仅使用GDI获得相同的结果,我仍然会感兴趣。

    唯一剩下的就是绘制一个合适的网格,以便文本可以正确地适应单元格。

    于2014年6月10日编辑:

    仔细阅读this MSDN link后,我能够改变字体创建代码以实现(在我看来)稳定的结果(字体的实际高度最终会变小,但我可以使用更大的数字):

     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
    

    为了安全起见,我会尽量坚持使用GDI+,但会在GDI时使用测试结果更新此帖子,并使用上述公式以防其他人偶然发现同一问题。我只是希望能节省那些人的时间......

1 个答案:

答案 0 :(得分:1)

问题很简单。您正在调整字体大小(以像素为单位)以匹配您要绘制的设备的DPI,但您调整矩形的大小。由于您的映射模式为MM_TEXT,因此两者都以像素为单位进行测量,并且您的边界框实际上是设备尺寸的一半,分辨率是其两倍。

解决方案是缩放矩形,类似于缩放字体大小的方式。在这种情况下,因为你确定这些坐标在300 DPI时是正确的,那就是我们相对于它的比例。

RECT r;
r.left = 0 * GetDeviceCaps(pdx.hDC, LOGPIXELSX) / 300;
r.top = 0 * GetDeviceCaps(pdx.hDC, LOGPIXELSY) / 300;
r.right = 550 * GetDeviceCaps(pdx.hDC, LOGPIXELSX) / 300;
r.bottom = 100 * GetDeviceCaps(pdx.hDC, LOGPIXELSX) / 300;

关于6月10日的编辑,它只能起作用,因为你已经使字体更小,以便它适合全尺寸边界框和半尺寸边框。我建议回到你对字体大小的原始定义,这与大多数其他Windows应用程序一致。