SH Sean Harding/blog
Automated wine lists with CellarTracker and InDesign
Monday, September 6th, 2010

This post will be of interest to about four people in the world (if I count myself). I hope the other three find it helpful. If you’re a wine geek, a user of both CellarTracker and Adobe InDesign, familiar with JavaScript, and want nicely formatted lists of your wine collection, this is for you.

winelist-clip-1.jpg

CellarTracker is great for managing your collection, but the wine list feature never really made me happy. The output is pretty generic and boring. For a while, I manually edited the HTML CellarTracker spit out to make it a little more presentable, but I still wasn’t satisfied. So I decided to go all out and connect InDesign with CellarTracker to build a wine list that’s much nicer looking with far better typography than HTML could ever produce1. It’s pretty cool — all I have to do is click a couple of times in InDesign and I have a completely formatted and totally up to date wine list. Here’s how it works.

Please note, this was all done with InDesign CS3 on a Mac; things may vary on other versions or platforms. I don’t feel like this project is completely done (e.g. I’m not totally satisfied with the way some of the wines are grouped and labeled), but I think it’s a huge improvement over what CellarTracker provides. I’m not going to explain the basics of writing a script for InDesign — the documentation isn’t great, but it’ll get you that far. I’m just going to cover the parts that are interesting.

1. Getting the data

Cellartracker provides a very Excel-centric “API” for getting at its data. One thing you can do is get a comma- or tab-separated download of your cellar. The URL for tab-separated (which I prefer) is http://www.cellartracker.com/xlquery.asp?table=List&Location=1&User=<username>&Password=<password>&Format=tab (“Location=1” makes it include bottle locations; change “Format=tab” to “Format=csv” if you want CSV). Yes, you have to embed your username and password in the URL.

InDesign supports writing scripts to do all sorts of interesting things. AppleScript, VBScript and JavaScript are supported, but JavaScript is the only one that works on both Mac and Windows. It’s also the only one I have significant experience in programming, so my decision on that front was easy.

So, with those two pieces, the obvious thing to do is to write an InDesign script to download the data from CellarTracker and shove it into an InDesign document. Unfortunately, InDesign CS3 doesn’t support accessing the Internet from JavaScript (I’m not sure if that’s changed in newer versions). So, initially I ended up just writing a script that prompted for a file of cellar data (downloaded separately) and used that. I later discovered a hack to make InDesign support downloading the data — I’ll save you the trouble of finding it on your own.

The secret is that Bridge CS3 does support Internet access, via a library called webaccesslib. On the Mac, the library lives in Adobe Bridge CS3/Bridge CS3.app/Contents/MacOS/webaccesslib.bundle. I copied that directory into Adobe InDesign CS3/Adobe InDesign CS3.app/Contents/MacOS/. Then I had to do this nastiness to make it work:

if (!ExternalObject.webaccesslib) {
  var pathToLib = '/Applications/Adobe Bridge CS3/Bridge CS3.app/Contents/MacOS/webaccesslib.bundle';
  var libfile = new File(pathToLib);
  ExternalObject.webaccesslib = new ExternalObject('lib:' + pathToLib);
}

After that, InDesign could download the CellarTracker data directly:

var http = new HttpConnection(ctUrl);
http.execute();
var cellarTsv = http.response;

2. Organizing it

How you organize your wine list is really your own business. Once you have the data, you can do whatever you want. I stuck with an organization scheme pretty similar to what the standard CellarTracker lists do — I group first by type (red, white, sparkling, etc.), then by country, region and what CellarTracker calls “MasterVarietal” (e.g. this groups “Shiraz” together with Syrah). Within each MasterVarietal, I just maintain the sorting that CellarTracker sends the data with — it mostly makes sense, and it’s easier than implementing an intelligent sort myself.

I also decided to include the CellarTracker drinking windows in the list. They’re not always reliable, and I don’t swear by them, but I figure it’s an interesting piece of info to have and it doesn’t take up much space.

