Auterytech http://auterytech.com Stressfreie Zone Wed, 19 Dec 2012 10:41:47 +0000 en-US hourly 1 http://wordpress.org/?v=3.5 Designing a Notes iPhone web app http://auterytech.com/technology/designing-a-notes-iphone-web-app/ http://auterytech.com/technology/designing-a-notes-iphone-web-app/#comments Fri, 22 Apr 2011 11:17:11 +0000 Curtis http://auterytech.com/?p=119 “A pedagogical decision hides behind every design decision” -Dan Meyer

For the last few weekends, I’ve been coding in the early mornings while my wife sleeps beside me, working on a replacement for the broken built-in Notes app on her iPhone. My replacement is usable and stable at this point, and I’m just putting the final polish on the code to remove duplication, improve comments, delete orphan code, etc.

The tools I used to design this are the same as the diet tracker app I wrote for her to track her protein and calcium during her pregnancy: handwritten JavaScript, and Google’s appspot.com webserver to properly serve the cache manifest’s content type. This was initially going to be a quick hack to learn the API for Web SQL Databases, so I could retrofit database support in the diet tracker, but along the way I learned a lot about designing an HTML Notes application, which was more involved than I anticipated.

This post recounts some of the lessons I learned, and the decisions I made between competing solutions that each had pluses and minuses. Some screenshots of the current product are available below the fold.

Custom icon on iPhone home screen.

Editing: The “back” button on the menu bar is three rectangles and a circle drawn using the HTML5 canvas spec. More on that below.

List: Normally all those minus signs won’t be there. The red minus sign at the very top is twisted to indicate being in delete mode. Then each note gets its own minus sign, and twisting them unlocks a delete button. Again, more on that below.

Impetus

The story starts a few years ago when the first iPhone came out. I bought one for Liberty, and it quickly became the source of many pictures of Scout, and of our adventures – pictures that we didn’t want to lose. Because of the fear of losing these images, we rarely synced the phone with iTunes, always backing the images up first, and fearing the bad things that would happen when newer iOS versions were installed.

During syncing, we’ve lost music, saw the built-in applications get slower, had mysterious timeouts backing up data, but fortunately have never lost any of the images. Since each upgrade seemed to make the whole system slower, and assuming newer OS versions would need even more horsepower, syncing again would make the phone virtually unusable.

“Why not upgrade to an iPhone 4″ you ask? I’ve pitched this idea to Liberty, which she soundly dismissed. Her reason: the newer iPhones look more boxy, whereas the original looks curvy and sleek. Well, there’s no arguing with that logic… or at least, I don’t really know where I’d start, so there’s nothing for it but to help her use the phone she has.

The current state of her phone is iOS 3.1.3 with the version of Safari using WebKit engine 528.18. Theses specific versions have some anomalies, namely the Notes app tends to break during the upgrade (file permission problems), and Safari’s HTML5 support had some minor issues that have since been ironed out (such as placeholders on textarea fields, which I’ll get into later).

The Notes app breaking was the real impetus of this project. Our symptoms consist of the app opening fine, but clicking on most of the notes results in the app simply closing. Only one note opens, but trying to alter it also results in the app closing. There are some suggestions for fixes online, which mainly involve monkeying around with the Settings app and trying again, but those haven’t worked for us.

Unix hacker that I am, I could jailbreak the phone, install an SSH daemon, then connect from my PC and do some commandline chmods on the note files, but Liberty didn’t like the idea of me hacking her phone like that. One of the things that is helping the two of us have a successful marriage is my not fixating on things like this like I would have a decade ago. If she wants an older phone with an unaltered OS, then that’s the environment I work in. If the puzzle is hard to solve under those conditions, so much the better: the puzzle is the fun part. Solving the puzzle is the main focus, not advocacy for being modern or edgy (whatever that means).

When I finished the diet tracker app enough to be usable, I looked for another app to write to learn web databases, and the broken Notes app was the obvious candidate. And so, I began.

My previous blog entry on the diet tracker web app shows (along with my radical views on childbirth) some basics of using offline application caches, and how to turn a website bookmarked to the iPhone home screen into something that behaves like a native application, so I won’t dwell on those points here. Let’s start instead with databases.

Web Databases

There are two basic flavors of JavaScript accessible local databases: the Web SQL Database, which is supported in WebKit browsers (Chrome and Safari), and the Indexed Database API. The latter was proposed by Oracle a couple years ago, and currently the W3C is declaring Web SQL a deprecated standard. The newer standard has been adopted by Firefox 4, so Wikipedia tells me, as well as Chrome 11 (meaning support for it should be rolled back into WebKit in the near future, propagating to Safari and Chromium). For now, though, Web SQL is what’s available to me for iPhone web apps, and that is what I used.

In Web SQL, the method window.openDatabase() returns a handle to a database object. You pass that method a few variables – the local and friendly names of the database, its version number, and the number of bytes that should be allocated for it – and the browser uses a built-in instance of SQLite to attempt to find or create the database.

A SQL transaction function, (transaction handle).executeSql(), can be created that specifies the SQL to execute, any variables to substitute for question marks in the SQL, and asynchronous functions to call on the success or failure of the SQL statement.

The database handle and the transaction function are married together with an odd construct, resulting in code that can look like this:

