Poppler: render with a target resolution - c++

I am writing a pdf viewer in Qt and C++ using Poppler. How can I render a pdf page to fit my widget size? Poppler provides a method named renderToImage which takes in a dpi and returns a QImage whose size varies with that dpi. How to calculate the right dpi?

pageSizeF() returns the page size in points, which divided by 72 gives you the page size in inches.
Each component of your widget size in pixels divided by each component of the size in inches gives you 2 dpi values (1 for each axis).
If you want to keep the page aspect ratio, you should pass the smaller of these two dpi values to renderToImage for both xres and yres parameters.

Related

How do I get the logical to device ratio from an EMF ()Enhanced MetaFile?

I have closely studied the MS documentation on EMF files and from the definitions for the 3 header types I can't see how to convert from logical coords (which the graphics records coords are stored as) to device coords. The header has a Frame part that specifies the page size surrounding (but not necessarily bounding) the composite image in 0.01mm units; and a Bounds part that specifies the actual bounds of the composite image in logical units. And finally there are the Device and Millimeters parts that specify the size of the recording device.
From these there seems no way that calculating the ratio to convert from logical coords to device coords is possible.
I must be missing something simple :-)
Think I sussed it: you use the records:
EMR_SETVIEWPORTEXTEX - device units
EMR_SETVIEWPORTORGEX - (ditto)
EMR_SETWINDOWEXTEX - logical units
EMR_SETWINDOWORGEX - (ditto)
EMR_SETWORLDTRANSFORM
Yes, the Bounds header property is specified as the actual bounds of the composite image (in logical units) but, on investigating Inkscape and Adobe Illustrator created emf's, I find that they do not adhere to this.
After creating your DC (createDC), use getdevicecaps to get the total number of dots (raster lines) available for your DC. Horzres for width, Vertres for height. The dots aren't square. Then after reading your EMF file with getenhmetafile, use getenhmetafileheader to get the header record. You then look at either rclbound or rclframe in the header record. The second rectangle is a multiple of the first rectangle. For emfs created by powerpoint, the top and left is zero in my experience, so you focus on the bottom and right. The ratio of the two is your aspect ratio. You use that ratio to calculate the rectangle in DC units that has the same aspect ratio as rclbound, but likely adds margins all around so your image doesn't go right to the edge of your device. That rectangle, with units that fall within the range provide by vertres and horzres is the third arguement to the playenhmetafile command where your finish up. In sum, you convert from the EMF logical units to the DC logical units by using vertres and horzres (from your DC) combined with the aspect ratio you calculate (from your EMF).

Incorrect metrics and sizes of font created by CreateFont()

