2010-05-03

Insert Glyph

Method to insert additional special characters
A client has been asking me for create an extension of Adobe InCopy and Adobe InDesign's Insert Special Character submenus. This blog already has code posted to create menu items with keyboard shortcuts. The code below actually inserts a glyph at the current insertion point. This code takes a slightly different approach than the Insert Special Characters in that it will not replace as selection, it only works when there is an insertion point.

What is interesting is that it is driven by the same Unicode text that the Info Panel displays when the desired glyph is selected in a story. So to insert a 1/4 fraction glyph you would use:
insertGlyph ('0xBC') ;

That unicode number is internally converted to a different encoding by replacing the 0x with a %u and adding any necessary leading zeros before being unencoded and forced to be a string. There is far more error checking than actual code.

//
function insertGlyph (charCodeOfGlyphToInsert) {
//-------------------------------------------------------------------------
//-- I N S E R T G L Y P H
//-------------------------------------------------------------------------
//-- Generic: Yes for Adobe InDesign and Adobe InCopy CS3 and newer
//-------------------------------------------------------------------------
//-- Purpose: To take a code for a glyph as listed by the Info panel and
//-- insert the the glyph at the insertion point if there is an insertion
//-- point.
//-------------------------------------------------------------------------
//-- Arguments:
//-- charCodeOfGlyphToInsert: a String appearing as 0xEB pr 0x2014
//-------------------------------------------------------------------------
//-- Calls: Nothing.
//-------------------------------------------------------------------------
//-- Returns: nothing truely useful. Does return true if successful, false
//-- if the arguments or the selection is invalid.
//-------------------------------------------------------------------------
//-- Sample Use:
//~ insertGlyph ('0xBC') ; // 1/4 fraction
//~ insertGlyph ('0x401') ; // Capital E with umlaut
//~ insertGlyph ('0x2020') ; // Dagger
//-------------------------------------------------------------------------
//-- Notes: Use the Info Panel with a single character selected to learn
//-- the glyph number. These are unicode numbers or in very simple cases
//-- they are ascii number. But they are Hexidicmal based. However, they
//-- may be large 4 digit numbers or smaller 2 digit numbers based upon
//-- the specific glyph.
//-------------------------------------------------------------------------
//-- Written: 2010.04.25 by Jon S. Winters of electronic publishing support
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------

//-- Verify that what we were passed was a valid glyph number with the 0x
var len = charCodeOfGlyphToInsert.length ;
if (((len != 4) && (len != 5) && (len != 6)) || (charCodeOfGlyphToInsert.substr (0, 2) != '0x')) {
return false ;
}
//-- Verify that the selection is an insertion point, but check docs first
if (1 > app.documents.length) {
return false ;
}
var curSel = app.selection ;
if ((curSel.length != 1) || (curSel[0].constructor.name != 'InsertionPoint')) {
return false ;
}
//-- create the glyph. Not really necessary to make it a spearate variable,
//-- but this makes it easier to debug.
var glyph = String (unescape ('%u' + ('00').substr (0, 6 - len) + charCodeOfGlyphToInsert.substr (2))) ;

//-- insert the glyph
curSel[0].insertionPoints[0].contents = glyph ;
return true ;
}
//

2010-04-26

Determine Publication Volume and Issue

Not just for print publications

This very generic function was designed to determine the publication volume and issue numbers for newspapers publishing 7 days a week. It can be used, however to compare any two dates. Just let volume represent years and issue represent days.

Of interest in this function is the methods of validating the arguments. ECMA script, JavaScript, and ExtendScript don't do nice jobs of validating object types. Looking at a constructor is as about as good as you can get, and even that isn't totally foolproof -- though it would take more than a fool to break it.

Also in this function is one of several ways to create a duplicate date object. In this case creating a new date by parsing the original date. Setting one date to another date with just an = will cause both variables to respond to a method applied to either one.