var db = openDatabase(‘etchnotes’, ’1.0′, ‘etchnotes’, 200000); db.transaction(function(t){ t.executeSql(‘SELECT rowid, name FROM notes’, [], function(tx, result) { var firstName = result.rows.item(0).name; // …do other stuff with results; }); });

First, that’s too much abstraction and punctuation for my taste; without a lot of familiarity with constructs like the above, it’s hard to parse, and harder still find syntax errors in it. But such is the world of asynchronous callbacks.

A slightly better construct is this:

var db = openDatabase(‘etchnotes’, ’1.0′, ‘etchnotes’, 200000); function onSuccess(tx, result) { …stuff… } function onFailure(tx, result) { …stuff… }

db.transaction(function(t){ t.executeSql(‘SELECT rowid, name FROM notes’, [], onSuccess, onFailure); });


That reads a lot more clean to me, but I still don’t like that the db object can’t take SQL directly. To address that, I created a closure function that has a runSql method, opens and initializes the database, and manages its own transactions and error callbacks. This is a simplified version of the closure:
function getNotesDB() { var db = openDatabase(‘etchnotes’, ’1.0′, ‘etchnotes’, 200000); var errorHandler = function(tx, e) { alert(“Database error\n” + e.message); }

this.runSql = function(sql, callback) { db.transaction(function(t){ t.executeSql(sql, [], callback, errorHandler); }); }

return this; }


And calling it is a breeze:
var dbClosure = getNotesDB(); dbClosure.runSql(“select rowid, name from notes”, onSuccess);

After a little exposure, I was able to get my head around async database work, and splitting functions into two parts: the click handler that submits a SQL query, and the callback function that parses the results. I won’t dwell on that any more here, but the source code is available below.

Rotating an HTML5 canvas

A standard design pattern on the iPhone is unlocking a delete button. Rather than each removable item having a delete button always present, and an “are you sure?” popup to deal with, some iPhone apps have an edit mode that provides each item with a minus sign on the left. Touching that makes it rotate counter-clockwise, and exposes a hidden delete button for that item. Click the delete button, and the app assumes you knew what you were doing and deletes the item with no verification. Or click the minus button again, and it rotate back to where it was, hiding the delete button. Intuitive and neat, typical of Apple’s UI design, and hard to find fault with.

Implementing a similar minus button rotation with JavaScript took a little research, but ultimately wasn’t very difficult. The two basic methods I chose between were CSS transforms using something like:

minusButton.style['-webkit-transform'] = “rotate(30deg)”;

…and HTML5 canvas context rotations, for example

var ctx = canvas.getContext(’2d’); ctx.rotate(angle); ctx.fillRect(x, y, w, h);

I chose the latter, choosing also to not use image files, but instead to draw the button objects on canvases using paths (think GeneralPath from Java2D). Since all I needed was a circle and a line for a minus button, this was pretty trivial. While rotating the canvas was easy after I figured out to use ctx.translate() to move the origin point to the center of the image, animating the rotation was a little more involved.

I may have been better off using a CSS transition, but having already headed down the path of canvases, I stuck with it, ending up with some arcane looking infrastructure around canvases and their states. Behold the Spinner Factory:

function SpinnerFactory(size) { var cr = Math.PI*2; // Circle var deg = Math.PI/180; // One degree var scale = size/100;

function getScaledContext(obj) { // Get canvas context var ctx = obj.getContext(’2d’);

if (!obj.scaled) { ctx.scale(scale, scale); ctx.translate(50, 50); obj.scaled = true; }

ctx.clearRect(-50, -50, 100, 100); return ctx; }

function drawMinus(obj) { // Get scaled canvas context, and clear previous image var ctx = getScaledContext(obj);

// Red circle ctx.fillStyle = ‘red’; ctx.beginPath(); ctx.arc(0, 0, 50, 0, cr, true); ctx.fill();

// White minus sign, rotated to obj.angle degrees ctx.save(); ctx.rotate(obj.angle * deg); ctx.fillStyle = ‘white’; ctx.fillRect(-29, -6, 58, 12); ctx.restore(); }

function spinLeft(obj) { spin(obj, -15, -90); } function spinRight(obj) { spin(obj, 15, 0); }

function spin(obj, step, goal) { // While spinning, disable object’s click handler to prevent competing spins if (obj.onclick) { obj.deferredOnclick = obj.onclick; obj.onclick = null; } if (step == 0) return; if ((step < 0 && obj.angle 0 && obj.angle >= goal)) { obj.angle = goal; if (obj.nextLeft) { obj.nextLeft = false; if (obj.unlockCbk != null) obj.unlockCbk(); obj.unlockObj.style.display = ‘block’; } else { obj.nextLeft = true; if (obj.lockCbk != null) obj.lockCbk(); obj.unlockObj.style.display = ‘none’; } if (obj.deferredOnclick) { obj.onclick = obj.deferredOnclick; obj.deferredOnclick = null; } return; } obj.angle += step; drawMinus(obj); setTimeout(function() {spin(obj, step, goal)}, 30); }

this.makeSpinner = function(className, unlockObj, lockCbk) { unlockObj.style.display = ‘none’; var cnv = document.createElement(‘canvas’); cnv.width = size; cnv.height = size; cnv.className = className; cnv.angle = 0; cnv.nextLeft = true; cnv.unlockObj = unlockObj; cnv.spinRight = function() { spinRight(cnv); } cnv.onclick = function(e) { cnv.nextLeft ? spinLeft(cnv) : spinRight(cnv); e.stopPropagation(); } if (lockCbk != null) cnv.lockCbk = lockCbk; drawMinus(cnv); return cnv; }

}


The Spinner Factory is a JavaScript closure that takes a single variable, size, which determines the diameter of the spinner buttons it creates. Individual spinners are created with the makeSpinner() method, which takes variables for the CSS class name for the spinner, the object to hide/unhide at the end of the spin, and an optional function reference to call after an object is re-locked. Typical usage would look like this:
sf = new SpinnerFactory(30); item.appendChild(sf.makeSpinner(‘minusBtn’, deleteBtn));

…where “item” is a div tag you want to add a spinner to, and “deleteBtn” is the object that gets unhidden.

After getting all that working, I created some additional methods for the Spinner Factory to use the same scale to draw plus signs and arrows, which don’t spin.

Sizing a textarea to fit its content

This was a fun one! After taking great pains to tweak CSS settings to make the note text line up with the lines on the background image, I noticed during testing that if I entered too much text, I couldn’t scroll to see it. To complicate matters, mobile Safari doesn’t appear to use scrollbars, so there wasn’t even an indication that there was more text to see.

After some online searching, I found that you can scroll textareas on the iPhone by using two-fingered swipes. I tested this, and although it worked, it was awkward, and transitions between edit mode (when the keyboard display pops up) and normal viewing mode left the display in unusual positions. I could struggle through it, but it wasn’t ideal, and certainly not good enough for my wife.

There are DOM properties for textareas that can be monitored after each character is entered. The one that seemed most important was scrollHeight. It remained constant until any text overflowed off of the screen, then the value changed to the height needed by all the text. This was all independent of font size or the width of the textarea.

I quickly hacked out an event listener that monitored for scrollHeight changes, and set the textarea height to match. Since no scrollbars ever appeared, the user would have no indication that anything interesting was happening. The main problem with this is that the box wouldn’t shrink back down if you deleted a bunch of text. The scrollHeight attribute doesn’t go below the current height of the textarea. Although not necessarily a showstopper, I didn’t like the idea that a user might think there was a lot of whitespace offscreen at the end of the note. I wanted a box that could grow and shrink with the text.

I switched instead to a method that uses a second textarea, hidden offscreen, to make measurements. First, all textareas in this app have some very specific CSS settings for positioning:

textarea { border: 0; background-image:url(‘images/paper.png’); margin:0; padding:0; padding-left: 13px; width: 307px; font-size: 17px; outline: none; font-family: Georgia; }

Next, some simple CSS tells the “tester” area to hide offscreen (and to turn off scrollbars for testing with desktop Safari).
.tester { position: absolute; top: -1000px; overflow: hidden; }

And finally the meat:
function textAreaSizer(obj) { hiddenTextArea = document.createElement(‘textarea’); hiddenTextArea.rows = 20; hiddenTextArea.className = ‘tester’; obj.parentElement.appendChild(hiddenTextArea); var last = ”;

this.size = function(e) { if (last != obj.value) { last = obj.value; hiddenTextArea.value = obj.value + “\n”; obj.style.height = hiddenTextArea.scrollHeight + ‘px’; } }

this.size(); }


This closure accepts a textarea as input, and creates a sibling textarea using the “tester” class name. Note that I’m setting the hidden textarea’s row size to 20, which means its scrollHeight will never fall below what’s needed for 20 rows of text.

The web app in its current state is available here. The source code doesn’t try to obfuscate anything, but I still have some commenting and cleanup to do.

Enjoy!

]]>
http://auterytech.com/technology/designing-a-notes-iphone-web-app/feed/ 0
Emulating iPhone rotating buttons with HTML5 canvases http://auterytech.com/technology/emulating-iphone-rotating-buttons-with-html5-canvases/ http://auterytech.com/technology/emulating-iphone-rotating-buttons-with-html5-canvases/#comments Wed, 13 Apr 2011 13:31:30 +0000 Curtis http://auterytech.com/?p=116 This is a quick proof of concept tested with Chrome and Safari (which might also work in Firefox… testers?). I’m using timeouts and HTML5 canvas operations to spin buttons, and some DOM tomfoolery to add and remove rows to the document’s body. The end result is an approximation of some iPhone settings screens that make you unlock delete buttons before you can remove items.

Enjoy!

]]>
http://auterytech.com/technology/emulating-iphone-rotating-buttons-with-html5-canvases/feed/ 0
HTML5 Web Databases in Chrome, round 1 http://auterytech.com/technology/html5-web-databases-in-chrome-round-1/ http://auterytech.com/technology/html5-web-databases-in-chrome-round-1/#comments Fri, 08 Apr 2011 13:15:06 +0000 Curtis http://auterytech.com/?p=113 New fact of life: To keep up with modern web programming, you need to get your head around asynchronous callbacks. To illustrate, here is a quick script I wrote to experiment with Chrome’s JavaScript calls into it’s SQLite implementation:
var output = null;

function onSuccess(transaction, result) { output = result.rows.item(0).sql; console.log(‘onSuccess completed’); }

function getDB(dbName) { var fm = 5*1024*1024; // Five Megs var db = openDatabase(dbName, ’1.0′, dbName, fm); console.log(‘Database opened’);

this.runSql = function(sql) { db.readTransaction(function(t){ t.executeSql(sql, [], onSuccess) }); console.log(‘runSql completed’); }

return this; }

var joe = getDB(‘db’); joe.runSql(“select * from sqlite_master”); console.log(‘output: ‘ + output); console.log(‘End of script’);


Until it dawned on me that the SQL execution was asynchronous, I spent about 30 minutes chasing my tail trying to find a variable scope problem. In the console debugger, everything seemed to be set right when I manually entered commands one at a time, but the script didn’t seem to be setting them when it ran. Didn’t seem to be, but it just wasn’t happening when I thought.

The above script was a re-write of what I was doing initially, with loggers at each step to show when the “output” variable gets set. Unsurprisingly, it happens on its own good time, not when the “procedural” part of the script reaches the end.

Here’s a screenshot of the console output:

The value of “output” is still null when the script reaches its last line. Manually checking the value after onSuccess completes shows that it does get set correctly, just later. So, there you have it. Writing that iPhone app or new web game? Start thinking async.

]]>
http://auterytech.com/technology/html5-web-databases-in-chrome-round-1/feed/ 0
Rage against the Pitocin-Cesarean Complex http://auterytech.com/technology/rage-against-the-pitocin-cesarean-complex/ http://auterytech.com/technology/rage-against-the-pitocin-cesarean-complex/#comments Wed, 06 Apr 2011 11:13:37 +0000 Curtis http://auterytech.com/?p=110 (and how I wrote my first iPhone web app)

“So I got me a pen and a paper and I made up my own little sign. I said thank you Lord for thinking about me, I’m alive and doing fine.” – Five Man Electrical Band

Before rambling about my latest tinkerings, I have a small announcement: My wife and I decided to have a baby. She asked me not to make a big deal about it online, not post a broadcast “guess what?!?!” message on Facebook, and in the spirit of that I won’t make flowery pronouncements here about the joys of fatherhood, or starting over at 40, or any of the hundreds of other things I would otherwise go on at length about. But there it is: I’m in love with a woman I think the world of, and at the beginning of January, with our third wedding anniversary near us and neither of us having one foot out the door or buyer’s remorse, we decided to stop using birth control and try to have a baby.

