net - c# stampa file



Come trovare l'area stampabile effettiva?(PrintDocument) (2)

Perché scoprire questo magico rettangolo è così difficile?

Nell'evento OnPrintPage ho PrintPageEventArgs e sto cercando di disegnare utilizzando la grafica entro i limiti dell'area massima stampabile.

Ho provato a utilizzare PageBounds, PrintableArea, Graphics.VisibleClipBounds, ecc. Tutti non riescono a ottenere costantemente l'area di disegno, soprattutto quando si passa dal layout orizzontale a verticale. PrintableArea sembra non cambiare mai quando passi da Landscape a Portrait.

Ho anche notato che c'è una differenza nel modo in cui Graphics.VisibleClipBounds è impostato a seconda se sto facendo un'anteprima di stampa e una stampa effettiva. In un'anteprima mostra sempre Portrait width / height, quindi devo controllare se è un'anteprima e devo scambiare manualmente la larghezza / altezza quando è Landscape.

Ho bisogno di un algoritmo per calcolare l'area stampabile in relazione al contesto grafico corrente , non un'area di stampa teorica arbitraria che non viene utilizzata nel disegno reale.

La mia preoccupazione riguarda l'offset della matrice grafica. Finora ho notato gravi incongruenze tra il modo in cui il contesto grafico è pre-tradotto usando i margini rigidi a seconda di fattori come:

  • Se OriginAtMargins è vero o falso (non comportarsi come penserei)
  • Se sto stampando su una stampante, o usando PrintPreviewControl (devo controllare se questa è una stampa per l'anteprima o una stampa per la pagina per gestire correttamente la traduzione)
  • Se sto usando la mia stampante a casa o la mia stampante al lavoro (entrambi si comportano in modo diverso)

C'è un modo standard per gestirlo? Dovrei semplicemente resettare la matrice? Quando imposto OriginAtMargins su true, Graphics viene pre-tradotto in 84,84, ma i miei margini sono 100.100. I margini duri sono 16,16. Non dovrebbe essere tradotto a 100.100? Poiché 0,0 dovrebbe essere ai limiti della pagina, non i margini difficili.

Fondamentalmente il mio metodo dovrebbe sempre funzionare per ottenere il miglior rettangolo stampabile. Ho solo bisogno di un modo coerente, indipendente dal dispositivo, per assicurarmi che l'origine del mio disegno (0, 0) sia in alto a sinistra nella pagina in modo che il Rettangolo sopra possa essere di qualche utilità per me.


Alla tua domanda manca un po 'di chiarezza su cosa sia il "migliore" rettangolo. Immagino tu intenda il rettangolo più grande che sarà visibile al 100% una volta stampato.

Iniziamo quindi assicurandoci di capire quali sono le "origini" dell'oggetto grafico di stampa e come la proprietà OriginAtMargins influisce su questa origine.

OriginAtMargins - Ottiene o imposta un valore che indica se la posizione di un oggetto grafico associato a una pagina si trova appena all'interno dei margini specificati dall'utente o nell'angolo in alto a sinistra dell'area stampabile della pagina.
- Definizione della classe PrintDocument su MSDN

Quindi, con OriginAtMargins impostato su false (impostazione predefinita), l'oggetto grafico verrà adattato al rettangolo di PrintableArea (circa 5/32 da ogni lato della pagina per la mia stampante laser, le vecchie stampanti laser potrebbero essere di più, i nuovi inkjet possono stampare fino al bordo, il software Le stampanti PDF stamperanno fino al limite). Quindi 0,0 nel mio oggetto grafico è in realtà 16,16 nella pagina fisica della mia stampante laser (la stampante potrebbe essere diversa).

Con i margini di pagina da 1 pollice predefiniti e OriginAtMargins impostati su true , l'oggetto grafico verrà adattato al rettangolo 100,100,650,1100 per una normale pagina con caratteri verticali. Questo è un pollice all'interno di ogni bordo della pagina fisica. Quindi 0,0 nell'oggetto grafico è in realtà 100.100 nella pagina fisica.

I margini sono anche noti come "margini flessibili", poiché sono definiti nel software e non sono influenzati dal dispositivo di stampa fisico. Ciò significa che saranno applicati alle dimensioni della pagina corrente nel software e rifletteranno la dimensione della pagina o il ritratto.

PrintableArea è anche noto come "margini rigidi" che riflettono i limiti fisici del dispositivo di stampa. Questo può variare da stampante a stampante, da produttore a produttore. Poiché si tratta di misurazioni hardware, non ruotano quando si imposta la pagina in orizzontale / verticale. Le limitazioni fisiche non cambieranno sulla stampante, indipendentemente dalle impostazioni di stampa del software, quindi è necessario accertarsi di applicarle sull'asse corretto in base alle impostazioni del software per il documento di stampa (orientamento).

Quindi, seguendo il modello approssimativo del codice di esempio che hai postato, ecco un gestore di eventi PrintDocument.PrintPage che disegnerà un rettangolo il più grande possibile pur rimanendo visibile (con il default PrintDocument.OriginsAtMargins è false ). Se si imposta PrintDocument.OriginsAtMargins su true , verrà disegnato un rettangolo il più grande possibile pur rimanendo visibile all'interno dei margini PrintDocument.OriginsAtMargins configurati (il valore predefinito è 1 "dai bordi della pagina).

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);
}

