Opened 8 years ago

Closed 6 months ago

Last modified 6 months ago

#3566 closed defect (fixed)

wxMSW printing: text rendered by wxGraphicsContext too big in the preview

Reported by: robertlang Owned by:
Priority: high Milestone:
Component: wxMSW Version: dev-latest
Keywords: wxPrintPreview wxGraphicsContext GDI+ font scale printing wxGCDC Cc: robertlang
Blocked By: Blocking:
Patch: yes

Description

This can be observed in the printing sample, compiling wxMSW-2.6.3
using VC++6.0.

  1. If you run the printing sample and select Print Preview, the preview

image, supposedly at 70%, comes up about 4x too large (much larger
than the enclosing window).

  1. Any margins set in the Page Setup dialog are apparently ignored.
  1. The printed image does not match the Print Preview image: 3a. printed image is missing the page number visible in Print Preview; 3b. text at bottom of Print Preview image is cut off of printout; 3c. icon visible in Print preview is missing from printout.

Attachments (9)

printing.png download (81.9 KB) - added by awi 7 months ago.
Printing sample.
print-preview.png download (29.1 KB) - added by awi 7 months ago.
Print preview (screen DPI)
font-scaled.patch download (5.9 KB) - added by awi 7 months ago.
Patch to enable creation of scaled fonts (for GDI+ purposes).
output.png download (115.2 KB) - added by awi 7 months ago.
Final output
printout-14136.png download (14.7 KB) - added by awi 7 months ago.
Output for sample code in ticket 14136.
print-72vs96.png download (227.2 KB) - added by awi 6 months ago.
72 vs 96 DPI
font-scaled-v2.patch download (2.4 KB) - added by awi 6 months ago.
Patch enabling scaled fonts (for GDI+ output purposes) v2.
font-scaled-v3.patch download (1.4 KB) - added by awi 6 months ago.
Patch fixing problems with wrong GDI+ font size (v3).
font-scaled-v4.patch download (676 bytes) - added by awi 6 months ago.
Patch fixing problems with wrong GDI+ font size (v4).

Download all attachments as: .zip

Change History (31)

comment:1 Changed 8 years ago by robertlang

All of these (except 3c) are fixed in wxALL-2.7.1 by patch 1583183.

comment:2 Changed 6 years ago by wojdyr

  • Component set to wxMSW
  • Keywords wxPrintPreview added

Changed 7 months ago by awi

Printing sample.

comment:3 Changed 7 months ago by awi

