Set page size using WIA (with scanner) - c#

I'm using WIA to acquire images from a scanner with C#. I can scan the papers, but I can't set up the page size correctly, it always defaults to A4 and I need to use Letter or Legal sometimes.
I tried with the WIA_DPS_PAGE_SIZE property, but when I try to set a value, I always get an error, that the value is out of the interval (tried a lot of possible values).
I wan't to be able to use WIA_DPS_PAGE_SIZE = WIA_PAGE_AUTO (for automatic page size), but I can't find anything on the web related to this.
Does anyone know a solution? thanks!

I know this is probably too late to actually help you with that, but it may become handy for future reference. To change scanned items properties use such code:
WIA.CommonDialog wiaDlg;
WIA.Device wiaDevice;
WIA.DeviceManager wiaManager = new DeviceManager();
wiaDlg = new WIA.CommonDialog();
wiaDevice = wiaDlg.ShowSelectDevice(WiaDeviceType.ScannerDeviceType, false, false);
foreach (WIA.Item item in wiaDevice.Items)
{
StringBuilder propsbuilder = new StringBuilder();
foreach (WIA.Property itemProperty in item.Properties)
{
IProperty tempProperty;
Object tempNewProperty;
if (itemProperty.Name.Equals("Horizontal Resolution"))
{
tempNewProperty = 75;
((IProperty)itemProperty).set_Value(ref tempNewProperty);
}
else if (itemProperty.Name.Equals("Vertical Resolution"))
{
tempNewProperty = 75;
((IProperty)itemProperty).set_Value(ref tempNewProperty);
}
else if (itemProperty.Name.Equals("Horizontal Extent"))
{
tempNewProperty = 619;
((IProperty)itemProperty).set_Value(ref tempNewProperty);
}
else if (itemProperty.Name.Equals("Vertical Extent"))
{
tempNewProperty = 876;
((IProperty)itemProperty).set_Value(ref tempNewProperty);
}
}
image = (ImageFile)item.Transfer(WIA.FormatID.wiaFormatPNG);
}
This means that scanned document will be size A4 with dimensions 619 x 876.

Related

LibreOffice Calc C# SDK: program to insert images into cells, stuck trying to create XGraphic

