frameworker

September 11, 2009

How to constrain window size in Cappuccino?

Filed under: Uncategorized — frameworker @ 8:13 pm

I’m displaying an image in a CPWindow and wanted to keep the window from getting bigger than the image it’s displaying. I tried setting the window’s maximum size in applicationDidFinishLaunching. But that didn’t accomplish what I wanted; I’m still able to make the window arbitrarily large.

However, there is a change in behavior when setMaxSize is called.  The scroll bars persist at the maxSize position until both x and y are greater than maxSize.

With maxSize set, as you grow the window vertically, the horizontal scroll bar is fixed at the maxSize position and there is white space between the scroll bar and the bottom of the window.  Also, the vertical scroll bar is depicted as an empty, shaded, rect.

CPWindow

Once you make the window bigger than both maxWidth and maxHeight both scroll bars disappear and the window looks just like it would in the case where maxSize isn’t set.

In the case when maxSize isn’t set, as you grow the window vertically, the horizontal scroll bar is displayed at the window’s bottom edge with white space separating the bottom of the image and the scroll bar.  And if you make the window wider than the image’s width, the horizontal scroll bar disappears.

However, there is a change in behavior when setMaxSize is called.  The scroll bars persist at the maxSize position until both x and y are greater than maxSize.
With maxSize set, as you grow the window vertically, the horizontal scroll bar is fixed at the maxSize position and there is white space between the scroll bar and the bottom of the window.  Also, the vertical scroll bar is depicted as an empty, shaded, rect.  Once you make the window bigger than both maxWidth and maxHeight both scroll bars disappear and the window looks just like it would in the case where maxSize isn’t set.
In the case when maxSize isn’t set, as you grow the window vertically, the horizontal scroll bar is displayed at the window’s bottom edge with white space separating the bottom of the image and the scroll bar.  And if you make the window wider than the image’s width, the horizontal scroll bar disappears

How do you constrain the window’s size? Do I need to do something in addition to setMaxSize? Is a different approach required to do this?

Here’s my AppController.j source code.

@import <Foundation/CPObject.j>
@import <Foundation/CPString.j>

@implementation AppController : CPObject
{

}

- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
    var kImageWidth  = 612.0,
        kImageHeight = 792.0,
            kMargin  = 16.0;

    // Create a window to take up the full screen

    var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],

        contentView = [theWindow contentView],

        bounds = [contentView bounds];

    // Create a scrollView to contain the pdf image

    var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(bounds), CGRectGetHeight(bounds))];

    [scrollView setAutoresizingMask: CPViewHeightSizable | CPViewWidthSizable];

    [scrollView setAutohidesScrollers:YES];

    // Create the image and imageView.

    var theImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle mainBundle] pathForResource:@"testImage.pdf"]];

    // Create the image view.

    var imageView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, kImageWidth, kImageHeight)];

    // Put the image in the imageView.

    [imageView setImage: theImage];

    // We don't want the image to resize.

    // (Could have [imageView setAutoresizingMask:CPViewNotSizable | CPViewNotSizable] instead.)

    [imageView setImageScaling: CPScaleNone];

    // Put the imageView in the scroller.

    [scrollView setDocumentView:imageView];

    // Put the scroller in the window's view hierarchy.
    [contentView addSubview:scrollView];

    var maxWidth   = kImageWidth  + kMargin,

        maxHeight  = kImageHeight + kMargin,

        theMaxSize = CPMakeSize(maxWidth, maxHeight);

    [theWindow setMaxSize: theMaxSize];

    // Bring the window forward to display it.

    [theWindow orderFront:self];
}

@end

June 29, 2009

Inside PDQForms

Filed under: Uncategorized — frameworker @ 9:31 pm

Overview

PDQForms is tax preparation software for State and Federal forms. It integrates spreadsheet logic into pdf forms using PDFKit to combine form field information with labels and expressions. The result is a spreadsheet-like application layer over the form.

Creating a PDQ Form

The idea was to automate
everything that could be.

The first step is to extract information for all the form’s fields, or “Annotations” in PDF lingo.

We need the type of Annotation: TEXT or CHECKBOX, and its PAGE, RECT and MAXLEN values.

This is accomplished with a PDFKit based program called PDQ Annotation Editor, which outputs a text file.

Next we must define expressions that contain the logic for each of the form’s fields. This is just like programming a spreadsheet.

Adding this information to the Annotation meta-data completes our task.

We now have a text file containing the information PDQForms will need to automate the form.

We then “bake” this back into the original PDF file.

And the form can now be opened with PDQForms!

