Is printing of dashed lines in WPF wrong? - c#

A dashed line has a different count of dashes depending on the resolution of printer. Why isn't it dpi independent? How can I fix it?
var line = new Line();
line.Stroke = Brushes.Black;
line.StrokeThickness = 1;
line.X1 = line.Y1 = 100;
line.X2 = line.Y2 = 200;
line.StrokeDashArray = new DoubleCollection(new[] { 8.0 });
var dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
{
Size pageSize = new Size(
dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);
line.Measure(pageSize);
line.Arrange(new Rect(0, 0, pageSize.Width, pageSize.Height));
dialog.PrintVisual(line, "description");
}
I've used PrintVisual, but you can to create FixedDocument with dashed lines and use PrintDocument. The result will be the same.

WPF is inherently responsive to DPI changes, and pretty much every control (unless specified otherwise) is a vector graphic - I would expect your printer is smart enough to interpret these vector graphics rather than print a dump of what is on your screen.
From my experience with WPF there are at least two options:
Option 1 - Create a bitmap and print it
You can capture WPF controls as bitmap objects, which will be completely unaware of DPI using RenderTargetBitmap and then print them as normal. It is worth noting that the generated bitmap will be different depending on the display resolution/scaling of the system is was generated on & anything you want to capture as a bitmap must be visible on screen (not off screen in a scrollbox etc).
Option 2 - Make your app DPI unaware
You probably don't want to use this option, as it will prevent scaling on high resolution displays (There may be a way to achieve this temporarily just for printing, however your printer may ignore this option as it's more of a UI thing, i'm not currently able to test it) but there are several ways to prevent your app from acknowledging DPI all together.
I'm not entirely sure these options will work as I can't currently test them. I would have posted this as a comment if I could. Good luck!

Related

CEF Sharp: printing resolution

I'm using CEFSharp (the C# wrapper for CEF) to print a web page to PDF, like this:
browser.PrintToPdfAsync(#"C:\out.pdf", new PdfPrintSettings
{
BackgroundsEnabled = true,
HeaderFooterEnabled = false,
Landscape = false,
MarginType = CefPdfPrintMarginType.Custom,
MarginBottom = 0,
MarginTop = 0,
MarginLeft = 0,
MarginRight = 0,
PageWidth = 210000,
PageHeight = 297000
});
However, the problem is that the resulting PDF is very blurry when compared to manually printing the same page in the "real" Chrome application.
I've made a comparison screenshot to show the difference:
(open it at full resolution to notice the difference)
Basically, as you can see, CEF seems to be compressing images and other non-vector graphics much more than the native Chrome printing function.
Ideally, I'd like to disable compression completely, or at least it bring it closer to native Chrome levels. Can it be done?
Also related: is there was a way to print at higher resolution? The PdfPrintSettings class only accepts width and height measurements in microns, but I don't see any way to set the rendering definition (DPIs)... is it possible?
I guess images are blurry because PDF gets printed as a preview:
https://bitbucket.org/chromiumembedded/cef/src/6006f77bd9e030e9b456e180798c7c13d19cae08/libcef/browser/printing/print_view_manager.cc?at=master&fileviewer=file-view-default#print_view_manager.cc-110
It was my pull-request which added PDF printing to CEF. Printing as a preview seemed good enough for me. It allowed to write less code and implement less components involved in PDF printing.
It's also possible that some other settings make images blurry. For example:
https://bitbucket.org/chromiumembedded/cef/annotate/6006f77bd9e030e9b456e180798c7c13d19cae08/libcef/browser/printing/print_view_manager.cc?at=master&fileviewer=file-view-default#print_view_manager.cc-50
It needs some debugging. In order to do that you most likely would need to build CEF from source:
https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding
DPI can not be set because Chromium itself does not have such setting in its PDF print dialog. Although you can try to set different DPI options here:
https://bitbucket.org/chromiumembedded/cef/src/6006f77bd9e030e9b456e180798c7c13d19cae08/libcef/browser/printing/print_view_manager.cc?at=master&fileviewer=file-view-default#print_view_manager.cc-53 But it's possible that those settings are just ignored by Chromium.