I trying to render a font into bitmap using WinAPI, but I can't reach needed sizes of font.
Here's how the font is initialized:
HDC dc = ::CreateCompatibleDC(NULL);
::SetMapMode(dc, MM_TEXT);
::SetTextAlign(dc, TA_LEFT | TA_TOP | TA_UPDATECP);
int size_in_pixels = 18;
HFONT font = ::CreateFontA(-size_in_pixels, ..., "Arial");
::SelectObject(dc, font);
::TEXTMETRICW tm = { 0 };
GetTextMetricsW(dc, &tm);
But after it I getting incorrect values both in GetGlyphOutlineW and GetTextMetricsW, it's not size I passed as parameter
I know that it's expecting value in logical units, but in MM_TEXT 1 unit should be 1 pixel, don't it?
I expecting that CreateFontA accepting point size when I passing a negative value (like here https://i.stack.imgur.com/tEt8J.png), but in fact it's wrong.
I tried bruteforcing values, and find out proper parameter for a few sizes:
18px = -19; 36px = -39; 73px = -78;
Also I tried formula that provided by Microsoft:
nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
But it's also giving me a wrong result, rendered text (using GetGlyphOutlineW) is larger if measure it (for example height of 'j' should have exact size that I passed)
Also metrics from GetTextMetricsW are wrong, for example tmAscent. I know that on Windows it's including internal leading, but even if subtract tmInternalLeading from tmAscent it's still incorrect.
By the way, values from GetCharABCWidthsW are correct, so a+b+c is width of glyph in pixels (while documentation says it should be in logical units).
Also I should say about DPI, usually I using 125% on Windows 10 scale in settings, but I tried even with 100%, interesting that ::GetDeviceCaps(dc, LOGPIXELSY) not changing with scale I using, it's always 96
Here's example of CreateFontA(-128, ...) with final atlas and metrics:
rendered atlas
Question #1: What should I do to pass wanted point size in pixels and receive glyphs in proper size with correct metrics in pixels?
Question #2: What the strange units all these functions are using?
When you use ::SetMapMode(dc, MM_TEXT); the font size is specified in device pixels. Negative value excludes internal leading, so for the same absolute value the negative ones produce visually bigger fonts. If you want to get same height from GetTextExtentPoint32 for different fonts, use positive values.
In your example with -128 height, you are requesting font for which, after internal leading exclusion, height is 128 pixels. Font mapper selects 143 which is correct for internal leading of 15 pixels (128+15=143). tmAscent + tmDescent are also correct (115+28=143). You get what you specified.
You should take into account that values in text metric don't state hard bounds. Designer can design fonts so its glyphs sometimes go beyond guiding lines or don't reach them.
for example height of 'j' should have exact size that I passed
Dot over j can go beyond or not reach top line if designer finds it visually plausible to design it that way.
interesting that ::GetDeviceCaps(dc, LOGPIXELSY) not changing with scale I using, it's always 96
Unless you log off and log in, system DPI doesn't change. For per monitor DPI aware application you have to get DPI from monitor parameters or cache value given by WM_DPICHANGED.
Question #1: What should I do to pass wanted point size in pixels and receive glyphs in proper size with correct metrics in pixels?
I think you want to get specific distance between top and bottom lines and this is exactly how you create font HFONT font = ::CreateFontA(-size_in_pixels, ..., "Arial");. The problem lies in your assumption that font design lines are hard boundaries for each glyph, but font's designer don't have to strictly align glyphs to these lines. If you want glyphs strictly aligned, probably there is no way to get it. Maybe check different font.
Question #2: What the strange units all these functions are using?
When mode is set to WM_TEXT, raw device pixels are used. Positive height specifies height including tmInternalLeading, negative excludes it.
For positive value:
tmAscent + tmDescent = requestedHeight
For negative value:
tmAscent + tmDescent - tmInternalLeading = requestedHeight
Bellow I have pasted screen shots with different fonts showing that depending on selected font glyphs could be designed so they don't reach top line or go beyond it and bottom line in most cases also isn't reached.
Seems that for your requirements Arial Unicode MS would be better fit (but j still doesn't reach where you want it).
Arial:
Arial Unicode MS
Input Mono
Trebuched MS

In what Units does QSvgGenerator operate in?

In the official documentation for QSvgGenerator there are two properties that relate to the size of the output. One is size and the other is resolution.
Resolution is set in DPI (Dots Per Inch), but which unit is size in?
I have tested multiple values now and none make sense to me when inspecting the output file.
size is in pixels. If you treat size as pixels then the output SVG width and height attributes will be approximately the correct number of mm.

How to make printing the same on any printer?

Ive been using HORZRES and VERTRES to print various strings. I had been using xps to test my printing and such but when I switched over to my actually computer I noticed that things werent printing the same.
How do you get the size of the actual page and print from there?
For example if I was printing from a letter(8 1/2 x 12 inches) How could I get a universal measurement that could be used for any printer
You can use SetMapMode to change the mapping mode.
If you set the mapping mode to, say, MM_LOENGLISH then all drawing will be in units of 1/100 inch. A line drawn with length 100 will then be one inch long on any printer, and you don't need to worry about the printer's resolution.
If you want further information about the page you can get other data from GetDeviceCaps:
LOGPIXELSX - horizontal pixels per inch
LOGPIXELSY - vertical pixels per inch
PHYSICALWIDTH - width of the page in device units
PHYSICALHEIGHT - height of the page in device units
Then the width of the page in inches is PHYSICALWIDTH / LOGPIXELSX and the height is PHYSICALHEIGHT / LOGPIXELSY.

Appropriate image sizes to support various android phones & tablets

I am developing an android game in cocos2d. How many different sizes of images do I need to support android devices and tablets?
I have never used that engine but if you mean by image size, device screen size, then you should use an scale.
I took for base the most bigger I could, 1280x800, the one that's on my tablet, just to be more precise in tablets too.
I apply the scale in (X,Y) to every image size and every operation that screen or screen size it's involved. i.e:
soldierBitmapX.move(movement*scaleX)
soldierBitmapY.move(movement*scaleY)
scaleX and scaleY represents your scale and movement represent how many pixel your soodier will move.
This is an example to understand how to apply the scale. I don't recommend you to move your sprites with this operation but have in mind if you should apply the scale.
You can apply this to every screen possible and your game will feet exactly in all of it. Beware of for example QVGA screens, more "squared" in comparision with other standards and very small.
EDIT (how to get the scale):
_xMultiplier = (_screenWidth/(1280.0f/100.0f))/100.0f;
_yMultiplier = (_screenHeight/(800.0f/100.0f))/100.0f;
matrix.setScale(_xMultiplier, _yMultiplier);
this is an example of the scale applied to the matrix that we'll use.
Through ScaleX and ScaleY Property you can easily scale the images .....as for example you take for tablet size is 1280 * 800 ,yo u can scale that sprite and use it; you can also use that image for smaller resolution e.g. 320 * 480.....