Detect all available scanner resolutions using WIA - resolution

How can I programmatically detect all available resolutions (in dpi) for a specified scanner using WIA 2.0? What about page sizes supported? Any ideas?

Pseudo code:
Assume you have device info, connect to it:
var device = deviceInfo.Connect();
if device is not null….. then you can get the Item
Note that items begin at index 1 for device items
Item item = device.Items[1];
An Item has various properties which you can enumerate
e.g.
foreach (Property prop in item.Properties)
{
var temp = prop.Name + " " + prop.PropertyID + " " + prop.get_Value();
}
In this case to find Maximum DPI supported by your scanner you could use.
"Horizontal Optical Resolution" (property id: 3090)
"Vertical Optical Resolution" property id: 3091
.. However if you examine the properties enumeration you will see there is nothing to tell you the minimum or list of all available DPI settings.
.. Now I could not find anything either….
However I did find a way of discovering the minimum DPI…
You may be aware of WIA intent, the enumeration allows you to set scanning type e.g. colour, grey scale etc. (property id: 6146)
var currentIntent = item.Properties.get_Item("6146");
// set to colour
currentIntent.set_Value(WiaImageIntent.ColorIntent);
.. However you can also set WIA image bias in this way (to either maximum quality or minimum size)
This is not via image intent enumeration but is done by OR ing the appropriate values
// set minimum size as WIA bias
var intent = (int)item.Properties.get_Item("6146").get_Value();
item.Properties.get_Item("6146").set_Value(intent | 0x00010000);
http://msdn.microsoft.com/en-us/library/ms630190%28v=vs.85%29.aspx
And if you now look at the DPI (assuming previously DOI was not actually at minimum)
var dpiHorizontal = item.Properties.get_Item("6147").get_Value();
var dpiVertical = item.Properties.get_Item("6148").get_Value();
.. you should see that DPI is now set to it’s lowest (to give minimum size scan)
A big warning.
As soon as you set bias (be it minimum size or maximum quality) any previous DPI values you set are changed (as are various other properties for image dimensions etc – it makes wide ranging changes).
.. So, if you want to stay in control I suggest just finding minimum dpi once and storing it in configuration – Or if you do it each time, discard that item (after getting minimum DPI) and use a new item with no bias set.
.. So you now have max and min DPI values – how to get a list?
.. Again I know of no way .. but
You could have a list of “sensible DPI values”
e.g. 75, 100, 150, 200, 300, 600, 1200, 2400
As (depending on scanner resolution) these are “popular” values to support
.. Depending on your max / min DPI values you know what “popular” values may be worth trying.
.. and when setting DPI do it via try / catch
e.g.
try
{
item.Properties.get_Item("6147").set_Value(myDPI); // horizontal DPI
item.Properties.get_Item("6148").set_Value(myDPI); // vertical DPI
}
catch
{
// not supported so have some logic where try a different “popular” value
}
I see this type of issue with one scanner I use – just because a DPI is within the max / min limits does not mean it is supported.
Although many scanners WIA drivers support 100, 100 DPI, this one does not but does provide 75,75 DPI or 150,150 DPI
This approach is nasty and kludgy but works (and again you could just do this once, run through your list of popular “DPI” values, try and set them, and be left with a list of which DPI settings your scanner supports.
For the sake of simplicity sake these pseudocode examples assume vertical & horizontal DPI may both be set to same value .. this may not be the case with all scanners!

You can use the SubTypeMin and SubTypeMax properties of WIA_IPS_XRES and WIA_IPS_YRES to get the allowed resolution ranges. You can probably use something similar to determine which page sizes are supported.
WIA.Property horizontalResolutionProperty = FindProperty(_scannerDevice.Items[1].Properties, HORIZONTAL_RESOLUTION);
if (horizontalResolutionProperty != null)
{
// SubTypeMin and SubTypeMax are subproperties that tell what the min and max values are for the given property
if (horizontalResolutionProperty.SubTypeMin > minResolution) minResolution = horizontalResolutionProperty.SubTypeMin;
if (horizontalResolutionProperty.SubTypeMax < maxResolution) maxResolution = horizontalResolutionProperty.SubTypeMax;
}
WIA.Property verticalResolutionProperty = FindProperty(_scannerDevice.Items[1].Properties, VERTICAL_RESOLUTION);
if (verticalResolutionProperty != null)
{
// SubTypeMin and SubTypeMax are subproperties that tell what the min and max values are for the given property
if (verticalResolutionProperty.SubTypeMin > minResolution) minResolution = verticalResolutionProperty.SubTypeMin;
if (verticalResolutionProperty.SubTypeMax < maxResolution) maxResolution = verticalResolutionProperty.SubTypeMax;
}

The horizontal and vertical resolution properties have a Subtype property which, depending on it's value, let you acces the allowed value or values for the resolution:
var xResolutionProperty = item.Properties["Horizontal Resolution"];
switch (xResolutionProperty.SubType)
{
case WIASubType.ListSubType:
// Here you can access property SubTypeValues, which contains a list of allowed values for the resolution.
break;
case WIASubType.RangeSubTypes:
// Here you can access SubTypeMin and SubTypeMax properties, with the minimum and maximum allowed values.
break;
case WIASubType.UnspecifiedSubType:
// Here you can access SubTypeDefault property, which contains the default resolution value.
break;
}

Related

Using CMFCMenuButton::SizeToContent does not seem to work as I would like. Why?

I am perplexed about the SizeToContent method of the CMFCMenuButton control.
This is my dialog in the IDE:
As you can see, I have specifically made the button wider than the two on the far right.
I added the following code to OnInitDialog:
// Resize (if required)
const auto sizNewButton = m_btnReset.SizeToContent(true);
CRect rctButton;
m_btnReset.GetWindowRect(&rctButton);
if(sizNewButton.cx > rctButton.Width())
{
m_btnReset.SizeToContent();
}
Yet, when I run my application in English:
It has made it smaller. My application supports 50+ languages by using satellite DLLs and I was hoping to only resize to content if it was required. But it seems to resize it anyway. Have I missed a step here?
I have checked the properties for the control in the IDE and it is not set to auto resize:
I notice that the help documentation states:
The new size of the button is calculated to fit the button text, image, and arrow. The framework also adds in predefined margins of 10 pixels for the horizontal edge and 5 pixels for the vertical edge.
I had a look at my button:
Default size: 48 x 23 (the GeWindowRect result).
Calculated size: 57 x 23 (the SizeToContent result).
If I adjusted my code like this:
if((sizNewButton.cx - 10) > rctButton.Width())
That would bring it down to 47 and thus would not resize. I am assuming the code is not working right because of the padded margin that GetWindowRect knows nothing about.
Searched it, and found that the problem is MFC's CMFCMenuButton::SizeToContent() implementation in afxmenubutton.cpp:
CSize CMFCMenuButton::SizeToContent(BOOL bCalcOnly)
{
CSize size = CMFCButton::SizeToContent(FALSE); // <==== The culprit!!!
size.cx += CMenuImages::Size().cx;
if (!bCalcOnly)
{
SetWindowPos(NULL, -1, -1, size.cx, size.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}
return size;
}
That is, it calls the base implementation of SizeToContent() with the bCalcOnly parameter set to FALSE, which means it will also resize the control to just fit the text (without the drop-down arrow). This is less than required for the text plus the arrow, and of course the original size is lost.
A workaround can be get the (original) width, before the SizeToContent() call, and work with this instead of the new one:
CRect rctButton;
m_btnReset.GetWindowRect(&rctButton);
const auto nOrigWidth = rctButton.Width(); // Store the original width
const auto sizNewButton = m_btnReset.SizeToContent(true); // This resizes the control!!!
if (sizNewButton.cx > nOrigWidth) // Compare to the original width rather than the new one
m_btnReset.SizeToContent();
else // Restore original width
m_btnReset.SetWindowPos(NULL, -1, -1, nOrigWidth, sizNewButton.cy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
Alternative Workaround:
Define a new CMFCMenuButton-based class, overriding SizeToContent() - in the implementation call the base CMFCButton::SizeToContent() with the bCalcOnly parameter passed by the caller, not with FALSE. Map the control to this class instead of CMFCMenuButton. That is use a class that fixes it. Too much of an overkill for just a workaround though.

How to find bit length of text with specific font and font size

I'm developing NativeScript JavaScript code to create dynamic text marker for maps. I have the code working that creates a marker for a specific string. My next step is to take any given string, determine its height and width in bits, and create the marker sized to contain the text.
My problem is finding the size of the text, given the text string itself, the font size, and the font family.
It looks like getMeasuredWidth could work, except that the string must already be loaded on a page before that function will return a value. In my case, I simply need to compute the size; the text won't otherwise appear as such on a page (the text in the marker becomes an image).
Is there a way to do this?
var bmp = BitmapFactory.create(200);
bmp.dispose(function (b) {
try {
b.drawRect(
"100,34", // size
'0,0', // upper-left coordinate
KnownColors.Black, // border color
KnownColors.Cornsilk // fill color
);
b.writeText(
"Parking",
"2,25",
{ color: KnownColors.Black, size: 8, name: 'fontawesome-webfont', });
...
In the code above, the width of "100" of the bounding rectangle actually represents the bit width of "Parking" with a small amount of padding. What I want to does calculate the rectangle's height and width and not hard-code it.
Try this, finding label size without adding it to Page upon button click
export function onFindButtonTap(args: EventData) {
const button = <any>args.object;
const label = new Label();
label.text = "Hello, found my size?"
label.fontSize = 20;
(<any>label)._setupAsRootView(button._context);
label.onLoaded();
label.measure(0, 0);
console.log(`Width : ${label.getMeasuredWidth()} x Height : ${label.getMeasuredHeight()}`);
}
Playground Sample
Note: I didn't get a chance to test it with iOS yet, let me know if you hit any issues.

How to manage tall menus (lists of languages)?

My application supports over 30 languages:
What is the right way to manage the situation where the menu is too tall for the screen? I will slowly get extra languages added and I do not know how to cater for it.
The standard menu implementation provides the functionality to automatically add scrollbars, should the number of entries exceed the menu's maximum height. By default a popup menu's height is set to 0, instructing the system to use the screen height as the menu's maximum height.
This works for a number of scenarios, and you don't have to do anything to get that behavior. It does fail, though, for multimonitor setups, where the height of the primary display is larger than the height of the display, where the application is displayed.
To work around this, you can set the respective popup menu's maximum height, whenever it is displayed. The appropriate place would be CWnd::OnInitMenuPopup:
void CMainFrame::OnInitMenuPopup( CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu ) {
CFrameWnd::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
if ( !bSysMenu && ( nIndex == 3 ) ) { // Apply appropriate filter
MENUINFO mi = { 0 };
mi.cbSize = sizeof( mi );
mi.fMask = MIM_MAXHEIGHT;
mi.cyMax = 150; // Pick an appropriate value
pPopupMenu->SetMenuInfo( &mi );
}
}
This callback is called, whenever a popup menu is about to be displayed. The height has been arbitrarily set to 150. You can pick any value you see fit in your application (e.g. the minimum height of all displays, a value based on the height of the display, where the menu will be displayed, etc.).
Scrollbars are added automatically, as can be seen in the following screenshot:

Extendscript: How to check whether text content overflows the containing rectangle

I am using Extendscript for Photoshop CS5 to change the text of a text layer. Is there a way of checking whether the text fits e.g. by checking whether it overflows after changing the content?
I created a solution that works perfectly fine :). Maybe someone else can use it as well. Let me know if it works for you too!
function scaleTextToFitBox(textLayer) {
var fitInsideBoxDimensions = getLayerDimensions(textLayer);
while(fitInsideBoxDimensions.height < getRealTextLayerDimensions(textLayer).height) {
var fontSize = parseInt(textLayer.textItem.size);
textLayer.textItem.size = new UnitValue(fontSize * 0.95, "px");
}
}
function getRealTextLayerDimensions(textLayer) {
var textLayerCopy = textLayer.duplicate(activeDocument, ElementPlacement.INSIDE);
textLayerCopy.textItem.height = activeDocument.height;
textLayerCopy.rasterize(RasterizeType.TEXTCONTENTS);
var dimensions = getLayerDimensions(textLayerCopy);
textLayerCopy.remove();
return dimensions;
}
function getLayerDimensions(layer) {
return {
width: layer.bounds[2] - layer.bounds[0],
height: layer.bounds[3] - layer.bounds[1]
};
}
How to use / Explanation
Create a text layer that has a defined width and height.
You can change the text layers contents and then call scaleTextToFitBox(textLayer);
The function will change the text/font size until the text fits inside the box (so that no text is invisible)!
The script decreases the font size by 5% (* 0.95) each step until the texts fits inside the box. You can change the multiplier to achieve a more precise result or to increase performance.
I haven't found a way to do this directly. But I've used the following technique to determine the height I needed for a textbox (I wanted to keep the width constant) before.
expand the textbox's height well beyond what is needed to accommodate the text inside it.
duplicate the layer
rasterize the duplicate
measure the bounds of the rasterized layer.
adjust the bounds of the original text layer as needed
delete the rasterized duplicate
Totally roundabout - but it did work.

How to limit frame resizing based on view's size? MFC doc/view architecture

MFC doc/view architecture, sdi (more precisely multiple top-level windows).
In my view class, I set my "playground" (i.e. logical space) with SetScrollSizes(); Then I want to limit maximum frame window size to that of view's maximum size.
Here is what I'm doing but I think there might be better solution, please advice:
I'm implementing OnGetMinMaxInfo() in my CMainFrame. There I try to get active view's scroll sizes, and set lpMMI->ptMaxTrackSize appropriately. Below is the code:
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
// Call base version:
CFrameWndEx::OnGetMinMaxInfo(lpMMI);
// Get active view:
CScrollView *pScrollView = (CScrollView *)GetActiveView();
if (pScrollView && pScrollView->IsKindOf(RUNTIME_CLASS(CMyFckinView)))
{
// Get total size of playground:
CSize sizePlayground = pScrollView->GetTotalSize();
// Test if the size is non-zero, i.e. there is at least one node displayed:
if (sizePlayground.cx && sizePlayground.cy/* && !IsPrintPreview()*/)
{
// Set maximum window size to match our playground size:
CRect rectClient, rectWindow;
pScrollView->GetClientRect(&rectClient);
this->GetWindowRect(&rectWindow);
if (rectWindow.top > -5000 && rectWindow.left > -5000) // Avoid when minimized...
{
lpMMI->ptMaxTrackSize.x = sizePlayground.cx + (rectWindow.Width() - rectClient.Width());
lpMMI->ptMaxTrackSize.y = sizePlayground.cy + (rectWindow.Height() - rectClient.Height());
return;
}
}
}
}
This works but has one problem: When print preview is displayed (standard MFC print preview), I obviously want to allow free window resizing, so I use runtime info GetActiveView()->IsKindOf(...) to determine that active view is really my view, and not print-preview's view (which is CPreviewViewEx). But when I close the print preview, OnGetMinMaxInfo is not called, so I'm unable to adjust frame size according to my view again. As soon as I move the window OnGetMinMaxInfo gets called again and correctly adjusts frame size, but without manually moving the window old size (to which the print preview was sized to) is retained and has ugly artifact.
What can I do? Basically if I could trap the moment when print preview is closed, I could use following trick:
// Trigger the WM_MINMAXINFO message:
CFrameWnd *pFrame = GetParentFrame();
RECT rectWindow;
pFrame->GetWindowRect(&rectWindow);
pFrame->MoveWindow(&rectWindow);
But I don't know how to trap print-preview closure.
What I'm trying to accomplish seems quite standard: who would want to have frame window resized bigger than view's logical size (set by SetScrollSizes())? So there should be some more natural solution maybe?
In your CMyFckinView, handle a message that is reliably sent when the print preview is closed and then post a user message to the mainframe which will trigger your "force minmax" code. Perhaps WM_FOCUS or WM_ACTIVATE?

Resources