There are a few gotchas and optimizations I dealt with. N.V. wines have the vintage “1001,” and I like to display it as “N.V.” so I had to fix that. Each bottle is listed individually in the data file and I like to show the locations and quantities on the list, so I had to be sure to track those and group the bottles together. I had a specific order in which I want the wine types to appear in the list (e.g. white before red), so I had to set up a manual sort order for those. I sort countries, regions and MasterVarietals alphabetically. At the end, I have a big JavaScript object containing all of the wines grouped as I like them; I just iterate over it and put the wines into the document.

3. Putting it into a document

The foundation of the document is an InDesign template with styles defined in it. I’ll talk a bit more about the styles I use in the next section. Some of the stuff I do with the InDesign document seems a bit kludgy, and I wouldn’t be surprised if there were better ways to go about things. Unfortunately, this stuff isn’t very well documented. That’s why I’m sharing it here. If you can improve on this, definitely speak up. All of the code samples here are simplified from what I use in my actual script, to make it easier to extract the interesting pieces.

First, I prompt the user to find a template and open it:

doc = app.open(File.openDialog("Choose template"));

I iterate over the cellar grouped by wine type (red, white, etc.), and for each type I create a new page and create a new text frame on that page:

page = doc.pages.add();
var textFrame = page.textFrames.add();
textFrame.geometricBounds = getPageBounds(doc, page);

getPageBounds() is basically the same as the “myGetBounds()” method shown in the InDesign CS3 Scripting Tutorial.

Here’s where it really starts getting ugly (to my eyes, at least). I insert the header at the beginning of the text frame and apply the right style to it:

frame.parentStory.insertionPoints.item(-1).contents = headerString + "\r";
var style = doc.paragraphStyles.item(WINE_TYPE_STYLE);
frame.parentStory.paragraphs.item(-1).applyParagraphStyle(style, true);

Then I go through the countries, regions, master varietals and wines, doing basically the same thing (with a different style for each). One thing that took me a while to get right is the em dash between the locale and the drinking window, and the en dash between the two ends of the drinking window:

wineDetails += locale + " \u2014" + " Drink " + beginWindow + "\u2013" + endWindow;
winelist-clip-3.jpg

Also, I wanted the cellar locations to be right-aligned, but on the same line as the other details that were left-aligned. That requires the “right indent tab:” \u0008.

At the end, I have one page for each wine type, with one text frame per page. But if there’s more data in the frame than will fit on one page, it’s truncated. I needed to make the each frame flow across extra pages until it can all be shown. That’s where this beauty comes in:

function flowAcrossPages(doc)
{
  var m = doc.pages[-1].marginPreferences;
  var gbounds = [m.top, m.left, doc.documentPreferences.pageHeight - m.bottom, doc.documentPreferences.pageWidth - m.right];
  doc.viewPreferences.rulerOrigin = RulerOrigin.pageOrigin;
  while (doc.pages[-1].textFrames[0].overflows)
  {
    var tf = doc.pages.add().textFrames.add ({geometricBounds: gbounds});
    tf.previousTextFrame = doc.pages[-2].textFrames[0];
  }
}

Oy. But hey, it works.

4. Making it look nice

The final piece of the puzzle is actually making everything look nice. The options for what your wine list looks like (and what information it includes) are really wide open. The key thing here is that my template includes styles with specific names that are referenced from within the script. I have a style for the wine type header, the country header, the varietal, the drinking window, etc. If the styles have the wrong names (or are missing), it doesn’t work right — a bit fragile, but it’s the best I could come up with within the limits of the InDesign scripting interface. The script controls which information is there, the order it’s in and what lines it’s on, but the typefaces, spacing, etc. are all based on the template. So I can have different templates with completely different looks. All I have to do is make sure all the styles are defined with the right names.

When you’re choosing fonts, be sure you pick ones that include all the accented characters and things you need to properly display wine names. Most should be fine, but I did run across one that had problems.

winelist-clip-2.jpg

If you’ve read this far, you’re probably crazy. Maybe even crazy enough to give this a shot. If you do decide to try it, I hope this makes it a little bit easier for you. I’m far from an expert on this stuff, but if you get stuck on anything, feel free to leave a comment or send me an email and I’ll see if I can help. Good luck!

1. The new version of CellarTracker might make all of this obsolete, but I kind of hope it doesn’t, because I’ve grown fond of this setup.

blog comments powered by Disqus
Home