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

时间:2012-01-06 17:11:48

标签: c# printing gdi+ printdocument

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

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

我尝试过使用PageBounds,PrintableArea,Graphics.VisibleClipBounds等。所有这些都无法始终获得绘图区域,尤其是从横向切换到纵向布局时。从Landscape切换到Portrait时,PrintableArea似乎永远不会改变。

我还注意到,根据我是否正在进行打印预览和实际打印,如何设置Graphics.VisibleClipBounds存在差异。在预览中它始终显示纵向宽度/高度,因此我必须检查它是否是预览,并且当它是横向时我必须手动交换宽度/高度。

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


我关注的是处理Graphics矩阵偏移。到目前为止,我已经注意到如何根据以下因素使用硬边缘对图形上下文进行预翻译之间存在严重的不一致:

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

有没有一种标准的方法来处理这个问题?我应该重置矩阵吗?当我将OriginAtMargins设置为true时,Graphics会预先转换为84,84,但我的边距是100,100。艰难的利润是16,16。它不应该被翻译成100,100吗?因为0,0应该在页面边界,而不是硬边距。

基本上我的方法应始终用于获得最佳的可打印矩形。我只需要一种与设备无关的一致方式来确保我的绘图原点(0,0)位于页面的左上角,以便上面的Rectangle对我有用。

3 个答案:

答案 0 :(得分:72)

你的问题对于什么是最好的"而言缺乏一点清晰度。矩形是。我打算假设你指的是印刷时100%可见的最大矩形。

让我们首先确保我们了解打印文档图形对象"起源"以及OriginAtMargins属性如何影响此原点。

  

OriginAtMargins - 获取或设置一个指示是否位置的值   与页面关联的图形对象位于...内部   用户指定的边距或可打印区域的左上角   页面。
    - PrintDocument Class Definition on MSDN

因此,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为{{ 1}})。如果您将false设置为PrintDocument.OriginsAtMargins,它将绘制一个尽可能大的矩形,同时仍然在配置的软边距内可见(默认为1"来自页边)。

true

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

我使用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); } 来简单地删除超过小数的任何内容(例如:817.96 - > 817),以确保可用的宽度和高度仅在可用尺寸内。我" m"安全失败"在这里,如果你想要保持基于浮点数的坐标(而不是int),请注意观察将导致剪裁图形的舍入错误(如果它将817.96舍入到818,然后打印机驱动程序决定& #39; s不再可见了。

我在纵向和横向上测试了此程序,在Dell 3115CN,Samsung SCX-4x28和CutePDF软件打印机上都有硬边距和柔化边距。如果这没有充分解决您的问题,请考虑修改您的问题以澄清"魔术矩形"和"最好的矩形"。


编辑:关于"软边距的注释"

软边距适用于软件,不考虑打印机的硬件限制。这是故意的和设计的。如果需要,可以在可打印区域外设置软边距,打印机的驱动程序可能会剪切输出。如果这对您的应用程序不合适,则需要调整程序代码中的边距。您可以阻止用户在可打印区域外选择边距(或者如果他们这样做就发出警告),或者在实际开始打印(绘图)文档时可以在代码中强制执行一些最小/最大条件。

示例案例:如果在Microsoft Word 2007中将页边距设置为0,0,0,0,则会弹出一个警告对话框,其中显示"一个或多个边距设置在页面的可打印区域。选择“修复”按钮以增加适当的边距。"如果单击修复,Word将简单地将硬边距复制到软边距,因此对话框现在显示0.16"所有利润(我的激光打印机的功能)。

这是预期的行为。如果打印页面被剪切,则不是Microsoft Word的错误/问题,因为用户忽略了此警告并使用了0,0,0,0页边距。这在您的应用程序中是相同的。如果适用于您的用例,您需要强制执行限制。使用警告对话框,或者您可以在代码中更强烈地强制限制(不要为用户提供选择)。


替代策略

好吧所以也许你不想获得硬边缘,而是获得柔软的边距,然后在打印时强制软边距保留在可打印区域内。让我们在这里制定另一种策略。

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

Math.Floor()

答案 1 :(得分:3)

目前以下版本适用于我的打印机。我将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;
}

如果有人可以在他们的打印机上尝试使用此代码来验证它是否可以正常运行,或者如果我错了就更正它,那就太棒了。

我不知道当OriginAtMargins为false时,是否将原点预转换为硬边距是所有打印机的标准配置,或者它是否只是在我的打印机上执行此操作。

答案 2 :(得分:0)

我认为您需要的只是重新绘制图像以适应所用纸张尺寸。这是我的代码:

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