c# - ¿Cómo encontrar el área de impresión real?(Imprimir documento)
printing gdi+ (3)
¿Por qué es tan difícil encontrar este Rectángulo mágico?
En el evento OnPrintPage tengo PrintPageEventArgs y estoy tratando de dibujar usando los Gráficos dentro de los límites del área de impresión máxima.
He intentado usar PageBounds, PrintableArea, Graphics.VisibleClipBounds, etc. Todos fallan en obtener constantemente el área de dibujo, especialmente al cambiar de diseño Horizontal a Vertical. PrintableArea no parece cambiar nunca cuando cambias de Horizontal a Vertical.
También he notado que hay una diferencia en cómo se configura Graphics.VisibleClipBounds dependiendo de si estoy haciendo una vista previa de impresión y una impresión real. En una vista previa siempre muestra el ancho / alto vertical, así que tengo que comprobar si es una vista previa y tengo que cambiar manualmente el ancho / alto cuando es un paisaje.
Necesito un algoritmo para calcular el área de impresión en relación con el contexto gráfico actual , no un área de impresión teórica arbitraria que no se use en el dibujo real.
Mi preocupación es tratar con la matriz de gráficos de desplazamiento. Hasta el momento, he notado graves inconsistencias entre cómo se traduce previamente el contexto de Gráficos utilizando los márgenes fijos en función de factores como:
- Si OriginAtMargins es verdadero o falso (no se comporta como pensaría)
- Si estoy imprimiendo en una impresora, o usando el PrintPreviewControl (tengo que comprobar si es una impresión para previsualizar o una impresión a página para manejar la traducción correctamente)
- Si estoy usando mi impresora en casa o mi impresora en el trabajo (ambos se comportan de manera diferente)
¿Hay una manera estándar de manejar esto? ¿Debo simplemente restablecer la matriz? Cuando configuro OriginAtMargins en verdadero, los Gráficos se traducen previamente a 84,84, pero mis márgenes son 100,100. Los márgenes duros son 16,16. ¿No debería ser traducido a 100,100? Como 0,0 debería estar en los límites de la página, no en los márgenes duros.
Básicamente, mi método siempre debería funcionar para obtener el mejor rectángulo imprimible. Solo necesito una forma coherente, independiente del dispositivo, de asegurarme de que el origen de mi dibujo (0, 0) esté en la parte superior izquierda de la página para que el Rectángulo anterior sea de alguna utilidad para mí.
A su pregunta le falta un poco de claridad sobre cuál es el "mejor" rectángulo. Voy a asumir que te refieres al rectángulo más grande que será 100% visible cuando se imprima.
Así que comencemos asegurándonos de que entendemos qué son los "orígenes" del objeto de gráficos de documentos de impresión y cómo la propiedad OriginAtMargins afecta a este origen.
OriginAtMargins: obtiene o establece un valor que indica si la posición de un objeto de gráficos asociado con una página se encuentra dentro de los márgenes especificados por el usuario o en la esquina superior izquierda del área de impresión de la página.
- Definición de la clase PrintDocument en MSDN
Por lo tanto, con OriginAtMargins
configurado en false
(predeterminado), el objeto gráfico se ajustará al rectángulo PrintableArea (aproximadamente 5/32 de cada borde de página para mi impresora láser, las impresoras láser antiguas pueden ser más, las impresoras de tinta nuevas pueden imprimir hasta el borde, software Las impresoras PDF se imprimirán hasta el borde). Entonces, 0,0 en mi objeto gráfico es en realidad 16,16 en la página física de mi impresora láser (su impresora puede ser diferente).
Con los márgenes de página de 1 pulgada predeterminados y OriginAtMargins
establecidos en true
, el objeto de gráficos se ajustará al rectángulo 100,100,650,1100 para una página de letra de retrato normal. Esto es una pulgada dentro de cada borde de la página física. Entonces, 0,0 en su objeto gráfico es en realidad 100,100 en la página física.
Los márgenes también se conocen como "márgenes blandos", ya que se definen en el software y no se ven afectados por el dispositivo físico de impresión. Esto significa que se aplicarán al tamaño de página actual en el software y reflejarán la dimensión real de la página vertical u horizontal.
PrintableArea también se conoce como "márgenes duros" que reflejan las limitaciones físicas de su dispositivo de impresión. Esto variará de una impresora a otra, de un fabricante a otro. Debido a que estas son medidas de hardware, no giran cuando configura la página en horizontal / vertical. Las limitaciones físicas no cambiarán en la impresora, independientemente de la configuración de impresión del software, por lo que debemos asegurarnos de que las apliquemos en el eje correcto dependiendo de la configuración del software para el documento de impresión (orientación).
Por lo tanto, siguiendo el modelo aproximado del código de muestra que publicó, aquí hay un controlador de eventos PrintDocument.PrintPage que dibujará un rectángulo lo más grande posible mientras aún esté visible (con el valor predeterminado PrintDocument.OriginsAtMargins
). Si configura PrintDocument.OriginsAtMargins
en true
, dibujará un rectángulo lo más grande posible y seguirá siendo visible dentro de los márgenes suaves configurados (el valor predeterminado es 1 "desde los bordes de la página).
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);
}
Las dos líneas que determinan el ancho disponible y la altura disponible son lo que creo que buscaba en su pregunta. Esas dos líneas tienen en cuenta si desea márgenes suaves o márgenes duros y si el documento impreso está configurado para horizontal o vertical.
Utilicé Math.Floor()
para una salida fácil y simplemente soltando cualquier cosa más allá del decimal (ej: 817.96 -> 817) solo para asegurarme de que el ancho y la altura disponibles estuvieran dentro de las dimensiones disponibles. Estoy "a salvo" aquí. Si quisiera, podría mantener las coordenadas basadas en flotación (en lugar de int), solo tenga cuidado de los errores de redondeo que darán como resultado los gráficos recortados (si redondea 817.96 hasta 818 y luego el controlador de la impresora decide que ya no está visible).
Probé este procedimiento tanto en vertical como en horizontal con márgenes duros y márgenes suaves en una impresora Dell 3115CN, Samsung SCX-4x28 y CutePDF. Si esto no abordó adecuadamente su pregunta, considere revisar su pregunta para aclarar "rectángulo mágico" y "rectángulo mejor".
EDITAR: Notas sobre "márgenes suaves"
Los márgenes suaves se aplican en el software y no tienen en cuenta las limitaciones de hardware de la impresora. Esto es intencional y por diseño. Si lo desea, puede establecer los márgenes suaves fuera del área de impresión y el controlador de la impresora puede recortar la salida. Si esto no es deseable para su aplicación, necesita ajustar los márgenes en el código de su programa. Puede impedir que el usuario seleccione márgenes fuera del área de impresión (o advertirles si lo hacen) o puede imponer algunas condiciones mín / máx en su código cuando realmente comienza a imprimir (dibujar) el documento.
Caso de ejemplo: si configura los márgenes de la página a 0,0,0,0 en Microsoft Word 2007, aparecerá un cuadro de diálogo de advertencia que dice "Uno o más márgenes se configuran fuera del área imprimible de la página. Elija el botón Reparar para aumentar el márgenes apropiados. " Si hace clic en corregir, Word simplemente copiará los márgenes duros en los márgenes blandos, por lo que el cuadro de diálogo ahora muestra 0.16 "para todos los márgenes (las capacidades de mi impresora láser).
Este es el comportamiento esperado. No es un error / problema con Microsoft Word si la página impresa se recorta porque el usuario ignoró esta advertencia y utilizó márgenes de página de 0,0,0,0. Esto es lo mismo en su aplicación. Debe hacer cumplir los límites de lo que sea apropiado en su caso de uso. Ya sea con un cuadro de diálogo de advertencia, o puede forzar el límite más fuertemente en el código (no ofrezca una opción al usuario).
Estrategia alternativa
De acuerdo, tal vez no desee simplemente obtener los márgenes duros, sino obtener los márgenes suaves y luego hacer que los márgenes suaves permanezcan dentro del área imprimible al imprimir. Vamos a desarrollar otra estrategia aquí.
En este ejemplo, usaré los orígenes en los márgenes y permitiré que el usuario seleccione el margen que desee, pero aplicaré en el código que el margen seleccionado no estará fuera del área imprimible. Si los márgenes seleccionados están fuera del área imprimible, simplemente los voy a ajustar para que estén dentro del área imprimible.
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);
}
Actualmente lo siguiente está trabajando en mi impresora. Tengo OriginAtMargins configurado como falso. Esto causa la traducción automática al HardMarginX y al HardMarginY cuando estoy imprimiendo en mi impresora, pero NO hay traducción cuando estoy imprimiendo en el PrintPreviewControl. Por lo tanto, tengo que revisar este caso.
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;
}
Si alguien puede probar este código en su impresora para verificar que funciona universalmente, o corregirlo si me equivoco, sería genial.
No sé si la traducción previa del origen a los márgenes duros cuando OriginAtMargins es falso es estándar en todas las impresoras, o si solo está haciendo esto en mi impresora.
Creo que lo que necesita es simplemente volver a dibujar la imagen para que se ajuste al tamaño del papel que se esté utilizando. Aquí está mi código:
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