By the end of the month, Liberty was pregnant. When she was pregnant with Scout, Liberty wanted as natural and non-medicalized birth as possible, preferably a home birth with a midwife to assist. In Ohio, home birth and birthing centers are in a grey area legally. Instead of outright declaring that birth is only allowed in hospitals, the cost of liability insurance for midwifery prices many centers out of existence, and not all medical insurance plans cover them. Liberty unfortunately could not find a local center who took her insurance, but found one in Cincinnati that did. During a 37th week appointment with them, Liberty’s blood pressure was elevated, so her midwife recommended going to a hospital for a urine test. At the hospital, the doctors were quick to diagnose preeclampsia, and demanded that she stay and be induced immediately. Needless to say, this wasn’t the birth plan she had in mind.

The hospital tried to enforce the standard medical interventions designed to get the doctor to the golf course by tee time: Pitocin, epidural, Cesarean. Her midwife (bless her heart) went to bat for Liberty, telling them to back the hell off, insisting on a private room with a bathtub, that she be allowed to get up and move around, have access to food to keep her strength up, and helped Liberty maintain as much control over the process as possible. Scout’s dad was caught between dueling ideologies and unsure of whose side to take. As much as I like him, I don’t think he had the life experience needed to cope with a hospital trying to force its agenda on you through scare tactics. He tended to side with the doctors more often than not, but nothing in his experience told him to have anything but faith in what a doctor recommended. Despite that, Liberty’s force of will and the strong advocacy of the midwife kept the unnecessary interventions to a minimum.

To be fair to Scout’s dad, when Stacey was born, I was of a similar mindset, and Stacey’s mom and I thought the hospital was just where you were supposed to go for labor. It wasn’t until years later that it dawned on me that doctors are normal people with their own agenda, and a healthy skepticism was needed. I also knew that people still gave birth before there were hospitals, and people were trained to assist the mother before the field of medical obstetrics was invented. It wasn’t until recently that it all clicked together for me: In the 60 years that hospital birth in developed countries has been the norm, we’ve progressed only marginally from twilight sleep and leather straps. The medical community continues to lack empathy and support for mothers in labor, and until this changes, hospital births should be avoided where possible. The field of obstetrics has done too much to disempower and dehumanize women in what is supposed to be an important, memorable, beautiful event. Women should be revered while they’re creating life; doing otherwise is God-damned unconscionable.

Back to the story: Because Liberty was induced before her body was ready, her body balked, and she endured a painful 3 day labor. Despite the long labor, the constant onslaught of doctors saying, as if scolding a small child for not cleaning her room, “you know, it’s going to be time for a C-section if you don’t progress soon,” and despite the physical pain of prolonged use of Pitocin, Scout was born vaginally, without need of scalpel or forceps, and from what I’ve divined in the 3 and a half years I’ve known her, is both healthy and intelligent. Everything ended well with Scout’s birth, but needless to say that isn’t the type of birth we want this time.

We found a good midwife center here in Columbus, CHOICE, to see during the pregnancy, and we intend to have the baby on The Farm, a commune that managed to survive after the 70s when the popular hippie movement died down, and home to the mother of modern midwifery, Ina May Gaskin. Ina May authored “Spiritual Midwifery”, which I can’t recommend highly enough to anyone planning on having a baby, and was interviewed in Ricki Lake’s movie, “The Business of Being Born”, a must-see for the expecting couple. She is also the namesake of the Gaskin Maneuver, a Guatemalan technique that she popularized that helps with shoulder dystocia – where an infant’s shoulder gets stuck when they crown. (When we went down to visit The Farm recently, Liberty saw Ina May driving by, and was all excited. It was cute.)

After being frank about the previous birth to our CHOICE midwife, she said some things that we were happy to hear, namely that preeclampsia is more common in first-time mothers, and a high protein, high calcium diet is good insurance against a repeat occurrence. I found some articles and a study that supported both of those assertions, which was reassuring to me. Before meeting Liberty, I had no experience with what midwives were supposed to be (as is true of most Americans). Based on my experiences so far, I’m sold on them. They are competent professionals, more knowledgeable about childbirth than the surgeons who call themselves obstetricians. Midwives believe, like I do, that childbirth doesn’t belong in a hospital with medical intervention, or with the mother on her back, scared, and taking orders.

So coupled with the protein/calcium diet advice, Liberty wanted to monitor her fluid and fiber intake as well, so she set out to look for an iPhone app that could help her track all that. Sadly there weren’t any that had all four, or that were customizable. And this is where I come in, using my geek mojo to try to attack the problem.

About 6 months back, I signed up for Apple’s developer program, giving them my gmail address, but never heard back. My main home workstations run XP and Linux, so to use XCode to write iPhone apps, I would need to confiscate Liberty’s Macbook, giving me two reasons not to code a native app right off the bat. After some digging, I found that Safari supports the offline application cache and web storage specs, and iOS lets you bookmark a website to the “desktop” with a custom icon and splash screen, and provides controls to put Safari in a sort of kiosk mode. In other words, you can click on a button on the main screen, have it open a web app that can save data locally, and the result is basically indistinguishable from a native app. With this approach, I would be able to code for the iPhone with Windows or Linux, and not pay Apple for the right to do so, so that’s the road I took.

Getting started

At the beginning of this effort, I decided to code the UI from scratch using un-obfuscated JavaScript to build tables, “buttons” ( tags, actually), and onclick handlers. In an attempt to move things along quickly, I didn’t fully separate out functionality using MVP, MVC, or other mainstream architectural patterns, but the UI and object functions are fairly split apart. To illustrate, here are functions to build a generic button (UI), and another to remove a food item from a day’s eating history (object model).
function makeButton(buttonText, callBack) { var button = document.createElement(‘span’); button.className = ‘button’; button.innerText = buttonText; button.onclick = callBack; return button; }

function removeItem(index, dayString) { if (lentil[dayString] == null || lentil[dayString][index] == null) return; lentil[dayString].splice(index, 1); saveData(); }
Whereas, here is sort of a kluge to return a table of a day’s eating, that can’t decide if it’s the user interface, the object model, or a controller:
function getDayTable(dayString) { var outTable = initTable(); if (lentil[dayString] == null) lentil[dayString] = []; var totals = []; for (var n = 0; n < trackElements.length; n++) totals.push(0);

var counts = {}; for (var n in lentil[dayString]) { var name = items[lentil[dayString][n]].name; counts[name] = (counts[name] == null) ? 1 : counts[name] + 1; }

var seen = {}; for (var n in lentil[dayString]) { var itemNum = lentil[dayString][n]; var item = items[itemNum]; if (seen[item.name] == null) { roundCollection(item.elements); var row = collectionToCountsRow(item.name, item.elements, counts[item.name]); var callback = new callbackRef(‘verifyRemove’, itemNum, n, dayString); row.onclick = callback.invoke; outTable.appendChild(row); incrementTotals(itemNum, totals, counts[item.name]); seen[item.name] = true; } } outTable.appendChild(totalsRow(totals)); return outTable; }
There’s still quite a bit of overlap, and as the code progresses I’ll get better role separation. Round one, though, quick and dirty, weighing in at only 400 lines of JavaScript and giving my wife something usable while she’s still pregnant, a more important concern than glass-tower programming theory.

Lentil

You may have noticed references above to a hash named “lentil”. When Liberty was early in her first trimester, she estimated the baby’s size to be about that of a lentil. When she would have baby-related symptoms such as an upset stomach, she would say things like “Lentil is really doing a number on me today.” So after she balked at my naming the first build of my program “Preggers”, she recommended the name Lentil instead, which was more agreeable to both of us.

We shamelessly stole an image off the net for the home screen button:

…but we have a wooden bowl and a bag of lentils at home with which to make a similar image of our own so we won’t be dirty cheaters. Or get sued.

Making a web page look like an app

There are a pair of HTML meta tags to enable fullscreen mode and darken the status bar (the top of the iPhone screen that shows your battery level, time, etc.) They are as follows:

Another meta tag can change viewport settings, like disabling zooming, and setting the viewport’s width and initial zoom levels:

The last iPhone-specific HTML-based hints I used are for setting the home screen icon and the splash-screen. The addresses are relative to the location of the page they are referenced from.

My splashscreen is a little cartoony right now; it’s definitely something that will be professionalled-up when this comes out of beta. Here’s the current image:

With those HTML tags on a page, you can visit it from Safari on the iPhone, bookmark it to your homescreen, and from then on it will open like a native app. More information about tags that will influence Safari on the iPhone are available here (as well as scores of other pages).

Offline Web applications

