预期和实际打印结果不匹配 [英] Expected and actual printing results do not match

查看:187
本文介绍了预期和实际打印结果不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

介绍及相关资讯:



我试图绕过我应用程式中的其他问题,尝试自行执行列印/列印预览。



我试图创建一个表格,如下图所示:





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



我没有打印机,因此我打印到 MS OneNote XPS档案

PROBLEM:



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



副效果:



我的测试代码的结果与字体大小有关。



中打印结果似乎很好:





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





我的努力解决这个任务:



我已经选中了 MSDN文档开始。到目前为止,我能够在打印表面上成功绘制文本和线条。



我使用了 DrawTextEx 通过使用标志 DT_WORDBREAK )。



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



问题:



重要备注:



如果以下问题被视为过于广泛,请留下评论,我将编辑我的帖子。我仍然认为我的错误是轻微的,可以在一个单一的文章中解释。



1。


  1. 为什么我的字体不一致地绘制? li>

一如既往,以下是有关创建 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是窗口拥有属性表。 
HRESULT DisplayPrintPropertySheet(HWND hWnd)
{
HRESULT hResult;
PRINTDLGEX pdx = {0};
LPPRINTPAGERANGE pPageRanges = NULL;

//分配一个PRINTPAGERANGE结构的数组。
pPageRanges =(LPPRINTPAGERANGE)GlobalAlloc(GPTR,10 * sizeof(PRINTPAGERANGE));

if(!pPageRanges)
return E_OUTOFMEMORY;

//初始化PRINTDLGEX结构。
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;

//调用打印属性表。

hResult = PrintDlgEx(& pdx);

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

//用户点击打印按钮
//因此使用DC和其他在
// PRINTDLGEX结构中返回的信息来打印文档。

/ *****************重要信息:******************** /
/ ******我已经在这里添加了额外的测试代码********* /
/ ****请参阅本文的编辑部分**** * /
/ *****************在底部! *************** /

DOCINFO diDocInfo = {0};
diDocInfo.cbSize = sizeof(DOCINFO);
diDocInfo.lpszDocName = L测试打印...;

// ********************初始化测试字体***************** //

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, Serif);

oldFont = SelectFont(pdx.hDC,font);

SetBkMode(pdx.hDC,TRANSPARENT);

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

// ********************初始化结束***************** * //

if(StartDoc(pdx.hDC,& diDocInfo)> 0)
{
if(StartPage(pdx.hDC)> 0)
{
//获取纸张尺寸
int pageWidth,pageHeight;

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

/ ************绘制测试网格*************** /

/ /绘制网格的垂直线
for(int i = 0; i {
MoveToEx(pdx.hDC,i,0,NULL );
LineTo(pdx.hDC,i,pageHeight);
}

//绘制网格的水平线
for(int j = 0; j< pageHeight; j + = pageWidth / 10)
{
MoveToEx(pdx.hDC,0,j,NULL);
LineTo(pdx.hDC,pageWidth,j);
}

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

//用于绘制文本的测试矩形
RECT r;
r.left = 0
r.top = 0;
r.right = 550;
r.bottom = 100;

//用浅灰色的笔刷填充矩形
//以便我们可以看到文本是否正确绘制

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

//在测试矩形中绘制文本
if(0 == DrawTextEx(pdx.hDC,
LThis is test string!,
wcslen 这是测试字符串!),
& r,
DT_CENTER | DT_WORDBREAK | DT_NOCLIP,NULL))
//现在弹出一个消息框,说出错了
MessageBox(hWnd,LDrawText failed!,LError,MB_OK);

if(EndPage(pdx.hDC)< 0)
//现在弹出一个消息框说出错了
MessageBox(hWnd,LEndDoc failed! ,LError,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: /测试我们的打印
{
if(FAILED(DisplayPrintPropertySheet(hWnd)))
MessageBox(hWnd,
L无法显示打印属性表! $ b LError,MB_OK);
}
// DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUTBOX),hWnd,About);
break;



已于2014年6月8日更改:



在提交的 SSCCE I中的块 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,LxDPI =%s,yDPI =%s,xDpi,yDpi);
MessageBox(hWnd,displayDPI,L,MB_OK);

switch(mapMode)
{
case MM_ANISOTROPIC:
MessageBox(hWnd,LMM_ANISOTROPIC,L,MB_OK);
break;
case MM_HIENGLISH:
MessageBox(hWnd,LMM_HIENGLISH,L,MB_OK);
break;
case MM_HIMETRIC:
MessageBox(hWnd,LMM_HIMETRIC,L,MB_OK);
break;
case MM_ISOTROPIC:
MessageBox(hWnd,LMM_ISOTROPIC,L,MB_OK);
break;
case MM_LOENGLISH:
MessageBox(hWnd,LMM_LOENGLISH,L,MB_OK);
break;
case MM_LOMETRIC:
MessageBox(hWnd,LMM_LOMETRIC,L,MB_OK);
break;
case MM_TEXT:
MessageBox(hWnd,LMM_TEXT,L,MB_OK);
break;
case MM_TWIPS:
MessageBox(hWnd,LMM_TWIPS,L,MB_OK);
break;
默认值:
MessageBeep(0);
break;
}

在这两种情况下,映射模式都是相同的( MM_TEXT MessageBox 中获得 xDPI = 600,yDPI = 600 / code>,而 OneNote xDPI = 300,yDPI = 300



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



于2014年6月9日修改:



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





2014年6月10日修改:



仔细阅读

  font = CreateFont(
//知道DPI,感谢以下方程式)
lfHeight /(GetDeviceCaps(pdx.hDC,LOGPIXELSY)/ 96),
0,0,0,FW_BOLD,TRUE,FALSE,FALSE,0,0,0,//保持不变
0,0,LMicrosoft Sans Serif); //保持不变

为了安全起见,我会尽力保持 GDI + ,但是当 GDI 时,将使用测试结果更新此帖子,并且所述等式用于其他人绊倒同样的问题。我只是希望它会节省那个人的时间...

问题很简单。您正在调整字体大小(以像素为单位),以匹配您要绘制的设备的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应用程序一致。


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 the picture below:

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 ?

  1. 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 :

#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 :

// 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) In WM_COMMAND handler, modify case IDM_ABOUT like this :

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 :

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 comments made by member * Carey Gregory* were correct -> 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 ( the actual height of the font ends up way smaller, but I could use bigger number I guess ) :

 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...

解决方案

The problem is simple. You are adjusting the font size (in pixels) to match the DPI of the device you're drawing to, but you're not adjusting the size of the rectangle. Since your mapping mode is MM_TEXT both are measured in pixels, and your bounding box is effectively half the size on the device with twice the resolution.

The solution is to scale the rectangle similarly to the way you scale the font size. In this case since you've determined that these coordinates are correct at 300 DPI, that's the constant we'll scale relative to.

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;

Regarding your edit of June 10, it only works because you've made the font much smaller so that it fits in both the full-size bounding box and the half-size one. I'd recommend going back to the original definition you had for font size, which is consistent with most other Windows applications.

这篇关于预期和实际打印结果不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