Thursday, April 26, 2012

Collecting PDF Form Data with ASP.NET C#

I recently had to investigate the technique to capture the form input data collected on Adobe PDF for radiological procedure report results.

This, though very simple, information was scattered around all over the places so I can provide you the gist of what you need to do by means of a simple demo code.
  • First you will need to use the PDF Form Editor, for example Adobe Acrobat Pro. I am sure there are a lot of different ones.
  • Next you either scan a form or create one which output the usual .doc file.
  • Import the document and edit the form using the PDF form editor, drag and dropping the fields.
  • Finally add a submit button with a submit action of http://localhost/xfdf
  • On Visual Studio, create the xfdf web project and you can add the following code in Page_Load of Default.aspx
public partial class xdf : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var name = "junk.xfdf";
        string filename = Path.Combine(Server.MapPath(""), name);
        using (FileStream fs = new FileStream(filename, FileMode.Create))
        {
            byte[] bytes = new byte[8192];
            int bytesRead;
            while ((bytesRead = Request.InputStream.Read(bytes, 0, bytes.Length)) > 0)
            {
                fs.Write(bytes, 0, bytesRead);
            }
        }
    }
}
  • Compile above code.
  • Submit the PDF form to the URL
Then if you open junk.xfdf on your text editor, you will see something like this. The key part is that you will see the field and value elements in the XML. From this point it is quite easy to parse this stuff using the XDocument class and LINQ.

<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve"
><fields
><field name="Heart Rate"
><value
>10</value
></field
><field name="Submit"
/></fields
><ids original="BFF11731D28F744F901901CF60BBED7A" modified="B0328D96D4E3F242A93BC5512C7B97E6"
/></xfdf
>

Monday, April 09, 2012

Getting XTIDE Up on Joyent SmartMachine (Solaris 11)

This is my note on getting the XTide (learn more) up on the Joyent SmartMachine on which I am running various personal web projects. My XTide was previously hosted on a shared Linux machine (i.e., no root access) before but the provider upgraded the Linux and then it broke. Since I now have an Intel Solaris 11 virtual machine of "my own," I have decided to transition to this server (as I find time, of course.) Installing and configuring XTide on a shared machine was a real hassle as they did not have X11 installed and they won't install it for me, but now with the virtual machine in which I have the "root" things are becoming much easier, in addition there are a lot of pre-built installs that's available (still require the root access.) {Why Joyent SmartMachine and Solaris? Don't ask. But I am extremely satisfied with it. }

Perhaps you may have the same problem and here is how I got around the issues.

Note that I am only interested in running this as a command line to compute the tide and graphics in the area I surf using a cron job. So if you have an X-Windows issue, this article won't help much.


Getting It Installed

Getting it installed is fairly simple.
  • The page http://www.flaterco.com/xtide/files.html tells that on Solaris you go to Blastware.COM, but almost all of the mirror sites it points to are broken. Instead, if you go to http://www.opencsw.org/packages/xtide/ it is still avilable. The maintenance of this stopped around 2.10 (in 2008) but for my purposes, it is OK. Someday, when I have more time, I may volunteer to contribute to Opencsw to maintain this. Not now...
  • It comes with required libraries, and also the original harmonics and wvs files it was built with in /opt/csw/share/xtide 
  • If you don't know what I am talking about but you have the root access to the machine, just follow the instructions on the OpenCSW web site, install the pkgutil installer first as a root (if you don't have) and then install the xtide with it. You would be all set in a matter of 5-10 minutes. And if you still don't know what I am talking about, then it is best to ask your IT admin buddy to get that done as it can affect other OpenCSW packages other people may be using on your machine. 
Getting it to Run
  • "Out of the box" by typing in {tide -l "princeton"} it did not run. In fact it threw an exception about saying  the following:

    XTide Fatal Error:  BAD_OR_AMBIGUOUS_COMMAND_LINE

    but do not dismay yet...!
  • Here is what I have done to get it going!
The "tide" command on /opt/csw/bin/tide is a shell script which, in turn, will run tide.bin after "tide" sets various environment variables. There appears to be an issue with the WVS_DIR environment variable and if only have the HFILE_PATH it works (in other words comment out WVS_DIR definition in the tide script), and it will output the tide. The graphics output also worked.

To test if anything works, you can first only EXPORT the HFILE_PATH in your shell then run /opt/csw/bin/tide.bin directly like this.

HFILE_PATH="/opt/csw/share/xtide/harmonics.tcd"
export HFILE_PATH
/opt/csw/bin/tide.bin -l "princeton"

The Harmonics Database Location Names Have Changed

In my old script I had -l "half moon bay." This time it did not work as the program could not find the station name. So be sure that you look at the Location List (http://www.flaterco.com/xtide/locations.html)  that matches your Harmonic file you've downloaded.

Hope this helps in your situation.





Saturday, April 07, 2012

ASP.NET GridView and LinqDataSource Stuff...

The DataGrid is one of the components I use quite a bit in my line of work, and they are convenient.

I have also been using LINQ quite a bit. It is a significant time saver as well as making my code more easier to understand. But until today I never bothered to use LinqDataSource just because I did not spend time to learn a bit about it. Instead I always built the DataSets using the designer. The problem with the Datasets for me is that it does not show me exactly what I am querying easily.

Before I get into that, I have to tell you also that if your requirement is very simple, then you can feed the result of the Linq query directly to the GridView (or other data binding controls.) This might be a great way to bind the pull down menu or a list view with small number of selections.

For example,

var customers = from c in context.customers select c;
gridview1.DataSource = c;

And that really all it takes to feed a GridView. For more information on this, learn from this MSDN page.

But this won't allow you to sort or page the grid. For that purpose you need to attach a LinqDataSource. You can easily do that by:

  • Defining the LinqToSQL object in your project, or pull one in from other assembly
  • Dragging and dropping the LinqDataSource component on to of the GridView. The wizard will show you which of the LinqToSQL database context object to use.

Well that's great, but I really do not want to use the connection string that's built into Web.config. In my case, I pull the connection string common to all my applications from my own company's registry set. But at any rate, you would assign a newly created data context to e.ObjectInstance of the event argument.

So here is another trick.
  • Define an event processing method for ContextCreating event.
  • Re-instantiate the LinqDataContext class object with your connection string. Yes, just the Context is all you need here. The rest will be handled with the LinqDataSource you've created with the designer. (Yes, it is the Data Context object, not the Linq Select result.)

       protected void LinqDataSource1_ContextCreating(object sender,
           LinqDataSourceContextEventArgs e)
        {
            string myConnectionString="your connection string";
            e.ObjectInstance = new MyDataConext(myConnectionString);
        }

Here is another bonus thing you can do. If you do this type of connection string swapping, it is best to create a new context factory class of your own and then call that. This factory class can do the connection string swapping for you. This will centralize the connection string swapping.

In my case, the connection information is stored locally in a registry and many of my own applications pull that information from there at run-time. This way, all I need to do is to set the connection strings once and then all other applications I write will not need separate connection configurations. There are of course many other ways of doing it.