The W3C’s HTML5 working draft has a nice summary of how offline caching of web applications works, available here. Briefly, the main HTML page references a manifest file. The manifest file lists what files to cache, and the browser caches them. The next time the page is opened, a single query to the web server is made to see if the manifest file has been altered. If it hasn’t (or if the browser is offline), all the web resources are loaded from the cache instead of the network.

In other words, if I open a cached web application while the iPhone is in airplane mode, it will still open. Additionally, if the manifest file hasn’t been updated, only a single HTTP GET is issued for the manifest file before the application opens, speeding up opening quite a bit.

The caveat is that the manifest file must be served with a content type of “text/cache-manifest” for browsers to turn on caching. In the near future, I imagine by default most web servers will serve .manifest files with this content type, but currently some twiddling is needed – at least, that’s true on my current web server of choice, Google’s App Engine.

I use Eclipse/Java for deploying App Engine projects, and changing the content type served for a given file extension is accomplished with a few lines in the project’s web.xml file:

manifest text/cache-manifest


To verify this was working as expected, I deployed a project, telnetted to the web server on port 80, and did a manual GET for the file:
GET /lentil.manifest HTTP/1.1 Host: lentiltracker.appspot.com

HTTP/1.1 200 OK ETag: “kRe4nQ” Date: Sat, 02 Apr 2011 08:47:45 GMT Expires: Sat, 02 Apr 2011 08:57:45 GMT Cache-Control: public, max-age=600 Content-Type: text/cache-manifest Server: Google Frontend Transfer-Encoding: chunked

a3 CACHE MANIFEST

# Beta 9.

# js files json2.js lentil.js …rest of file 0
As you can see, the content type came through correctly. Rather than the browser downloading the entire file each time and comparing it with a cached copy, the HTTP entity tag reference (ETag) is used to determine if the web server thinks the file has changed. This is accomplished by including an “If-None-Match” field in the GET request, specifying the last ETag value. If the browser gets back an HTTP 304 response, it knows there’s no need to re-download the entire file. Here’s how that looks over telnet:
GET /lentil.manifest HTTP/1.1 Host: lentiltracker.appspot.com If-None-Match: “kRe4nQ”

HTTP/1.1 304 Not Modified ETag: “kRe4nQ” Date: Sat, 02 Apr 2011 08:53:31 GMT Server: Google Frontend
My project has a number of resource files with it: The main HTML page, a CSS file, JavaScript file, the icon and splashscreen images, and of course the manifest file. My App Engine resource usage will look something like this for requests that come after all of those files have been cached:

Ideally, just the manifest file, and most of those should be HTTP 304s. What I’ve found in practice, though, is that App Engine gives the occasional HTTP 200 for a file that hasn’t changed, which leads me to believe they don’t always store the ETag field the same way on all mirrored servers, or possibly each App instance regenerates new values. Consequently, Safari busies itself with comparing the file contents, and sees that all is well so doesn’t re-download the CSS, JavaScript or main page files. In some of these cases, the iPhone’s desktop subsystem will re-download the icon and splashscreen files, sending a user agent containing CFNetwork and Darwin, contrasted with Safari’s AppleWebKit. A typical set of log entries looks like this:

Even considering that the 304s aren’t consistent, this is still a lot less traffic than having each file be downloaded fresh each time the app runs. The web traffic benefit is good reason to use application caching of static content (or code) where possible, even if you aren’t trying to make a web page look like a native iPhone app.

Since Safari on the iPhone and Google Chrome both use the WebKit engine, I did most of my testing for this project with Chrome on Windows for the basic reason that its debugging tools are fantastic. For example, from the chrome://net-internals/ URL, you can capture and view raw network bytes, as well as see when Chrome is grabbing a resource from the application cache instead of the network. For example, here is how Chrome responded to the order to load my app’s CSS file:
Start Time: Sat Apr 02 2011 04:33:06 GMT-0400 (Eastern Daylight Time)

t=1301733186906 [st= 0] +REQUEST_ALIVE [dt=69] t=1301733186906 [st= 0] +URL_REQUEST_START_JOB [dt=29] –> load_flags = 65536 (VERIFY_EV_CERT) –> method = “GET” –> priority = 1 –> url = “http://lentiltracker.appspot.com/lentil.css” t=1301733186916 [st=10] APPCACHE_DELIVERING_CACHED_RESPONSE t=1301733186935 [st=29] -URL_REQUEST_START_JOB t=1301733186975 [st=69] -REQUEST_ALIVE
In fact, the net-internals page is where I first noticed the business with ETags and HTTP 304 responses:

If you want to see all the stored manifest files, or delete individual application caches, you can accomplish that from chrome://appcache-internals/, as shown in this screenshot:

Storing data locally

In the past, having a browser store data locally could only be accomplished with HTTP cookies, the much-maligned bastard stepchild of the Internet. There are two major drawbacks to using them, namely size limits (4k per cookie, 20 cookies per domain), and the fact that they are sent by the client with every request. The original Netscape recommendation for cookies indicated the idea was for them to manage “state”. The server would be notified what state the browser thought it was in, and the server could make decisions based on that.

A newer recommendation for web storage is being hashed out for the HTML5 standard, and Chrome and Safari (and I assume other browsers) already have it implemented, complete with a handy JavaScript accessor: localStorage.[name] = [string] for storing simple strings. There is also a web database recommendation that allows local DB objects that can be queried with SQL, but I chose the simple localStorage method so I could get something functional quickly, and used JSON to turn my JavaScript objects into strings.

I ended up with a pair of simple functions that manage turning an eating history object and a food types object into localStorage strings, and back again:
var lentil = {}; // History hash var items = []; // Array of food types

function init() { if (localStorage == null) { alert(“Local storage features won’t work in this browser.”); } else { if (localStorage.lentilHistory) lentil = JSON.parse(localStorage.lentilHistory); if (localStorage.lentilItems) items = JSON.parse(localStorage.lentilItems); } }

function saveData() { localStorage.lentilHistory = JSON.stringify(lentil); localStorage.lentilItems = JSON.stringify(items); }
Chrome’s developer tools can be used to see the raw contents of the local storage DOM tree, as shown here:

Now that I have my feet under me with web apps, I can either databasify this, or at least split foods and days into unique objects so that the entire history can’t be blown away with one bad write. How to manage doing that and save all the data Liberty already has entered will take some hemming and hawing, but shouldn’t be too much of a puzzler.

The rest is all JavaScript to handle events and turn arrays of food items into tables. Here are a few screenshots of how the pages look now, but it needs a little usability tweaking, and a lot of prettying up.

More tweaks of the app will come, and in all likelihood I’ll leave it up on the App Engine page as a free tool. The end result that I care about, though, is Liberty’s use of it. She is tracking her calcium and protein, and can see at a glance if she’s on the road to avoiding a second hospital birth. As a husband who cares about this sort of thing, I’m doing everything in my power to support a natural birth. I’m just glad in this case there is enough overlap between what I’m good at and what Liberty needs so that I can contribute.

]]>
http://auterytech.com/technology/rage-against-the-pitocin-cesarean-complex/feed/ 0
Success through embracing mistakes http://auterytech.com/technology/success-through-embracing-mistakes/ http://auterytech.com/technology/success-through-embracing-mistakes/#comments Sat, 05 Mar 2011 23:57:44 +0000 Curtis http://auterytech.com/?p=107 “The dogmas of the quiet past, are inadequate to the stormy present. The occasion is piled high with difficulty, and we must rise — with the occasion. As our case is new, so we must think anew, and act anew. We must disenthrall ourselves, and then we shall save our country.” – Abraham Lincoln

“I hate math, dad” my daughter said to me a few days ago. It wasn’t really a shock to hear it, rather it was the final nail in the coffin, which I’d seen coming for the last few years. The middle school years, sitting in rows, staring at the backs of her peers’ heads, listening to lectures, seeing story problems which were so fanciful as to be ridiculous. So Bill’s dad’s age is 5 less than 3 times Bill’s age, and the sum of their ages is 49? Really? Does that sort of thing come up in life a lot?

As a toddler, we played with shapes and numbers for fun. I showed her how fractions work at 4 years old, by drawing on my whiteboard at the office. I drew a circle and drew lines through it, then shaded parts in and wrote down the fraction it represented. With some experimentation, she saw how shading two quarters of a circle took up the same space as one half. She “got it”, and was thrilled to figure out fractions. Innoculated against the idea that math is hard, she spent all of her elementary school years scoring off the charts in math, and was offered a spot in an advanced math/science program in the 4th and 5th grades, going to a different school once a week to work solely on math and science topics.

And then there was middle school. Now in high school, with the middle school craziness coupled with years of peer pressure to reject appearing smart in order to be more popular, her standardized test scores slipped from “phenomenal” to merely “advanced”. The coup de grâce happened this year, when she told me flatly how she feels about math. Another creative spirit successfully crushed by an education system designed in the 19th century to meet the needs of industrialism.

