C# 如何找到实际的可打印区域?(打印文档)

C# 如何找到实际的可打印区域?(打印文档),c#,printing,gdi+,printdocument,C#,Printing,Gdi+,Printdocument,为什么找到这个神奇的长方形如此困难 在OnPrintPage事件中,我有PrintPageEventArgs,我试图在最大可打印区域的范围内使用图形进行绘制 PrintAction printAction = PrintAction.PrintToFile; private void printDocument_BeginPrint(object sender, PrintEventArgs e) { // Save our print action so we know if we

为什么找到这个神奇的长方形如此困难

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

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);
}
我尝试过使用PageBounds、PrintableArea、Graphics.VisibleClipBounds等。所有这些都无法始终获得绘图区域,尤其是在从横向布局切换到纵向布局时。从横向切换到纵向时,可打印区域似乎永远不会改变

我还注意到Graphics.VisibleClipBounds的设置方式有所不同,这取决于我是否正在进行打印预览和实际打印。在预览中,它总是显示纵向宽度/高度,因此我必须检查它是否是预览,并且当它是横向时,我必须手动交换宽度/高度

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


我关心的是处理图形矩阵偏移。到目前为止,我注意到图形上下文如何使用硬边距预翻译之间存在严重的不一致,这取决于以下因素:

  • 如果是真是假(行为不像我想的那样)
  • 如果我正在打印到打印机,或使用PrintPreviewControl(我必须检查这是要预览的打印还是要正确处理翻译的打印到页面)
  • 如果我在家使用打印机或在工作中使用打印机(两者的行为都不同)
有没有一个标准的方法来处理这个问题?我应该重置矩阵吗?当我将页边距设置为true时,图形预转换为84,84,但我的页边距为100100。硬利润率为16,16。不应该翻译成100100吗?因为0,0应该位于页面边界,而不是硬边距


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

目前,以下内容正在我的打印机上运行。我已将页边距设置为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;
}
如果有人能在他们的打印机上尝试这段代码来验证它是否能普遍工作,或者如果我错了就纠正它,那就太好了


我不知道当原始页边距为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

您的问题对于“最佳”矩形是什么缺乏一点明确性。我假设你指的是打印时100%可见的最大矩形

因此,让我们首先确保了解打印文档图形对象“原点”是什么,以及OriginateMargins属性如何影响该原点

OriginateMargins-获取或设置一个值,该值指示位置 与页面关联的图形对象的 用户指定的页边距或可打印区域的左上角 这是本页的第一页。
-

因此,当
originalmargins
设置为
false
(默认值)时,图形对象将调整为可打印区域矩形(对于我的激光打印机,每个页面边缘大约5/32,旧的激光打印机可能更多,新的喷墨打印机可能直接打印到边缘,软件PDF打印机将直接打印到边缘)。因此,我的图形对象中的0,0在激光打印机的物理页上实际上是16,16(您的打印机可能不同)

当默认的1英寸页边距和
原始边距
设置为
时,图形对象将调整为普通纵向字母页的1001006501100矩形。这是每个物理页面边缘内的一英寸。因此,图形对象中的0,0在物理页面上实际上是100100

页边距也称为“软页边距”,因为它们是在软件中定义的,不受物理打印设备的影响。这意味着它们将应用于软件中的当前页面大小,并反映实际页面维度纵向或横向

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

因此,按照您发布的示例代码的粗略模型,下面是一个PrintDocument.PrintPage事件处理程序,它将在仍然可见的情况下绘制一个尽可能大的矩形(默认的
PrintDocument.OriginatMargins
false
)。如果将
PrintDocument.OriginatMargins
设置为
true
,它将绘制一个尽可能大的矩形,同时在配置的软边距内仍然可见(默认为距页面边缘1英寸)

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

我使用了
Math.Floor()
,这是一种简单的方法,只需删除超过小数点的任何内容(例如:817.96->81)
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);
}