ZedGraph to PDF using MigraDoc

I am currently working on a C# application that aims to do some computation and output graphs in a pdf file.
I use the Zedgraph library to draw my graphs so something like graphPane.AddCurve(PointPairList). Now I tried to output these graphs to pdf file via MigraDoc package.
Currently, I have a script that map the Zedgraph to bitmap then paste it on the pdf file. So something like this:
private Bitmap getBitMap()
{
ZedGraphControl graph = new ZedGraphControl();
newGraph = graphPane.Clone();
SizeF s = new SizeF(3.5f, 4.5f);
newGraph.Scale(s);
newGraph.DrawToBitmap(bit, new Rectangle(0, 0, newGraph.Width, newGraph.Height));
return bit;
}
The problem is that this give me a slightly pixellated image on the pdf page. And I need this graph to be in a very high quality. So are there anything I can change the improve the quality or do i have to change my entire approach for such thing.
Thank you so much in advance.
By default a Bitmap you create has your current screen resolution which could be as low as 75dpi, more common 96dpi; more modern monitors have 120dpi or more, but a good print quality starts 150dpi. For really crips images you want 300dpi and to allow zooming you may want to have 600dpi or more..
So you need to create and fill a bitmap with a larger size and take control of its dpi resolution.
Assuming your size of 3.5f x 4.5f is inches, for 300dpi you need a Bitmap with 1050 x 1350 pixels.
So you should create such a Bitmap..:
Bitmap bmp = new Bitmap(1050, 1350);
..and set the resolution:
bmp.SetResolution(300, 300);
To fill it up your control must have the same size:
newGraph.ClientSize = bmp.Size;
Now DrawToBitmap should create an image that is crisp and fit to zoom in..
Note that it does not matter if the control is too large to fit on the screen; DrawToBitmap will still work.
Update In addidtion to a sufficient resolution it is of interest to draw quality lines etc.. A speciality of ZedGraph is that one can turn on Antialiasing, either for individual lines:
curve_x.Line.IsAntiAlias = true;
or other elements:
myPane.XAxis.Scale.FontSpec.IsAntiAlias = true;
or the whole Chart:
zedGraphControl1.IsAntiAlias = true;
All examples are taken from this post.

How to adjust axis scales for datavisualization.charting after resizing a chart?