Something similar happened to me, back in the day. One of the reasons that I didn’t go to college is that traditional education doesn’t agree with me. In school I learned many strategies for test-taking that did not involve learning the subject matter of the class. I felt stifled by the needless repetition of lessons. I dislike the overall layout of textbooks, and the transparent committee thinking that goes into them.

What I disliked the most, though, was the idea that mistakes were bad, and not a tool for learning. A test would have, say, 20 questions on a chapter’s topic, and you would have 20 chances to make a mistake that would cost you points. In classes where I “got it” to the point of being bored and wanting to move on, the repetition would get me. By question 5 I’d be hurrying through the uninteresting test and making speed-based errors rather than mistakes based on not knowing the material. In classes where I didn’t “get it”, the opposite behavior of going slower and using test-taking strategies artificially inflated my test scores, and made it seem that I knew material that I didn’t.

Somehow I managed to come out of school with a love of numbers, and of science, history, and books, but all of that was at risk of being crushed by traditional schooling. For me it was a close shave with rejecting all academics, as I was more focused on passing tests and getting the hell away from school than I was in embracing what was being taught. It was a lot of effort to find the tiny nuggets of “interesting” buried in the rubble of textbooks and lesson plans, but I eventually found them. My high school helped when I was on the brink of throwing it all away. I went to the Linworth Alternative Program, whose teachers were much easier to cope with. The AP was a much better approach to education than I had seen in the seven schools I had been to previously, but even they were subjected to the tyranny of test = grade.

The running theme that school threw at both my daughter and myself is this: Pass the test. The more mistakes you make, the worse you “do” in the class, regardless of what skills you learn. Measuring knowledge or understanding is not happening, only test-taking ability is being measured. To prepare for a test, one crams, regurgitates, and forgets. Next year, the teacher has to re-explain the basics supposedly learned the previous year before starting any new material.

Outside of schools, making mistakes is a tool that increases your understanding of the world around you. When learning a new skill, you will be very error-prone at first as your brain and body try to cope with something they have never done before. When you first learn to play the piano, you can’t make it through a piece error-free to save your life. Eventually the smaller skills that make a musician get mastered: sight-reading music, hand separation, hearing the piece in your head, finger strength and speed, passion.

Any junior level software developer tasked with solving a new problem will tell you this story: “I took the problem statement and started coding for it. Once I figured out what I was doing wrong, I better understood the nature of the problem. Then I started over and wrote a better program.” A more advanced developer will tell you the real value of tests is in failing them. In fact, the Test Driven Development model is exactly that: Write a test for the requirement before you write any code, a test that is guaranteed to fail out of the gate. Run the test and watch it fail. (If it doesn’t fail, your test is flawed). Code for the new test, then run all the tests again, until nothing fails. (There’s more to it than that outside the scope of this screed, and if you’re interested you can get more info here.)

In my job, tests are for tuning functionality, and if you don’t fail a bunch of tests in the process of writing your program, you’re either a damned genius or you’re writing something simplistic and boring. In school, tests immediately serve to bring down your grade in the class, so the students must concentrate not on learning skills, but on passing tests. Will this be on the test? What’s the answer the teacher wants?

This is all ass-backwards on its face, and judging by my daughter Stacey’s experiences 20 years after my own, schools are still being run the same way. There is advocacy for change, by the likes of Dan Meyer and Ken Robinson, but change is slow in coming.

My solution? Grade all classes the way arts classes are graded, with the three Ps: Participation, passion, and projects. In drama class, a play can demonstrate the skills of memorization, timing, getting into character. In drawing classes, projects can demonstrate understanding of perspective, observation, different mediums.

In math classes, is it important that students can calculate by rote dozens of times in a row? No, unless they are yearning for assembly line work. Instead, how about projects demonstrating the desired skills? Look for a real life problem that can be measured and have math principles applied to it. Mom and Dad each have cars with different MPGs, and they work different distances from home. How can they save gas? Dad’s credit cards are maxed out, and he has $40 extra dollars a month dedicated to paying them off. Each card has a different balance and interest rate. In what order should he apply the $40 to pay them off the quickest?

Math class would be the easiest to reform, since I think everyone would agree it’s the most broken. Other classes would be harder (by me, anyway) to modernize, but I believe similar project-based approaches are possible for them as well. I also believe schools should no longer be aligned with the needs of the business world. They should export thinkers, not workers. Businesses can take care of their own needs, and will find other ways to make drones when they no longer have the explicit consent of public education.

]]>
http://auterytech.com/technology/success-through-embracing-mistakes/feed/ 0
4,046 http://auterytech.com/technology/4046/ http://auterytech.com/technology/4046/#comments Fri, 21 Jan 2011 13:31:43 +0000 Curtis http://auterytech.com/?p=103 I’ve disabled this blog’s Google Analytics feed. The feed assures me that there were 4,046 visits here over the past year, most of which occurred after a swell of visits in August ’10 when I started posting about various coding experiments I was working on. From that time forward, there were over 100 visits per week to the site, most of which turned out not to be loyal fans, but rather one time visits referred here by search engines.

It turns out that Google was ranking me fairly high on some search terms, and when I noticed the increased traffic, I tried to leverage that a couple different ways. First, I succumbed briefly to temptation and put Adwords advertisements on the site for a couple weeks. Now, 4,046 is a nice number for a lone blogger who started out just writing about his kid, but it’s not enough to pay the bills on click-throughs. So I took them down when my Adwords balance slowly crawled to the 7 cent mark.

Next, I put up a couple different attempts to solicit feedback from visitors, which was about as popular with visitors as the ads. I got a total of 4 responses, one of which was just a smartass comment from a friend I work with, another was not honest feedback, but a solicitation to help grow a content farm.

So I’ve taken down the “feedback” link, and turned off analytics, as it never really bought me anything other than some nebulous insight into what people search for, and how hard it is to get the attention of people looking for homework help, job interview answers, or instructions on gaming Facebook’s PuzzleMaster coding puzzles… not that I look down on any of that. If you see how I solved a problem, maybe something will click and you’ll be better off. If your goal is to cheat an assignment or test, well, good luck with that, but I doubt I’m improving anyone’s chances of passing a class they weren’t meant to. For now, “The Sky” is back to being just a plain old blog, where I write about whatever suits me, and visitors are welcome, but not the focus.

Before turning off analytics, my most popular pages were:

“Quest for Interesting” is a whimsical post that became popular only because it contains an image I created in MS Paint in about 10 minutes, which shows how to cross-multiply to compare fractions. For whatever reason, that image was for a few months the number one hit on Google Images for “cross multiplication”. It currently fluctuates between 2nd and 3rd place, and still drives traffic here.

“Solving the Facebook Gattaca Puzzle” is one of my solutions to a Facebook puzzle on their “careers” page. They use interesting puzzles involving optimization and datasets as a filtering tool for potential coders, which I think is a good idea. The number of people attempting to use search engines to game the system is phenomenal. To illustrate, here are the top 5 search terms used to find any page on my site:

“Oldschool Mainframe/COBOL geek-out” is me going on at length about mainframe topics like RPF scripts in ROSCOE, compiling and linking from JCL, why EBCDIC is even a thing, and how no one “plays” on mainframes.

The last, “Creating an Archimedean Spiral generator in Java using the NetBeans IDE” is one of several quick hacks of mine, this one attempting to be a basic introduction to using NetBeans, the basics of painting and the Java2D library, and polar math vs. Euclidean. Because it’s fun. And I’m a geek who despairs not taking Calculus in high school, and not paying more attention in the math classes I did take.

So, I have dismissed my Joab from his census-taking duties, before being punished by the almighty for my pride. Now it’s back to just writing about stuff, without the marketing angle.

]]>
http://auterytech.com/technology/4046/feed/ 0
OSU Math Placement B-test, 1 of ??? http://auterytech.com/general/su-math-placement-b-test-1-of/ http://auterytech.com/general/su-math-placement-b-test-1-of/#comments Sun, 16 Jan 2011 10:42:45 +0000 Curtis http://auterytech.com/?p=97 A little experiment with Google Docs, Wikipedia’s TeX engine for rendering math equations, and trying to help my wife pass her college math requirements.

Where’s that Check 21 2 of 2 I promised for yesterday? Still coming, I just need to get my inspiration back after a few, um, let’s call them “challenging” weeks at my day job. Anyway, enjoy!

]]>
http://auterytech.com/general/su-math-placement-b-test-1-of/feed/ 0
Adventures in Barretting http://auterytech.com/technology/adventures-in-barretting/ http://auterytech.com/technology/adventures-in-barretting/#comments Sun, 09 Jan 2011 17:13:33 +0000 Curtis http://auterytech.com/?p=93 In an attempt to be funny, I made up the term “Barretting” to describe the act of driving to Tennessee to see my friend Chris Barrett. My wife, Liberty, and I had some fun throughout the week using the term whenever it would fit. “Well, we don’t need to stop at the store on Friday, we can get to that when we’re back from Barretting.”