June 23, 2009

TABLE LOOKUPS IN PDQFORMS

Filed under: Uncategorized — frameworker @ 5:26 am

Since some tax forms use tables as well as formulae, I had to implement a tax table lookup algorithm. Things were complicated by the fact that these tables were not always available in sorted format.

I first tried using the unsorted tax tables. This required doing a comparison of each table entry until the proper tax bracket was found. But tax lookup tends to occur whenever you change any numeric field on the form, since this usually affects taxable income. So, doing a comparison for each entry, while easy, was way too slow! Suddenly I was seeing a totally unacceptable delay.

The solution was to pre-sort the tables, shifting the performance burden completely out of the user’s work flow, and then to use an efficient, recursive binary search, algorithm to do the table lookup in PDQForms.

PREPARING A TABLE

Copy the table from the tax handbook into a text file and clean-out any patches of non-table data. Fortunately, the topology of non-table data is amenable to doing this, and also, the table data consists of an integral number of tuples on each line.

Finally, filter-out commas, and prepend TUPLESIZE to the table.

The table we’re creating will have the same name as the form it applies to, but with a suffix that’s specified in the call to createTupleTable. Here it’s “tax”.


    NSString * theTableContents = [self createTupleTable: theTableData named: @"tax"];

PRE-SORTING THE TABLES

1. Put the elements (NSStrings) of each tuple into an array (NSArray)

2. Put these tuples into an array so they can be sorted.

3. Sort the array of tuples, from smallest to largest, using “sortedArrayUsingFunction”
and passing it the comparison function “sortByValue”:


    NSArray *sortedTuples = [tuples sortedArrayUsingFunction: sortByValue context: 0];

    NSInteger sortByValue(id tuple1, id tuple2, void *context)
    {
        double value1 = [[tuple1 objectAtIndex: 0] doubleValue];
        double value2 = [[tuple2 objectAtIndex: 0] doubleValue];

        if      (value1 > value2) return NSOrderedDescending;
        else if (value1 < value2) return NSOrderedAscending;
        else                      return NSOrderedSame;
    }

4. Convert the sorted tuples back into a single NSString.

5. Finally, “bake” the table data into the pdq file using PDFKit (10.5).

DOING TABLE LOOKUPS IN PDQFORMS

This stack-trace depicts the tax calculation mechanism:

[PDQAbstractWidget evaluateExpression: theTableLookup]
Table lookups are processed as a special case by evaluateExpression.
e.g. =TAXTABLE(“TAXTABLE”,”6″,INCOME,FILING_STATUS)
evaluateExpression calls doTableLookup.

  [PDQAbstractWidget doTableLookup: theTableLookup]
  doTableLookup is analagous to evaluateFunction, but for tableLookups.
  doTableLookup packages the call’s parameters and calls “execute.”
  Note that quoted parameters are passed in as literal strings.

    [NSString+PDQFunctionAdditions execute: parameterArray]
    execute dispatches the call to taxTableLookup.

    [NSString+PDQFunctionAdditions taxtablelookup: parameterArray]
    taxTableLookup unpacks the parameters, finds the taxBracket
    and uses it to determine the tax.

      [NSArray+PDQTableAdditions taxBracket]
      taxBracket is the recursive binary search algorithm,
      initially called from taxTableLookup,
      that does the “heavy lifting.”
      taxBracket calls the helper routine taxInTuple

      [NSArray+PDQTableAdditions taxInTuple]
      taxInTuple tests to see if taxable income falls
      within a bracket, to its left, or to its right.
      If income isn’t in the current tax bracket
      taxInTuple’s return value is then used
      to seed recursive calls to taxBracket.

taxBracket and taxInTuple are shown below.


- (int) taxBracket: (double) income
        tupleWidth: (int) tupleSize
      startingWith: (int) firstTuple
     andEndingWith: (int) lastTuple
{
    int tupleIndex = -1;

    int comparison;

    int middleTuple = firstTuple+(lastTuple-firstTuple)/2;

    comparison = [self taxInTuple: income
                          atIndex: middleTuple
                       tupleWidth: tupleSize];

    if (comparison == 0)
    {
        tupleIndex = middleTuple;
    }
    else
    if (comparison == 1)
    {
        tupleIndex = [self taxBracket: income
                           tupleWidth: tupleSize
                         startingWith: middleTuple+1
                        andEndingWith: lastTuple];
    }
    else
    if (comparison == -1)
    {
        tupleIndex = [self taxBracket: income
                           tupleWidth: tupleSize
                         startingWith: firstTuple
                        andEndingWith: middleTuple-1];
    }

    return tupleIndex;
}

