Welcome to another episode of the Lightning Brain Podcast. We'll talk a little bit about InDesign and user-interface code.
When it comes to extending InDesign, there are many options available: you could create a C++-based plug-in, you can create an ExtendScript (JavaScript) solution, you can build an AppleScript or VBScript-based solution, you can build a Flash-based UI and then use it in InDesign, you can glue some other development environment 'into' InDesign, and you can also make a hybrid of the aforementioned solutions...
Which approach to choose depends on what your need is, what portion of the project is user-interface functionality as opposed to faceless functionality, what development environments you're familiar with, what your potential users are willing to accept, what budgets (time, money, resources, people, testers,...) are available for the project, what the politics involved are,... As in all things automation, there is no single 'best' solution - it all depends.
In this podcast I want to describe an approach we've very successfully used for a few real-size, real-life projects, and what the advantages and disadvantages are.
I first want to include a little disclosure: Rorohiko resells the Active Page Items Developer product as a commercial solution, and Active Page Items is very much part of the approach described here - so you might think this is podcast is a veiled advertisement for Active Page Items. Well, it is - but you have to keep in mind that Active Page Items has been created and has grown out of our own need for such a tool - Active Page Items was first; commercializing it came later.
One of the things we've learned is that C++ development for InDesign can be quite expensive: simple principles and algorithms often take a surprising amount of code to express. I'd describe C++ development around InDesign as 'fluffy'. High-level concepts and patterns result in lots of classes and source code files and fairly large amounts of C++ code, much of which is often quite repetitive in a number of respects. Especially the development of user interface elements takes a lot of doing.
At present, the only way to currently get a really 'native' InDesign UI look is to use C++ - e.g. if you want to create floating palettes, with all their end-user flexibility (tearing off, parking to the side,...) you need to use C++.
From a UI perspective, C++ might be 'perfect', but often there are other approaches that could be classified as 'good enough'. They might not look as nice, but they might do the job.
For example, using ExtendScript with InDesign CS3 one can develop quite complex user interfaces which often are 'good enough'. ExtendScript development is easily an order of magnitude cheaper than C++ development - so if the parameters of the project at hand don't require an absolute perfect-looking interface with floating palettes, ExtendScript can be the way to go.
Often there are also user-interfaces that need something a little bit more complex than what can be accomplished using ExtendScript, yet don't need a full blown native InDesign user interface. That's where the 'hybrid' approach that we've been using comes in.
The approach we've chosen basically boils down to: REALbasic, Active Page Items, ExtendScript.
We create our more complex user interfaces in REALbasic. However, this is not an absolute requirement - on the whole, we might have used Java instead.
The main reasons for choosing REALbasic over Java are 1) that it allows us to create cross-platform code that looks sligthly more 'native' on Mac as well as on Windows 2) easy access to global floating windows (both on Mac and Windows) and 3) purely personal preference: I find I personally can build and implement user-interfaces faster with REALbasic than with Java.
Easy access to global floating windows is one feature of REALbasic that comes in really handy, for which I don't know whether Java offers an easy alternative.
Global floating windows are windows that remain 'on top', even if the application that owns them is not the foreground application. This is fairly important for the illusion we want to maintain.
Active Page Items is a fairly large C++ plug-in, which we extend as we need with new functionality.
One of its many functions is coordinating InDesign with external applications. Through Active Page Items, we are able to create an illusion that makes the external 'satellite' apps seem to be part of InDesign.
One of the tricks is to 'lock' InDesign in a modal mode while one of our REALbasic satellite apps is running. That creates the illusion that a dialog owned by the satellite app seems to belong to InDesign.
However, the modal mode is not real - it's a simulated modal mode, and while this simulated modal mode is active, InDesign is actually still very much 'alive' and able to execute ExtendScript code - so it is possible to create a 'live' session between the satellite app and InDesign while the user interacts with this 'simulated modal dialog', while 'locking out' the user from any undesirable interactions with InDesign.
The illusion is not perfect: on the Mac's Dock and on the Windows start bar it is fairly apparent some secondary app is running, but that is a cosmetic issue, and it does not really seem to annoy our end-users too much.
- Active Page Items is also used to manage menu items and context menu items. This is mostly because our solutions needed to support CS2 as well as CS3 - InDesign CS3's ExtendScript has all you need to create menu items and context menus, so if you have the luxury of an InDesign-CS3-only setup, you can stick with standard ExtendScript in that respect.
- For communication between the various disparate components, we use temporary files. This is a very low-tech and crude approach, but it works 'well enough'. In a future version of Active Page Items we might add support for a more 'high-tech' information exchange mechanism, but for now temp files do us just fine.
If you want to try things out for yourself, I've created a very small sample of such a hybrid solution; the source code to it comes as part of our Active Page Items Developer Toolkit. If you download the latest demo version of the Toolkit from our web site, you'll find my example code tucked away amongst the other examples.
Some of the scripts can also be viewed at the end of the blog entry.
The sample, which deals with overset text, has no real practical applications as such, but you should be able to see how it can be made into a practical solution for particular problems.
The sample performs the following function: it looks out for overset text. As soon as a text frame gets overset, some ExtendScript code jumps into action, and fires up an external application, which then shows the contents of the text frame in a scrolling text field. The idea is that the user edits the text down to a shorter version to stop the overset. Of course, is not a practical approach at all, but it does allow us to demonstrate the various techniques involved.
In the sample, the active bit of ExtendScript code is currently 'attached' to a 'dummy' page item that sits on the pasteboard. The page item is not meant to be printed or have any sensibly printable content; all it does is hold some script code. We call such a page item a 'controller'. The controller 'watches' one or more page items, and waits for the events to occur.
In a 'real' solution based on Active Page Items, we'd instead 'package' that script code into a so-called 'Scripted Plug-in', in a .spln file.
In this particular case the controller is set to watch all page items, and the 'interesting events' it might watch out for are
- an event called 'subjectModified-recomposed-overset' which occurs when any page item ends up being overset after something happened to it (user typed something, frame resized,...)
- an event called 'idle' which occurs at regular intervals. In this particular solution, we rarely look out for 'idle' events as to not unnecessarily tax the computer's performance. We only do so while InDesign is in 'simulated modal mode' when the satellite application is running.
So, when any page item becomes overset, the subjectModified-recomposed-overset event is captured by the controller.
The controller's ExtendScript then launches the external satellite application using a special Active Page Items method attached to the application object - app.launchWith().
app.launchWith() has a number of functions. The most common use is as an extension to the File.execute() method.
File.execute() is similar to double-clicking an icon in Explorer or in the Finder, and will pick the default application to open a particular document.
app.launchWith() allows you to designate a particular application to open a particular document with - it is more akin to drag-dropping a document file icon onto an application's icon.
On top of that, app.launchWith() has a special feature - it allows us to lock InDesign into simulated modal model for as long as the launched application continues to run.
That makes for a crude, yet effective way to synchronize a satellite app with InDesign: you launch the satellite application using app.launchWith(), and when the user clicks 'OK' in the dialog presented by the satellite application, the application simply exits. Active Page Items is monitoring the satellite app, and as soon as it sees it exit, it will release the simulated modal lock.
So, the controller's ExtendScript first writes the contents of the overset frame into a temporary text file.
It then uses app.launchWith() to tell the satellite app to open this temp text file and pick up the data being communicated.
The satellite app then runs until the user clicks OK in the dialog, after which it writes the new data to the same temporary text file. When the application exits, Active Page Items will release the simulated modal lock automatically.
While the satellite app is running and InDesign is in simulated modal mode, the controller is catching idle events (roughly once per second). During these events, we could perform more communication backwards and forwards with the satellite application (e.g. for live previews or so), but in this case, all we do is check the simulated modal lock: as long as that is not lifted, we know the app is still running and we do nothing.
When we notice that the simulated modal lock has disappeared upon receiving one of the idle events, we know it the user has clicked OK, the app has written the new data to the temp file, and has quit, and we know we can now read the returned data, and then we can stop looking for idle events - things come back to normal, with the controller only watching out for overset events.
This sample should give you a little bit of insight on how we approached some real-live projects with very good results.
The advantages we had were:
- fast development of a good-looking UI that was beyond what can be accomplished with ExtendScript.
- cross-platform (Mac & Win) support: the same code works on both platforms only minute amounts of conditional code.
The disadvantages:
- don't pay attention to the man behind the curtain. Global floating windows and simulated modal mode allow you to get close, but nothing identical to the real thing (an InDesign-generated dialog or palette). The satellite app is visible - we worked around that by giving it a good-looking icon.
On the whole, the disadvantages were acceptable for the particular projects, and as a result we were able to offer very high efficiency in realizing these projects.
Thanks for your attention!
---
//
// Example of using an external program for dialogs.
//
// This document needs a file called "ExampleInDesignSatellite.app" (on Mac)
// or "ExampleInDesignSatellite.exe" (on Windows) in the same folder
// as the document.
//
// This event handler handles subjectModified-recomposed-overset and
// idle events.
//
// In normal circumstances, the event filter is set to just
// subjectModified-recomposed-overset - i.e. the handler only
// activates when there is a text frame that has just recomposed,
// and shows overset
//
// When that happens, the handler below will launch an external
// program to edit the text frame contents, and also change the event
// filter to "idle" - causing repeated calls to this handler while
// the user is editing the text in the external program. The
// external program is launched using launchWith and a mode equal
// to 4 - meaning: InDesign is modal locked for the user until the
// external program terminates.
//
// So, what happens is that we repeatedly receive and handle
// idle events, until app.callExtension(0x90b6C,10003) returns
// false (meaning: not modal locked), which only happens when
// the external program has terminated.
//
// As soon as the external program terminates, we restore the
// normal event filter, and read the output of the external
// program to stuff into the text frame
//
do
{
//
// tempFile is used to communicate data to and from the
// external program
//
var tempFile = File(Folder.temp + "/tempText.txt");
//
// Check if we're in the "idle" phase - waiting for the external
// program to finish
//
if (theItem.eventCode == "idle")
{
//
// We check whether InDesign is still modal locked. If so, then
// the external program has not finished yet - bail out of the event
// handler. In a second or so, on the next idle event, we'll give
// it another go
//
var indesignModalLocked = app.callExtension(0x90b6C,10003);
if (indesignModalLocked)
break;
//
// The modal lock is gone - so the external program is finished.
// We restore the event filter to what it was before it all started
//
theItem.eventFilter = "subjectModified-recomposed-overset";
//
// Did the external program communicate some data back to us?
// If so, then it is in the temporary file
//
if (! tempFile.exists)
break;
//
// Read the edited text and stuff it back into the story being edited
// We've stored a reference to the story in the data store associated to
// theItem
//
tempFile.open("r");
var editedStory = theItem.getDataStore("editedStory");
editedStory.contents = tempFile.read();
tempFile.close();
//
// And we're done for now!
//
break;
}
//
// Ok, we're handling a subjectModified-recomposed-overset event here.
//
var theDocument = GetParentDocument(theItem);
//
// We need the document's path to find the satellite app. If the document
// has not been saved yet, there is no path - so bail out
//
if (! theDocument.saved)
break;
//
// Is this a Mac or a PC? The Mac uses .app files, the PC uses .exe
//
var isMac = $.os.charAt(0) == "M";
if (isMac)
{
var theSatelliteApp = File(theDocument.fullName.parent + "/ExampleInDesignSatellite.app");
}
else
{
var theSatelliteApp = File(theDocument.fullName.parent + "/ExampleInDesignSatellite.exe");
}
//
// If we cannot find the satellite app, bail out
//
if (! theSatelliteApp.exists)
break;
//
// Write the overset story to a temporary text file
//
var theStory = theItem.eventSource.parentStory;
tempFile.open("w");
tempFile.write(theStory.contents);
tempFile.close();
//
// If we cannot find the temp file we just created, bail out
//
if (! tempFile.exists)
break;
//
// Open the temp file with the satellite app, and use flag "4"
// which means: lock InDesign into a user-modal mode until
// the satellite app terminates
//
app.launchWith(tempFile.fsName,theSatelliteApp.fsName,4);
//
// Change the event filter to process idle events - so we
// can regularly check whether the satellite app has
// terminated or not
//
theItem.eventFilter = "idle";
//
// We need to remember the story so we can put the edited
// text somewhere later on
//
theItem.setDataStore("editedStory",theStory);
}
while (false);
// End of event handler. Utility functions below
function GetParentDocument(pageItem)
{
var document = null;
do
{
var err;
try
{
document = pageItem.parent;
}
catch(err)
{
document = null;
}
if (document == null)
{
break;
}
if (document instanceof Document)
{
break;
}
if (document == pageItem)
{
document = null;
break;
}
pageItem = document;
}
while (true);
return document;
}
Monday, October 29, 2007
Wednesday, August 22, 2007
Going overseas
The next podcast will be a bit delayed - I am travelling to Europe to run some developer trainings, and I have not had the time to get my next podcast done. Stay tuned!
Tuesday, August 7, 2007
Lightning Brain Podcast: Click here to listen to "Writing code like a story"
Book mentioned in podcast:
Code Craft
The practice of writing excellent code
by Pete Goodliffe
ISBN 1-59327-119-0
Rough transcript of podcast:
Writing code like a story
Hi, my name is Kris Coppieters from Rorohiko. This is the third Lightning Brain podcase - writing code like a story.
I've been programming for over 30 years now - and my programming style has changed over time, to suit new languages, to suit new ideas, to suit programming styles.
If I look back at code I wrote, say, ten years ago, the changes are not all that great when compared to code I wrote earlier; I seem to have settled into a number of habits that work well, and have not felt much need to change.
Many of the things I do also seem to coincide with the advice given in many books about coding style - most of it is common sense.
I want to make it clear that some of my preferences are just that - preferences; sometimes, as a coder, you get 'locked in' to a certain approach. There are often many other approaches that are at least as valid, but there is no clear benefit to switch between them, so you stay 'locked' into a particular way of doing things.
When I was younger, I would let myself be enticed into endless debates - things like where to put the braces {} and whether to use tabs or spaces, that kind of stuff.
Through experience, I've learned that those things are highly irrelevant. I have found that consistency is more important rather than what your are consistent in.
So, if you like the braces one way or another, you won't get any argument from me. But you will get an argument if your braces are sprinkled one way here, another way there. To me that's a bit the same as having to read a book where the font size jumps up and down at random: it makes reading the stuff harder for no reason.
I often have to work on other people's code - and currently, my approach is to follow whatever consistent coding convention is used in each individual source file. I have no trouble with a project that consists of source files created by individuals who all had different ideas on how to structure their code - as long as there is a consistency to it within each source code file.
I know of developers that cannot stand working on source code that is not exactly structured 'their' way. I consider such inability a major disadvantage: these people tend to waste large amounts of time on 'restructuring' some perfectly consistent piece of code.
I strive to write code with as few comments as possible. Yes - you heard that right. I think comments are a last-resort type of thing.
I try to write the code first and foremost in such a way that it is as clear and as easy to understand as possible; I think this might be more of an art than a technique.
I only use comments if I cannot make everything crystal clear by means of clean code. You do need as little comments as possible, but no less. Gratuitous comments that simply re-state what the code does are a no-no - things like:
// Increment i
i++;
// This is the constructor
make me cringe.
Comments are dangerous - they have a tendency to get outdated as the code around them evolves, and instead of being helpful, they often become a liability. So it is important to try hard to make the code so clear it does not need comments.
Instead, I restructure and rewrite my code until it is as close to self-explanatory as possible.
My priorities are always to write readable and understandable code first, and efficient code second. When someone else (often me) needs to pick up the code in a few months or years, the most important goal is to make it easy on the developer to grasp the meaning of the code. Not doing so is inviting bugs to be introduced during maintenance by a developer who only half-grasps or half-remembers what is going on.
Often, if the code cannot be made self-explanatory, that is a symptom that something is fundamentally wrong with the approach used, and you need to take a step back and rethink things.
Writing good code is almost the same as writing a good book or a good story: it has good content, is easy to understand, is consistent in as many respects as possible, is nicely laid out. Code also needs those traits.
I also pay a lot of attention to the code layout and neatness. Small example: I will often order all of the functions, procedures and declarations and alike in alphabetical order.
I know that many IDEs make it easy to 'jump' around to functions in a source code file - but sometimes I find myself separated from my finely honed development environment, working in an unfamiliar debugger, or staring at my source code in printed form, or using WordPad to view the code. Having everything alphabetical makes it easy to guess which direction to scroll to find something in the source code.
Just recently I also figured out a way to describe how I like to pick names for things.
One basic rule of thumb is: short scope allows short names. For example, if I need an index variable, and it will be needed for just two or three lines, I am perfectly happy to call it 'idx' or even 'i'. If the scope gets larger, and spans a few tens of lines, I will make the index name more descriptive - for example 'spreadIdx' or 'pageIdx'. If there are similar variables around, I find what sets them apart and name them accordingly, making sure they are not easily confused - unless in rare circumstances, I'll never use variables like 'idx' and 'idx2'.
Names that have large scope (e.g. span multiple source files) are often longer, and often tell a little story (but again - not too long; it's easy to go overboard).
When creating names, I also try to be consistent in how a name gets formed, and I will often build name that first states the more general and then the more specific.
Similar names have a similar 'lead-in', so in case they get ordered alphabetically (e.g. in a debugger or some IDEs), similar things end up 'close to' other similar things. For example, I'll use
const kFileName_Template = "bla.indt";
const kFileName_MainDocument = "yaya.ind";
instead of
const kTemplate_FileName = "bla.indt";
const kMainDocument_FileName = "yaya.ind";
Till next time!
Code Craft
The practice of writing excellent code
by Pete Goodliffe
ISBN 1-59327-119-0
Rough transcript of podcast:
Writing code like a story
Hi, my name is Kris Coppieters from Rorohiko. This is the third Lightning Brain podcase - writing code like a story.
I've been programming for over 30 years now - and my programming style has changed over time, to suit new languages, to suit new ideas, to suit programming styles.
If I look back at code I wrote, say, ten years ago, the changes are not all that great when compared to code I wrote earlier; I seem to have settled into a number of habits that work well, and have not felt much need to change.
Many of the things I do also seem to coincide with the advice given in many books about coding style - most of it is common sense.
I want to make it clear that some of my preferences are just that - preferences; sometimes, as a coder, you get 'locked in' to a certain approach. There are often many other approaches that are at least as valid, but there is no clear benefit to switch between them, so you stay 'locked' into a particular way of doing things.
When I was younger, I would let myself be enticed into endless debates - things like where to put the braces {} and whether to use tabs or spaces, that kind of stuff.
Through experience, I've learned that those things are highly irrelevant. I have found that consistency is more important rather than what your are consistent in.
So, if you like the braces one way or another, you won't get any argument from me. But you will get an argument if your braces are sprinkled one way here, another way there. To me that's a bit the same as having to read a book where the font size jumps up and down at random: it makes reading the stuff harder for no reason.
I often have to work on other people's code - and currently, my approach is to follow whatever consistent coding convention is used in each individual source file. I have no trouble with a project that consists of source files created by individuals who all had different ideas on how to structure their code - as long as there is a consistency to it within each source code file.
I know of developers that cannot stand working on source code that is not exactly structured 'their' way. I consider such inability a major disadvantage: these people tend to waste large amounts of time on 'restructuring' some perfectly consistent piece of code.
I strive to write code with as few comments as possible. Yes - you heard that right. I think comments are a last-resort type of thing.
I try to write the code first and foremost in such a way that it is as clear and as easy to understand as possible; I think this might be more of an art than a technique.
I only use comments if I cannot make everything crystal clear by means of clean code. You do need as little comments as possible, but no less. Gratuitous comments that simply re-state what the code does are a no-no - things like:
// Increment i
i++;
// This is the constructor
make me cringe.
Comments are dangerous - they have a tendency to get outdated as the code around them evolves, and instead of being helpful, they often become a liability. So it is important to try hard to make the code so clear it does not need comments.
Instead, I restructure and rewrite my code until it is as close to self-explanatory as possible.
My priorities are always to write readable and understandable code first, and efficient code second. When someone else (often me) needs to pick up the code in a few months or years, the most important goal is to make it easy on the developer to grasp the meaning of the code. Not doing so is inviting bugs to be introduced during maintenance by a developer who only half-grasps or half-remembers what is going on.
Often, if the code cannot be made self-explanatory, that is a symptom that something is fundamentally wrong with the approach used, and you need to take a step back and rethink things.
Writing good code is almost the same as writing a good book or a good story: it has good content, is easy to understand, is consistent in as many respects as possible, is nicely laid out. Code also needs those traits.
I also pay a lot of attention to the code layout and neatness. Small example: I will often order all of the functions, procedures and declarations and alike in alphabetical order.
I know that many IDEs make it easy to 'jump' around to functions in a source code file - but sometimes I find myself separated from my finely honed development environment, working in an unfamiliar debugger, or staring at my source code in printed form, or using WordPad to view the code. Having everything alphabetical makes it easy to guess which direction to scroll to find something in the source code.
Just recently I also figured out a way to describe how I like to pick names for things.
One basic rule of thumb is: short scope allows short names. For example, if I need an index variable, and it will be needed for just two or three lines, I am perfectly happy to call it 'idx' or even 'i'. If the scope gets larger, and spans a few tens of lines, I will make the index name more descriptive - for example 'spreadIdx' or 'pageIdx'. If there are similar variables around, I find what sets them apart and name them accordingly, making sure they are not easily confused - unless in rare circumstances, I'll never use variables like 'idx' and 'idx2'.
Names that have large scope (e.g. span multiple source files) are often longer, and often tell a little story (but again - not too long; it's easy to go overboard).
When creating names, I also try to be consistent in how a name gets formed, and I will often build name that first states the more general and then the more specific.
Similar names have a similar 'lead-in', so in case they get ordered alphabetically (e.g. in a debugger or some IDEs), similar things end up 'close to' other similar things. For example, I'll use
const kFileName_Template = "bla.indt";
const kFileName_MainDocument = "yaya.ind";
instead of
const kTemplate_FileName = "bla.indt";
const kMainDocument_FileName = "yaya.ind";
Till next time!
Wednesday, August 1, 2007
Lightning Brain Podcast: Click here to listen to "Fun with Drop Shadows in InDesign"
Example Files:
Click here to download sample script and sample document
Example result:

Rough Transcript of Podcast:
Hi, my name is Kris Coppieters from Rorohiko, and this is my second 'Lighting Brain' podcast - about having fun with drop shadows in InDesign.
Initially, I'll try to create a number of podcasts in fairly quick succession, as to build up some content on the Rorohiko blog, but in the long run I aim to release a new podcast about once every two weeks.
This episode I wanted to talk a bit about some fun experiments I did with drop shadows in InDesign. My brain seems to be a magnet for ideas - which would be fine, if only all ideas that pop up would be useful. Sadly enough, some of my ideas are rather silly, and this podcast is based on one of the silly ones.
The podcast will also delve a little bit into some mathematical aspects of my experiments. However, even if you are not mathematically inclined you should still be able to have fun with the example document and example script - so even if the mathematics gives you the blue shivers, don't worry! Just download the sample files and have fun!
My idea was this: assume there are a number of page items scattered over a page or spread. Why not coordinate all these drop shadows so they cause some visual effect to occur? Standard drop shadows are rather dull. They simulate what would happen if there was a light source at infinite distance throwing light on a page item floating at a particular height above the page.
What I was wondering about was: what would happen if the simulated light source was not at infinite distance, but instead somewhere else - for example, located on the viewer's head, close to the paper, like a head-torch, or if the light source was simulating one of those swiveling desk lights.
The other thing I wondered about was: what if the page items that are throwing drop shadows would be pretending to float at smoothly varying distances from the page - for example: page items near the middle of the page would float higher, and page items near the edges would float lower above the page.
So, I set out to do some experiments using InDesign and ExtendScript, and it turns out the results are interesting - interesting enough to share as a podcast.
I started with the following assumptions and limitations:
All distances are expressed in points
Pages and spreads have a coordinate system: an X-axis pointing to the right, a Y-axis pointing down. The origin of the coordinate system can be pretty much anywhere, but often it is somewhere in the upper left hand corner of the page or spread. In the script, I rather arbitrarily use X-Y coordinates relative to an origin that sits in the middle of each page.
I introduce a third axis - a Z-axis which is assumed to point out of the page towards the viewer. A page item that floats above the page will have a positive Z-coordinate. A page item that does not float but sits on the page has a Z-coordinate equal to zero. Because pages are considered to be opaque, negative Z-coordinates don't make much sense in this model, as they would simulate page items behind the page.
I assume there is a simulated light source hovering at some point above the page. The point has some X and Y coordinates (which express the point on the page above which the light source is located) and a Z coordinate (which expresses how high above the page the light source is hovering).
As far as the page items go: in addition to their X- and Y-coordinate data, selected page items are assumed to also have a Z-coordinate which shows how high above the page these page items are supposed to be floating (so they can cast a shadow).
I simplified things by representing each page item by a single point - the page item's center, which is easily calculated as the mean values of the bounding box X- and Y-coordinates.
To get an interesting Z-coordinate I used a mathematical formula that takes these X and Y coordinates and returns a Z coordinate.
One of the formulas in the example scripts gives a Z coordinate that has its highest values for page items in the middle of the page, and gets lower for page items near the borders of the page (for the mathematically inclined: an elliptic paraboloid).
To calculate the parameters of the drop shadow of each individual page item, I went back to some of my high-school geometry formulas. Using the (X,Y,Z) coordinates of the light source and the (X,Y,Z) coordinates of each page item's center, I calculated the amount of X- and Y shift to apply to the drop shadows.
To work the magic, I also assumed that the script would only affect page items that are located on a page layer - I called the layer "magic carpet" (all lowercase, one space). Only page items on this layer are affected by my example script.
Finally, to get some visuals, I decided to first create a page filled with a regular pattern of square page items. The script will of course work with any kind and any amount of page items on the "magic carpet" layer, but I expected the best visual effect with a regular spaced grid of page items.
I created a new document, created a layer "magic carpet". Then I used two step-and-repeat operations to sprinkle a host of small, colored squares all over the page (about 6 squares horizontally and 11 squares vertically, with a good gap between them). You first create a single square, and use step-and-repeat with a horizontal displacement of..., then select the row of squares, and do a second step-and-repeat with a vertical displacement of ...)
The I ran my little script - and suddenly I was presented with quite a nice 3-D effect.
The script and example documents I used are available for download from this blog (rorohiko.blogspot.com).
Step-by-step:
On InDesign CS or CS2, you install the file 'ShadowDance.js' into the Presets - Scripts subfolder of your InDesign application folder. The script uses .js as its file name extension instead of .jsx - that way the same script works on CS as well as CS2 and CS3.
On InDesign CS3, you install the file 'ShadowDance.js' into one of the script folders - I installed it in Scripts - Scripts Panel.
Launch InDesign and create a new document.
Create a new layer called 'magic carpet'.
On this layer, create a single square, about 60x60 points in size (about 20 mm x 20 mm); position it in the top left hand corner.
Fill the square with a color.
Use Edit - Step and Repeat... to create five or six duplicates of the square, with a horizontal offset slightly larger than the side of the square (e.g. 72 pt or 25 mm), and a vertical offset of zero.
Select the whole row of squares, and create 9 or 10 duplicates of the row of squares, this time with a horizontal offset of zero and a vertical offset slightly larger than the side of the square.
You should now have a page that is covered with a regular grid of square page items, all of which sit on the 'magic carpet' layer.
Bring up your scripts pallette (Window - Scripting - Scripts in CS, Window - Automation - Scripts in CS2 and CS3. In CS3, look under 'Application' if you installed the script into the Scripts - Scripts Panel folder).
Double-click the script 'ShadowDance.js'. You should get a result that looks similar to what you find in the example document 'ShadowDanceCS.indd'.
Till next time!
Click here to download sample script and sample document
Example result:
Rough Transcript of Podcast:
Hi, my name is Kris Coppieters from Rorohiko, and this is my second 'Lighting Brain' podcast - about having fun with drop shadows in InDesign.
Initially, I'll try to create a number of podcasts in fairly quick succession, as to build up some content on the Rorohiko blog, but in the long run I aim to release a new podcast about once every two weeks.
This episode I wanted to talk a bit about some fun experiments I did with drop shadows in InDesign. My brain seems to be a magnet for ideas - which would be fine, if only all ideas that pop up would be useful. Sadly enough, some of my ideas are rather silly, and this podcast is based on one of the silly ones.
The podcast will also delve a little bit into some mathematical aspects of my experiments. However, even if you are not mathematically inclined you should still be able to have fun with the example document and example script - so even if the mathematics gives you the blue shivers, don't worry! Just download the sample files and have fun!
My idea was this: assume there are a number of page items scattered over a page or spread. Why not coordinate all these drop shadows so they cause some visual effect to occur? Standard drop shadows are rather dull. They simulate what would happen if there was a light source at infinite distance throwing light on a page item floating at a particular height above the page.
What I was wondering about was: what would happen if the simulated light source was not at infinite distance, but instead somewhere else - for example, located on the viewer's head, close to the paper, like a head-torch, or if the light source was simulating one of those swiveling desk lights.
The other thing I wondered about was: what if the page items that are throwing drop shadows would be pretending to float at smoothly varying distances from the page - for example: page items near the middle of the page would float higher, and page items near the edges would float lower above the page.
So, I set out to do some experiments using InDesign and ExtendScript, and it turns out the results are interesting - interesting enough to share as a podcast.
I started with the following assumptions and limitations:
All distances are expressed in points
Pages and spreads have a coordinate system: an X-axis pointing to the right, a Y-axis pointing down. The origin of the coordinate system can be pretty much anywhere, but often it is somewhere in the upper left hand corner of the page or spread. In the script, I rather arbitrarily use X-Y coordinates relative to an origin that sits in the middle of each page.
I introduce a third axis - a Z-axis which is assumed to point out of the page towards the viewer. A page item that floats above the page will have a positive Z-coordinate. A page item that does not float but sits on the page has a Z-coordinate equal to zero. Because pages are considered to be opaque, negative Z-coordinates don't make much sense in this model, as they would simulate page items behind the page.
I assume there is a simulated light source hovering at some point above the page. The point has some X and Y coordinates (which express the point on the page above which the light source is located) and a Z coordinate (which expresses how high above the page the light source is hovering).
As far as the page items go: in addition to their X- and Y-coordinate data, selected page items are assumed to also have a Z-coordinate which shows how high above the page these page items are supposed to be floating (so they can cast a shadow).
I simplified things by representing each page item by a single point - the page item's center, which is easily calculated as the mean values of the bounding box X- and Y-coordinates.
To get an interesting Z-coordinate I used a mathematical formula that takes these X and Y coordinates and returns a Z coordinate.
One of the formulas in the example scripts gives a Z coordinate that has its highest values for page items in the middle of the page, and gets lower for page items near the borders of the page (for the mathematically inclined: an elliptic paraboloid).
To calculate the parameters of the drop shadow of each individual page item, I went back to some of my high-school geometry formulas. Using the (X,Y,Z) coordinates of the light source and the (X,Y,Z) coordinates of each page item's center, I calculated the amount of X- and Y shift to apply to the drop shadows.
To work the magic, I also assumed that the script would only affect page items that are located on a page layer - I called the layer "magic carpet" (all lowercase, one space). Only page items on this layer are affected by my example script.
Finally, to get some visuals, I decided to first create a page filled with a regular pattern of square page items. The script will of course work with any kind and any amount of page items on the "magic carpet" layer, but I expected the best visual effect with a regular spaced grid of page items.
I created a new document, created a layer "magic carpet". Then I used two step-and-repeat operations to sprinkle a host of small, colored squares all over the page (about 6 squares horizontally and 11 squares vertically, with a good gap between them). You first create a single square, and use step-and-repeat with a horizontal displacement of..., then select the row of squares, and do a second step-and-repeat with a vertical displacement of ...)
The I ran my little script - and suddenly I was presented with quite a nice 3-D effect.
The script and example documents I used are available for download from this blog (rorohiko.blogspot.com).
Step-by-step:
On InDesign CS or CS2, you install the file 'ShadowDance.js' into the Presets - Scripts subfolder of your InDesign application folder. The script uses .js as its file name extension instead of .jsx - that way the same script works on CS as well as CS2 and CS3.
On InDesign CS3, you install the file 'ShadowDance.js' into one of the script folders - I installed it in Scripts - Scripts Panel.
Launch InDesign and create a new document.
Create a new layer called 'magic carpet'.
On this layer, create a single square, about 60x60 points in size (about 20 mm x 20 mm); position it in the top left hand corner.
Fill the square with a color.
Use Edit - Step and Repeat... to create five or six duplicates of the square, with a horizontal offset slightly larger than the side of the square (e.g. 72 pt or 25 mm), and a vertical offset of zero.
Select the whole row of squares, and create 9 or 10 duplicates of the row of squares, this time with a horizontal offset of zero and a vertical offset slightly larger than the side of the square.
You should now have a page that is covered with a regular grid of square page items, all of which sit on the 'magic carpet' layer.
Bring up your scripts pallette (Window - Scripting - Scripts in CS, Window - Automation - Scripts in CS2 and CS3. In CS3, look under 'Application' if you installed the script into the Scripts - Scripts Panel folder).
Double-click the script 'ShadowDance.js'. You should get a result that looks similar to what you find in the example document 'ShadowDanceCS.indd'.
Till next time!
Monday, July 30, 2007
Lightning Brain Podcast: Click here to listen to "Getting Started with the Adobe InDesign SDK"
Opinions, ideas and other musings about software development for printing and prepress...
Recommended book list:
| Added remark, 15-Jul-2008: I have since written my own book
| about getting started:
|
| http://www.lulu.com/content/6890858
|
Effective C++
by Scott Myers
Addison-Wesley
ISBN 0-201-92488-9
More Effective C++
by Scott Myers
Addison-Wesley
ISBN 0-201-63371-X
STL Tutorial and Reference Guide
by Musser, Derge & Saini
Addison-Wesley
ISBN 0-201-37923-6
Effective STL
by Scott Myers
Addison-Wesley
ISBN 0-201-74962-9
Design Patterns – Elements of Reusable Object-Oriented Software
by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
Addison-Wesley
ISBN 0-201-63361-2
The C++ Programming Language (Special 3rd Edition)
by Bjarne Stroustrup
Addison-Wesley
ISBN 0-201-70073-5
Links:
Boost:
http://www.boost.org
Adobe InDesign Developer Centre:
http://www.adobe.com/devnet/indesign/sdk/
Rough podcast transcript:
InDesign can be approached in two ways - either you can use a high-level approach, using one of supported script languages, or you can use a low-level approach, using C++.
The high-level approach is often sufficient for automating various repetitive tasks; we'll discuss this high-level approach in more detail in some future podcasts. At Rorohiko, for our own sofware development, we always try to achieve as much as possible using scripting, because development-wise scripting is so much more cost-efficient when compared to using the low-level approach and the SDK.
The low-level approach becomes necessary when it comes to really tight integration, for example, getting invoived in the various drawing processes, or providing new user-interface elements. Then there is no other alternative but to use the InDesign SDK and C++ to achieve the desired results.
The main aim of this particular podcast is to offer some insights into the low-level approach using C++.
If you are a software developer, you might already have experience with a number of environments and languages - QuarkXPress XTension development, C, C++, Java, JavaScript,... and expect to ease into InDesign development with roughly the same amount of effort as you have needed when you started using these other environments and languages.
The InDesign SDK is probably very different to anything you've ever handled before.
The actual InDesign SDK is not very difficult. Granted, it is very extensive, but the concepts behind it are not much different from those in other, similar SDKs.
The main difference is that the InDesign SDK is built on top of a fair number of other methodologies and concept, all fairly recent developments. If you try to step into the InDesign SDK without being well versed in most of these underlying foundations, nothing much will make sense.
A would-be InDesign SDK developer should keep in mind that there are no shortcuts: you must first cover the basics before trying to work with the InDesign SDK, at the risk of repeatedly losing a lot of head-scratch time trying to understand things that are actually quite simple.
It would be like trying to build and launch a satellite without first studying mathematics and physics.
So, these are the four things you need to do before you can get started:
1) You MUST have a very good grasp of C++ and various techniques. This is the first and foremost requirement. My recommendation is to at least read Scott Meyer's books (Effective C++, More Effective C++) a few times, especially if you come from a C background. I am sure even experienced C++ programmers who have not yet read these books will learn some important new things.
2) You must gave a grasp of C++ Standard Library, specifically the STL (Standard Template Library), and the boost C++ Libraries (Boost provides free peer-reviewed portable C++ source libraries).
You don't need to become an expert on these, but you need to have a good idea about what a vector is, what an iterator is, and how you use them. You also need to get a good idea of the 'mindset' behind STL and boost, and how C++ templates are used in a clever way to generate a lot of magic.
3) You must be able to read UML diagrams. UML diagrams use a number of similar, but slightly different symbols to express relationships between things. Unless you know what the symbols mean, you'll miss out on a lot of information that is packed into the UML diagrams inside the InDesign SDK documentation.
4) You must have had some exposure to the idea of 'Software Patterns' - the book 'Design Patterns' by the gang of four is highly recommended. You don't need to read this book from cover to cover, but you should at least read about the most important patterns and ideas.
Once these four requirements are fulfilled (good grasp of C++, grasp of STL and boost, understanding UML, grasp of common software patterns) you are ready to tackle InDesign SDK programming.
Recommended book list:
| Added remark, 15-Jul-2008: I have since written my own book
| about getting started:
|
| http://www.lulu.com/content/6890858
|
Effective C++
by Scott Myers
Addison-Wesley
ISBN 0-201-92488-9
More Effective C++
by Scott Myers
Addison-Wesley
ISBN 0-201-63371-X
STL Tutorial and Reference Guide
by Musser, Derge & Saini
Addison-Wesley
ISBN 0-201-37923-6
Effective STL
by Scott Myers
Addison-Wesley
ISBN 0-201-74962-9
Design Patterns – Elements of Reusable Object-Oriented Software
by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
Addison-Wesley
ISBN 0-201-63361-2
The C++ Programming Language (Special 3rd Edition)
by Bjarne Stroustrup
Addison-Wesley
ISBN 0-201-70073-5
Links:
Boost:
http://www.boost.org
Adobe InDesign Developer Centre:
http://www.adobe.com/devnet/indesign/sdk/
Rough podcast transcript:
InDesign can be approached in two ways - either you can use a high-level approach, using one of supported script languages, or you can use a low-level approach, using C++.
The high-level approach is often sufficient for automating various repetitive tasks; we'll discuss this high-level approach in more detail in some future podcasts. At Rorohiko, for our own sofware development, we always try to achieve as much as possible using scripting, because development-wise scripting is so much more cost-efficient when compared to using the low-level approach and the SDK.
The low-level approach becomes necessary when it comes to really tight integration, for example, getting invoived in the various drawing processes, or providing new user-interface elements. Then there is no other alternative but to use the InDesign SDK and C++ to achieve the desired results.
The main aim of this particular podcast is to offer some insights into the low-level approach using C++.
If you are a software developer, you might already have experience with a number of environments and languages - QuarkXPress XTension development, C, C++, Java, JavaScript,... and expect to ease into InDesign development with roughly the same amount of effort as you have needed when you started using these other environments and languages.
The InDesign SDK is probably very different to anything you've ever handled before.
The actual InDesign SDK is not very difficult. Granted, it is very extensive, but the concepts behind it are not much different from those in other, similar SDKs.
The main difference is that the InDesign SDK is built on top of a fair number of other methodologies and concept, all fairly recent developments. If you try to step into the InDesign SDK without being well versed in most of these underlying foundations, nothing much will make sense.
A would-be InDesign SDK developer should keep in mind that there are no shortcuts: you must first cover the basics before trying to work with the InDesign SDK, at the risk of repeatedly losing a lot of head-scratch time trying to understand things that are actually quite simple.
It would be like trying to build and launch a satellite without first studying mathematics and physics.
So, these are the four things you need to do before you can get started:
1) You MUST have a very good grasp of C++ and various techniques. This is the first and foremost requirement. My recommendation is to at least read Scott Meyer's books (Effective C++, More Effective C++) a few times, especially if you come from a C background. I am sure even experienced C++ programmers who have not yet read these books will learn some important new things.
2) You must gave a grasp of C++ Standard Library, specifically the STL (Standard Template Library), and the boost C++ Libraries (Boost provides free peer-reviewed portable C++ source libraries).
You don't need to become an expert on these, but you need to have a good idea about what a vector is, what an iterator is, and how you use them. You also need to get a good idea of the 'mindset' behind STL and boost, and how C++ templates are used in a clever way to generate a lot of magic.
3) You must be able to read UML diagrams. UML diagrams use a number of similar, but slightly different symbols to express relationships between things. Unless you know what the symbols mean, you'll miss out on a lot of information that is packed into the UML diagrams inside the InDesign SDK documentation.
4) You must have had some exposure to the idea of 'Software Patterns' - the book 'Design Patterns' by the gang of four is highly recommended. You don't need to read this book from cover to cover, but you should at least read about the most important patterns and ideas.
Once these four requirements are fulfilled (good grasp of C++, grasp of STL and boost, understanding UML, grasp of common software patterns) you are ready to tackle InDesign SDK programming.
Started a blog
Everyone is blogging - so I decided to give that a go too. This is the Rorohiko blog. Expect various posts, rants, musings,... about development, printing and prepress, various programming languages, mathematics, photography,... The next few posts will just be some testing material - trying out getting a podcast going, that kind of stuff.
Subscribe to:
Posts (Atom)