How to calculate FixedPage dimensions - c#

This snippet is part of some code used to generate an XPS document. XPS document generation is no joke. I wish to avoid pasting any of that XPS code here if at all possible. Instead, this code focuses on the WPF portion of the problem.
The problem I am asking for you to help with is here. I have hard coded the dimensions to work for a test image:
double magicNumber_X = 3.5;//trial and error...3 too small 4 too big
fixedPage.Arrange(new Rect(new Point(magicNumber_X, 0), size));
Instead, how can I fix this code to calculate the coordinates?
Full Method:
private PageContent AddContentFromImage()
{
var pageContent = new PageContent();
var fixedPage = new FixedPage();
var bitmapImage = new BitmapImage(new Uri(hardCodedImageSampleFilePath, UriKind.RelativeOrAbsolute));
var image = new Image();
image.Source = bitmapImage;
fixedPage.Children.Add(image);
((IAddChild)pageContent).AddChild(fixedPage);
double pageWidth = 96 * 8.5;//XPS documents are 96 units per inch
double pageHeight = 96 * 11;
fixedPage.Width = pageWidth;
fixedPage.Height = pageHeight;
var size = new Size(8.5 * 96, 11 * 96);
fixedPage.Measure(size);
double magicNumber_X = 3.5;//trial and error...3 too small 4 too big
double magicNumber_Y = 0;
fixedPage.Arrange(new Rect(new Point(magicNumber_X, magicNumber_Y), size));
fixedPage.UpdateLayout();
return pageContent;
}
I'm a little surprised FixedPage.Measure(size) does not correct the issue by itself. I tried passing no params, e.g. fixedPage.Arrange(new Rect(), size)) still no go.
FWIW, this calculation worked fine when I was using PrintDocument.
private void pd_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
RectangleF marginBounds = e.MarginBounds;
RectangleF printableArea = e.PageSettings.PrintableArea;
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));
Rectangle rectangle = new Rectangle(0,0, availableWidth -1, availableHeight - 1);
g.DrawImage(_image, rectangle);

I hooked into FixedPage.Loaded event because FixedPage.ActualHeight is required in order to perform the calculation and will not be set until the control has loaded. This also means that with this mechanism FixedPage has to be displayed to correctly perform an automated print.
void fixedPage_Loaded(object sender, RoutedEventArgs e)
{
var fixedDocument = sender as FixedPage;
CalculateSize(fixedDocument);
}
private void CalculateSize(FixedPage fixedPage)
{
PrintQueue printQueue = LocalPrintServer.GetDefaultPrintQueue();
PrintCapabilities capabilities = printQueue.GetPrintCapabilities();
//get scale of the print wrt to screen of WPF visual
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / fixedPage.ActualWidth, capabilities.PageImageableArea.ExtentHeight / fixedPage.ActualHeight);
//Transform the Visual to scale
fixedPage.LayoutTransform = new ScaleTransform(scale, scale);
//get the size of the printer page
var sz = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
//update the layout of the visual to the printer page size.
fixedPage.Measure(sz);
double x = capabilities.PageImageableArea.OriginWidth;
double y = capabilities.PageImageableArea.OriginHeight;
fixedPage.Arrange(new Rect(new Point(x, y), sz));
fixedPage.UpdateLayout();
}

Related

Confirmation, Printing Graphic location C#