Chris introduced us to the “Flying Saucer” pub last year, which has a giant wall full of taps (possibly 20 or 30), as well as friendly wait-staff, not too intolerable prices, and a 100-beer tour that gets you some sort of free party there and your name on a plate if you finish it. Liberty and I went once with Chris to the Memphis location, and have been twice to the one in Nashville while taking an impromptu “let’s see where this road takes us” vacation.

Chris used to live in Columbus like we do, but now works in western North Carolina. Nashville is a good midpoint between us, so we planned to meet up this weekend and catch up for a few hours, and go our separate ways. We did so, adding beers 10 through 12 to our tours (16 to 18 for Chris) and a good time was had by all. But something happened on the way down that trumps that story.

On Saturday morning we started our drive down, and by mid-afternoon, with the sun starting to dip low in preparation for nightfall, we found ourselves on I-65, a little South of Louisville Kentucky. There was this idyllic moment as we drove down a stretch of road with rock structures on either side. There were a couple clouds visible, contrasting the darkening blue sky, a flock of birds crossed the road in front of us, first horizontal, then swirling to a vertical diamond-shape as the crossed the opposite rock facing. Liberty was sitting beside me, engrossed in knitting a new creation, and a local radio station was playing the Zeppelin song “In My Time of Dying”.

And there it was. Our life and responsibilities back home – to kids, house, school, and job – was a fiction that neither of us believed in. The truth was the road, the song, the shawl, the birds, and the setting sun. And the real truth: each other.

I felt a sense of overwhelming bliss just driving around with the woman I love. No matter what happened after we decided our fiction back home was real again, Liberty would be with me still, and hence, the world would be less bleak.

In 71 days we will have been married for three years. When we look at each other, we both know we found the right person. The house may collapse, my job may give me the boot, the mounting school debt may be a beast difficult to slay, but that will all be illusion. The real world will continue to be the birds serenading us before sunset, as we head down the road together.

]]>
http://auterytech.com/technology/adventures-in-barretting/feed/ 0
Check 21 Java app – 1 of 2 http://auterytech.com/technology/check-21-java-app-1-of-2/ http://auterytech.com/technology/check-21-java-app-1-of-2/#comments Tue, 28 Dec 2010 13:14:38 +0000 Curtis http://auterytech.com/?p=90 Extracting data and images from Image Cash Letter files

Background

There are many terms relating to Image Cash Letter files: Check 21, DSTU X9.37, X9.100-187, and check truncation, to name a few.

“Check 21″ refers to an act passed in 2003. From Wikipedia’s “Check 21 Act” article:

The Check Clearing for the 21st Century Act (or Check 21 Act) is a United States federal law, Pub.L. 108-100 … The law allows the recipient of the original paper check to create a digital version of the original check—called a “substitute check,” thereby eliminating the need for further handling of the physical document…

…Recently, Check 21 software providers have developed a “Virtual Check 21″ system which allows online and offline merchants to create and submit demand draft documents to the bank of deposit. This process which combines remotely created checks (RCC) and Check 21 X9.37 files enables merchants to benefit from direct merchant-to-bank relationships, lower NSFs, and lower chargebacks.

The last paragraph bears some resemblance to reality, but the article’s footnote refers to just one of many companies capitalizing on the Check 21 act, and the lofty goal of direct merchant-to-bank relationships is not met due to the very fact of requiring a middle-man’s software to manage the relationship.

The text of the act itself is available at this page on gpo.gov, the opening paragraph being:

To facilitate check truncation by authorizing substitute checks, to foster innovation in the check collection system without mandating receipt of checks in electronic form, and to improve the overall efficiency of the Nation’s payments system, and for other purposes.

My interpretation is that the act clearly states that no one is forcing banks to receive checks electronically, but new law is being written allowing banks to use electronic copies (or photocopies, or re-prints) of checks in place of the originals at their discretion. Later text in the act provides for a future study to see how banks are adopting it, whether their profits are affected, whether it’s useful, etc.

When the act was passed, ANSI published DSTU X9.37 “for Electronic Exchange of Check and Image Data”. “DSTU” stands for “Draft Standard for Trial Use”. “X9″ is the banking arm of ANSI (where “X12″ is the EDI arm). So basically in 2003 a trial standard was published for exchanging check images, with an evaluation of the standard to come later. That came in 2008, when ANSI published X9.100-180 for Canadian use, and X9.100-187 for use in the states, with minor alterations from the original standard.

Background on the file format

An image cash letter is designed for mainframe use. It is record-based, with a 4-byte word at the beginning of each record indicating its length. It contains mixed text and image data, with the text part being in the EBCDIC character set, and the image part being TIFF data. If you are a small business that happens to have a mainframe laying around, this is good news. For merchants living in the real world, not so much.

The file format poses a number of challenges for Windows, Mac, and Unix programmers. Unless you are versed in the mainframe/AS400 world, variable length records with length bytes and the EBCDIC character set are probably unfamiliar to you. Once you figure out EBCDIC translation, you then need to tackle converting the record-length header to a number, which, unless you are in the habit of programming at a “bits on the wire” level (checksums, for instance), you’ve probably never needed to do before.

Once you tackle record lengths and EBCDIC and start playing with the image data, you’ll quickly realize a simple fact that you never noticed before: nothing supports TIFF images. Your web browser doesn’t display them, and your programming language doesn’t have support for them in its native image library. You either need to invest more research time into finding a TIFF library and learning its API, or just dump image data to individual files each time you come across it, leaving your end-user with a large collection of files to open by hand. This would lead to the following uncomfortable conversation:

User: “So I’ll have a bunch of image files representing checks that I have to click on one at a time to view?”

You: “Right.”

User: “And there’s no way to correlate those back to the text data?”

You: “Right.”

User: “Well, do I at least get the front and back of checks in the same image?”

You: “Um, well… no.”

Because of these mild but unfamiliar technical hurdles, the third-party app that supports Check21 files looks more attractive to a CTO than watching the internal IT team bumble around trying to build an app. In fact, an industry has popped up around these files, costing companies money in vendor lock-in that they tried to save by converting from ACH files and mailing checks, all in the name of getting money to the bank a little faster. Not a good situation.

Impetus for this entry

In July of ’09, I published a perl script that does just what I described above. It takes a Check 21 file and prints its ASCII-translated text to STDOUT, and saves the image data into individual TIFF files. It was intended only as a quick experiment with handling the Check21 file format, but its Google rank grew until it was on the front page for searches like “x9.37 parse”. This is odd because the script is a quick hack (in fact the first comment on it complains about its lack of exception handling) that I threw together in about 30 minutes, so it’s possible that there aren’t many “open” apps available that handle this file type.

Recently someone asked me for help with a problem using the extractor. Between my script’s unexpected high search rank, and the fact that questions about my tech posts are pretty rare, one can reasonably assume that Check21 files are currently a big deal in “the wild”, and knowledge about them is pretty slim. Assuming that to be the case, this post is the first in a two-part Check21 java series exploring the file format, and concerns with parsing, displaying, and creating Check21 files. This first post shows how to write an extractor similar to my perl script, but in Java.

Bits on the wire

Let’s start by opening a file in a hex editor to see what it looks like. The file I’m using is available on this page on x937.com, a private site run by software developer Norman Graham.

To the untrained eye, this looks like so much garbage. To those more mainframe-savvy, the top section peppered with at-signs and letters with diacritics means you’re probably looking at EBCDIC rendered with an ASCII viewer. To the image geeks, the II* on line 210h is indicative of the beginning of TIFF data.

The first four bytes combine as one hex number: 0×00000050, or decimal 80, meaning the next 80 bytes constitute a record. The next few bytes (F0, F1, F0, F3) are EBCDIC representations of numeric digits, as EBCDIC places 0 through 9 in hex F0 through hex F9 (where ASCII has them at hex 30 through 39), so the first four characters of the record should be “0103″.

Tracing through the JRE

With those basic assumptions, let’s throw some code at it and see what we get. The record length header can be turned into a number with very little effort. The Java class javax.imageio.stream.ImageInputStreamImpl provides the following method:

