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.
Related
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
See here: How to get size of check and gap in check box?
It does not seem to quite answer the question as it applies to DPI.
I have tried several methods, but none yield the results of actual drawn checkboxes at various scale choices in Windows 10. The closest is
12 * GetDeviceCaps (LOGPIXELSX) / 96 + 1
This yields 22 pixels # 168 DPI, however, but Windows draws a 20 pixel checkbox.
Is there a reliable way to determine this? Below is a grid of results I captured, with greens being those that match the "on screen" values.
I am still a beginner in coding. I am currently working on a program in C/C++ that is determining pixel position of a defined mark (which is a black circle with white surroundings) in a photo.
I made a mask from the mark and a vector, which contains mask's every pixel value as it's elements (using Magick++ I summed values for Red, Green and Blue). Vector contains aprox. 10 000 values since the mask is 100x100px. I also used threshold functions for simplifying the image.
Than I made a grid, that is doing the same for the picture, where I want to find the coordinates of the mark. It is basically a loop, that is going throught the image and when the program knows pixel values in the grid it immediately compares them with the mask. Main idea is to find lowest difference between the mask and one of the grid positions.
The problem is however that this procedure of evaluating all grids position takes huge amount of time (e.g. the image has 1920x1080px so more than 2 million vectors containing 10 000 values). I decided to cycle the grid not every pixel but for example every 10th column and row, and than for the best corellation from this procedure I selected area where I used every pixel loop. But, this still takes lot of time.
I would like to ask you, if there is some way of improving this method for better (faster) results or this whole idea is not time efficient and I should use different approach.
Thanks for every advice!
Edit: The program will be used for processing multiple images and on all of them the size will be same. This is the picture after threshold, the mark is the big black dot.
Image
The idea that I find interesting is a pyramidal scheme - or progressive refinement: you find the spot at a lower size image then search only a small rectangle in the larger image.
If you reduce your image by 2 in each dimension then you would reduce the time by 4 plus some search effort in the larger image.
This has some problems: the reduction will affect accuracy I expect. You might miss the spot.
You have to cut the sample (template) by the same so you create a half-size template in this case. As you half half half... the template will get blurred into the surrounding objects so it will not be possible to have a valid template; for half size once I guess the dot has a couple of pixels around it.
As you haven't specified a tool or OS, I will choose ImageMagick which is installed on most Linux distros and is available for OSX and Windows. I am just using it at the command-line here but there are C, C++, Python, Perl, PHP, Ruby, Java and .Net bindings available.
I would use a "Connect Components Analysis" or "Blob Analysis" like this:
convert image.png -negate \
-define connected-components:area-threshold=1200 \
-define connected-components:verbose=true \
-connected-components 8 -auto-level result.png
I have inverted your image with -negate because in morphological operations, the foreground is usually white rather than black. I have excluded blobs smaller than 1200 pixels because your circles seem to have a radius of 22 pixels which makes for an area of 1520 pixels (Pi * 22^2).
That gives this output, which means 7 blobs - one per line - with the bounding box and area of each:
Objects (id: bounding-box centroid area mean-color):
0: 1358x1032+0+0 640.8,517.0 1296947 gray(0)
3: 341x350+1017+287 1206.5,468.9 90143 gray(255)
106: 64x424+848+608 892.2,829.3 6854 gray(255)
95: 38x101+44+565 61.5,619.1 2619 gray(255)
49: 17x145+1341+379 1350.3,446.7 2063 gray(0)
64: 43x43+843+443 864.2,464.1 1451 gray(255)
86: 225x11+358+546 484.7,551.9 1379 gray(255)
Note that, as your circle is 42x42 pixels you will be looking for a blob that is square-ish and close to that size - so I am looking at the second to last line. I can draw that in in red on your original image like this:
convert image.png -fill none -stroke red -draw "rectangle 843,443 886,486" result.png
Also, note that as you are looking for a circle, you would expect the area to be pi * r^2 or around 1500 pixels and you can check that in the penultimate column of the output.
That runs in 0.4 seconds on a reasonable spec iMac. Note that you could divide the image into 4 and run each quarter in parallel to speed things up. So, if you do something like this:
#!/bin/bash
# Split image into 4 (maybe should allow 23 pixels overlap)
convert image.png -crop 1x4# tile-%02d.mpc
# Do Blob Analysis on 4 strips in parallel
for f in tile-*mpc; do
convert $f -negate \
-define connected-components:area-threshold=1200 \
-define connected-components:verbose=true \
-connected-components 8 info: &
done
# Wait for all 4 to finish
wait
That runs in around 0.14 seconds.
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.
I'm drawing text using QPainter on a QImage, and then saving it to TIFF.
I need to increase the DPI to 300, which should make the text bigger in terms of pixels (for the same point size).
You can try using QImage::setDotsPerMeterY() and QImage::setDotsPerMeterX(). DPI means "dots per inch". 1 inch equals 0.0254 meters. So you should be able to convert to dots per meter (dpm):
int dpm = 300 / 0.0254; // ~300 DPI
image.setDotsPerMeterX(dpm);
image.setDotsPerMeterY(dpm);
It's not going to be exactly 300DPI (it's actually 299.9994), since the functions only work with integral values. But for all intents and purposes, it's good enough (299.9994 vs 300 is quite good, I'd say.)
There are 39.37 inches in a meter. So:
Setting:
qimage.setDotsPerMeterX(xdpi * 39.37);
qimage.setDotsPerMeterY(ydpi * 39.37);
Getting:
xdpi = qimage.dotsPerMeterX() / 39.37;
ydpi = qimage.dotsPerMeterY() / 39.37;