//
function determineVolumeAndIssue (pubDate, refDate, refVolume) {
//-------------------------------------------------------------------------
//-- D E T E R M I N E V O L U M E A N D I S S U E
//-------------------------------------------------------------------------
//-- Generic: Yes. Should work with any ECMA based scripting language
//-- including ExtendScript and JavaScript.
//-------------------------------------------------------------------------
//-- Purpose: To return an object with the publication volume and
//-- publication issue for a passed publication date given a reference
//-- date and the volume number on that reference date. This function
//-- can also be used to count forwards or backwards in years and days
//-- between
//-------------------------------------------------------------------------
//-- Arguments:
//-- pubDate: a Date object for the publication date. Or, if you are
//-- comparing dates, this is the date you want to investigate.
//-- refDate: a Date object. This date is the date that Issue 1 for the
//-- passed volume number was published (will be published).
//-------------------------------------------------------------------------
//-- Calls: Nothing.
//-------------------------------------------------------------------------
//-- Returns: a custom object with two properties:
//-- .volume: a Number for the volume number
//-- .issue: a Number for the issue number
//-- NOTE: both values are set to -1 if any of the arguments are
//-- incorrect.
//-------------------------------------------------------------------------
//-- Sample Use:
//~ var pubDate = new Date (Date.parse('April 21, 2010')) ;
//~ var refDate = new Date (Date.parse('August 4, 2009')) ;
//~ var refVolume = 101 ;
//~ var vi = determineVolumeAndIssue (pubDate, refDate, refVolume) ;
//~ var volume = vi.volume ;
//~ var issue = vi.issue ;
//-------------------------------------------------------------------------
//-- Notes:
//-- The refDate is the date of Issue 1 for the refVolume. The
//-- function assumes a daily publication schedule.
//-- This version does not work for 1-6 days per week. That code is more
//-- complex and not posted with this version.
//-- Issues are always positive numbers. If the refDate is after the
//-- pubDate, the Volume number will be decremented, but the Issue
//-- number will still be a postive.
//-- See the note about return values
//-------------------------------------------------------------------------
//-- Written: 2010.04.21 by Jon S. Winters of electronic publishing support
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
//-- create the return object with the default error values
var ro = {volume:-1, issue:-1} ;
//-- Verify that the arguments are valid, leaving them as separate if
//-- statuements to make it easier to debug.
if ((typeof pubDate != 'object') || (pubDate.constructor.name != 'Date')) {
return ro ;
}
else if ((typeof refDate != 'object') || (refDate.constructor.name != 'Date')) {
return ro ;
}
else if (isNaN (refVolume = parseInt (refVolume))) {
return ro ;
}
//-- Get a two key comparisons
var y0 = refDate.getYear () ;
var y1 = pubDate.getYear () ;
//-- Create a comparison date to determine if the current date
//-- is before or after the volume referece date.
//-- Using the Date.parse because just setting the date creates
//-- a pointer which then destroys the volumeReferenceDate.
//-- this is slightly slower, but not a deathly slow
var comparisonDate = new Date (Date.parse (refDate)) ;
//-- Move to the same year as the passed date
comparisonDate.setYear (1900 + y1) ;
var timeDifference = comparisonDate.getTime () - pubDate.getTime () ;
//-- Using the time difference determine if we are a before or after the
//-- reference date, add {using the normal ids for Issue and Volume}
//-- calcuate the volume and issue numbers and put them back into
//-- the passed object.
if (0 >= timeDifference) {
//-- we are this number of issues past the volume referece date
ro.issue = String (1 + Math.ceil (Math.abs(timeDifference) / (1000 * 60 * 60 * 24))) ;
ro.volume = String (Math.floor (y1 - y0) + refVolume) ;
}
else {
//-- back up a year.
comparisonDate.setYear (1900 + (y1 - 1)) ;
timeDifference = comparisonDate.getTime () - pubDate.getTime () ;
ro.issue = String (1 + Math.ceil (Math.abs (timeDifference) / (1000 * 60 * 60 * 24))) ;
ro.volume = String (Math.floor ((y1 -1) - y0) + refVolume) ;
}
//-- all done
return ro ;
}
//
//

2010-04-19

Set Text Variables

Part of a routine to set folios
I used to set folios by leaving misspelled tags on pages such as 'mMonth' and then scripting their replacement for folios. The advantage of this is that if a user fails to run the script that does the swap, or if the document doesn't conform to the site's naming convention, the misspelled tags remain and hopefully they would be found during a spell check of the document. Alas, users don't seem to follow the rules.

So, I've changed to using Adobe InDesign text variables for folios. The template would be set to have no values and then at key moments (like opening, and saving as) the values would get set.

The function below makes it easy to set values as it adds the text variable if it doesn't exist.

//
function setCustomTextVariable (docRef, name, value) {
//-------------------------------------------------------------------------
//-- S E T C U S T O M T E X T V A R I A B L E
//-------------------------------------------------------------------------
//-- Generic: Yes for InDesign CS4 and newer.
//-------------------------------------------------------------------------
//-- Purpose: To set a Custom Text Variable value. These can be used for
//-- a multitude of items, but they are commonly used for folios.
//-------------------------------------------------------------------------
//-- Arguments:
//-- docRef: The reference to the document to alter
//-- name: The name of the CustomText Variable
//-- value: The text that the CustomText Variable should be set to.
//-------------------------------------------------------------------------
//-- Calls: Nothing.
//-------------------------------------------------------------------------
//-- Returns: true unless an error occurs, which returns false.
//-------------------------------------------------------------------------
//-- Sample Use:
//~ setCustomTextVariable (app.documents[0], 'Publication', 'Star-Ledger')
//-------------------------------------------------------------------------
//-- Notes: The Text Variable does not have to exist, this will create it.
//-- But if the text variable is not a Custom Text Variable, its value
//-- won't be able to be set, and this function will fail silently. See
//-- returns above.
//-------------------------------------------------------------------------
//-- Written: 2010.04.19 by Jon S. Winters of electronic publishing support
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
try {
var atv = docRef.textVariables.everyItem() ;

if (atv.hasOwnProperty (name)) {
//-- Note, not testing to see if the value can be set. If the TV
//-- isn't a 'Custom' TV, this will fail.
atv [name].variableOptions.contents = value ;
}
else {
//-- Create the TV and set the value.
var newTV = docRef.textVariables.add({name:name, variableType:VariableTypes.CUSTOM_TEXT_TYPE}) ;
newTV.variableOptions.contents = value ;
}
return true ;
}
catch (failSilently) {
var localError = failSilently ;
return false ;
}
}
//