I have created the code below to help me print the admin form as a "report" like document showing the date and graph needed for documentation.
try
{
Graphics g = this.CreateGraphics();
AdminPage = new Bitmap(Size.Width, Size.Height, g);
Graphics Printed = Graphics.FromImage(AdminPage);
Printed.CopyFromScreen(519, 340, 0,0,this.Size);//519,340 this.Location.Y,this.Location.X
printPreviewAdminDialogue.ShowDialog();
}
catch(Exception )
{
MessageBox.Show("Please check printer connection!");
}
I have used the coordinate data of the screen as:
Printed.CopyFromScreen(519, 340, 0,0,this.Size);
Will this still work on any size screen or will this result in some formatting problems on other devices rather than just my laptop?
So far it looks fine Print Preview of what i want with the current code
This method takes a Control (SourceControl) reference and returns a Bitmap resulting from a screen capture of the Control's Window.
Parameters:
SourceControl: The control to be printed. It can be a TopLevel Window (a Form) or a Child Control.
Dpi: The DPI resolution of the resulting Bitmap.
ScaleToDpi: if set to true, the size of the Bitmap will be scaled to match the Dpi parameter, defining a scale factor relative to current screen resolution. E.g.: If parameter Dpi = 300 and the current screen resolution is 96 Dpi, the resulting Bitmap size will be scaled with a factor of 3.125
ClientAreaOnly: If true, captures the SourceControl client area, otherwise the full window bounds.
InterpolationMode: Defines the resulting quality of a scaled bitmap. For enlargements, Bicubic or HighQualityBicubic gives better results. Bicubic may render a shaper image. The usefulness of this parameter depends on how the image is used. The perceived visual quality may not be the same when the image is printed on paper. When printing, sharper images give a better result.
The results can be tested using a PrintPreviewControl to see the difference between a scaled and a non-scaled Bitmap.
Print the a Form including its borders with a resolution of 300 DPI but not scaled to the new resolution (modifies the resulting Bitmap resolution only):
Bitmap FullSize300Dpi = PrintControlFromScreen(this, false, 300, false, InterpolationMode.Default);
Print the a Form ClientArea with a resolution of 300 DPI and scale its dimensions to the new resolution using a HighQualityBicubic Interpolation:
Bitmap ClientArea300DpiScaled = PrintControlFromScreen(this, true, 300, true, InterpolationMode.HighQualityBicubic);
The same, but it prints the client area of a button1 control with a resolution of 96 DPI with a Bilinear Interpolation:
Bitmap Child96DpiUnscaled = PrintControlFromScreen(this.button1, true, 96, false, InterpolationMode.Bilinear);
public Bitmap PrintControlFromScreen(Control SourceControl, bool ClientAreaOnly, float Dpi, bool ScaleToDpi, InterpolationMode Interpolation)
{
using (Graphics graphics = SourceControl.CreateGraphics())
{
SizeF ScaleFactor = new SizeF((Dpi / graphics.DpiX), (Dpi / graphics.DpiY));
SizeF BitmapSize;
if (ScaleToDpi)
{
BitmapSize = ClientAreaOnly ? new SizeF((SourceControl.ClientRectangle.Size.Width * ScaleFactor.Width),
(SourceControl.ClientRectangle.Size.Height * ScaleFactor.Height))
: new SizeF((SourceControl.Bounds.Size.Width * ScaleFactor.Width),
(SourceControl.Bounds.Size.Height * ScaleFactor.Height));
}
else
{
BitmapSize = ClientAreaOnly ? SourceControl.ClientRectangle.Size : SourceControl.Bounds.Size;
}
using (Bitmap bitmap = new Bitmap((int)BitmapSize.Width, (int)BitmapSize.Height))
{
bitmap.SetResolution(ScaleFactor.Width * graphics.DpiX, ScaleFactor.Height * graphics.DpiY);
using (Graphics ImageGraph = Graphics.FromImage(bitmap))
{
ImageGraph.CompositingQuality = CompositingQuality.HighQuality;
ImageGraph.CompositingMode = CompositingMode.SourceCopy;
ImageGraph.SmoothingMode = SmoothingMode.HighQuality;
ImageGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
ImageGraph.InterpolationMode = Interpolation;
if (ClientAreaOnly)
{
ImageGraph.CopyFromScreen(SourceControl.PointToScreen(SourceControl.ClientRectangle.Location),
new Point(0, 0), SourceControl.ClientRectangle.Size);
}
else
{
if (SourceControl.TopLevelControl == SourceControl)
{
ImageGraph.CopyFromScreen(SourceControl.Bounds.Location,
new Point(0, 0), SourceControl.Bounds.Size);
}
else
{
ImageGraph.CopyFromScreen(SourceControl.PointToScreen(SourceControl.ClientRectangle.Location),
new Point(0, 0), SourceControl.Size);
}
}
if (ScaleToDpi) ImageGraph.ScaleTransform(ScaleFactor.Width, ScaleFactor.Height);
ImageGraph.DrawImage(bitmap, new Point(0, 0));
return (Bitmap)bitmap.Clone();
};
};
};
}
UPDATE1 (Example of Print Preview):
This is one possible way to show a PrintPreview of the Bitmap. It should of course be adapted to an actual Printer. This is for general use.
A Bitmap is created in a Button event handler and a PrintPreview Dialog is shown to seee the result.
This creates a ScreenShot of a PictureBox control in the current Form
(this), takes the ClientArea only, sets the resuluton to 300Dpi, does
not scale the image (keeps the screen original size), using a Bicubic
Interpolation for rendering.
Bitmap screenCapture = PrintControlFromScreen(this.pictureBox1, true, 300, false, InterpolationMode.Bicubic);
This is the Button click handler from where you can call the PrintControlFromScreen() method:
private void button1_Click(object sender, EventArgs e)
{
Bitmap screenCapture = PrintControlFromScreen(this.pictureBox1, true, 300, false, InterpolationMode.Bicubic);
PrintDocument PrintDoc = new PrintDocument();
PrintDoc.DocumentName = "ScreenShot";
PrintDoc.DefaultPageSettings.PrinterResolution = new PrinterResolution() { X = 300, Y = 300 };
PrintDoc.DefaultPageSettings.Landscape = PrintDoc.DefaultPageSettings.PaperSize.Width < screenCapture.Width;
PrintDoc.OriginAtMargins = true;
PrintDoc.PrintPage += (s, ppe) =>
{
Rectangle BitmapSize = new Rectangle(new Point(0, 0),
new Size(screenCapture.Width, screenCapture.Height));
Graphics _imagegraph = Graphics.FromImage(screenCapture);
_imagegraph.CompositingMode = CompositingMode.SourceCopy;
_imagegraph.CompositingQuality = CompositingQuality.HighQuality;
_imagegraph.SmoothingMode = SmoothingMode.HighQuality;
_imagegraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
_imagegraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
ImageAttributes ImageAttr = new ImageAttributes();
ImageAttr.ClearThreshold(ColorAdjustType.Bitmap);
ppe.Graphics.DrawImage(screenCapture, BitmapSize, 0F, 0F,
BitmapSize.Width, BitmapSize.Height, GraphicsUnit.Pixel, ImageAttr);
};
PrintPreviewDialog pPreviewDiag = new PrintPreviewDialog();
pPreviewDiag.Document = PrintDoc;
pPreviewDiag.AutoScaleDimensions = new SizeF(Screen.PrimaryScreen.BitsPerPixel * 1.5F,
Screen.PrimaryScreen.BitsPerPixel * 1.5F);
pPreviewDiag.AutoScaleMode = AutoScaleMode.Dpi;
pPreviewDiag.StartPosition = FormStartPosition.CenterScreen;
pPreviewDiag.ShowDialog();
}

