Comment trouver la réelle zone imprimable? (PrintDocument)

Pourquoi est-il si difficile de découvrir ce Rectangle magique?

dans L'événement OnPrintPage j'ai PrintPageEventArgs et j'essaie de dessiner en utilisant les graphiques dans les limites de la zone maximale imprimable.

j'ai essayé D'utiliser des PageBounds, PrintableArea, Graphics.VisibleClipBounds, etc. Tous ne parviennent pas toujours à obtenir la zone de dessin, en particulier lors du passage du paysage à la mise en page du Portrait. Printable area ne semble pas changer lorsque vous passez de Paysage à Portrait.

j'ai aussi remarqué qu'il y a une différence dans la façon dont les Graphiques.VisibleClipBounds est défini en fonction de si je fais une prévisualisation d'impression et une impression réelle. Dans un aperçu, il affiche toujours Largeur/Hauteur du Portrait, donc je dois vérifier s'il s'agit d'un aperçu et je dois changer manuellement la largeur/hauteur quand il s'agit d'un paysage.

j'ai besoin d'un algorithme pour calculer la zone imprimable en ce qui concerne le contexte graphique actuel, pas un zone d'impression théorique arbitraire qui n'est pas utilisée dans le dessin réel.


mon souci est de traiter la matrice graphique offset. Jusqu'à présent, j'ai remarqué de graves incohérences entre la façon dont le contexte graphique est pré-traduit en utilisant les marges dures en fonction de facteurs comme:

  • Si OriginAtMargins est vrai ou faux (ne pas se comporter comme je le pense)
  • si j'imprime sur une imprimante, ou en utilisant le PrintPreviewControl (je dois vérifier si c'est une impression de l'aperçu ou l'impression de la page pour gérer la traduction correctement)
  • si j'utilise mon imprimante à la maison ou mon imprimante au travail (les deux se comportent différemment)

y a-t-il un moyen standard de gérer cela? Dois-je tout simplement réinitialiser la matrice? Quand je mets OriginAtMargins à true, les graphiques sont pré-traduits à 84,84, mais mes marges sont de 100,100. Les marges brutes sont de 16,16. Ne devrait-il pas être traduit en 100 10000? Depuis 0,0 devrait être à la page limites, pas dur marges.

fondamentalement ma méthode devrait toujours travailler à obtenir le meilleur rectangle imprimable. J'ai juste besoin d'un moyen cohérent, indépendant du périphérique pour m'assurer que mon dessin origine (0, 0) est en haut à gauche de la page afin que le Rectangle ci-dessus soit d'une quelconque utilité pour moi.

40
demandé sur Trevor Elliott 2012-01-06 21:11:47

3 réponses

Votre question manque un peu de clarté sur ce qu'est le "meilleur" est rectangle. Je suppose que vous voulez dire le plus grand rectangle qui sera visible à 100% lors de l'impression.

commençons donc par nous assurer de comprendre ce que sont les "origines" de l'objet print document graphics et comment la propriété OriginAtMargins affecte cette origine.

OriginAtMargins-Obtient ou fixe une valeur indiquant si la position d'un objet graphique associé à une page est situé juste à l'intérieur de la les marges spécifiées par l'utilisateur ou à la coin supérieur gauche de la zone d'impression de la page.

- Définition de la Classe PrintDocument sur MSDN

OriginAtMarginsfalse (par défaut) l'objet graphique sera ajusté au rectangle Printablea (environ 5/32 du bord de chaque page pour mon imprimante laser, les anciennes imprimantes laser peuvent être plus, de nouvelles encoches peuvent être imprimées jusqu'au bord, les imprimantes de logiciel PDF imprimeront jusqu'au bord). Donc 0,0 dans mon objet graphique est en fait 16,16 sur la page physique de mon imprimante laser (votre imprimante peut être différente).

Avec la valeur par défaut de 1 pouce de marges de page et OriginAtMarginstrue, l'objet graphique sera ajusté au rectangle 100.100.650.1100 pour une page normale de lettre de portrait. C'est un pouce à l'intérieur de chaque bord de page physique. Donc 0,0 dans votre objet graphique est en fait 100,100 sur le physique page.

les marges sont également appelées "marges douces", car elles sont définies dans le logiciel et ne sont pas affectées par le dispositif d'impression physique. Cela signifie qu'ils seront appliqués à la taille actuelle de la page dans le logiciel et reflètent le portrait ou le paysage réel de la dimension de la page.

Printablarea est aussi connu sous le nom de "marges dures" qui reflètent les limites physiques de votre appareil d'impression. Cela varie d'une imprimante à l'autre, d'un fabricant à l'autre. Parce que ces sont des mesures matérielles, ils ne tournent pas lorsque vous mettez la page à Paysage/portrait. Les limites physiques ne changeront pas sur l'imprimante, quels que soient les paramètres d'impression du logiciel, nous devons donc nous assurer de les appliquer sur le bon axe en fonction de nos paramètres logiciels pour le document d'impression (orientation).