I updated a chart from essentially being 72dpi, to 300dpi. This is because I am using itextsharp to add an image to my pdf and the quality was poor. So I increased the size of the image by 3X and the image does look better, but here is the problem.
DPI has increased, but detail has become very hard to see.
Original Chart Image
Refactored Chart Image
Code
This is how I resized my chart.
private static System.Drawing.Bitmap GetChartBitmap()
{
System.Drawing.Rectangle targetBounds = new System.Drawing.Rectangle(0, 0, chart_runs.Width, chart_runs.Height);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(targetBounds.Width, targetBounds.Height);
bitmap.SetResolution(1000, 1000);
chart_runs.DrawToBitmap(bitmap, targetBounds);
bitmap.Save(#"C:\Temp\OriginalChartImage.bmp");
System.Drawing.Bitmap bitmap3 = new System.Drawing.Bitmap(1650, 990);
bitmap3.SetResolution(300, 300);
chart_runs.DrawToBitmap(bitmap3, new System.Drawing.Rectangle(0, 0, 1650, 990));
bitmap3.Save(#"C:\Temp\RefactoredChartImage.png");
//This stuff below is for my code elsewhere. Using bitmap3 to be added to pdf.
//chart_runs.DrawToBitmap(bitmap, targetBounds);
string path = System.IO.Path.GetTempPath();
bitmap1.Save(path + #"\Image.png");
return bitmap1;
}
I have looked at the Microsoft msdn examples and haven't found anything that addresses my problem. Namely, how can I either increase the size of my labels so people can read them again. OR, is there a way for me to increase the DPI and keep the same label x and label y scale that was used in the first picture? That is, have a larger image and 300DPI, but scale 0 to 300 by 20's and not 5's like my refactored picture?
Attempts to fix
Scaling the axis? See here. I don't think this is working right. Not much success here.
Been trying to find a way in Chart class to see if there is a way to specify strict scales. (20 on y scale vs 15 seconds on x scale).
Most online resources are pleased just to increase the scale of the picture and walk away. And things like this here.
I would greatly appreciate any help and assistance.
Couple different questions, with a couple different answers. The easiest would be to change the font size of your axis labels to be bigger. This can be done via
chart1.ChartAreas[0].AxisX.LabelStyle.Font = new Font...;
Without doing that, your labels won't be readable no matter what else you do, and that's just because you changed the DPI (that's exactly what changing the DPI does).
If you want the labels to be displayed every 20 units on the y axis and every 15 on the x, you can use the Interval and IntervalType properties of the axis. The IntervalType is used when you have DateTime objects being displayed:
chart1.ChartAreas[0].AxisX.Interval = 15;
chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Seconds;
chart1.ChartAreas[0].AxisY.Interval = 20;
Your first link about scaling the axis is essentially zooming in or out, which is why you haven't had success.

different ScaleTransform for every printer?

Im printing on a labelprinter with 7.5X8 cm labels using the regular GDI+/.NET method and it works fine
void pdPrintLabel(object sender, PrintPageEventArgs ev)
{
Graphics g = ev.Graphics;
using (Font smallFont = new Font(FontFamily.GenericSansSerif, 6),
mediumFont = new Font(FontFamily.GenericSansSerif, 8),
bigFont = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Bold))
{
SizeF smallSize = g.MeasureString("XXX", smallFont);
SizeF mediumSize = g.MeasureString("XXX", mediumFont);
SizeF bigSize = g.MeasureString("XXX", bigFont);
Brush blackBrush = Brushes.Black;
g.DrawString((bagNumber / 50 + 1) + bagNumber, bigFont, blackBrush, pos);
// etc
}
}
Now the printer is changed, same model just a different printer. The same code now fills only half the label. Some setting is different I guess.
I could fix this with
ev.Graphics.ScaleTransform(1.3f, 1.4f);
but only after finetuning/recompiling/testing the arguments. I dont want to change that code every time a new printer is used of course.
So is there an algorithm with which to compute the correct ScaleTransform arguments?
edit:
And on top of that, importing the settings from one printer and exporting them to the other did not change anything
The default scaling mode for printers is GraphicsUnit.Display, one pixel in your code is 0.01 inch on paper. Which is a resolution-independent scaling mode, output will always have the same size on paper regardless of the printer resolution. So you never need to use Graphics.ScaleTransform to adjust the scaling yourself.
There's something wrong with the printer driver for this printer, it appears to mis-report the printer resolution. Which is very rare, particularly so when you use the exact same printer model. Make sure it isn't a built-in feature of the printer to scale output that just happens to have a different setting on the second printer. Not entirely unusual for label printers, they like to accommodate software that isn't designed to deal with various label sizes. You'll need assistance from the manufacturer if you cannot sort it out from the manual.

How do I force a Windows Forms C# application to ignore when a user choose 125% or 150% for the OS font size?

I need a quick way of forcing my C# Windows Forms application to not scale fonts when a user choose a larger or smaller percentage in the OS settings.
Is this even possible?
Here is what worked for me...
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Font = new System.Drawing.Font("Arial", 14F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Pixel, ((byte)(0)));
The above two lines are copied from my BaseForm.Designer.cs file, but basically I found two easy steps to get "no font scaling":
Set AutoScaleMode to None.
Use "Pixel" as the Unit Type for all Fonts, instead of the default Point value.
As far as if you should let Windows scale your fonts or not, that's up to you. I for one don't like the design, so if I feel my application needs to be scaled, I'll do it myself, with my own options and design.
Over the years of speaking with actual end-users, I've also found that most of them have no idea about DPI settings, and if they have anything other than the default set, it wasn't because they wanted it that way... and they just never noticed because all they use is the web browser and maybe Excel and Microsoft Word (which use whatever font they set it to).
If my application had respected the system font settings, they wouldn't have liked it as much == less sales, because it would have had this huge ugly font like the system dialogs do (and they don't know how to change it, but they don't care about system dialogs they never use).
The problem is that the Font property of a Form or a control specifies the font size in Points. That's a measurement that affect the height of the letters when the DPI setting changes. One point is 1/72 inches. The default DPI, 96 dots per inch and a font size of 9 points yields a letter that is 9 / 72 x 96 = 12 pixels high.
When the user bumps up the DPI setting to, say, 120 DPI (125%) then the letter becomes 9 / 72 x 120 = 15 pixels high. If you don't let the control get larger then the text won't fit in the control anymore. Very ugly to look at.
The Form.AutoScaleMode property solves this problem. It checks at which size the form was designed and compares it against the DPI on the machine on which it runs. And resizes and relocates the controls to ensure this kind of clipping won't happen. Very useful, it is completely automatic without you having to do anything about it.
The typical problem is the "relocates" bit in the previous paragraph. If you give controls their own font size instead of inheriting the size of the form or if the automatic layout of the form isn't kosher then controls may end up in the wrong spot, destroying the organized look of the form.
You need to fix that, it isn't clear from your question what the source of the problem might be. Trying to prevent this auto-scaling from doing its job is not sustainable. You'll have to iterate all of the controls in the form and change their Font, picking a smaller font size. This is however going to get you into trouble a couple of years from now, if not already. Your user is going to complain about having to work with a postage stamp.
The easiest way to debug the layout problem, avoiding the pain of constantly changing the DPI size, is to temporarily paste this code into your form class:
protected override void OnLoad(EventArgs e) {
this.Font = new Font(this.Font.FontFamily, this.Font.SizeInPoints * 125 / 96);
base.OnLoad(e);
}
I found a pretty easy workaround.
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
This will make the text the same size regardless of the autoscale size.
Going against the user wishes like this is not something MS eagerly accommodates.
Rather than fixing the symptom (your app not scaling correctly), would it not be probably just as quick to fix the problem? When a user selects larger fonts, it's usually because they need the larger letters to be able to read them; so scaling correctly is more important than not scaling and remaining illegible to the user.
No. GDI and Windows Forms are resolution dependent. The best option is to design your layouts in a way that they scale appropriately when a window is resized. This tends to allow font size scaling to work correctly, as well, as the layouts will adjust as needed.
This, by the way, is one of the big improvements in WPF - it's designed to be resolution independent.
I suspect you already tried
yourform.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
in your forms, which prevents your forms from scaling, but not your fonts. So what you have to do (even if it is not recommended, see the other posts) is setting the font size for all controls in your form to a fixed size:
Here is a code snippet to grab all controls of a Form:
public List<Control> FindAllControls(Control container)
{
List<Control> controlList = new List<Control>();
FindAllControls(container, controlList);
return controlList;
}
private void FindAllControls(Control container, IList<Control> ctrlList)
{
foreach (Control ctrl in container.Controls)
{
if (ctrl.Controls.Count == 0)
ctrlList.Add(ctrl);
else
FindAllControls(ctrl, ctrlList);
}
}
Under http://www.csharp411.com/change-font-size/ you will find a link how to change the font size of a control, especially when you want to use fixed pixel sized. I think with these tools you can wire it together on your own.
EDIT: Don't forget to say: if you are finally going not to ignore the OS font size and to use AutoScaleMode as intended: this has some quirks, see my previous post here on SO.
In Windows 8.0, even if you set the Font Unit as Pixel, with the AutoScaling set to None, it is auto scaling.
In Windows 7, the same application is not auto scaling.
So therefore, the only way to not to let your text auto scale on Windows 7 is to set the Font Unit as Pixel and the Window's AutoScaling property as None.
And in Windows 8.0, I guess you have no other choice but to set the dpi to the dpi of the developer machine and ask the user to never change it!

Categories