Print Text with DrawString() next to an existing bitmap

Greetings fellow users,
A virgin post on my end since its the first time i am abusing stack overflow with a question! I have been trying to get a bitmap print along with a String to print. Basically the view i want to achieve is the Image and the text to the right of the image as we see the printout. Below is the code I am using
Bitmap qrCodeImage = qrCode.GetGraphic(20);
senderQR = qrCodeImage;
PrintDocument pd = new PrintDocument();
Margins margins = new Margins(10, 10, 10, 10);
pd.DefaultPageSettings.Margins = margins;
pd.PrintPage += PrintPage;
pd.Print();
Here is the PrintPage method
private void PrintPage(object sender, PrintPageEventArgs e)
{
System.Drawing.Image img = senderQR;
Bitmap batchCode = new Bitmap(80, 700);
Rectangle m = e.MarginBounds;
RectangleF batch1 = new RectangleF(80, 700, 650, 1000);
m.Width = img.Width / 5;
m.Height = img.Height / 5;
Graphics g = Graphics.FromImage(batchCode);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.DrawString(batch, new Font("Arial", 40), Brushes.Black, batch1);
g.Flush();
e.Graphics.DrawImage(img, m);
}
What am i doing wrong? what seems to be the issue? I have been struggling a whole lot to achieve this but no luck!
Additional Notes:
I want the text on the right to Wrap under itself and not under or on top of the existing bitmap within a size of 3,5 x 2 (inches) (label printing).
This is the printout i get with the existing code;
https://prnt.sc/h1ecb0
https://prnt.sc/h1edex
The image you're drawing on (batchCode) is 80 pixels wide and 700 high. When you write your text over it, you set the top-left point of your writing to 80,700 - exactly to the bottom-right corner of your picture. Basically, you write your text outside of the picture.
Update
I've created a small example to make it reproducible, below is a form class for a basic WinForms application:
public partial class Form1 : Form
{
private PictureBox pictureBox2;
public Form1()
{
InitializeComponent();
pictureBox2 = new PictureBox();
pictureBox2.Size = ClientSize;
pictureBox2.SizeMode = PictureBoxSizeMode.AutoSize;
this.Click += Form1_Click;
pictureBox2.Click += Form1_Click;
Controls.Add(pictureBox2);
}
private void Form1_Click(object sender, EventArgs e)
{
var batch = "hello there!";
Bitmap batchCode = new Bitmap(1000, 1000);
var batch1 = new RectangleF(150, 150, 850, 850);
using (Graphics g = Graphics.FromImage(batchCode))
{
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.DrawString(batch, new Font("Arial", 40), Brushes.Black, batch1);
}
pictureBox2.Image = batchCode;
}
}