Background: I'm trying to write a program to insert an image into a cell of a spreadsheet. LibreOffice recently changed how this is done, and all the samples I could find use the old method which no longer works.
Technically I know that you can't "insert" an image into a cell and that such an image is an overlay on a DrawPage that sits on top of the spreadsheet to "decorate" it.
One of the first steps in doing this (the new way) is to create an XGraphic object which contains the image. The process is to create an XGraphicProvider and call it with MediaProperties that specify the image file URL to be loaded. I have a program that is supposed to do this but the resulting XGraphic is null. The LO SDK gives pretty much no information when you do something wrong; it just doesn't work.
Here is the code I have, with all the headers removed:
// addpic
// add picture to spreadsheet - debug version
class OpenOfficeApp {
[STAThread]
static void Main(string[] args) {
bool lreadonly;
string pqfile;
string pqURL;
string pqpic;
pqfile = "file:///D:/Documents/NSexeye/ODS%20File%20Access/"+
"addpix/addpic.ods";
pqpic = "addpic2";
pqURL = pqpic+".jpg";
lreadonly = false;
Console.WriteLine("Using: "+pqfile);
// get the desktop
XComponentContext XCC = uno.util.Bootstrap.bootstrap();
XMultiComponentFactory XMCF =
(XMultiComponentFactory)XCC.getServiceManager();
XMultiServiceFactory XMSF = (XMultiServiceFactory)XCC.getServiceManager();
XComponentLoader XCL =
(XComponentLoader)XMSF.createInstance("com.sun.star.frame.Desktop");
// open the spreadsheet
PropertyValue[] pPV = new PropertyValue[2];
pPV[0] = new PropertyValue();
pPV[0].Name = "Hidden";
pPV[0].Value = new uno.Any(true);
pPV[1] = new PropertyValue();
pPV[1].Name = "ReadOnly";
if (lreadonly) pPV[1].Value = new uno.Any(true);
else pPV[1].Value = new uno.Any(false);
XComponent XCo = XCL.loadComponentFromURL(pqfile,"_blank",0,pPV);
// create graphic object containing image
object oGP = XMCF.createInstanceWithContext(
"com.sun.star.graphic.GraphicProvider",XCC);
if (oGP == null) {
Console.WriteLine("oGP is null. Aborting.");
return;
}
XGraphicProvider XGP = (XGraphicProvider)oGP;
if (XGP == null) {
Console.WriteLine("XGP is null. Aborting.");
return;
}
pPV = new PropertyValue[1];
pPV[0] = new PropertyValue();
pPV[0].Name = "URL";
pPV[0].Value = new uno.Any(pqURL);
Console.WriteLine("Creating XGraphic containing "+pqURL);
XGraphic XG = XGP.queryGraphic(pPV);
// *** XG is null here
if (XG == null) {
Console.WriteLine("XG is null. Aborting.");
return;
}
// ... lots of stuff to be added here
// save and close the spreadsheet
XModifiable XM = (XModifiable)XCo;
XM.setModified(true);
XStorable XSt = (XStorable)XCo;
XSt.store();
XCloseable XCl = (XCloseable)XCo;
XCl.close(true);
// terminate LibreOffice
// *** I want this to not terminate it if something else is open
XDesktop XD = (XDesktop)XCL;
if (XD != null) XD.terminate();
}
}
I get a null for the XGraphic, in the place indicated in the comments. I don't know if the call to create it is failing, or if one of the earlier steps of the process are incorrect.
My goal here, in addition to getting my program working, is to create a sample program showing how to add an image to a Calc spreadsheet cell, and to manipulate such images. There are a fair number of people asking questions about this and none of the examples I've found will work. I think a good working sample will be of value.
I've spent a lot of time searching for information and code samples for this, with nothing that helps. I've tried to find ways to verify the validity of the XGraphicProvider interface with no luck. I've run out of things to try.
I'm hoping someone who knows about the LibreOffice SDK can take a look and maybe see what I'm doing wrong.
Update: I figured out what I was doing wrong: I was passing a bare filename in the "URL" property to XGraphicProvider. It has to be the same format (starting with "file:///") as the spreadsheet's file name specification.
Now I'm stuck with another property problem. The XGraphic has to be specified as a parameter to the GraphicObjectShape's Graphic property, but the setPropertyValue() function requires that it be a uno.Any type. I can't figure out how to specify an interface name like XGraphic as a uno.Any.
Here is the piece of code that won't compile, complaining that it can't convert an XGraphic to a uno.Any, in the first setPropertyValue call:
// set image XGraphic
XPropertySet XPS = (XPropertySet)XS;
XPS.setPropertyValue("Graphic",XG);
XPS.setPropertyValue("Name",new uno.Any(pqpic));
XG is an XGraphic type. Using "new uno.Any(XG)" doesn't work either, giving a similar compiler error.
After trying unsuccessfully for a few hours to get the latest LO SDK up and running, let me offer some untested ideas.
First of all, here is some working Basic code, no doubt similar to what you're translating from. The important line is oShape.Graphic = oProvider.queryGraphic(Props()).
oDoc = ThisComponent
oSheet = oDoc.CurrentController.ActiveSheet
pqURL = "file:///C:/Users/JimK/Desktop/addpic.jpg"
oProvider = createUnoService("com.sun.star.graphic.GraphicProvider")
oShape = oDoc.createInstance("com.sun.star.drawing.GraphicObjectShape")
Dim Props(0) as new com.sun.star.beans.PropertyValue
Props(0).Name= "URL"
Props(0).Value = pqURL
oShape.Graphic = oProvider.queryGraphic(Props())
oCell = oSheet.getCellByPosition(5,5)
oShape.Name = oCell.AbsoluteName + "##" + Props(0).Value
oShape.Anchor = oCell
oSheet.DrawPage.add(oShape)
'Resize
w = oShape.Graphic.Size.Width
h = oShape.Graphic.Size.Height
wcl = oCell.Size.Width
hcl = oCell.Size.Height
If w<>0 and h<>0 then
oCell.String=""
Dim Size as new com.sun.star.awt.Size
Size.Width = wcl
Size.Height = h*wcl/w
If Size.Height > hcl then
Size.Width = hcl*w/h
Size.Height = hcl
Endif
oShape.setSize(Size)
oShape.setPosition(oCell.Position)
erase oShape
Else
oShape.dispose()
Endif
Now, how to translate this to C#? It looks like you may need to explicitly specify the type. In the SDK example, there are calls like this.
xFieldProp.setPropertyValue(
"Orientation",
new uno.Any(
typeof (unoidl.com.sun.star.sheet.DataPilotFieldOrientation),
unoidl.com.sun.star.sheet.DataPilotFieldOrientation.DATA ) );
So in your case, something like this:
XPS.setPropertyValue(
"Graphic"
new uno.Any(
typeof(unoidl.com.sun.star.graphic.XGraphic),
XG));
Alternatively, follow the suggestion here: set GraphicURL, which should load the image and set Graphic for you.