public int readInt() throws IOException { if (read(byteBuf, 0, 4) < 0) { throw new EOFException(); }

if (byteOrder == ByteOrder.BIG_ENDIAN) { return (((byteBuf[0] & 0xff) << 24) | ((byteBuf[1] & 0xff) << 16) | ((byteBuf[2] & 0xff) << 8) | ((byteBuf[3] & 0xff) << 0)); } else { return (((byteBuf[3] & 0xff) << 24) | ((byteBuf[2] & 0xff) << 16) | ((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0)); } }


The variable “byteBuf” is a byte array. Four bytes are read into it, and using bitwise math, a single 32-bit integer is returned. This is a less processor heavy version of looping through the byte array, multiplying by 256, and adding the next byte.

So if I open the file as an ImageInputStream, I can use readInt() to grab record lengths and move forward in the file. Next, the EBCDIC to ASCII translation. The java.lang.String class has a constructor that accepts a string input declaring the character set:

public String(byte bytes[], String charsetName)

This is ultimately a convenience method for java.lang.StringCoding.decode(), which looks up a CharSet class and returns a new String by converting each byte using a translation table. For EBCDIC translation, specify “Cp1047″ as the character set name, and Java will find the class sun.nio.cs.ext.IBM1047, which contains this table:

private final static String byteToCharTable =

“\u00D8\u0061\u0062\u0063\u0064\u0065\u0066\u0067″ + // 0×80 – 0×87 “\u0068\u0069\u00AB\u00BB\u00F0\u00FD\u00FE\u00B1″ + // 0×88 – 0x8F “\u00B0\u006A\u006B\u006C\u006D\u006E\u006F\u0070″ + // 0×90 – 0×97 “\u0071\u0072\u00AA\u00BA\u00E6\u00B8\u00C6\u00A4″ + // 0×98 – 0x9F “\u00B5\u007E\u0073\u0074\u0075\u0076\u0077\u0078″ + // 0xA0 – 0xA7 “\u0079\u007A\u00A1\u00BF\u00D0\u005B\u00DE\u00AE” + // 0xA8 – 0xAF “\u00AC\u00A3\u00A5\u00B7\u00A9\u00A7\u00B6\u00BC” + // 0xB0 – 0xB7 “\u00BD\u00BE\u00DD\u00A8\u00AF\u005D\u00B4\u00D7″ + // 0xB8 – 0xBF “\u007B\u0041\u0042\u0043\u0044\u0045\u0046\u0047″ + // 0xC0 – 0xC7 “\u0048\u0049\u00AD\u00F4\u00F6\u00F2\u00F3\u00F5″ + // 0xC8 – 0xCF “\u007D\u004A\u004B\u004C\u004D\u004E\u004F\u0050″ + // 0xD0 – 0xD7 “\u0051\u0052\u00B9\u00FB\u00FC\u00F9\u00FA\u00FF” + // 0xD8 – 0xDF “\\\u00F7\u0053\u0054\u0055\u0056\u0057\u0058″ + // 0xE0 – 0xE7 “\u0059\u005A\u00B2\u00D4\u00D6\u00D2\u00D3\u00D5″ + // 0xE8 – 0xEF “\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037″ + // 0xF0 – 0xF7 “\u0038\u0039\u00B3\u00DB\u00DC\u00D9\u00DA\u009F” + // 0xF8 – 0xFF “\u0000\u0001\u0002\u0003\u009C\t\u0086\u007F” + // 0×00 – 0×07 “\u0097\u008D\u008E\u000B\f\r\u000E\u000F” + // 0×08 – 0x0F “\u0010\u0011\u0012\u0013\u009D\n\b\u0087″ + // 0×10 – 0×17 “\u0018\u0019\u0092\u008F\u001C\u001D\u001E\u001F” + // 0×18 – 0x1F “\u0080\u0081\u0082\u0083\u0084\u0085\u0017\u001B” + // 0×20 – 0×27 “\u0088\u0089\u008A\u008B\u008C\u0005\u0006\u0007″ + // 0×28 – 0x2F “\u0090\u0091\u0016\u0093\u0094\u0095\u0096\u0004″ + // 0×30 – 0×37 “\u0098\u0099\u009A\u009B\u0014\u0015\u009E\u001A” + // 0×38 – 0x3F “\u0020\u00A0\u00E2\u00E4\u00E0\u00E1\u00E3\u00E5″ + // 0×40 – 0×47 “\u00E7\u00F1\u00A2\u002E\u003C\u0028\u002B\u007C” + // 0×48 – 0x4F “\u0026\u00E9\u00EA\u00EB\u00E8\u00ED\u00EE\u00EF” + // 0×50 – 0×57 “\u00EC\u00DF\u0021\u0024\u002A\u0029\u003B\u005E” + // 0×58 – 0x5F “\u002D\u002F\u00C2\u00C4\u00C0\u00C1\u00C3\u00C5″ + // 0×60 – 0×67 “\u00C7\u00D1\u00A6\u002C\u0025\u005F\u003E\u003F” + // 0×68 – 0x6F “\u00F8\u00C9\u00CA\u00CB\u00C8\u00CD\u00CE\u00CF” + // 0×70 – 0×77 “\u00CC\u0060\u003A\u0023\u0040\’\u003D\”"; // 0×78 – 0x7F


Client code

Here is a quick test class to open the file as an ImageInputStream, and attempt to find the first record’s length, read that many bytes, and translate to ASCII.

package com.cea.check21;

import java.io.File; import java.io.IOException; import javax.imageio.stream.FileImageInputStream;

public class Test { public static void main(String[] args) throws IOException { FileImageInputStream is = new FileImageInputStream(new File(“one.x9″)); int recLen = is.readInt(); System.out.println(“Record length: ” + recLen); byte[] rec = new byte[recLen]; is.read(rec); System.out.println(new String(rec, “Cp1047″)); is.close(); } }


After running that, the following output is displayed:
Record length: 80 0103T113000609111012822200408052030NUS BANKO NORM First Bank of NormAUS

This looks good. 80 matches the record length we calculated from the hex dump, and the first four characters are “0103″ as we expected. The rest of the line looks like numbers and semi-readable text, so we’re definitely on the right track.

Now let’s flesh out the class a little by iterating over each record and see what happens. The new class:

package com.cea.check21;

import java.io.File; import java.io.IOException; import javax.imageio.stream.FileImageInputStream;

public class Test { public static void main(String[] args) throws IOException { FileImageInputStream is = new FileImageInputStream(new File(“one.x9″)); int recLen; while ((recLen = is.readInt()) > 0) { System.out.println(“Record length: ” + recLen); byte[] rec = new byte[recLen]; is.read(rec); System.out.println(new String(rec, “Cp1047″)); } is.close(); } }


The output:

…and finally:

Exception in thread “main” java.io.EOFException at javax.imageio.stream.ImageInputStreamImpl.readInt(ImageInputStreamImpl.java:237) at com.cea.check21.Test.main(Test.java:11)

OK, so a closer inspection of the API would have shown that readInt() throws an exception when it reads past the end of the file, but other than that, we’re good up to the point where the image data is encountered. The image data occurs where the spec says it will: on the 52 record, at column 118.

So to finish up text conversion, we need to do two things. First, handle EOF more gracefully, and second, print only the first 117 characters of 52 records.

package com.cea.check21;

import java.io.File; import java.io.IOException; import javax.imageio.stream.FileImageInputStream;

public class Test { public static void main(String[] args) throws IOException { File file = new File(“one.x9″); FileImageInputStream is = new FileImageInputStream(file);

while (is.getStreamPosition() < file.length()) { int recLen = is.readInt(); byte[] rec = new byte[recLen]; is.read(rec); String recNum = new String(rec, 0, 2, “Cp1047″); if (recNum.equals(“52″)) System.out.println(new String(rec, 0, 117, “Cp1047″)); else System.out.println(new String(rec, “Cp1047″)); } is.close(); } }


Which outputs:
0103T113000609111012822200408052030NUS BANKO NORM First Bank of NormAUS 100111300060911101282220060109200601092030FG16410001nfg MANAGEMENTxxxxxxxxxxC0 200109100002211101282220070315200703150020300001000104053000196 25 1104 113000609 123456789012345/00000004320999987267040 G01Y000 50111101282220060109000002206260000 0 5211101282220060109040991400001 0 0000000000018628 50111101282220060109000002206261000 0 5211101282220060109040991400001 0 0000000000023720 70000100000000043200000000043200001 900000010000000100000000000432000000001 l NORMPOINTE BANK20060109 9900000100000001000000010000000000000432NORMAN AGEMENT2145085900

Success!

The last step for a simple extractor is to dump TIFF data into files. We’ll iterate a counter for each 52 record, and name the files img(count).tiff. Additionally, I’ve added some handling for specifying file names on the commandline, and finding their working directories. Lastly the class name has been changed to something more descriptive than “Test”.

The final code:

package com.cea.check21;

import java.io.File; import java.io.IOException;

import javax.imageio.stream.FileImageInputStream; import javax.imageio.stream.FileImageOutputStream;

public class Extractor { public static void main(String[] args) throws IOException {

if (args.length < 1) { System.out.println(“Usage: java Extractor “); System.exit(0); }

File file = new File(args[0]); FileImageInputStream is = new FileImageInputStream(file); int tiffCount = 0; String workDir = file.getParent(); if (workDir == null) workDir = “.”; String sep = File.separator;

while(is.getStreamPosition() < file.length()) { int recLen = is.readInt(); byte[] rec = new byte[recLen]; is.read(rec); String recNum = new String(rec, 0, 2, “Cp1047″); if (recNum.equals(“52″)) { System.out.println(new String(rec, 0, 117, “Cp1047″)); tiffCount++; String numberPart = String.valueOf(tiffCount); while (numberPart.length() < 4) numberPart = “0″ + numberPart; String fileName = workDir + sep + “img” + numberPart + “.tiff”; FileImageOutputStream out = new FileImageOutputStream(new File(fileName)); out.write(rec, 117, rec.length – 117); out.close(); } else System.out.println(new String(rec, “Cp1047″)); } is.close(); } }


The text output is the same, and two image files are created in the directory containing one.x9:

And they look like this:

Next steps

This Java class is useful only so far as exploring the Check 21 file format, but provides very little real-world utility. What’s needed is an app that can display check information more intelligently, including breaking down the record information into something meaningful, and displaying the front and back of a check alongside that information.

To do that, we’ll need to figure out how to turn TIFF data into a Java Image object, and to explore the X9.100-187 record layouts in a little more detail.

I’m giving myself an arbitrary deadline of January 15, 2011 to post my progress with that app. Tune in then!

]]>
http://auterytech.com/technology/check-21-java-app-1-of-2/feed/ 0
Java greyscale conversion http://auterytech.com/technology/java-greyscale-conversion/ http://auterytech.com/technology/java-greyscale-conversion/#comments Thu, 16 Dec 2010 14:31:22 +0000 Curtis http://auterytech.com/?p=87 Using Java, how do you convert a color image to greyscale? The most straightforward answer I’ve found was on Code Beach, and is as follows:
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); Graphics g = image.getGraphics(); g.drawImage(colorImage, 0, 0, null); g.dispose();

