c# - 如何找到实际的可打印区域? (打印文件)

标签 c# printing gdi+ printdocument

为什么找出这个神奇的矩形如此困难?

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

我曾尝试使用 PageBounds、PrintableArea、Graphics.VisibleClipBounds 等。都无法始终如一地获得绘图区域,尤其是从横向布局切换到纵向布局时。当您从横向切换到纵向时,PrintableArea 似乎永远不会改变。

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

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

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

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

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

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

    最佳答案

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

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

    OriginAtMargins - Gets or sets a value indicating whether the position of a graphics object associated with a page is located just inside the user-specified margins or at the top-left corner of the printable area of the page.
    - 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。

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

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

    因此,按照您发布的示例代码的粗略模型,这是一个 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);
    }
    

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

    我用过 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"(我的激光打印机的功能)。

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

    替代策略

    好吧,也许您不想只获得硬边距,而是要获得软边距,然后在打印时强制软边距保留在可打印区域内。让我们在这里制定另一个策略。

    在这个例子中,我将在边距处使用原点,并允许用户选择他们想要的任何边距,但我将在代码中强制所选边距不在可打印区域之外。如果选定的边距在可打印区域之外,我只是将它们调整到可打印区域内。
    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);
    }
    

    关于c# - 如何找到实际的可打印区域? (打印文件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8761633/

    相关文章:

    c# - Entity Framework 核心 : Get changes occured in entity and related data

    c# - 屏幕截图后图像上的 Unity 水印

    c# - WebRequest.DefaultWebProxy 和 WebRequest.GetSystemWebProxy() 有什么区别?

    java - 使用 Java 在特定位置打印

    c++ - 使用 GDI+ 打印无效

    c# - 在 C#/.NET 中,Graphics.MeasureString 方法给出的高度不正确

    c# - 允许双击标题栏以最大化对话框但没有最大最小按钮

    printing - 将页脚与打印的 HTML 页面的底部对齐?

    c# - 在 C# 应用程序中使用 b-pac SDK 进行标签打印开发

    c# - 如何精确测量字符串的宽度?