How to vertically format embedded fields

Current Formatting For Embed Fields
Here is an embed I currently use for my semi-public Ark Servers.
First field is the Map name,
Second field is the direct connect IP Address,
Third field is if/where there is a community base on that map.
As you can see it works as intended but if there's to much info on a single line in the field the formatting is screwed up. Is there a way to fix this?
I'm using 3 separate stream builders to build the different fields and then adding them to the embed. If code is needed I can post a "dumbed down version" so it doesn't take up the whole page.
var linkHeading = "steam://connect/";
var sb = new StringBuilder();
var sb2 = new StringBuilder();
var sb3 = new StringBuilder();
var embed = new EmbedBuilder();
embed.WithColor(new Color(0, 255, 0));
embed.Title = "List of Server Ips";
JObject o1;
using (StreamReader file = File.OpenText("serverips.json"))
using (JsonTextReader reader = new JsonTextReader(file))
{
o1 = (JObject)JToken.ReadFrom(reader);
}
var ipsObject = JsonConvert.DeserializeObject<Rootobject>(o1.ToString());
sb.AppendLine("The Island: ");
sb2.AppendLine($"{linkHeading}{ipsObject.TheIsland.ip}:{ipsObject.TheIsland.port}/");
if(ipsObject.TheIsland.comm != "")
{
sb3.AppendLine($"Comm: {ipsObject.TheIsland.comm}");
} else { sb3.AppendLine($"No Comm Info Available"); };
sb.AppendLine("Aberration: ");
sb2.AppendLine($"{linkHeading}{ipsObject.Aberration.ip}:{ipsObject.Aberration.port}/");
if (ipsObject.Aberration.comm != "")
{
sb3.AppendLine($"Comm: {ipsObject.Aberration.comm}");
} else { sb3.AppendLine($"No Comm Info Available"); };
embed.WithDescription($"Cluster Ip and Comm Information");
embed.AddField(x =>
{
x.Name = "Map";
x.Value = sb.ToString();
x.IsInline = true;
});
embed.AddField(x =>
{
x.Name = "IP";
x.Value = sb2.ToString();
x.IsInline = true;
});
embed.AddField(x =>
{
x.Name = "Comm?";
x.Value = sb3.ToString();
x.IsInline = true;
});
await Context.User.SendMessageAsync(null, false, embed.Build());
await ReplyAsync("Server Ip List was sent directly to your inbox! :)");
You don't have that much control over how embed fields are displayed. The only thing you control in regards to fields are if they inline or not. The rendering is completely up to Discord and the end users screen size. For example, your current output on mobile will ignore the inline setting and list the fields one on top of the other instead of side by side.
Unless your fields consistently contain a small amount of text each you can't guarantee how the end use will see the output. If you need to guarantee some sort of consistent structured display across all devices, your best bet is to use an image.

How to load pins just around your current location and load more as you zoom out in Xamarin.Forms.Maps

