如何find实际的可打印区域? (的PrintDocument)

为什么发现这个神奇的矩形如此困难?

在OnPrintPage事件中,我有PrintPageEventArgs,并试图在最大可打印区域的范围内使用graphics进行绘制。

我已经尝试过使用PageBounds,PrintableArea,Graphics.VisibleClipBounds等等。所有都无法一直得到绘图区域,特别是从横向切换到纵向布局时。 从横向切换到纵向时,PrintableArea似乎不会改变。

我也注意到,如何设置Graphics.VisibleClipBounds是有区别的,如果我正在做一个打印预览和一个实际的打印。 在预览中,它总是显示肖像的宽度/高度,所以我必须检查是否是预览,我必须手动交换宽度/高度时,它是一个景观。

我需要一个algorithm来计算与当前Graphics上下文相关的可打印区域,而不是在实际绘图中不使用的任意理论打印区域。


我关心的是处理graphicsmatrix偏移。 到目前为止,我已经注意到graphics上下文是如何根据如下因素使用硬边缘进行预翻译的严重不一致:

  • 如果OriginAtMargins是真的或假的(不像我想象的那样)
  • 如果我正在打印到打印机,或使用PrintPreviewControl(我必须检查这是预览打印还是打印到页面以正确处理翻译)
  • 如果我在家中使用我的打印机,或者我的打印机在工作(两者行为不同)

有没有一个标准的方法来处理这个? 我应该重置matrix吗? 当我将OriginAtMargins设置为true时,graphics预先翻译为84,84,但我的边际是100,100。 硬边缘是16,16。 不应该把它翻译成100,100吗? 由于0,0应该在页面边界,而不是硬边缘。

基本上我的方法应该总是在获得最好的可打印的矩形。 我只需要一个一致的,独立于设备的方法来确保我的绘图原点(0,0)位于页面的左上angular,以便上面的Rectangle对我有任何用处。

你的问题对于什么是“最好”的矩形是没有一点清晰的。 我打算假设你的意思是打印时100%可见的最大矩形。

因此,让我们从确保我们理解打印文档graphics对象“起源”是什么以及OriginAtMargins属性如何影响这个起点开始。

OriginAtMargins – 获取或设置一个值,该值指示与页面关联的graphics对象的位置是否位于用户指定的页边空白内,或位于页面可打印区域左上angular
– MSDN上的PrintDocument类定义

因此,在OriginAtMargins设置为false (默认)的情况下,graphics对象将被调整为PrintableArea矩形(激光打印机的每页边距大约为5/32,旧的激光打印机可能更多,新的喷墨打印机可能打印到边缘,软件PDF打印机将打印到边缘)。 所以我的graphics对象中的0,0实际上是我的激光打印机的物理页面上的16,16(您的打印机可能不同)。

使用默认的1英寸页边距和OriginAtMargins设置为true ,graphics对象将被调整为100,100,650,1100矩形为一个正常的肖像字母页。 这是每个物理页面边缘内的一英寸。 所以在你的graphics对象中的0,0实际上是100,100的物理页面。

页边距也被称为“软边距”,因为它们是在软件中定义的,不受物理打印设备的影响。 这意味着他们将被应用到当前页面大小的软件,并反映实际的页面尺寸肖像或风景。

PrintableArea也被称为“硬边距”,它反映了打印设备的物理限制。 这将因打印机而异,从制造商到制造商。 由于这些是硬件测量,因此当您将页面设置为横向/纵向时,它们不会旋转。 无论软件打印设置如何,打印机的物理限制都不会改变,因此我们需要确保将其应用于正确的轴上,具体取决于打印文档(方向)的软件设置。

因此,下面是您发布的示例代码的粗略模型,这里是一个PrintDocument.PrintPage事件处理程序,它将尽可能大地绘制矩形,同时仍然可见(默认PrintDocument.OriginsAtMarginsfalse )。 如果将PrintDocument.OriginsAtMargins设置为true ,则会在configuration的软边距内仍可见的情况下绘制尽可能大的矩形(默认为页边距为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); } 

确定可用宽度和可用高度的两条线是我认为您在您的问题中寻找的。 这两行考虑您是否需要软边距或硬边距,以及是否将打印文档configuration为横向或纵向。

我使用Math.Floor()来简单地删除小数点后的任何东西(例如:817.96 – > 817),以确保可用的宽度和高度恰好在可用的维度内。 如果你想维护基于浮点的坐标(而不是整数),那么我在这里是“失败的”,只要注意四舍五入的错误会导致截图(如果它轮到817.96到818然后打印机驱动程序决定不再可见)。

我在戴尔3115CN,三星SCX-4×28和CutePDF软件打印机上,以纵向和横向两种方式testing了这个过程。 如果这不能解决您的问题,请考虑修改您的问题以澄清“魔术矩形”和“最佳矩形”。


