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.
Related
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!
I'm trying to print a pdf with Ghostscript using those settings :
var switches = new List<string>
{
#"-empty",
#"-dPrinted",
#"-dNOPAUSE",
#"-dNOSAFER",
#"-dQUIET",
#"-dPDFSETTINGS=/printer",
#"-dNumCopies=1",
#"-sDEVICE=mswinpr2",
#"-dCompatibilityLevel=1.4",
#"-sOutputFile=%printer%" + printerSettings.PrinterName,
#"-f",
pdfFileName
};
but either the pdf or Ghostscript have bad margins and while it's good when I print it to file it clips when I print it on my printer.
Is there any way to add those programatically with Ghostscript ? I tried many different solutions from first pages of google but none of them work and they seem to have no effect on the printed pdf.
When I try to print it out with Adobe or IE it magicaly adds margins as soon as I choose the printer and it prints fine.
How to achive the same with Ghostscript ?
OK well the first thing is that many of the switches you are setting have no effect:
-empty isn't a Ghostscript-understood switch, and I'm slightly surprised it doesn't cause an error.
-dPDFSETTINGS only affects the pdfwrite device, which is why it is documented in the vector devices section.
-dCompatabilityLevel only affects the output of the pdfwrite device.
-dNOSAFER doesn't have any effect, as that's the default setting.
-f is used to 'close' direct PostScript insertion begun with -c, if you don't use -c you don't need -f
Now almost certainly neither Ghostscript nor your PDF has 'bad margins', the most likely explanation for your problem is that the printer you are using cannot print to the boundaries of the page, the left/right edges, and potentially the top and bottom edges are used by the paper transport mechanism, and the printer cannot print there.
In order to deal with that, you will need to reduce the size of the image, which you can 'probably' do by setting -dDEVICEWIDTHPOINTS and -dDEVICEHEIGHTPOINTS and -dFIXEDMEDIA. Its going to be up to you to work out the correct values for width and height.
Added after the comments below
There are two parts to this problem, the first is to deduce the size of the actual available area to print on, and to scale the output to that size. The second is to then reposition the output on the media so that it is all printed. If as you say the content is significantly smaller than the media then you can ignore rescaling it, but the entire solution is presented here for completeness.
Now as previously mentioned, the first part of this is achieved primarily by creating a fixed size canvas; this is done with any of the media selection switches and the addition of -dFIXEDMEDIA.
NOTE if you alter the media size, then you must, obviously, also alter the scale of the contents, or it won't fit. So you also need to set PSFitPage, EPSFitPage or PDFFitPage depending on the type of input (very recent versions of Ghostscript can use -dFitPage no matter what the input type).
As an experiment I used the file /ghostpdl/examples/text_graphic_image.pdf and sent the output to a printer on FILE:
This command line:
gswin32 -dDEVICEHEIGHTPOINTS=391 -dDEVICEWIDTHPOINTS=306 -dFIXEDMEDIA -sDEVICE=mswinpr2 -sOutputFile=%printer%KensPrinter /ghostpdl/examples/text_graphic_image.pdf
produces output where 3/4 the image is clipped away (the content lies off the newly defined fixed canvas size). If I modify that to:
gswin32 -dDEVICEHEIGHTPOINTS=391 -dDEVICEWIDTHPOINTS=306 -dFIXEDMEDIA -dPDFFitPage -sDEVICE=mswinpr2 -sOutptuFile=%printer%KensPrinter /ghostpdl/examples/text_graphic_image.pdf
then the result is a perfect reproduction of the original, at 1/4 the size (half in each direction).
So, the first thing you need to do is establish the actual printable area of the media on your printer, then you can set the width and height correctly as fixed media, and tell Ghostscript to scale the page to fit.
However, this will still leave the printed image at the bottom left of the media. Since that's in an area that can't be printed, you need to shift the printed image until its centred on the page. As I suggested, you can do this with a BeginPage procedure. This:
gswin32 -dDEVICEHEIGHTPOINTS=391 -dDEVICEWIDTHPOINTS=306 -dFIXEDMEDIA -dPDFFitPage -sDEVICE=mswinpr2 -sOutptuFile=%printer%KensPrinter -c "<</BeginPage {100 100 translate}>> setpagedevice" -f /ghostpdl/examples/text_graphic_image.pdf
produces output where the printed image is shifted up and right by 100 points each.
I believe that a little investigation will allow you to figure out where exactly your printer is able to print, and to create appropriately sized unprintable margins.
Note that, for me, the %printer% syntax does not result in a printer selection dialog. I suspect that your code (whatever language that is) is expanding the %p, resulting in a corruption of the name. Or possibly whatever you use to fork a Ghostscritp process does it. In either event you probably need to double the % signs.
You should get this working from the command line first, then work on getting it into an application.
How can I print a visual as grayscale without actually showing the PrintDialog, e.g.
PrintDialog dialog = new Dialog();
dialog.PrintQueue = new PrintQueue(new PrintServer(), printerNameAsString);
dialog.PrintTicket.InputBin = InputBin.AutoSelect;
// Further settings, e.g. PageMediaSize and scaling the visual.
dialog.PrintVisual(myVisual, "myDescription");
Can I somehow get the PrinterDialog to print the visual in grayscale? Or is there a completely other way to achieve a grayscale printout of my visual?
Edit: myVisual (the Visual I want to print) is a Grid, so it inherits from UIElement.
Edit 2: If possible I would prefer not to use any external libraries (because of company policies).
In the PrintDialog the OutputColor can be set:
myPrintDialog.PrintTicket.OutputColor = OutputColor.Grayscale;
Also, the PrintCapabilities make it possible to actually check which OutputColors are possible:
PrintCapabilities capabilities = myPrintDialog.PrintQueue.GetPrintCapabilities(myPrintDialog.PrintTicket);
ReadOnlyCollection<OutputColor> possibleColors = capabilities.OutputColorCapability;
On the hardware available to me this works fine.
You can try using the standard lib in Microsoft.Expression.Effects assembly. The effect here is MonochromeEffect. Just apply this effect before printing your visual:
myVisual.Effect = new MonochromeEffect();//make grayscale
dialog.PrintVisual(myVisual, "myDescription");
myVisual.Effect = null; //turn it off
You have to import the library I mentioned above and add this using instruction:
using Microsoft.Expression.Media.Effects;
I ran into a similar problem where DrawString would always print solid black--even though the Brushes specified a lighter shade. I finally discovered that by placing an image onto the graphics object, it would print lighter shades that were in the image. It feels like a hack, but it works! (The printdialog solution might have worked too--I don't know since I'm printing automatically from a service where there is no printdialog.)
We're using migradoc api to create an rtf document.. intermittently when we add an image it;s being resized and coming out absolutely tiny.
Code sample as follows:
MigraDoc.DocumentObjectModel.Shapes.Image image = section.AddImage(imagePath);
image.WrapFormat.Style = MigraDoc.DocumentObjectModel.Shapes.WrapStyle.Through;
If I set LockAspectRatio to true and set a width it does stop is from rendering very small but ideally would like to be able to set a MaxWidth.
Has anyone experiences similar issue?
You have a choice of either changing the image itself by storing a different DPI value in it.
The .Net command to do so with a Bitmap bmp is:
bmp.SetResolution(newHRes , newVRes);
This should not involve reencoding, but I'm not sure.
However you also can simply set the desired DPI value in the Migradoc Image by using its Image.Resolution Property, which
Gets or sets a user defined resolution for the image in dots per inch.
I am not familiar with it at all but I found this code that might help you :
image.RelativeVertical = RelativeVertical.Page;
image.RelativeHorizontal = RelativeHorizontal.Page;
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!