I’m trying to show pins on the map but only the ones that can fit the screen around your current location depending on the zoom level I’ve set on when the map appears, because I have 10’s of pins and when I open the map it loads every pin and takes a long time to load.
Any idea on how to do it ?
Method to load pins:
async Task ExecuteLoadPinsCommand()
{
IsBusy = true;
try
{
Map.Pins.Clear();
Map.MapElements.Clear();
Map.CustomPins.Clear();
var contents = await placeRepository.GetAllPlacesWithoutRelatedDataAsync();
if (contents == null || contents.Count < 1)
{
await App.Current.MainPage.DisplayAlert("No places found", "No places have been found for that category, please try again later", "Ok");
await ExecuteLoadPinsCommand();
}
if (contents != null)
{
places.Clear();
var customPins = this.Map.CustomPins;
places = contents;
foreach (var item in places)
{
CustomPin devicePin = new CustomPin
{
Type = PinType.Place,
PlaceId = item.PlaceId.ToString(),
Position = new Position(item.Latitude, item.Longitude),
Label = $"{item.Name}",
Address = $"{item.Name}"
};
Map.CustomPins.Add(devicePin);
Map.Pins.Add(devicePin);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
CustomMapRenderer:
protected override MarkerOptions CreateMarker(Pin pin)
{
CustomPin pin = (CustomPin)pin;
var thePlace = Task.Run(async () => await placeRepository.GetPlaceByIdWithMoodAndEventsAsync(Guid.Parse(pin.PlaceId)));
var place = thePlace.ConfigureAwait(true)
.GetAwaiter()
.GetResult();
var marker = new MarkerOptions();
marker.SetPosition(new LatLng(place.Position.Latitude, place.Position.Longitude));
if (place.Category == "" && place.SomethingCount == 0)
{
marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.icon));
}
//else if ...
return marker;
}
A major part of programming is learning to debug well.
When facing a performance problem, it is important to isolate the time delay to the smallest bit of code that you can.
Here's the thought process I would go through:
YOUR OBSERVATION: When there is one pin, it takes less than a second. When there are 20 pins, it takes maybe 10 seconds. (9 second difference.)
YOUR HYPOTHESIS (Given the question you posted): Maybe adding 20 pins to map takes much or most of the 9 seconds.
TESTS: How can we test EXACTLY the code that "adds pins to map"?
A: "Divide and conquer":
Let all the other code run 20 times. That is, have 20 pins as data. BUT suppress the code that adds those pins.
Test #1: Have 20 pins returned by GetAllPlacesWithoutRelatedDataAsync. So all that work is done 20 times.
Comment out JUST the code that ADDS the pins. Make this change:
//Map.CustomPins.Add(devicePin);
//Map.Pins.Add(devicePin);
Result #1: _____ seconds
Its possible that having NO pins allows the map to skip loading some pin-related code. Lets find out how quick it is when we only ADD ONE of the 20 pins.
Test #2: Have 20 pins in the data. BUT only ADD one of them. Make this ONE-LINE change:
foreach (var item in places)
{
CustomPin devicePin = new CustomPin
{
Type = PinType.Place,
PlaceId = item.PlaceId.ToString(),
Position = new Position(item.Latitude, item.Longitude),
Label = $"{item.Name}",
Address = $"{item.Name}"
};
Map.CustomPins.Add(devicePin);
Map.Pins.Add(devicePin);
break; // <--- ADD THIS LINE.
}
Result #2: _____ seconds
(Test #2 is what I was trying to ask you to do, in one of my comments on the question. I've deleted those comments.)
Now we have enough information to determine how much of the ~9 extra seconds are due to going through that foreach loop 20 times, to ADD 20 pins.
This will determine whether there is any point in trying to speed up the ADDS, or whether there is a problem elsewhere.
If most time is spent elsewhere, then you need to add the suspect code to the question. Then do similar tests there. Until you can report exactly what code takes most of the time.
IF 20x map.Pins.Add(..) takes a significant amount of time, THEN here are two techniques, either of which should be faster, imho.
FASTER ADD #1:
Use Map.ItemsSource.
FASTER ADD #2:
Create the map WITH its pins, BEFORE displaying it.
using Xamarin.Forms;
using Xamarin.Forms.Maps;
namespace XFSOAnswers
{
// Based on https://github.com/xamarin/xamarin-forms-samples/blob/main/WorkingWithMaps/WorkingWithMaps/WorkingWithMaps/PinPageCode.cs
public class PinPageCode : ContentPage
{
public PinPageCode()
{
Title = "Pins demo";
Position position = new Position(36.9628066, -122.0194722);
MapSpan mapSpan = new MapSpan(position, 0.01, 0.01);
Map map = new Map(mapSpan);
Pin pin = new Pin
{
Label = "Santa Cruz",
Address = "The city with a boardwalk",
Type = PinType.Place,
Position = position
};
map.Pins.Add(pin);
// ... more pins.
Content = new StackLayout
{
Margin = new Thickness(10),
Children =
{
map
}
};
}
}
}

how to add map box tiles to gmap?

I want to get map box tiles from database but it does not work. I get MBTilesMapProvider class from here.
It is invoked like below:
map.Manager.Mode = AccessMode.ServerAndCache;
map.MapProvider = new MBTilesMapProvider("C:\\Users\\NPC\\Desktop\\test\\ne.mbtiles");
result:
but if google maps used as map provider like below it works well
map.Manager.Mode = AccessMode.ServerAndCache;
map.MapProvider = GoogleSatelliteMapProvider.Instance;
When i debuged i noticed that GetTiles method is invoked never.
Note: I think there is no problem about finding database because it reads meta data from database.
I solved solution by make a bit changes at MBTilesHelper.cs.
First i realized that when reading metadata from database MinZoom and MaxZoom values, they always stay as zero because it does not contain MinZoom or MaxZoom. Therefore, i set them manually.
and secondly i changed a bit "getImage" method.
private PureImage getImage(GPoint pos, int zoom)
{
PureImage retval = null;
var resultImage = _mbtiles.GetTileStream(pos.X, pos.Y, zoom);
if (resultImage != null && resultImage.Length > 0)
{
//resultImage.Position = 0L;
retval = GetTileImageFromArray(resultImage);
}
return retval;
}

WebBrowser class - how to print document without header/footer?

I'm using the WebBrowser class to open document, change values, save and print. The problem is, that it prints the document including the header("Page 1 of 1") and footer(root of the document + date)
I looked at the documentation and didn't find a way to remove them. Is it even possible using WebBrowser or should I look for alternatives?
There is a solution, probably not as cleaner as it could have been. Since WebBrowser inhertis it's settings from Internet Explorer it is possible to change the values in the registry. Luckily the values are under HKCU so no administration permissions are needed.
Take a look at https://stackoverflow.com/a/1321314/1630928
The trick to doing this is to pass a Variant containing a ByRef SafeArray of Variants to the WebBrowser control. I haven't figured out how to do it from C#. Here's someone else who was working on the same problem who resorted to using managed C++
http://www.limilabs.com/blog/printing-in-webbrowser-control-custom-header-and-footer
Not a C#, but here's C++ code that I came up with based on a now defunct KB267240. It will remove the header and the footer while printing:
BOOL bRes = FALSE;
//Get IWebBrowser2 from your IE control
CComPtr<IWebBrowser2> pWebBrowser = this->GetIWebBrowser2();
if(pWebBrowser)
{
HRESULT hr;
COleVariant varNull;
SAFEARRAYBOUND psabBounds[1];
SAFEARRAY *psaHeadFoot;
hr = S_OK;
VARIANT vArg;
BOOL bGot_vArg = FALSE;
VARIANT vHeadStr, vFootStr;
long rgIndices;
VariantInit(&vHeadStr);
VariantInit(&vFootStr);
// Initialize header and footer parameters to send to ExecWB().
psabBounds[0].lLbound = 0;
psabBounds[0].cElements = 3;
psaHeadFoot = SafeArrayCreate(VT_VARIANT, 1, psabBounds);
if(psaHeadFoot)
{
// Argument 1: Header
vHeadStr.vt = VT_BSTR;
vHeadStr.bstrVal = SysAllocString(L" "); //Must be at least one space
if (vHeadStr.bstrVal)
{
// Argument 2: Footer
vFootStr.vt = VT_BSTR;
vFootStr.bstrVal = SysAllocString(L" "); //Must be at least one space
if(vFootStr.bstrVal)
{
rgIndices = 0;
SafeArrayPutElement(psaHeadFoot, &rgIndices, static_cast<void *>(&vHeadStr));
rgIndices = 1;
SafeArrayPutElement(psaHeadFoot, &rgIndices, static_cast<void *>(&vFootStr));
rgIndices = 2;
SafeArrayPutElement(psaHeadFoot, &rgIndices, static_cast<void *>(&varNull)); //Set stream to NULL as we don't need it
//NOTE: Currently, the SAFEARRAY variant must be passed by using
// the VT_BYREF vartype when you call the ExecWeb method.
VariantInit(&vArg);
vArg.vt = VT_ARRAY | VT_BYREF;
vArg.parray = psaHeadFoot;
//Got it
bGot_vArg = TRUE;
}
}
}
//Did we get all the vars?
if(bGot_vArg)
{
if(SUCCEEDED(hr = pWebBrowser->ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, &vArg, NULL)))
{
//All good
bRes = TRUE;
}
}
else
{
//Use fallback (that will keep the footer & header)
if(SUCCEEDED(hr = pWebBrowser->ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, varNull, varNull)))
{
//Printed via fallback
bRes = TRUE;
}
}
//Clean up
VariantClear(&vHeadStr);
VariantClear(&vFootStr);
if(psaHeadFoot)
{
SafeArrayDestroy(psaHeadFoot);
psaHeadFoot = NULL;
}
}

Categories

Resources