donc en suivant le modèle approximatif du code que vous avez posté, voici un PrintDocument.Le gestionnaire d'événements PrintPage qui dessinera un rectangle aussi grand que possible toujours visible (avec la valeur par défaut PrintDocument.OriginsAtMargins étant false). Si vous définissez PrintDocument.OriginsAtMarginstrue il va dessiner un rectangle aussi grand que possible tout en restant visible à l'intérieur des marges douces configurées (par défaut à 1" des bords de la page).

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

les deux lignes qui déterminent la largeur disponible et la hauteur Disponible sont ce que je pense que vous cherchiez dans votre question. Ces deux lignes prennent en compte si vous voulez des marges douces ou des marges dures et si le document d'impression est configuré pour paysage ou portrait.

j'ai utilisé Math.Floor() pour la manière facile de simplement laisser tomber tout ce qui dépasse la décimale (ex: 817.96 - > 817) juste pour s'assurer que la largeur et la hauteur disponibles était juste à l'intérieur des dimensions disponibles. Je suis "failing safe" ici, si vous voulez, vous pourriez maintenir flottant basé des coordonnées (au lieu de int), juste faire attention aux erreurs d'arrondi qui résulteront dans les graphiques découpés (si elle arrondit 817.96 jusqu'à 818 et puis le pilote d'imprimante décide qui n'est plus visible).

j'ai testé cette procédure dans portrait et landscape avec des marges dures et des marges douces sur une imprimante Dell 3115CN, une imprimante Samsung SCX-4x28 et une imprimante CutePDF. Si cela ne répond pas adéquatement à votre question, envisagez de réviser votre question pour clarifier "rectangle magique" et "meilleur rectangle".


EDIT: Notes à Propos de "Soft Marges"

des marges douces sont appliquées dans les logiciels et ne tenez pas compte des limites matérielles de l'imprimante. C'est intentionnel, et de par leur conception. Vous pouvez définir les marges douces en dehors de la zone d'impression si vous voulez et la sortie peut être coupée par le pilote de votre imprimante. Si cela n'est pas souhaitable pour votre application, vous devez ajuster les marges de votre code de programme. Soit vous pouvez empêcher l'utilisateur de sélectionner des marges en dehors de la zone imprimable (ou les prévenir s'ils le font) , soit vous pouvez appliquer certaines conditions min/max dans votre code vous commencez à imprimer (dessiner) le document.

Exemple De Cas: si vous définissez les marges de la page à 0,0,0,0 dans Microsoft Word 2007, une boîte de dialogue d'avertissement s'affiche qui se lit comme suit: "une ou plusieurs marges sont placées en dehors de la zone imprimable de la page. Choisissez le bouton fixer pour augmenter les marges appropriées."Si vous cliquez sur Corriger, Word copiera simplement les marges dures dans les marges douces, de sorte que la boîte de dialogue affiche maintenant 0,16" pour toutes les marges (mon imprimante laser capacité.)

C'est le comportement attendu. Ce N'est pas un bug/problème avec Microsoft Word si la page imprimée est clippée parce que l'Utilisateur a ignoré cet avertissement et utilisé des marges de 0,0,0,0 page. C'est la même chose dans votre demande. Vous devez appliquer les limites de ce que si approprié dans votre cas d'utilisation. Soit avec un dialogue d'avertissement, soit vous pouvez forcer la limite plus fortement dans le code (n'offrez pas de choix à l'utilisateur).


Alternative Stratégie

D'accord donc peut-être que vous ne voulez pas simplement obtenir les marges dures, mais plutôt obtenir les marges douces et ensuite forcer que les marges douces restent à l'intérieur de la zone imprimable lors de l'impression. Développons une autre stratégie.

dans cet exemple, je vais utiliser les origines aux marges, et permettre à l'utilisateur de sélectionner la marge qu'il veut, mais je vais appliquer en code que la marge sélectionnée ne soit pas en dehors de la zone imprimable. Si les marges sont à l'extérieur la zone imprimable, je vais simplement les ajuster pour être à l'intérieur de la zone imprimable.

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);
}
67
répondu BenSwayne 2017-04-27 02:39:05

Actuellement les suivants travaille sur mon imprimante. J'ai mis L'Originatmargin à false. Cela provoque la traduction automatique vers le HardMarginX et le HardMarginY lorsque j'imprime vers mon imprimante, mais pas de traduction lorsque j'imprime vers le PrintPreviewControl. Par conséquent, je dois vérifier pour cette affaire.

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 quelqu'un peut tester ce code sur leur imprimante pour vérifier qu'il fonctionne universellement, ou les corriger si je me trompe, ce serait génial.

je ne sais pas si la pré-traduction de l'origine vers les marges dures lorsque L'origine est fausse est standard avec toutes les imprimantes, ou si elle le fait simplement sur mon imprimante.

2
répondu Trevor Elliott 2012-01-10 15:17:58

je pense que ce dont vous avez besoin est simplement de redessiner l'image pour s'adapter à n'importe quelle taille de papier utilisée. Voici mon code:

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
0
répondu Eric 2012-01-11 19:47:34