Le due linee che determinano la larghezza disponibile e l'altezza disponibile sono ciò che penso stavi cercando nella tua domanda. Queste due linee prendono in considerazione se si desidera margini morbidi o margini rigidi e se il documento di stampa è configurato per orizzontale o verticale.

Ho usato Math.Floor() per la facile uscita per far cadere qualsiasi cosa oltre il decimale (es: 817.96 -> 817) solo per assicurarmi che la larghezza e l'altezza disponibili fossero solo all'interno delle dimensioni disponibili. In questo caso sto "fallendo in sicurezza", se volessi potresti mantenere le coordinate basate sul float (invece di int), fai attenzione a vedere gli errori di arrotondamento che si tradurranno nella grafica ritagliata (se arrotonda 817.96 fino a 818 e quindi il driver della stampante decide che non è più visibile).

Ho testato questa procedura sia in verticale che orizzontale con margini rigidi e margini morbidi su un Dell 3115CN, una stampante software Samsung SCX-4x28 e CutePDF. Se ciò non risolve adeguatamente la tua domanda, valuta la possibilità di rivedere la tua domanda per chiarire "rettangolo magico" e "rettangolo migliore".

EDIT: Note su "Margini morbidi"

I margini soft vengono applicati nel software e non tengono conto dei limiti hardware della stampante. Questo è intenzionale e di design. È possibile impostare i margini morbidi al di fuori dell'area di stampa se lo si desidera e l'uscita potrebbe essere ritagliata dal driver della stampante. Se questo non è auspicabile per la tua applicazione, devi modificare i margini nel codice del programma. O puoi impedire all'utente di selezionare i margini al di fuori dell'area stampabile (o avvisarli se lo fanno) oppure puoi applicare alcune min / max condizioni nel tuo codice quando inizi effettivamente a stampare (disegnando) il documento.

Caso di esempio: se si impostano i margini della pagina su 0,0,0,0 in Microsoft Word 2007, viene visualizzata una finestra di avviso con la dicitura "Uno o più margini sono impostati all'esterno dell'area di stampa della pagina. Scegliere il pulsante Correggi per aumentare la margini adeguati. " Se si fa clic su Correggi, Word semplicemente copierà i margini rigidi nei margini morbidi, quindi la finestra di dialogo mostra ora 0,16 "per tutti i margini (le funzionalità della mia stampante laser).

Questo è un comportamento previsto. Non è un bug / problema con Microsoft Word se la pagina stampata è ritagliata perché l'utente ha ignorato questo avviso e ha utilizzato 0,0,0,0 margini di pagina. Questo è lo stesso nella tua applicazione. È necessario applicare i limiti per qualsiasi cosa sia appropriata nel proprio caso d'uso. O con una finestra di avviso, oppure puoi forzare il limite più fortemente nel codice (non offrire una scelta all'utente).

Strategia alternativa

Va bene, quindi forse non vuoi solo ottenere i margini duri, ma piuttosto ottenere i margini morbidi e poi far rispettare i margini morbidi che rimangono all'interno dell'area stampabile durante la stampa. Sviluppiamo un'altra strategia qui.

In questo esempio userò le origini ai margini e consentirò all'utente di selezionare qualsiasi margine desiderato, ma imporrò in codice che il margine selezionato non si trovi al di fuori dell'area stampabile. Se i margini selezionati sono al di fuori dell'area stampabile, semplicemente li modificherò per essere all'interno dell'area stampabile.

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);
}

Attualmente quanto segue sta funzionando sulla mia stampante. Ho impostato OriginAtMargins su false. Ciò causa la traduzione automatica in HardMarginX e HardMarginY quando sto stampando sulla mia stampante, ma NON c'è alcuna traduzione quando stampo su PrintPreviewControl. Pertanto, devo verificare questo 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;
}

Se qualcuno può provare questo codice sulla sua stampante per verificare che funzioni universalmente, o correggerlo se sbaglio, sarebbe fantastico.

Non so se la pre-traduzione dell'origine sui margini rigidi quando OriginAtMargins è falso è standard con tutte le stampanti, o se è solo facendo questo sulla mia stampante.





printdocument