您的问题对于“最佳”矩形是什么缺乏一点清晰度。我将假设您的意思是打印时 100% 可见的最大矩形。
因此,让我们首先确保我们了解打印文档图形对象的“原点”是什么以及 OriginAtMargins 属性如何影响该原点。
OriginAtMargins - 获取或设置一个值,该值指示与页面关联的图形对象的位置是位于用户指定的边距内还是位于页面可打印区域的左上角
。
- MSDN 上的 PrintDocument 类定义
因此,OriginAtMargins
设置为false
(默认)图形对象将调整为 PrintableArea 矩形(对于我的激光打印机,每个页面边缘大约 5/32,旧的激光打印机可能更多,新的喷墨打印机可能会打印到边缘,软件 PDF 打印机将打印到边缘)。所以我的图形对象中的 0,0 实际上是我的激光打印机物理页面上的 16,16(您的打印机可能不同)。
使用默认的 1 英寸页边距并OriginAtMargins
设置为true
,图形对象将调整为 100,100,650,1100 矩形,用于正常的纵向字母页面。这是每个物理页面边缘内一英寸。所以图形对象中的 0,0 实际上是物理页面上的 100,100。
边距也称为“软边距”,因为它们是在软件中定义的,不受物理打印设备的影响。这意味着它们将应用于软件中的当前页面大小,并反映实际页面尺寸纵向或横向。
PrintableArea 也称为“硬边距”,它反映了打印设备的物理限制。这将因打印机而异,因制造商而异。因为这些是硬件测量,所以当您将页面设置为横向/纵向时,它们不会旋转。无论软件打印设置如何,打印机的物理限制都不会改变,因此我们需要确保根据打印文档的软件设置(方向)将它们应用到正确的轴上。
因此,按照您发布的示例代码的粗略模型,这里有一个 PrintDocument.PrintPage 事件处理程序,它将绘制一个尽可能大的矩形,同时仍然可见(默认PrintDocument.OriginsAtMargins
为false
)。如果您设置它PrintDocument.OriginsAtMargins
,true
它将绘制一个尽可能大的矩形,同时在配置的软边距内仍然可见(默认为距页面边缘 1 英寸)。
PrintAction printAction = PrintAction.PrintToFile;
private void printDocument_BeginPrint(object sender, PrintEventArgs e)
{
// Save our print action so we know if we are printing
// a preview or a real document.
printAction = e.PrintAction;
// Set some preferences, our method should print a box with any
// combination of these properties being true/false.
printDocument.OriginAtMargins = false; //true = soft margins, false = hard margins
printDocument.DefaultPageSettings.Landscape = false;
}
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
// If you set printDocumet.OriginAtMargins to 'false' this event
// will print the largest rectangle your printer is physically
// capable of. This is often 1/8" - 1/4" from each page edge.
// ----------
// If you set printDocument.OriginAtMargins to 'false' this event
// will print the largest rectangle permitted by the currently
// configured page margins. By default the page margins are
// usually 1" from each page edge but can be configured by the end
// user or overridden in your code.
// (ex: printDocument.DefaultPageSettings.Margins)
// Grab a copy of our "soft margins" (configured printer settings)
// Defaults to 1 inch margins, but could be configured otherwise by
// the end user. You can also specify some default page margins in
// your printDocument.DefaultPageSetting properties.
RectangleF marginBounds = e.MarginBounds;
// Grab a copy of our "hard margins" (printer's capabilities)
// This varies between printer models. Software printers like
// CutePDF will have no "physical limitations" and so will return
// the full page size 850,1100 for a letter page size.
RectangleF printableArea = e.PageSettings.PrintableArea;
// If we are print to a print preview control, the origin won't have
// been automatically adjusted for the printer's physical limitations.
// So let's adjust the origin for preview to reflect the printer's
// hard margins.
if (printAction == PrintAction.PrintToPreview)
g.TranslateTransform(printableArea.X, printableArea.Y);
// Are we using soft margins or hard margins? Lets grab the correct
// width/height from either the soft/hard margin rectangles. The
// hard margins are usually a little wider than the soft margins.
// ----------
// Note: Margins are automatically applied to the rotated page size
// when the page is set to landscape, but physical hard margins are
// not (the printer is not physically rotating any mechanics inside,
// the paper still travels through the printer the same way. So we
// rotate in software for landscape)
int availableWidth = (int)Math.Floor(printDocument.OriginAtMargins
? marginBounds.Width
: (e.PageSettings.Landscape
? printableArea.Height
: printableArea.Width));
int availableHeight = (int)Math.Floor(printDocument.OriginAtMargins
? marginBounds.Height
: (e.PageSettings.Landscape
? printableArea.Width
: printableArea.Height));
// Draw our rectangle which will either be the soft margin rectangle
// or the hard margin (printer capabilities) rectangle.
// ----------
// Note: we adjust the width and height minus one as it is a zero,
// zero based co-ordinates system. This will put the rectangle just
// inside the available width and height.
g.DrawRectangle(Pens.Red, 0, 0, availableWidth - 1, availableHeight - 1);
}
确定可用宽度和可用高度的两条线是我认为您在问题中寻找的。这两行考虑了您是否需要软边距或硬边距,以及打印文档是否配置为横向或纵向。
我使用Math.Floor()
了一种简单的方法来删除小数点后的任何内容(例如:817.96 -> 817),以确保可用的宽度和高度正好在可用尺寸之内。我在这里“不安全”,如果您愿意,您可以维护基于浮点的坐标(而不是 int),只需注意会导致裁剪图形的舍入错误(如果将 817.96 舍入到 818然后打印机驱动程序决定不再可见)。
我在戴尔 3115CN、三星 SCX-4x28 和 CutePDF 软件打印机上使用硬边距和软边距在纵向和横向上测试了这个过程。如果这不能充分解决您的问题,请考虑修改您的问题以阐明“魔术矩形”和“最佳矩形”。
编辑:关于“软边距”的注释
软边距在软件中应用,不考虑打印机的硬件限制。这是有意和设计的。如果需要,您可以在可打印区域之外设置软边距,并且输出可能会被打印机驱动程序裁剪。如果这不适合您的应用程序,则需要调整程序代码中的边距。您可以阻止用户选择可打印区域之外的边距(或在他们这样做时警告他们),或者您可以在实际开始打印(绘制)文档时在代码中强制执行一些最小/最大条件。
示例:如果您在 Microsoft Word 2007 中将页边距设置为 0,0,0,0,则会弹出一个警告对话框,显示“一个或多个页边距设置在页面的可打印区域之外。选择修复按钮以增加适当的边距。” 如果单击修复,Word 将简单地将硬边距复制到软边距,因此对话框现在显示所有边距为 0.16"(我的激光打印机的功能)。
这是预期的行为。如果打印页面被剪裁,这不是 Microsoft Word 的错误/问题,因为用户忽略了此警告并使用了 0,0,0,0 页边距。在您的应用程序中也是如此。您需要对用例中适当的任何内容实施限制。使用警告对话框,或者您可以在代码中更强烈地强制限制(不要向用户提供选择)。
替代策略
好吧,也许您不想只获得硬边距,而是要获得软边距,然后在打印时强制软边距保持在可打印区域内。让我们在这里制定另一个策略。
在此示例中,我将使用边距的原点,并允许用户选择他们想要的任何边距,但我将在代码中强制选择的边距不在可打印区域之外。如果选定的边距在可打印区域之外,我只需将它们调整到可打印区域内。
PrintAction printAction = PrintAction.PrintToFile;
private void printDocument_BeginPrint(object sender, PrintEventArgs e)
{
// Save our print action so we know if we are printing
// a preview or a real document.
printAction = e.PrintAction;
// We ALWAYS want true here, as we will implement the
// margin limitations later in code.
printDocument.OriginAtMargins = true;
// Set some preferences, our method should print a box with any
// combination of these properties being true/false.
printDocument.DefaultPageSettings.Landscape = false;
printDocument.DefaultPageSettings.Margins.Top = 100;
printDocument.DefaultPageSettings.Margins.Left = 0;
printDocument.DefaultPageSettings.Margins.Right = 50;
printDocument.DefaultPageSettings.Margins.Bottom = 0;
}
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
// If you set printDocumet.OriginAtMargins to 'false' this event
// will print the largest rectangle your printer is physically
// capable of. This is often 1/8" - 1/4" from each page edge.
// ----------
// If you set printDocument.OriginAtMargins to 'false' this event
// will print the largest rectangle permitted by the currently
// configured page margins. By default the page margins are
// usually 1" from each page edge but can be configured by the end
// user or overridden in your code.
// (ex: printDocument.DefaultPageSettings.Margins)
// Grab a copy of our "hard margins" (printer's capabilities)
// This varies between printer models. Software printers like
// CutePDF will have no "physical limitations" and so will return
// the full page size 850,1100 for a letter page size.
RectangleF printableArea = e.PageSettings.PrintableArea;
RectangleF realPrintableArea = new RectangleF(
(e.PageSettings.Landscape ? printableArea.Y : printableArea.X),
(e.PageSettings.Landscape ? printableArea.X : printableArea.Y),
(e.PageSettings.Landscape ? printableArea.Height : printableArea.Width),
(e.PageSettings.Landscape ? printableArea.Width : printableArea.Height)
);
// If we are printing to a print preview control, the origin won't have
// been automatically adjusted for the printer's physical limitations.
// So let's adjust the origin for preview to reflect the printer's
// hard margins.
// ----------
// Otherwise if we really are printing, just use the soft margins.
g.TranslateTransform(
((printAction == PrintAction.PrintToPreview)
? realPrintableArea.X : 0) - e.MarginBounds.X,
((printAction == PrintAction.PrintToPreview)
? realPrintableArea.Y : 0) - e.MarginBounds.Y
);
// Draw the printable area rectangle in PURPLE
Rectangle printedPrintableArea = Rectangle.Truncate(realPrintableArea);
printedPrintableArea.Width--;
printedPrintableArea.Height--;
g.DrawRectangle(Pens.Purple, printedPrintableArea);
// Grab a copy of our "soft margins" (configured printer settings)
// Defaults to 1 inch margins, but could be configured otherwise by
// the end user. You can also specify some default page margins in
// your printDocument.DefaultPageSetting properties.
RectangleF marginBounds = e.MarginBounds;
// This intersects the desired margins with the printable area rectangle.
// If the margins go outside the printable area on any edge, it will be
// brought in to the appropriate printable area.
marginBounds.Intersect(realPrintableArea);
// Draw the margin rectangle in RED
Rectangle printedMarginArea = Rectangle.Truncate(marginBounds);
printedMarginArea.Width--;
printedMarginArea.Height--;
g.DrawRectangle(Pens.Red, printedMarginArea);
}