In Windows 10, Delphi XE7, when font size is set to > 100%, Delphi's screen[0].workarea.rect returns dimensions that reflect the font scaling. E.G., with the graphics card for screen 0 set to 1920x1080, screen[0].workarea.rect.width returns 1280 (assuming there is no vertical task bar, etc.). However, I can't find a way to programatically determine that the width is being returned as 1280 due to the font scaling vs. 1280 being the actual graphics card setting. Can anyone tell me how to do 1 or more of the following -- any 1 of them would give me the info I need to differentiate among the possibilities: 1) determine what the font percentage is; 2) determine what the hardware resolution is.
GetDeviceCaps function returns a parameter, defined by index, of chosen device context.
Works in Windows 7, gives true font size value for my desktop (96 dpi is default value "small font 100%")
dc := GetDC(0);
Memo1.Lines.Add(Format('hor res %d', [GetDeviceCaps(dc, HORZRES)]));
lp := GetDeviceCaps(dc, LOGPIXELSX);
Memo1.Lines.Add(Format('logx %d percents %d%%', [lp, 100 * lp div 96]));
hor res 1280
logx 120 percents 125%
Related
I would like to get the actual screen dpi/ppi, not the dpi setting used for font in C++.
I tried with the following codes:
Version 1, reports 72 dpi, which is wrong.
SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hSize = GetDeviceCaps(screen, HORZSIZE);
double vSize = GetDeviceCaps(screen, VERTSIZE);
double hRes = GetDeviceCaps(screen, HORZRES);
double vRes = GetDeviceCaps(screen, VERTRES);
double hPixelsPerInch = hRes / hSize * 25.4;
double vPixelsPerInch = vRes / vSize * 25.4;
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;
Version 2, reports 96 dpi, which is the Windows dpi setting for font, but not the actual screen dpi.
SetProcessDPIAware(); //true
HDC screen = GetDC(NULL);
double hPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSX);
double vPixelsPerInch = GetDeviceCaps(screen,LOGPIXELSY);
ReleaseDC(NULL, screen);
return (hPixelsPerInch + vPixelsPerInch) * 0.5;
I'm honestly confused by the answers here.
Microsoft has a GetDpiForMonitor method:
https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
And monitors DO expose their physical dimensions to tools. You can read your monitors width and height, in centimeters, using the HWiNFO64 tool. So if they're getting it (DDI?), it stands to reason that you can access that information yourself.
Even a different Stack Overflow post mentions using WmiMonitorBasicDisplayParams to get the data.
How to get monitor size
So the top post is flat-out, 100%, wrong.
What you're asking for is, unfortunately, not possible in the general case.
Windows doesn't know the physical screen size. Windows might know that your screen has 1024x768 pixels, but it doesn't know how big the screen actually is. You might pull the cable out of your old 13" screen and connect it to a 19" monitor without changing the resolution. The DPI would be different, but Windows won't notice that you changed monitors.
You can get the true physical dimensions and DPI for a printer (assuming the driver isn't lying), but not for a screen. At least not reliably.
UPDATED
As others have pointed out, there are standards for two-way communication between newer monitors and the OS (EDID), that might make this information available for some devices. But I haven't yet found a monitor that provides this information.
Even if EDID were universally available, it's still not solvable in the general case, as the display could be a video projector, where the DPI would depend on the zoom, the focus, the lens type, and the throw distance. A projector is extremely unlikely to know the throw distance, so there's no way for it to report the actual DPI.
Getting DPI information is found to produce exact value using the below method.
ID2D1Factory* m_pDirect2dFactory;
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
FLOAT dpiX, dpiY;
m_pDirect2dFactory->GetDesktopDpi( &dpiX, &dpiY );
I think what you're after is:
GetDeviceCaps(hdcScreen, LOGPIXELSX);
GetDeviceCaps(hdcScreen, LOGPIXELSY);
Trying to change dimensions of my FMX form to emulate a 'FullScreen' mode, but using the Screen size backfires when a user has its settings with some bigger scaling, since the whole form becomes bigger than the screen.
How can I retrieve the scaling value so I can Size the form accordingly?
EDIT:
That's a little snippet that shows what was my intention with the question and how it was solved. Thank you for you time and help.
procedure TMyForm.ApplyFullScreen;
var
tmpEscale: Extended;
begin
BorderStyle := TFmxFormBorderStyle.None;
Left := 0;
Top := 0;
tmpEscala := USER_DEFAULT_SCREEN_DPI / GetDeviceCaps(GetDC(0), LOGPIXELSX);
Height := Round(Screen.Height * tmpEscala);
Width := Round(Screen.Width * tmpEscala);
end;
When you call EnumDisplaySettings the resulting DEVMODE structure contains the DPI setting in the dmYResolution field. Beware that passing NULL as the device name to EnumDisplaySettings will get the information only for one screen, on a multimonitor system you should enumerate all the display devices.
You can also call GetDeviceCaps on a device context, and query for LOGPIXELSX and LOGPIXELSY.
The DPI corresponds to font scaling as follows:
100% = 96 dpi
125% = 120 dpi
150% = 144 dpi
200% = 192 dpi
For more information, it would be good to refer to the MSDN article on DPI-related APIs
I want to make reproductible tests of some custom widgets renderings. In order to do that, I paint them onto a QImage and save the result as PNG. The output is really different on Windows compared to MacOSX.
I took care of :
Selecting the same font on all platform (I provide the "TTF" font file and point the code to it)
Drawing onto a QImage and not a QPixmap, as the documentation says QImage painter is supposed to be platform independant
I also selected Antialisating and TextAntialiasing hints
Requesting the font via QFontDatabase::font() so that pointSize is specified and not pixelSize
How can I make sure the rendering is exactly the same on all platforms so that my test runs are reproductible ? In other words, is it maybe possible to force QT5 to use the same font engine on all platforms (for instance freetype) ?
**
I nailed down the issue to a simple rendering test program.
So the code is looks like :
QFontDatabase fontDb;
fontDb.addApplicationFont(".../fonts/Vera.ttf");
QImage result(width, height, QImage::Format_RGB32);
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
QBrush background(QColor(205, 205, 205));
painter.fillRect(0, 0, 800, 600, background);
QFont font = fontDb.font("Bitstream Vera Sans", "Normal", 10);
painter.setFont(font);
painter.setPen(QColor(0, 0, 0));
painter.drawText(10, 10, "ABCD abcd 01234567");
The Bitstream Vera font can be downloaded on fontsquirel.com for instance.
See the result on MacOSX (left) and on Win32 (right), which are very different:
Following answer and comments by N1ghtLight below, and after reading the links he suggested, I changed the code to get the font to :
QFont font = fontDb_->font(("Bitstream Vera Sans", "Normal", -1);
qreal screenDPI = QApplication::primaryScreen()->physicalDotsPerInch();
qreal RENDER_DPI = 72;
int pixelSize = (int)((qreal)10 * screenDPI / RENDER_DPI);
font.setPixelSize(pixelSize);
This seems to mostly solve the problem of the font of very different size. At least on MacOSX, the font is exactly 10 pixels high now. On Windows though the font renders much thinner and a bit smaller too. I'm still lost and confused...
Here is the new result (left MacOSX, right Windows). The white scale indicates true 10 pixels size.
Following answer by G_G below I adapted the code (what about Linux ? Mobile platforms ? This gets very complicated...). Now the fonts are 10 pixels in the output on both Windows and MacOSX, still the rending remains very different (still MacOSX on the left, Windows on the right).
Thanks.
your render DPI variable should be 96 for Windows and 72 for OSX
according to:
http://www.rfwilmut.clara.net/about/fonts.html
On a Macintosh monitor, the notional resolution is 72 dots-per -inch
(dpi), so that a graphic 72 pixels wide would notionally be 1 inch
wide - though obviously the actual size would depend on the individual
monitor. However it will always print one inch wide.
But on a Windows monitor the resolution is (usually) 96 dpi. This
means that though the picture is still 72 pixels wide, it will print
at 0.75 inches.
QFont font = fontDb_->font(("Bitstream Vera Sans", "Normal", -1);
qreal screenDPI = QApplication::primaryScreen()->physicalDotsPerInch();
#ifdef WINDOWS
qreal RENDER_DPI = 96;
#else
qreal RENDER_DPI = 72;
#endif
int pixelSize = (int)((qreal)10 * screenDPI / RENDER_DPI);
font.setPixelSize(pixelSize);
It happens because you set size in pixels. You need to use setPointSize() instead.
From the Qt 5 docs:
void QFont::setPixelSize(int pixelSize)
Sets the font size to pixelSize pixels.
Using this function makes the font device dependent. Use setPointSize() or setPointSizeF() to set the size of the font in a device independent manner.
Also, for more info, you can check this post. This difference happens because of different display density on different OSs. I have experienced such problem myself on one OS X, Windows cross platform project.
[Updated] After my additional research, I've found, that current behavior of Qt fonts is just a bug. That's why solution above doesn't work (it worked for Qt 4)
Here is described different workarounds to solve this problem. Good luck!
You can try this. this will generate an image with the text which you will write. I using this code.
QPixmap photo;
QFont qfont;
QPainter painter;
QString txt;
QImage img(x,y,QImage::Format_RGB32);
painter.begin(&img);
img.fill(0xffffffff);
painter.drawPixmap(0,0,x,y,photo);
qfont.setFamily("Sampige");
qfont.setPixelSize(28);
painter.setFont(qfont);
txt = QString::fromUtf8("ನಮಸ್ಕಾರ");
painter.drawText(1,26,txt);
painter.end();
img.save("abcd.jpg");
I'm using Flying Saucer for HTML to PDF conversion. I need to produce an output PDF with 600dpi in Letter size. How can I achieve this?
There are four different factors at play, and they are all interrelated:
Target Page Metrics
You want the page metrics to be correct, so that when you ask Flying Saucer to produce a "letter" size page, the resulting PDF will show up in Acrobat as measuring 8.5" x 11". You configure the page size directly in FS by specifying the page-size property in your CSS, as obourgain noted in the other answer: #page { size: letter; }
Resolution
You want the final output to be suitable for printing on some printer at a certain XXX dpi. This is all well and good, but remember that PDF is (mostly) a vector format. I didn't check the spec, but as far as I know, the PDF file/page don't "have" resolution because they are vector-based. That having been said, things placed inside the page have an effective resolution, so we'll need your desired XXX dpi number to calculate the numbers below.
Dots per point
In the FlyingSaucer (and Java) world, a point is always a constant 1/72 of an inch. Period. So we can calculate the dots-per-point value by taking the desired resolution and dividing by the size of a point. For example, if you want 300 dpi output:
dots-per-point = 300 dpi / 72 ppi = 300 dots/inch / 72 points/inch = 300/72 dots/point = 4.1666 dots/point
Dots per pixel
This is not a magic number and this value is directly related to the dots-per-point number, as well as the expected resolution of the graphical images that you are trying to feed into FlyingSaucer. More specifically, given a graphic image that is X x Y pixels in dimensions, you need to decide how big you want that to render in your PDF. If you are using an image that was prepared for screen (web) use, you probably are starting out at the standard 96 pixels/inch (so a 96 x 96 px image would render as a one-inch square on the PDF output).
So we can easily calculate dots-per-pixel as follows, again assuming we wanted 300 dpi output:
dots-per-pixel = dots-per-inch (dots) / pixels-per-inch (pixels)
dots-per-pixel = 300 dots/inch / 96 pixels/inch = 3.125 dots/pixel
If you go this approach, your images will be sized correctly, but they won't be the 300 dpi print quality that you're looking for. That's because your images were not of high enough resolution to begin with. More on that in a minute.
Getting it all set up
If you just call SharedContext#setDPI directly as suggested in one of the other answers, you'll likely get the wrong results. This is because it doesn't make logical sense to change the dots-per-point without also changing the resolution (dots-per-inch). The ITextRenderer constructor makes a fixed call to setDPI(72*dotsPerPoint), and when it goes to create a new page, it also uses the dotsPerPoint value set by the constructor to calculate the correct page width in points. If you have changed the resolution under its nose by calling setDPI, you'll end up with the wrong page size.
The correct way is to create a new ITextRenderer object using the values we calculated above. If we wanted 300 dpi output and we had 96 ppi images to feed it, we would call:
ITextRenderer renderer = new ITextRenderer(4.1666f, 3);
Note that the dots-per-pixel parameter only accepts integers, so we're rounding 3.125 to the closest integer above. However...it's truly the ratio between the two numbers that seems to be important, so to make the last argument an integer, we can multiply both numbers by 8 (the lowest integer multiplier that yields an integer), which gives 33.3333 and exactly 25. This also is my guess for the origin of the magic "20" number in the Flying Saucer sources.
Check your output
By this point, your output PDF should look pretty much the same as it did when you started (assuming that you were previously using the default 96 ppi configuration of Flying Saucer). But now we know the parameters that we need to tune to make everything work.
To get high-quality output, you need high quality input
So we've verified that the above parameters work for our purposes, but our images are still a lowly 96 ppi. If we want to print this stuff at high resolution, all you need to do is swap out the images for 300 ppi versions, change the constructor parameters, and then you're done, right?
Maybe. Let's work through the numbers:
Your expected output resolution (300 dpi) does not change, so dots-per-point is still 4.1666. But your input images are now at 300 ppi, so your dots-per-pixel = 300 dots/inch / 300 pixels/inch = 1 dot/pixel. So you'll now call the constructor like this:
ITextRenderer renderer = new ITextRenderer(4.1666f, 1);
Once you do this, your new 300 px x 300 px image will end up as 1" square on the PDF, which is exactly the print quality you wanted.
But wait! All of my text got really small too!
Flying Saucer uses the dots-per-pixel measurement to convert a number of things, not just images. In particular, if you have specified anything in your stylesheets that uses pixels, the dots-per-pixel measurement will have an impact on their sizes too.
If you have stylesheet rules like font-size: 10px; then increasing the dots-per-pixel supplied to the constructor will make that text smaller, which is probably also not what you want. After all, you should be able to increase the resolution of the images in your PDF while leaving your text in the same size and place.
The answer is to convert everything in your stylesheet to use points. (Or inches. At least something other than pixels!) If you started out with the default Flying Saucer settings (meaning pixels are 96 ppi), you simply need to convert all of your "px" measurements into points. Since 72 points = 1 inch, you would change "px" to "pt" and multiply the value by 72/96.
For example, the font-size: 10px; above would become font-size: 7.5pt;. If you want true consistency with what you had before, everything in your CSS that mentions "px" (as well as any inline styles) must be changed into "pt" with the the same conversion too.
Once you have made this change, your text and other layout will be consistent, and if you decide that you need 600 dpi output later, you can just adjust your images and change the constructor argument, but the rest of the layout will still remain constant. Done!
You can set the letter size with the CSS page size property in your HTML document:
#page {
size: letter;
}
You can change the dpi of the document using the following ITextRenderer constructor :
public ITextRenderer(float dotsPerPoint, int dotsPerPixel)
I don't understand what those values really represent, but default values are dotsPerPoint = 20f * 4f / 3f and dotsPerPixel = 20, and it will output a 96dpi document.
To get 600dpi, you can use dotsPerPoint = 500f / 3f and dotsPerPixel = 20.
Looking at the code of ITextRenderer, the final dpi is given by the formula: dpi = dotsPerPoint * 72 / dotsPerPixel.
A simpler answer for setting the DPI when using Flying Saucer:
renderer.getSharedContext().setDPI(600);
As with obourgain's answer, to be used in conjuction with the #page { size:letter; } CSS.
Source
I am posting here on the off-chance that someone knows why my screen size is reported as 611 instead of 600?
GetSystemMetrics(SM_CYFULLSCREEN)
returns 611. My netbook is 1024x600, so I expected 600, not 611.
Googling for 1024x611 turns up a surprising number of results, also.
from other forum threads, it seems that GetSystemMetrics(SM_CYFULLSCREEN) does not always return the exptected result. it seems to be adjusted, and may not be the value you want.
try using GetSystemMetrics(SM_CYSCREEN), which will return the size of your primary monitor.
(also, note that some people use multiple monitors: take care of not constraining your application to a single monitor)
SM_CXFULLSCREEN and SM_CYFULLSCREEN returns the size of a full screen window, so would usually be less than your screen size due to the taskbar:
The width of the client area for a full-screen window on the primary
display monitor, in pixels. To get the coordinates of the portion of
the screen that is not obscured by the system taskbar or by
application desktop toolbars, call the SystemParametersInfo function
with the SPI_GETWORKAREA value.
Compare it to what SPI_GETWORKAREA in SystemParametersInfo returns for your display? Or what is your display set to in the control panel??
A 1024x640 screen would be a true 16:10 ratio and would amount to ~ 611 y available desktop size.