我正在使用Embarcadero RAD Studio C构建器XE7编译器 . 在一个应用程序项目中,我使用Windows GDI和GDI来绘制几个设备上下文 .
我的绘图内容是这样的:
在上面的示例中,文本背景和用户图片是使用GDI绘制的 . 用户图片也被剪切为圆形路径 . 所有其他项目(文本和表情符号)都是使用GDI绘制的 .
当我画到屏幕DC时,一切正常 .
现在我想在打印机设备上下文中绘图 . 我用于测试的是Windows 10中新增的“导出到PDF”打印机设备 . 我准备设备上下文以这种方式在A4视口上绘制:
HDC GetPrinterDC(HWND hWnd) const
{
// initialize the print dialog structure, set PD_RETURNDC to return a printer device context
::PRINTDLG pd = {0};
pd.lStructSize = sizeof(pd);
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDC;
// get the printer DC to use
::PrintDlg(&pd);
return pd.hDC;
}
...
void Print()
{
HDC hDC = NULL;
try
{
hDC = GetPrinterDC(Application->Handle);
const TSize srcPage(793, 1123);
const TSize dstPage(::GetDeviceCaps(hDC, PHYSICALWIDTH), ::GetDeviceCaps(hDC, PHYSICALHEIGHT));
const TSize pageMargins(::GetDeviceCaps(hDC, PHYSICALOFFSETX), ::GetDeviceCaps(hDC, PHYSICALOFFSETY));
::SetMapMode(hDC, MM_ISOTROPIC);
::SetWindowExtEx(hDC, srcPage.Width, srcPage.Height, NULL);
::SetViewportExtEx(hDC, dstPage.Width, dstPage.Height, NULL);
::SetViewportOrgEx(hDC, -pageMargins.Width, -pageMargins.Height, NULL);
::DOCINFO di = {sizeof(::DOCINFO), config.m_FormattedTitle.c_str()};
::StartDoc (hDC, &di);
// ... the draw function is executed here ...
::EndDoc(hDC);
return true;
}
__finally
{
if (hDC)
::DeleteDC(hDC);
}
}
在StartDoc()和EndDoc()函数之间执行的绘制函数与我在屏幕上绘制的绘图函数完全相同 . 唯一的区别是我在整个页面上添加了一个全局剪切矩形,以避免当尺寸过大时绘图重叠在页边距上,例如当我在第一张图片下重复几次上面的图画时 . (这是实验性的,稍后我会添加一个页面切割过程,但这不是现在的问题)
这是我的剪辑功能:
int Clip(const TRect& rect, HDC hDC)
{
// save current device context state
int savedDC = ::SaveDC(hDC);
HRGN pClipRegion = NULL;
try
{
// reset any previous clip region
::SelectClipRgn(hDC, NULL);
// create clip region
pClipRegion = ::CreateRectRgn(rect.Left, rect.Top, rect.Right, rect.Bottom);
// select new canvas clip region
if (::SelectClipRgn(hDC, pClipRegion) == ERROR)
{
DWORD error = ::GetLastError();
::OutputDebugString(L"Unable to select clip region - error - " << ::IntToStr(error));
}
}
__finally
{
// delete clip region (it was copied internally by the SelectClipRgn())
if (pClipRegion)
::DeleteObject(pClipRegion);
}
return savedDC;
}
void ReleaseClip(int savedDC, HDC hDC)
{
if (!savedDC)
return;
if (!hDC)
return;
// restore previously saved device context
::RestoreDC(hDC, savedDC);
}
如上所述,我期望在我的页面周围剪辑 . 但结果只是一个空白页面 . 如果我绕过裁剪功能,则所有内容都会正确打印,但绘图可能会在页边距上重叠 . 另一方面,如果我在屏幕上的任意矩形上应用剪辑,一切正常 .
我剪裁的错误是什么?当我启用它时,为什么页面完全被破坏了?
1 回答
所以我发现了什么问题 . 尼基接近解决方案 . 剪切函数似乎始终以像素为单位应用于页面,忽略坐标系和视口定义的单位 .
在我的例子中,传递给CreateRectRgn()函数的值是错误的,因为它们保持未被视口转换,尽管剪辑是在设备上下文中设置视口后应用的 .
这使得问题的识别变得困难,因为剪辑在读取代码时显示为已转换,因为它在视口之后应用,就在处理图形之前 .
我不知道这是一个GDI错误还是一个希望的行为,但遗憾的是我从未在我阅读的有关剪辑的所有文档中看到过这个细节 . 虽然我觉得剪辑不受视口的影响似乎很重要 .