// Tuples are laid out end to end as one long array.
// The first two items of a tuple are its income bracket.
// the trailing items are the tax for each filing status
// in that tuple's income bracket.
- (int) taxInTuple: (double) income
           atIndex: (int) tupleIndex
        tupleWidth: (int) tupleSize
{
	int leftIndex  = tupleIndex*tupleSize;
	int rightIndex = leftIndex + 1;

	NSString * leftItem  = [self objectAtIndex:  leftIndex];
	NSString * rightItem = [self objectAtIndex: rightIndex];

	double leftValue  = [leftItem  doubleValue];
	double rightValue = [rightItem doubleValue];

	// Test if intervals overlap:
	//
	// IF YES use <= for right value
	// IF NO  use <  for right value.

	// CA brackets don't overlap
	// they're [x,y] [y+1,z].
	// So when income is exactly "y"
	// we want to match the ONLY bracket containing "y",
	// not the higher of two brackets!

	// One even, one odd means the intervals shouldn't "overlap."
	if ((int)leftValue%2 != (int)rightValue%2)
	{
		if ((income >= leftValue) && (income <= rightValue))
		{
			return 0;
		}
	}
	else
	// IRS brackets are [x,y] [y,z] so when income is exactly "y"
	// we want to match the higher of the two brackets!
	{
		if ((income >= leftValue) && (income < rightValue))
		{
			return 0;
		}
	}

	if (income < leftValue)
	{
		return -1;
	}
	else
	{
		return 1;
	}
}

HOW I TEST FORMS

Filed under: Uncategorized — frameworker @ 4:58 am

OVERVIEW

The PDQForms Debug target contains a Debug menu with commands that are enabled if a PDQDocument is open.

  Read test file…
  Save test file…

The “Save test file…” command journals edited forms into test files. The “Read test file…” command causes the test file to be read back into the current form, and verifies that values of the calculated fields are correct. This allows for rapid regression testing of forms after making changes to the code base.

I TEST FILE

Test files have the same names as the forms they “exercise,” but with the suffix “test”.

Test files are kept in the same folder as their corresponding “pdq” file.

The test file consists of FIELD_NAME, STRING_VALUE pairs, one pair per line.

II TEST COMMAND

Saving the test file writes out the form’s FIELD_NAME, STRING_VALUE data.

Reading the file back into the current form, populates each FIELD_NAME with its STRING_VALUE and causes that field’s dependents to update.

But if a STRING_VALUE read from the test file begins with an “=”, then it is an expected result and it will be compared to the calculated value of the form’s field, not stuffed into the form.

III TEST RESULTS

The test command logs descrepencies in calculated values:

“Unexpected value for calculated widget: ‘WidgetID’ shown: ‘itsValue’ expected: ‘itsExpectedValue’”

If there are no descrepencies, the test command logs the message:

“All calculated widgets have expected values :-)

IV SUMMARY

Since the accuracy of calculations in forms is paramount, this simple but powerful approach solves an important problem.

EXPRESSIONS IN PDQFORMS

Filed under: Uncategorized — frameworker @ 4:50 am

I realize this document is pretty dry, but I needed to document this implementation to facilitate discussion with othere in the quest to improve it. Without going into why I took the approach I did, I will say that I did become very proficient in using strings with Objective C :-)

I OVERVIEW OF EXPRESSION SYNTAX

Expressions may be FORMULAE (including BOOLEAN FORMULAE), FUNCTIONS, TABLE LOOKUPS or CONDITIONAL EXPRESSIONS.

Expressions are written in infix notation, just as you’d expect. They consist of OPERATORS, OPERANDS, SEPARATORS and FUNCTION NAMES.

A. OPERATORS include BOOLEAN OPERATORS

== <= or == or => != or && || !

and ARITHMETIC OPERATORS

+ – * % (modulus) ^ (exponentiation)

B. OPERANDS may be cell references or numbers. Numbers are evaluated as double-precision floating point.

C. SEPARATORS include ( ) , ;

The “=” character is prepended to all expressions, just like in VisiCalc.

D. All other tokens, those which are not OPERATORS, OPERANDS or SEPARATORS, are FUNCTION NAMES.

FUNCTION NAMES that end with “TABLE” are a special case. They perform Table Lookups for forms that use Tax Tables.

II FORMULAE

Operands, within formulae, may be cell references or numbers. Numbers are evaluated as double-precision floating point.

III BOOLEAN FORMULAE