Reported issues 1, 2, 3a, 3b seems to be fixed also in 3.0
Issue 3c is fixed by r75729 (ref #379)

The only problematic thing with printing sample could be too large text and its frame drawn with use of wxGraphicsContext ("Text by wxGraphicsContext"). It fits in the window but it doesn't fit in the preview window and in the printout (see attached screenshots).

comment:4 follow-up: Changed 7 months ago by vadz

  • Status changed from new to confirmed
  • Summary changed from wxMSW printing: preview too big, margins ignored to wxMSW printing: text rendered by wxGraphicsContext too big in the preview

Thanks for testing!

There is definitely something wrong here (probably due to using wrong DPI in the print preview DC?). Out of curiosity, does it appear correctly on the real printout (where "real" could mean PDF or XPS output)?

Changed 7 months ago by awi

Print preview (screen DPI)

comment:5 in reply to: ↑ 4 Changed 7 months ago by awi

Replying to vadz:

There is definitely something wrong here (probably due to using wrong DPI in the print preview DC?). Out of curiosity, does it appear correctly on the real printout (where "real" could mean PDF or XPS output)?

I completely forgot that resolution matters!
When I set printer DPI the same value as reported by wxGraphicsContext:GetDPI() then everything started to look good, both on the preview and on the PDF printout (screenshot).

It seems wxGraphicsContext:GetDPI always returns value 72 so one need to have in mind that it's necessary to set printer's DPI to this value when wxGraphicsContext is in use.

I think the ticket can be closed.

comment:6 follow-up: Changed 7 months ago by vadz

Sorry, I'm not sure to follow here... Shouldn't wxGC adapt to the resolution really used by the printer? I.e. I think that wxGraphicsCoontext::Create(wxPrinterDC) should return a wxGC ready to use, without having to do anything special with it.

Am I missing something?

comment:7 in reply to: ↑ 6 Changed 7 months ago by awi

Replying to vadz:

Sorry, I'm not sure to follow here... Shouldn't wxGC adapt to the resolution really used by the printer? I.e. I think that wxGraphicsCoontext::Create(wxPrinterDC) should return a wxGC ready to use, without having to do anything special with it.

Am I missing something?

I tested printing sample again and I can confirm that objects created within the scope of wxGraphicsCoontext are printed as expected only if printer DPI is the same as screen DPI (96 in my case). It seems that printer DPI is not taken into account when wxGraphicsCoontext is created (what is funny wxGraphicsCoontext:GetDPI reports 72 DPI in my case and this value seems to be hard-coded).
If this behavior is not intended I will take a look at it.

comment:8 Changed 7 months ago by vadz

I don't think this can be intended but I admit that I never did understand the DPI handling in wxGC code...

Changed 7 months ago by awi

Patch to enable creation of scaled fonts (for GDI+ purposes).

Changed 7 months ago by awi

Final output

comment:9 Changed 7 months ago by awi

  • Keywords wxGraphicsContext GDI+ font scale added
  • Patch set

After some tests I found that only wxGraphicsContext text operations (like DrawString) are affected by this issue. Other graphic operations are OK.
Wrong GDI+ text output is drawn if resolution of output DC (like print preview or printer) is different from resolution of screen DC.

Apparently, if drawing is done in the context of GDI+ then output must be rescaled in proportion to output-DPI/screen-DPI ratio.
This rescaling must be done individually for normal graphic operations (via SetPageScale function) and for text operations (by rescaling the font(s) in use).
First thing is already implemented and hence graphic output is OK.
Second thing is missing - font scale ratio is calculated but never used for font rescaling.

Attached patch implements creation of fonts with given scale. Such fonts can be used for GDI+ text operations.
Unfortunately, it was necessary to implement small changes but in many places, including class interfaces.

After applying this patch the output looks the same on the screen (96 DPI), print preview (300 DPI) and final printout (600 DPI in my case) - see attached screenshot.

comment:10 Changed 7 months ago by awi

  • Keywords wxGCDC added
  • Version set to dev-latest

Supplied patch solves also the issue reported in #14136 - see attached screenshot.

Changed 7 months ago by awi

Output for sample code in ticket 14136.

comment:11 Changed 7 months ago by vadz

  • Priority changed from normal to high

First, it's great that you've apparently managed to solve this problem as it's a pretty important one if you do any printing at all.

Unfortunately I'm still not sure whether what we do here is really the right thing to do. Notably, I don't really understand how hardcoding 96 DPI can be the right thing to do any more than I understood hardcoding 72 DPI... Does this really work even under Windows in high DPI? And, also, it just must affect other ports because for them nothing else changed (the scale factors are always 1) and I can't believe that changing the DPI doesn't break anything else there. If you can't test under the other platforms, you should be able to test Cairo renderer even under Windows.

Also, API-wise, I find it a bit ugly to add workarounds for MSW-specific problems to the common API. And CreateFont() is public -- but we add a parameter to it which is simply ignored under anything but MSW. Couldn't we deal with this entirely inside wxGDIPlusFontData which seems to be the only object actually needing this scale?

comment:12 Changed 6 months ago by awi

I can agree that maybe there is much ado about (almost) nothing but I haven't found so far easiest way to pass font scale from wxGDIPlusPrintingContext to CreateFont(). Maybe I will find something at second attempt.

I found experimentally then when hardcoded DPI value is 96 then there is a better similarity between screen and printout then for value 72 - see attached screenshot.
But this may be machine-dependent effect and perhaps this value shouldn't be hardcoded at all but taken from the system?

Changed 6 months ago by awi

72 vs 96 DPI

comment:13 Changed 6 months ago by neis

Having a look at #10942 and the resulting [63781] might be interesting.

Changed 6 months ago by awi

Patch enabling scaled fonts (for GDI+ output purposes) v2.

comment:14 Changed 6 months ago by awi

I found some solution which gives not as good results as solution implemented by first patch but it is much simpler and doesn't modify public interfaces.

Attached patch takes advantage of existing functions to scale the font (wxFont::Scaled(), wxFont::Scale()) in order to adjust the font size based on the font scale ratio taken from wxGCDC.
The main limitation of this approach is that these scaling functions work only with font sizes of integer value which could lead to rounding errors and at the end the text output size can be not as expected. Fonts created directly via GDI+ can have size of REAL type and the output is much better.

Generally, the greater difference between screen and output resolution, the worse final effect:

screen DPI: 96
printout DPI: 300
font scale: 0.32
demanded font size: 10
scaled font size: 3.2 -> ~3 (~6%, not a big deal)

screen DPI: 96
printout DPI: 600
font scale: 0.16
demanded font size: 10
scaled font size: 1.6 -> ~2 (25%, output clearly too large)

But, however this is not a perfect solution, the output text looks is better then now.

comment:15 follow-up: Changed 6 months ago by vadz

Thanks, this is much closer to what I had in mind in comment:11.

However I'm still worried by changing the DPI from 72 to 96 in the common code, I am almost sure that this is going to break something on some other platform and so I'd really like to avoid doing it there and do it for wxMSW only, especially if we want to apply this to 3.0 branch (which I think would be nice).

Also, to avoid rounding errors in font scaling, I wonder why can't we override SetFont() in wxGDIPlusContext and scale the font properly (using REAL size of GDI+ Font) there, instead of doing it imprecisely in the base class SetFont()?

comment:16 in reply to: ↑ 15 Changed 6 months ago by awi

Replying to vadz:

Thanks, this is much closer to what I had in mind in comment:11.

However I'm still worried by changing the DPI from 72 to 96 in the common code, I am almost sure that this is going to break something on some other platform and so I'd really like to avoid doing it there and do it for wxMSW only, especially if we want to apply this to 3.0 branch (which I think would be nice).

Also, to avoid rounding errors in font scaling, I wonder why can't we override SetFont() in wxGDIPlusContext and scale the font properly (using REAL size of GDI+ Font) there, instead of doing it imprecisely in the base class SetFont()?

Regarding 72 vs 96 DPI question: It seems that, GetDPI() function (from which DPI values are taken) is a virtual function and then it will possible to override it and return 96 DPI for wxGDIPlusContext purposes only.

It seems that wxGraphicsContext::SetFont(font, colour) is not a virtual function and it's hard to override it.

comment:17 Changed 6 months ago by vadz

But SetFont() uses CreateFont() which is virtual, wouldn't it be enough to do it there?

Changed 6 months ago by awi

Patch fixing problems with wrong GDI+ font size (v3).

comment:18 Changed 6 months ago by awi

I have just discovered that instead of manually rescaling the font size there is enough to enforce creating font based on its size provided in logical units (pixels), not in points. In this case GDI+ subsystem does all the magic with rescaling on their own.
Patch applying this idea is attached.

It seems that all this confusing mess with manual rescaling is caused by wxNativeFontInfo::GetPointSize() which returns value referring to the screen resolution and not to the resolution of the current output device (there is even a warning in the code).

comment:19 Changed 6 months ago by vadz

Sorry, I still have a question: shouldn't we do this in wxGDIPlusRenderer::CreateFont(wxFont, wxColour)? If not, why?

Thanks again!

Changed 6 months ago by awi

Patch fixing problems with wrong GDI+ font size (v4).

comment:20 Changed 6 months ago by awi

Indeed, CreateFont() can be overridden one level closer to the GDI+ core.
Patch attached.

comment:21 Changed 6 months ago by VZ

  • Resolution set to fixed
  • Status changed from confirmed to closed

(In [76008]) Fix font size when using wxGraphicsContext with wxPrinterDC in wxMSW.

Use pixel size which is scaled correctly by GDI+ itself instead of the size in
points which is currently not scaled correctly by wx.

Closes #3566.

comment:22 Changed 6 months ago by VZ

(In [76012]) Fix font size when using wxGraphicsContext with wxPrinterDC in wxMSW.

Use pixel size which is scaled correctly by GDI+ itself instead of the size in
points which is currently not scaled correctly by wx.

Closes #3566.

Note: See TracTickets for help on using tickets.