编辑:关于“软边”的说明

软边缘应用软件,并没有考虑到打印机的硬件限制。 这是故意的和devise的。 如果需要,可以在可打印区域外设置软边距,并且输出可能会被打印机驱动程序剪切。 如果这不适合您的应用程序,则需要调整程序代码中的边距。 您可以防止用户select可打印区域以外的页边空白(或者如果是这样的话可以发出警告),或者您可以在实际开始打印(绘制)文档时在代码中强制执行一些最小/最大条件。

示例:如果在Microsoft Word 2007中将页边距设置为0,0,0,0,则会popup一个警告对话框,其中显示“在页面的可打印区域外设置了一个或多个页边距。select”修复“button以增加适当的利润“。 如果单击“修复”,Word将简单地将软边距复制到软边距,所以对话框现在显示0.16“(对于所有边距(我的激光打印机的function))。

这是预期的行为。 如果由于用户忽略了此警告并使用了0,0,0,0页边距而导致打印的页面被裁剪,这不是Microsoft Word的错误/问题。 这在你的应用程序中是一样的。 您需要在您的用例中强制执行任何适当的限制。 可以通过警告对话框,也可以在代码中强制使用限制(不要向用户提供select)。


替代战略

好,所以也许你不想只得到硬性的利润空间,而是得到软性的利润空间,然后强制在打印时保留在可打印的区域内。 我们在这里制定另一个策略。

在这个例子中,我将使用边距的原点,并允许用户select他们想要的任何边距,但是我要在代码中强制select的边距不在可打印区域之外。 如果选定的边距在可打印区域之外,我只是将其调整到可打印区域内。

 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); } 

目前,以下是我的打印机正在工作。 我有OriginAtMargins设置为false。 这会导致在打印到打印机时自动转换为HardMarginX和HardMarginY,但在打印到PrintPreviewControl时没有转换。 所以,我要检查这个案子。

 private void printDocument_BeginPrint(object sender, PrintEventArgs e) { printAction = e.PrintAction; printDocument.OriginAtMargins = false; } private void printDocument_PrintPage(object sender, PrintPageEventArgs e) { Graphics g = e.Graphics; if (printAction != PrintAction.PrintToPreview) g.TranslateTransform(-e.PageSettings.HardMarginX, -e.PageSettings.HardMarginY); RectangleF printArea = GetBestPrintableArea(e); g.DrawRectangle(Pens.Red, printArea.X, printArea.Y, printArea.Width - 1, printArea.Height - 1); } public RectangleF GetBestPrintableArea(PrintPageEventArgs e) { RectangleF marginBounds = e.MarginBounds; RectangleF printableArea = e.PageSettings.PrintableArea; RectangleF pageBounds = e.PageBounds; if (e.PageSettings.Landscape) printableArea = new RectangleF(printableArea.Y, printableArea.X, printableArea.Height, printableArea.Width); RectangleF bestArea = RectangleF.FromLTRB( (float)Math.Max(marginBounds.Left, printableArea.Left), (float)Math.Max(marginBounds.Top, printableArea.Top), (float)Math.Min(marginBounds.Right, printableArea.Right), (float)Math.Min(marginBounds.Bottom, printableArea.Bottom) ); float bestMarginX = (float)Math.Max(bestArea.Left, pageBounds.Right - bestArea.Right); float bestMarginY = (float)Math.Max(bestArea.Top, pageBounds.Bottom - bestArea.Bottom); bestArea = RectangleF.FromLTRB( bestMarginX, bestMarginY, pageBounds.Right - bestMarginX, pageBounds.Bottom - bestMarginY ); return bestArea; } 

如果任何人都可以在打印机上试试这个代码来validation它是否可以普遍使用,或者如果我错了,那么这个代码会很好。

我不知道当OriginAtMargins为false时,是否将原始文件翻译成硬边缘是所有打印机的标准,或者只是在我的打印机上这样做。

我想你需要的只是重绘图像,以适应任何纸张大小正在使用。 这是我的代码:

 Protected Overrides Sub OnPrintPage(ByVal e As System.Drawing.Printing.PrintPageEventArgs) Dim img As Image = Nothing 'Your image source Dim ps As PaperSize = MyBase.PrinterSettings.DefaultPageSettings.PaperSize Dim pF As RectangleF = MyBase.PrinterSettings.DefaultPageSettings.PrintableArea Dim srcF As New RectangleF(0, 0, pg.ImageSize.Width, pg.ImageSize.Height) Dim dstF As New RectangleF(0, 0, pF.Width, pF.Height) e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic e.Graphics.DrawImage(img, dstF, srcF, GraphicsUnit.Pixel) MyBase.OnPrintPage(e) End Sub