Why is my Shapes.Line twice the size I want it to be on Canvas?

Using this method, I want to render a canvas to a bitmap.
When I add a Shape to the Canvas, it is rendered twice the specified size.
In the example below, I am drawing a line from (0;0) to (50;50) on a canvas of size 200 by 200.
public bool exportToBmp(string path, int dpi = 96)
{
if (path == null)
return false;
var canvas = new System.Windows.Controls.Canvas();
// This diagonal Line should span a quarter of the rendered Image
var myLine = new System.Windows.Shapes.Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
myLine.X1 = 0;
myLine.X2 = 50;
myLine.Y1 = 0;
myLine.Y2 = 50;
myLine.StrokeThickness = 2;
canvas.Children.Add(myLine);
canvas.Height = 200;
canvas.Width = 200;
Size size = new Size(canvas.Width, canvas.Height);
canvas.Measure(size);
canvas.Arrange(new Rect(size));
var width = (int)canvas.ActualWidth;
var height = (int)canvas.ActualHeight;
RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, dpi, dpi, PixelFormats.Pbgra32);
bmp.Render(canvas);
PngBitmapEncoder image = new PngBitmapEncoder();
image.Frames.Add(BitmapFrame.Create(bmp));
using (Stream fs = File.Create(path))
{
image.Save(fs);
}
return false;
}
The rendered image I get is 200 by 200 px big, but the diagonal goes all the way to (100;100)
What am I doing wrong?
When I run your code, I see the following image (border added for clarity):
Are you passing a DPI other than 96?
What DPI settings are in use on your computer?

WPF print window without displaying it

