打印:错误的底边距
Print: Wrong bottom margin
我正在打印一个编辑控件 - 首先将其内容复制到一个 rich edit 控件(至少 2.0 - 可能是 rich edit 3.0) - 然后从那里打印。
一切正常...但边距草率/不正确。
我反复检查了代码,验证了调试器中的数字,但打印页面上的边距(对于我可用于测试的打印机)仍然出现错误。
该代码或多或少是从网上可用的丰富编辑打印的各种示例的副本 - 包括旧新事物、MSDN 的文档和 CodeProject 等。
归结为(hdc是针对使用用户选择的纸张来源的用户选择的打印机):
// printer dot's per inch (pixels)
const CSize dpi = { GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY) };
// paper size (in printer's dots ~ pixels)
const CSize paper = { GetDeviceCaps(hdc, PHYSICALWIDTH), GetDeviceCaps(hdc, PHYSICALHEIGHT) };
// printable size (pixels) - this defines the largest possible usable rect for this printer
const CRect rcPrintable(CPoint(GetDeviceCaps(hdc, PHYSICALOFFSETX), GetDeviceCaps(hdc, PHYSICALOFFSETY)), CSize(GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES)));
// determine the paper extents using our desired margins without violating the device's minimum margins
const CSize margin = { dpi.cx / 4, dpi.cy / 2 }; // 1/4" horizontal x 1/2" vertical margins
const CRect rcPaper(__max(rcPrintable.left, margin.cx), __max(rcPrintable.top, margin.cy), __min(rcPrintable.right, paper.cx - margin.cx), __min(rcPrintable.bottom, paper.cy - margin.cy));
// convert paper size in printer dots (pixels) to paper size in TWIPS
const CRect rcPage(MulDiv(rcPaper.left, 1440, dpi.cx), MulDiv(rcPaper.top, 1440, dpi.cy), MulDiv(rcPaper.right, 1440, dpi.cx), MulDiv(rcPaper.bottom, 1440, dpi.cy));
// build our format range data
FORMATRANGE fr;
Zero(fr);
fr.hdc = hdc;
fr.hdcTarget = hdc;
// Set page rect to physical page size in TWIPS
fr.rc = rcPage;
fr.rcPage = rcPage;
// set our target device to the printer
m_RichEdit.SetTargetDevice(hdc, rcPage.Width());
m_RichEdit.SetSel(0, -1); // Select the entire contents.
m_RichEdit.GetSel(fr.chrg); // Get the selection into a CHARRANGE
// track the number of pages generated
unsigned nPages = 0;
// give this job a reasonable name
CString strPrintJobName = GetPrintJobName();
// Use GDI to print successive pages
DOCINFO di = { sizeof(di) };
di.lpszDocName = strPrintJobName;
if (!StartDoc(hdc, &di))
throw CLabeledException(_T("Unable to start the print job"));
BOOL fSuccess = TRUE;
while (fr.chrg.cpMin < fr.chrg.cpMax)
{
// start page
fSuccess = StartPage(hdc) > 0;
if (!fSuccess)
break;
// format page
int cpMin = m_RichEdit.FormatRange(&fr, TRUE);
// ensure we made forward progress (avoid infinite loop!)
fSuccess = cpMin > fr.chrg.cpMin;
if (!fSuccess)
break;
// render page
fSuccess = m_RichEdit.DisplayBand(const_cast<CRect&>(rcPage));
if (!fSuccess)
break;
// end page
fSuccess = EndPage(hdc) > 0;
if (!fSuccess)
break;
// update no. pages printed
++nPages;
// update our new position
fr.chrg.cpMin = cpMin;
}
// release internal cached data from rich edit control
m_RichEdit.FormatRange(nullptr, FALSE);
// complete or abort the print job
if (fSuccess)
{
EndDoc(hdc);
MessageBox(FormatString(_T("Printed %u pages"), nPages));
}
else
{
DWORD dwError = GetLastError();
AbortDoc(hdc);
throw CContextException(FormatString(_T("Print failed on page %u"), nPages+1), dwError);
}
我的 600dpi 打印机的 rcPaper 输出为水平 5100 像素 x 垂直 6600 像素,由于此打印机的最小物理偏移/限制,所有尺寸都少于 100 点。
所以我的 1/4" x 1/2" 边距总是大于打印机的限制,我们最终得到一个页面 = { 150, 300, 4950, 6300 }(像素)。
但实际打印时,我得到的上边距约为 5/8",下边距约为 3/16",左边距约为 3/8",右边距约为 1/8 ".
所以这就像打印机本身在发送给它的值之上添加了它的限制(它的 PHYSICALOFFSETX
和 PHYSICALOFFSETY
)(或者丰富的编辑控件正在抵消一切这些值)。
...或发生其他事情and/or我误会了什么!
想法?
答案似乎是上面的数学计算是正确的,可以找到与打印机的能力和限制相匹配的一组所需边距的 "absolute" 矩形,但是要创建输出矩形,需要减去物理偏移量回退以使 0,0 在实际物理页面上变为 0,0:
// subtract out the physical offsets
// note: I see no documentation that this is what must be done, yet, it must be done
// otherwise the entire page is off by this exact amount :(
rcPaper.left -= rcPrintable.left;
rcPaper.right -= rcPrintable.left;
rcPaper.top -= rcPrintable.top;
rcPaper.bottom -= rcPrintable.top;
我正在打印一个编辑控件 - 首先将其内容复制到一个 rich edit 控件(至少 2.0 - 可能是 rich edit 3.0) - 然后从那里打印。
一切正常...但边距草率/不正确。
我反复检查了代码,验证了调试器中的数字,但打印页面上的边距(对于我可用于测试的打印机)仍然出现错误。
该代码或多或少是从网上可用的丰富编辑打印的各种示例的副本 - 包括旧新事物、MSDN 的文档和 CodeProject 等。
归结为(hdc是针对使用用户选择的纸张来源的用户选择的打印机):
// printer dot's per inch (pixels)
const CSize dpi = { GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY) };
// paper size (in printer's dots ~ pixels)
const CSize paper = { GetDeviceCaps(hdc, PHYSICALWIDTH), GetDeviceCaps(hdc, PHYSICALHEIGHT) };
// printable size (pixels) - this defines the largest possible usable rect for this printer
const CRect rcPrintable(CPoint(GetDeviceCaps(hdc, PHYSICALOFFSETX), GetDeviceCaps(hdc, PHYSICALOFFSETY)), CSize(GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES)));
// determine the paper extents using our desired margins without violating the device's minimum margins
const CSize margin = { dpi.cx / 4, dpi.cy / 2 }; // 1/4" horizontal x 1/2" vertical margins
const CRect rcPaper(__max(rcPrintable.left, margin.cx), __max(rcPrintable.top, margin.cy), __min(rcPrintable.right, paper.cx - margin.cx), __min(rcPrintable.bottom, paper.cy - margin.cy));
// convert paper size in printer dots (pixels) to paper size in TWIPS
const CRect rcPage(MulDiv(rcPaper.left, 1440, dpi.cx), MulDiv(rcPaper.top, 1440, dpi.cy), MulDiv(rcPaper.right, 1440, dpi.cx), MulDiv(rcPaper.bottom, 1440, dpi.cy));
// build our format range data
FORMATRANGE fr;
Zero(fr);
fr.hdc = hdc;
fr.hdcTarget = hdc;
// Set page rect to physical page size in TWIPS
fr.rc = rcPage;
fr.rcPage = rcPage;
// set our target device to the printer
m_RichEdit.SetTargetDevice(hdc, rcPage.Width());
m_RichEdit.SetSel(0, -1); // Select the entire contents.
m_RichEdit.GetSel(fr.chrg); // Get the selection into a CHARRANGE
// track the number of pages generated
unsigned nPages = 0;
// give this job a reasonable name
CString strPrintJobName = GetPrintJobName();
// Use GDI to print successive pages
DOCINFO di = { sizeof(di) };
di.lpszDocName = strPrintJobName;
if (!StartDoc(hdc, &di))
throw CLabeledException(_T("Unable to start the print job"));
BOOL fSuccess = TRUE;
while (fr.chrg.cpMin < fr.chrg.cpMax)
{
// start page
fSuccess = StartPage(hdc) > 0;
if (!fSuccess)
break;
// format page
int cpMin = m_RichEdit.FormatRange(&fr, TRUE);
// ensure we made forward progress (avoid infinite loop!)
fSuccess = cpMin > fr.chrg.cpMin;
if (!fSuccess)
break;
// render page
fSuccess = m_RichEdit.DisplayBand(const_cast<CRect&>(rcPage));
if (!fSuccess)
break;
// end page
fSuccess = EndPage(hdc) > 0;
if (!fSuccess)
break;
// update no. pages printed
++nPages;
// update our new position
fr.chrg.cpMin = cpMin;
}
// release internal cached data from rich edit control
m_RichEdit.FormatRange(nullptr, FALSE);
// complete or abort the print job
if (fSuccess)
{
EndDoc(hdc);
MessageBox(FormatString(_T("Printed %u pages"), nPages));
}
else
{
DWORD dwError = GetLastError();
AbortDoc(hdc);
throw CContextException(FormatString(_T("Print failed on page %u"), nPages+1), dwError);
}
我的 600dpi 打印机的 rcPaper 输出为水平 5100 像素 x 垂直 6600 像素,由于此打印机的最小物理偏移/限制,所有尺寸都少于 100 点。
所以我的 1/4" x 1/2" 边距总是大于打印机的限制,我们最终得到一个页面 = { 150, 300, 4950, 6300 }(像素)。
但实际打印时,我得到的上边距约为 5/8",下边距约为 3/16",左边距约为 3/8",右边距约为 1/8 ".
所以这就像打印机本身在发送给它的值之上添加了它的限制(它的 PHYSICALOFFSETX
和 PHYSICALOFFSETY
)(或者丰富的编辑控件正在抵消一切这些值)。
...或发生其他事情and/or我误会了什么!
想法?
答案似乎是上面的数学计算是正确的,可以找到与打印机的能力和限制相匹配的一组所需边距的 "absolute" 矩形,但是要创建输出矩形,需要减去物理偏移量回退以使 0,0 在实际物理页面上变为 0,0:
// subtract out the physical offsets
// note: I see no documentation that this is what must be done, yet, it must be done
// otherwise the entire page is off by this exact amount :(
rcPaper.left -= rcPrintable.left;
rcPaper.right -= rcPrintable.left;
rcPaper.top -= rcPrintable.top;
rcPaper.bottom -= rcPrintable.top;