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

Tre*_*ott 43 c# printing gdi+ printdocument

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

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

我尝试过使用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对我有用.

Ben*_*yne 72

关于"最佳"矩形是什么,你的问题缺乏一点清晰度.我打算假设你指的是印刷时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.OriginsAtMarginsfalse).如果设置PrintDocument.OriginsAtMarginstrue它,将绘制一个尽可能大的矩形,同时仍然在配置的软边距内可见(默认为页面边缘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);
}
Run Code Online (Sandbox Code Playgroud)

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

Math.Floor()为了确保可用的宽度和高度在可用尺寸内,我只使用了简单的方法来删除超过小数的任何东西(例如:817.96 - > 817).我在这里"安全失败",如果你想保持基于浮点的坐标(而不是int),请注意观察将导致剪裁图形的舍入错误(如果它绕817.96到818)然后打印机驱动程序决定不再可见).

我在纵向和横向上都测试了这个程序,在Dell 3115CN,Samsung 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);
}
Run Code Online (Sandbox Code Playgroud)