Create a BufferedImage of type TYPE_BYTE_GRAY, and grab its Graphics object. Draw the color image onto it. Fin.

The described method uses a small amount of client code for a fast, good quality greyscale conversion, but a lot goes on under the hood to make this happen in the Java runtime jar. Tracing through the code, eventually you arrive at the getDataElements method of java.awt.image.IndexColorModel, and the following line is executed over every pixel in the image:

int gray = (int) (red*77 + green*150 + blue*29 + 128)/256;

This takes the red, green, and blue components of a pixel’s composite color, and calculates its relative brightness (on a scale here that is, oddly, between 0.5 and 1.5), and later uses this value to mix a grey color of the same brightness for the replacement pixel.

A quick (and admittedly non-expert) course on color theory as it relates to computers:

Your computer screen does not have pixels that can change color. For every perceived pixel, there are three “lights” (for simplicity’s sake) very close together that are the primary colors red, green, and blue. When all three lights are off, you see black. When they are all turned on at full brightness, you see white. Mixing the lights at different power levels produces very close to the full range of colors that our eyes can perceive.

The lights have 256 power levels, for a total color space of 16,777,216 colors (256 cubed). Black is seen when red, green, and blue are all 0; white when they are all 255. There are also 254 shades of grey, seen when the red, green, and blue values all match and are between 1 and 254. This brings us back to the brightness equation above: find a color’s brightness, scale it between 0 and 255, and you can make a grey with the same brightness by setting the red, green, and blue of the pixel to that value. Repeat for all the pixels in an image and you have a greyscale copy of it.

Whence the 77/150/29 ratio?

In 1982, when I was 11 years old and a holy terror on my 6th grade teachers, the International Telecommunication Union published Recommendation 601 for transmitting video signals digitally. In it the following luminance (brightness) equation is mentioned in section 2.1:

E’Y = 0.299 E’R + 0.587 E’G + 0.114 E’B

These numbers add up to 1, where 77, 150, and 29 add up to 256. Dividing each of those by 256 gives a close approximation to the Rec 601 ratios, with the added benefit of using only integers and dividing by a power of 2, which computer processors are very good at. These specific ratios of red, green, and blue to determine luminance can be traced back to the XYZ color space, created by the International Commission on Illumination (CIE) in 1931, which in turn references work by James Clerk Maxwell (Experiments on colour, as perceived by the eye, with remarks on colour-blindness [1855]) which itself references work done by none other than Isaac Newton in the book Opticks (1704) which talks about breaking light into its component parts using prisms, discussing compound vs. homogeneal light, building lenses and telescopes, etc.

I won’t attempt a full analysis of all these works to trace the source of the numbers, but suffice to say in the 19th century, people stared at colored lights and twisted knobs to match colors, and scientists wrote down the final settings. After some math and repeated experiments, it was determined that green light looks pretty damned bright to us, red a little less, and blue less than that.

Are there better ratios to create a crisper greyscale image?

In 1993, the ITU published Rec 709, a standard for modern HDTV systems. In section 4.2, the recommended luminance formula for the 1250/50/2:1 scanning standard used the old Rec 601 values. For 1125/60/2:1 (1080i) scanning, new values are recommended:

E’Y = 0.2126 E’R + 0.7152 E’G + 0.0722 E’B

That section’s footnote says “The coefficients for the equations have been calculated following the rules laid down in SMPTE RP177-1993.” This refers to a 1993 publication called “Derivation of Basic Television Color Equations” by the Society of Motion Picture and Television Engineers.

…and for the low cost of $50, I can buy the paper and see what their math was. From what I gather from other sources, the new calculations change when Gamma levels are applied, with the result being values better lined up with the 1931 XYZ color space. Finding out more isn’t worth $50 to me. Some hoodoo is done with the gamma to produce a better luminance value – I’ll take their word for it.

Can I use the new values to greyscale an image in Java.

Yes. Instead of using Graphics.drawImage and letting Java figure out what to do with the pixels, you have to get your hands a little dirtier and work with PixelGrabbers, bitwise operators and integer arrays. But it turns out to not be that difficult.

Colors are defined in Java as 32-bit integers in four octets: alpha(opacity), red, green, and blue. For example, 0xff007f00 would be an opaque hunter-green, with the alpha set to ff(255) and the green set to 7f(127), with red and blue at 0. To take this integer value and pull out only the green part, you can either create a Color object and call its getGreen() method, or not waste the construction overhead and just do the math: right-shift 8 bits, then bitwise “and” the value to 0xff to zero out all but the last octet, with a statement like this:

int g = (color >> 8) & 0xff;

Getting the red value is similar, just bitshift 16 places instead 8, and with blue no bitshift is needed, just the bitwise “and”.
int r = (color >> 16) & 0xff; int g = (color >> 8) & 0xff; int b = (color ) & 0xff;

These values can then be used to construct a luminance value by plugging them into the Rec 709 numbers:
int lum = (int) ((double)r * 0.2126 + (double)g * 0.7152 + (double)b * 0.0722);

And finally a grey can be constructed by creating an integer whose red, green, and blue values all match the luminance number. This can be done with little overhead by left-shifting and using the bitwise “or” function:

int greyColor = (lum << 16) | (lum << 8) | lum;

The sample code below is a method that can be called specifying which luminance standard to use. A PixelGrabber grabs all the RGB values and sticks them into an integer array. Then the array is iterated over, converting all the colors to matching greys, and finally updating a BufferedImage with the new pixels:

private Image greyScale(int mode, Image sourceImage) { double redLum, greenLum, blueLum; switch(mode) { case 601: redLum = 0.299; greenLum = 0.587; blueLum = 0.114; break; case 709: redLum = 0.2126; greenLum = 0.7152; blueLum = 0.0722; break; default: return null; }

int w = sourceImage.getWidth(null); int h = sourceImage.getHeight(null); int[] pixels = new int[w * h];

PixelGrabber pg = new PixelGrabber(sourceImage, 0, 0, w, h, pixels, 0, w); try { pg.grabPixels(); } catch (InterruptedException e) { e.printStackTrace(); return null; }

BufferedImage greyImage = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);

int offset = 0; for(int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int color = pixels[offset++]; int r = (color >> 16) & 0xff; int g = (color >> 8) & 0xff; int b = (color ) & 0xff; int lum = (int) ((double)r * redLum + (double)g * greenLum + (double)b * blueLum); int greyColor = (lum << 16) | (lum << 8) | lum; greyImage.setRGB(x, y, greyColor); } } return greyImage; }


Here are a couple screengrabs from an app using this method to compare the original and greyscaled images.

First using the 601 standard:

Then the 709:

The difference is subtle, but the print on Scout’s dress is a little darker and better defined in the 709 image. And with that anecdotal and completely subjective comparison, I declare the 709 formula the better of the two. On a whim, I shot Oracle an email about the IndexColorModel class using the outdated formula, showing a simple change to use the updated one:

int gray = (int) (red*55 + green*183 + blue*18 + 128)/256;

I doubt that will come to anything, but, you know, it was worth at least mentioning it to them. If I get the added bonus of my line of code in the official rt.jar, so much the better.

]]>
http://auterytech.com/technology/java-greyscale-conversion/feed/ 0