I have several WPF windows with controls. I would like to print them one after the other without displaying them. Is there a way of doing that?
This is the printing method:
public void printItemList(string printerName)
{
printButton.Visibility = Visibility.Collapsed;
cancelButton.Visibility = Visibility.Collapsed;
this.Height = 1250;
this.Width = 815;
PrintDialog printDlg = new System.Windows.Controls.PrintDialog();
printDlg.PrintQueue = new System.Printing.PrintQueue(new System.Printing.PrintServer(), printerName);
System.Printing.PrintCapabilities capabilities = printDlg.PrintQueue.GetPrintCapabilities(printDlg.PrintTicket);
//get scale of the print wrt to screen of WPF visual
double scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / this.ActualWidth, capabilities.PageImageableArea.ExtentHeight /
this.ActualHeight);
//Transform the Visual to scale
this.LayoutTransform = new ScaleTransform(scale, scale);
//get the size of the printer page
Size sz = new Size(this.ActualWidth, this.ActualHeight); //(8.5 * 96.0, 11.0 * 96.0);
//update the layout of the visual to the printer page size.
this.Measure(sz);
this.Arrange(new Rect(new Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz));
//now print the visual to printer to fit on the one page.
printDlg.PrintVisual(this, String.Empty);
}
Moving out the window was the key, thanks for the tip!
ItemListWindow il = new ItemListWindow();
il.Show();
var desktopWorkingArea = System.Windows.SystemParameters.WorkArea;
il.Top = desktopWorkingArea.Bottom + 100;
il.printItemList(printerComboBox.SelectedItem.ToString());
il.Close();

Print High Resolution Image in C#

I am printing image 2349 x 3600 pixels. I have resized image but printing is blurred not clean. Please looke at code -
using System.Drawing.Drawing2D;
public Bitmap resizeimage(Bitmap bitmap)
{
Bitmap result = new Bitmap(850, 1101);
using (Graphics grap = Graphics.FromImage(result))
{
grap.CompositingQuality = CompositingQuality.HighQuality;
grap.InterpolationMode = InterpolationMode.Bicubic;
grap.SmoothingMode = SmoothingMode.HighQuality;
grap.CompositingQuality = CompositingQuality.HighQuality;
grap.DrawImage(bitmap, 0, 0, 850, 1101);
}
return result;
}
I tried everything from changing bitmap size, quality of graphics but still image blurred.
I used microsoft office 2007 and resized image and printed it , it was so clear.
How I can get exact printing quality as I got in microsoft office 2007.
Please help.
Here is code before drawing -
PrintPreviewDialog printpreview = new PrintPreviewDialog();
PrintDocument printdocument = new PrintDocument();
printdocument.PrinterSettings.PrinterName = "EPSON L100 Series";
int horizantal_dpi = printdocument.PrinterSettings.DefaultPageSettings.PrinterResolution.X;
int vertical_dpi = printdocument.PrinterSettings.DefaultPageSettings.PrinterResolution.Y;
decimal final_width_dpi = (((int)printdocument.DefaultPageSettings.PrintableArea.Width * horizantal_dpi) / 100);
decimal final_height_dpi = (((int)printdocument.DefaultPageSettings.PrintableArea.Height * vertical_dpi ) / 100);
printimagaprint = new Bitmap((int)final_width_dpi, (int)final_height_dpi);
//set resoultion
printimagaprint.SetResolution(horizantal_dpi, vertical_dpi);
Graphics g = System.Drawing.Graphics.FromImage(printimagaprint);
g.DrawImage(bitmap, 0, 0, printimagaprint.Width, printimagaprint.Height);
printdocument.PrintPage +=new PrintPageEventHandler(printdocument_PrintPage);
//printdocument.Print();
printdocument.DocumentName = textBox1.Text;
printpreview.Document = printdocument;
printpreview.ShowDialog();
Try matching the printer resolution before printing.
printDialog.PrinterSettings.PrinterName = GetTargetPrinter();
int horizontal_dpi = printDialog.PrinterSettings.DefaultPageSettings.PrinterResolution.X;
int vertical_dpi = printDialog.PrinterSettings.DefaultPageSettings.PrinterResolution.Y;
Decimal final_width_dpi = (((int)printDialog.PrinterSettings.DefaultPageSettings.PrintableArea.Width * horizontal_dpi) / 100);
Decimal final_height_dpi = (((int)printDialog.PrinterSettings.DefaultPageSettings.PrintableArea.Height * vertical_dpi) / 100);
printImage = new Bitmap((int)final_width_dpi, (int)final_height_dpi);
// Set Resolution
printImage.SetResolution(horizontal_dpi, vertical_dpi);
Graphics g = System.Drawing.Graphics.FromImage(printImage);
And please try to provide more descriptive code. I am just making assumption for now.

Categories