Jun 25 2012

Fountain Update

Today I pushed the first significant update to Fountain, and boy is it a big one. This is the result of fantastic feedback from users, and work we’ve been doing with Highland. Here are the key changes:

1. An All New Parser

The old parser, FountainParser, works by hammering text files with a series of regular expressions. The advantage of this approach is that to port this to another language/system all one has to do is copy and paste a bunch of regular expressions. The downside is that this is obviously not the most efficient way to parse a document. This was considered an acceptable trade off for two reasons. One, since script documents aren’t expected to crack 200 pages, there’s a ceiling to how large files would ever expected to be. Two, on a modern computer it’s fast enough.

But, it came to our attention that on iOS devices the parser’s performance was less than stellar. In fact, it was pretty terrible.

Long story short, now there’s FastFountainParser. It’s a traditional line-by-line parser and roughly ten times faster than the old one. So, that’s a win.

FNScript now defaults to using the new parser, but if for some reason you want to use the old one it’s still available from the new methods - (id)initWithFile:parser: and -(void)loadFile:parser:, and the string equivalents. Parser options are FNParserTypeRegex for the old one, and FNParserTypeFast for the new one.

2. Descriptions on FNScript and FNElement

As I was putting together the new parser I realized that debugging FNScript and FNElement was kind of a nightmare. The problem is trying to use something like NSLog(@"%@", script.elements) to get the output of FNScript’s elements array would give you something like this:

    "<FNElement: 0x7f9abab03d30>",
    "<FNElement: 0x7f9abab030e0>",
    "<FNElement: 0x7f9abab03120>",

That is completely unhelpful. The fix to this was to implement NSObject’s - (NSString *)description method. This method allows for the creation of a string representation of an object. Now we get this:

    "Action: TITLE OVER:",
    "Action (centered): BIG FISH",
    "Scene Heading: INT.  HOSPITAL ROOM - DAY  ",

Much better. FNElement’s description returns an NSString in the pattern “Type: Text”. As shown above, centered elements are marked, as are dual dialogue and the depth of a section. For FNScript we return the full Fountain output of the script object.

Hopefully between the new parser, with it’s non-reliance on complex regular expressions should make it easier to modify and adapt, and the improved ability to debug people will have an easier time working with and integrating Fountain into their projects.

3. A Sample Project, HTML exporter, and FNPaginator

When we originally released Fountain we included the Fountain parsing and writing code, and the Xcode project consisted solely of tests. Several people requested that we provide a little more to get up and running. You’ll now find a sample project included that parses a script (the wonderful Big Fish) and displays it as HTML. Naturally, for this to work we added an HTML converter to the project. It’s very simple, but should at the very least illustrate how to work with FNScript and FNElement.

More importantly, we’ve decided to make our pagination code open source. FNPaginator was built under the supervision of a bonafide screenwriting expert, so it’s safe to say it’s pretty legit. It splits large dialogue blocks up across pages, adding the appropriate MORE and CONT’D, and is smart enough not to split in the middle of a sentence. I’m immensely proud of our work on this thing, and we’re giving it away for free.

What’s Next?

As previously mentioned, we’re going to release our FDR parser as soon as we’re done with Highland, and that will hopefully be soon. We’re also eager to hear about Fountain integration into your apps. Several apps, including Fade In Pro and Storyist, have already added support for Fountain. If you’ve developed an app to use Fountain or are incorporating Fountain into an existing app let us know. It makes us feel all warm and happy inside. :-)