Boolean Formulae, expressions that resolve to YES (1) or NO (0), are used in the scriptIf component of Conditional Expressions. They aren’t used elsewhere at this time, but they could be. Any positive number could be interpreted as YES, but we currently require “1″.

IV FUNCTIONS

Functions are snippets of code that get dispatched interpretively.

Function arguments may be formulae or can, themselves, be functions.

Quotes are used to transmit function (and table lookup) arguments as literals.

*Describe how a function gets added to PDQForms and the function dispatch mechanism.*
*Show the recursive function parsing routine.*

V TABLE LOOKUPS

Table Lookups are used to find income tax, for example, for Federal filers whose taxable income is less than $100,000.

See the blog post on TABLE LOOKUPS IN PDQFORMS for a more detailed description of how they’re implemented.

VI CONDITIONAL EXPRESSIONS

Conditional Expressions are of the form =IF(scriptIf;scriptThen;scriptElse)

(“IF” is a reserved word)

ScriptIf, a Boolean Formula, resolves to a value of zero or one. If scriptIf resolves to 0, scriptThen is executed, otherwise scriptElse is executed. ScriptThen and scriptElse can be Table Lookups, Functions or Formulae.

Conditional Expressions may not be “nested.”

VII LIMITATIONS

Expressions may not contain functions. For now, if an expression needs an embedded function, an invisible widget, that calls the function, can be referenced from the expression.

April 30, 2009

[RECOMMEND] Jordan Langille, Iconographer

Filed under: Uncategorized — frameworker @ 7:53 pm

Jordan is an Artist and Iconographer at OneToad Design

He designed the icons for PDQForms :-)

I found Jordan through CIMGF; thanks guys!

Jordan responded to my initial query, worked with me and delivered my icons in a very reasonable amount of time!

He communicates well, is easy to work with, and does EXCELLENT WORK!

And he’s very concerned with his customers’ satisfaction!

Thank you, Jordan!!

Daring Furball

Filed under: Uncategorized — frameworker @ 5:20 pm

daring-furball1

Buster was taking a break from writing his application for the 2009 Apple Design Awards – he’d been working extensively with Mouse Events, of course.

Perhaps he was drawn to the laundry basket by my – freshly minted and washed – Daring Fireball t-shirt :-)

April 16, 2009

Windows considered harmful :-)

Filed under: Uncategorized — frameworker @ 8:49 pm

April 10, 2009

Announcing PDQForms

Filed under: Uncategorized — frameworker @ 6:02 am

HIGH-QUALITY LOW-COST TAX SOFTWARE

PDQForms is a Mac OS X application that adds spreadsheet logic to regular tax forms* so you can file your Federal and State** tax returns quickly, avoiding tedious calculation, and with accurate results. PDQForms is for people who do their own taxes, want to maintain the privacy of their financial information, and want high-quality, low-cost tax software.

* These forms have been “fixed up” to contain embedded logic.
** The 2009 edition supports only CA and NY State returns.

PLATFORM INNOVATION

“Things should be as simple as possible, but no simpler.”
- Albert Einstein

PDQForms seamlessly integrates intelligence into familiar, paper-based forms, eliminating tedious calculation while ensuring accurate results. This simplicity of familiarity is PDQForms’ “platform innovation.”

DESIGN

PDQForms implements smart layout to always center the active field within its focus ring. Almost all calculations are performed automatically, including tax calculations, which are done using the appropriate tax table or formula.

Compare this to the haphazard experience of filling out tax forms using Acrobat Reader or Preview, with their erratic field positioning, dollars and cents fields that are split in two, and no built-in support for calculations.

PERFORMANCE & STABILITY

PDQForms has a small memory footprint to launch quickly. It’s quite responsive, with no noticeable hesitation when editing or tabbing between fields. Tested with Leopard Instruments and the LLVM/Clang static analyzer, PDQForms is also extremely stable.

LOCALIZATION

PDQForms is fully Unicode-compliant, ensuring ease of localization.

WEBSITE

The PDQForms website is here.

December 17, 2008

One More Thing…

Filed under: Uncategorized — frameworker @ 3:10 am

Amit Sing has posted a great writeup of his, and Ted Bonkenburg’s, most excellent “MacFUSE State of the Union Talk (2008).”

If you weren’t planning on watching the video, due to its hour and twenty minute length, fast forward to 01:12:17 and start there. You’re in for a real treat! While it would be better if viewed in its entirety, after watching the end you just might want to rewind and take in the whole thing.

As I said to Deric Horn, who was there from Apple, afterwards: “My mind is completely blown.”

Older Posts »

Blog at WordPress.com.