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

Alw*_*uff 5 c++ printing winapi

介绍及相关资料:

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

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

在此输入图像描述

我使用C++WinAPI,在WindowsXP的SP3.我在MS Visual Studio 2008中工作.

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

问题:

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

副作用:

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

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

在此输入图像描述

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

在此输入图像描述

我努力解决这个任务:

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

我曾经习惯DrawTextEx执行断字(使用旗帜DT_WORDBREAK).

为了获得我使用的打印区域的大小GetDeviceCaps,并且为了获得打印机设备上下文,我使用了打印属性表.

问题:

重要说明:

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

你能解释一下如何调整细胞以使整个细胞系适合吗?

  1. 为什么我的字体不一致?

与往常一样,以下是创建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='*'\"")
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

2014年6月8日编辑:

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

2014年6月9日编辑:

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

对我来说唯一的事情是绘制一个合适的网格,以便文本可以正确地适应单元格.

2014年6月10日编辑:

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

 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
Run Code Online (Sandbox Code Playgroud)

为了安全起见,我会尽力坚持下去,GDI+但会在测试结果的基础上更新这篇文章,GDI并且会使用上面提到的等式,以防其他人偶然发现同样的问题.我只希望能节省那些人的时间......

Mar*_*som 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;
Run Code Online (Sandbox Code Playgroud)

关于您 6 月 10 日的编辑,它之所以有效,是因为您将字体做得小得多,以便它既适合全尺寸边界框又适合半尺寸边界框。我建议返回到字体大小的原始定义,这与大多数其他 Windows 应用程序一致。