2009-10-23

Delete Unused Master Pages

Master Pages in Adobe InDesign are a great way to automate the production of pages. But Master Pages, also called Master Spreads, can have a serious impact of production in a negative way too. Every Master Page added to a template or document increases the file size. And sometimes, if the Master Pages aren't built in a efficient and productive manner, the increase in size can be substantial.
And late in the production cycle, that extra file size slows down every save, every print, every export, every open, everything.
The partial solution is to delete the unused Master Pages. And this function does that.


function xUnusedMasters( docRef ) {
//-------------------------------------------------------------------------
//-- X U N U S E D M A S T E R S
//-------------------------------------------------------------------------
//-- Generic: Yes for Adobe InDesign.
//-------------------------------------------------------------------------
//-- Purpose: To delete all the master pages that are not applied to a
//-- a document page or a Master Page that is applied to a document.
//-------------------------------------------------------------------------
//-- Arguments: docRef: the document to examine.
//-------------------------------------------------------------------------
//-- Calls: addMasterName() an internal recursive function to generate
//-- an associative array of master page names that are in use.
//-------------------------------------------------------------------------
//-- Returns: nohting.
//-------------------------------------------------------------------------
//-- Sample Use:
//~ xUnusedMasters( app.documents[0] ) ;
//-- OR:
//~ xUnusedMasters( returnDocumentReference ( pageOfWindow () ) ) ;
//-------------------------------------------------------------------------
//-- Notes: Preserves the entire hierachy of all the master pages that
//-- are based on other master pages.
//-------------------------------------------------------------------------
//-- Written: 2009.10.22 by Jon S. Winters of electronic publishing support
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------

//-- Create an object to record the names of the master pages to keep.
var mpNames = new Object () ;

//-- get a reference to all the pages of the passed document.
var allPages = docRef.pages ;
//-- loop through each page and add to the list of
for ( var pIndex = allPages.length - 1 ; pIndex >= 0 ; pIndex-- ) {
//-- Add the name of the master page applied to this page
//-- to the list of master pages to keep. Using and
//-- Associative Array allows the list to only contain
//-- a single instance if a single master page is used
//-- more than once in the document.
mpNames = addMasterName ( mpNames , allPages[pIndex] ) ;
}
//-- At this point the mpNames object has a property for the
//-- name of every master page that needs to be kept.

//-- Get a reference to all the master pages.
//-- NOTE: the '[None]' master page will not show up in this.
var allMasterPages = docRef.masterSpreads ;
//-- Loop through all the master pages.
for ( var mpIndex = allMasterPages.length - 1 ; mpIndex >= 0 ; mpIndex-- ) {
//-- compare this against the database of master pages to keep
if ( ! mpNames[allMasterPages[mpIndex].name] ) {
//-- If the associative array dosn't have that master page name
//-- then delete the master page.
allMasterPages[mpIndex].remove() ;
}
}
return ; //-- nothing, just return
//-- Below is an internal function to keep the main function generic
function addMasterName ( MPDB , pageRef ) {
//---------------------------------------------------------------------
//-- A D D M A S T E R N A M E
//---------------------------------------------------------------------
//-- Generic: Yes for Adobe InDesign
//---------------------------------------------------------------------
//-- Purpose: A recursive function that adds the name of the master
//-- page applied to the passed pageRef to the passed
//-- associative array MPDB
//---------------------------------------------------------------------
//-- Arguements: 2
//-- MPDB: An Associative Array with a value for each Master Page
//-- used.
//-- pageRef: The page or master page to get and add the master
//-- page name
//---------------------------------------------------------------------
//-- Calls: itself. This is a recursive function.
//---------------------------------------------------------------------
//-- Returns: The associative array MPDB with any new master page
//-- names from the passed pageRef
//---------------------------------------------------------------------
//-- Written entirely from scratch to replace a flawed version.
//-- Written 2009.10.22 by Jon S. Winters
//-- eps@electronicpublishingsupport.com
//---------------------------------------------------------------------

//-- Because the function is recursive, check to see if it was sent
//-- a reference to a non-existent master page. If so, return
//-- to the caller the same object it was sent.
if ( pageRef == null ) { return MPDB ; }

//-- Check to see if the passed pageRef is a page.
//-- If it is a page don't add page name.
//-- But if pageRef is master page then add its name.
if ( pageRef.constructor.name == 'MasterSpread' ) {
MPDB[pageRef.name] = true ;
}
//-- Call the function recursively, but send the appliedMaster
//-- of the passed pageRef and return the value.
return addMasterName ( MPDB , pageRef.appliedMaster ) ;
}//-- end of internal function
}//-- end of xUnusedMasters
//

2009-10-22

Get Page of Window

What is the user looking at?

I've found this function very handy lately. If you are opening files without showing the window ( as strange as that might seem, it can be very useful ), you will need a reference to what the user is viewing. This function returns the page the the Adobe InDesign thinks the user is looking at.

From that page reference you can easily get the document reference (remember it might not be app.activeDocument or app.documents[0]. See some prior posts to know why, but opening documents without showing the window is a really good reason!

Now, for some ideas. If you know the page that the user is looking at, you have ways to control printing of just that page, exporting just that page, adding items to just that page, applying master pages to just that page, etc...

//
function pageOfWindow () {
//-------------------------------------------------------------------------
//-- P A G E O F W I N D O W
//-------------------------------------------------------------------------
//-- Generic: Yes for Adobe InDesign, but not InCopy
//-------------------------------------------------------------------------
//-- Purpose: To return a reference to a the page that Adobe InDesign
//-- considers the active page.
//-------------------------------------------------------------------------
//-- Arguments: None
//-------------------------------------------------------------------------
//-- Calls: Nothing.
//-------------------------------------------------------------------------
//-- Returns: A page reference to the page that Adobe InDesign considers
//-- the active page. If no pages can be found it returns null ;
//-------------------------------------------------------------------------
//-- Sample Use:
//~ var currentPage = pageOfWindow() ;
//-------------------------------------------------------------------------
//-- Notes: The page returned can be a Master Page
//-------------------------------------------------------------------------
//-- Written: 2009.09.22 by Jon S. Winters of electronic publishing support
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
//-- 2009.09.24 added error handling

try {
//-- close any StoryWindows
while ( new RegExp ('Story','i').test( app.activeWindow.reflect.name ) ) {
app.activeWindow.close() ;
}
} catch (err) {
var errorObject = err ;
return null ;
}
//-- Get the page reference for the window displayed in the
//-- active Layout Window
return app.activeWindow.activePage ;
}
//

2009-10-20

Build Menu Item

This is both some simple and some complex code to construct a menu item in a menu.

One advantage of what this does is that it creates a stand alone script in the Scripts Panel folder that can be used to Assign a Keyboard Shortcut to a custom menu item -- something normally not possible.


function buildMenuItem (aMenu, menuItemName, eventFunction , checkItem , addShortcutScript , forceUpdateShortcut ) {
//-------------------------------------------------------------------------
//-- B U I L D M E N U I T E M
//-------------------------------------------------------------------------
//-- Generic: Yes, but calls an external function for creating the
//-- shortcut scripts.
//-------------------------------------------------------------------------
//-- Purpose: To add items and event listners to a passed menu in the
//-- menu bar.
//-------------------------------------------------------------------------
//-- Parameters: 6
//-- aMenu: A menu object created with the 'makeMainMenu' function.
//-- menuItemName: A string for the text as it should appear in the
//-- menu.
//-- eventFunction: This is the name of the function that should be
//-- invoked when the menu item is accessed
//-- checkItem: OPTIONAL. A boolean to indicate that the item should
//-- be checked. The default is false.
//-- addShortcutScript: OPTIONAL. a boolean to indicate that an
//-- auxilary script should be added which could be used as
//-- the recipitant of a keyboard shortcut. The default is false.
//-- forceUpdateShortcut: OPTIONAL, a boolean that when true will
//-- rewrite the shortcut script regarless of if it already exists
//-- we don't want to do this all the time because of the speed
//-- and the chance that it would trigger the keyboard shortcut
//-- to get lost.
//-------------------------------------------------------------------------
//-- Calls:
//-- findScriptsPanelFolder()
//-- buildMenuFolderStructure()
//-------------------------------------------------------------------------
//-- Returns: Nothing.
//-------------------------------------------------------------------------
//-- Written by Jon S. Winters.
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
//-- Edited: 2009.01.23 to add the option to check the menu item.
//-- Edited: 2009.10.06 to start the process of making stand alone scripts
//-- that can be used for shortcuts for each menu item added.
//-- Verify that the checked item has been sent, else assume false
if ( checkItem == undefined ) {
checkItem = false ;
}
//-- Build the pat menu by constructing the action and then the menu item
var mMenuItem = aMenu.menuItems.item( menuItemName ) ;
if ( mMenuItem == null ) {
var mAction = app.scriptMenuActions.add( menuItemName ) ;

//-- Version 2r, add check
mAction.checked = checkItem ;
//alert ("Making menu: " + menuItemName + " has Action ID: " + mAction.id ) ;
var mListener = mAction.eventListeners.add( "onInvoke", eventFunction , false ) ;
var mMenuItem = aMenu.menuItems.add( mAction , LocationOptions.AT_END ) ;
//-- 2009.10.06 start the process of adding keyboard shortcut scripts.
//-- 2009.10.11 Some sub menus use '-' to create separators
//-- 2009.10.11 Also skip when the name doesn't exist
if ( addShortcutScript && ( menuItemName != '-' ) && ( menuItemName != '' ) ) {
try {
//-- Call an external function to determine the location of the
//-- application's Scripts Panel Folder. Then get the name
//-- of the parent menu of the menu item being created.
var shortcutDestination = Folder ( findScriptsPanelFolder() + '/' + buildMenuFolderStructure ( aMenu ) ) ;
//-- Verify that the folder exists because ExtendScript will
//-- lie about the file if the folder isn't there.
if ( shortcutDestination.verify ( ) ) {
//-- Add to that the name of the menu and create a file
//-- Object.
//-- Version 2.67 remove any / in name
var f = File ( shortcutDestination + '/' + menuItemName.replace(new RegExp ( "/","gm"),encodeURIComponent ('⁄')) + '.jsx' ) ;
//-- Test that file object. If it already exists and the function
//-- wasn't asked to overwrite it, skip the process.
if ( ( ! f.exists ) || forceUpdateShortcut ) {
//-- Create a string which will be written to a file with a
//-- .jsx file extension. The string will include a
//-- preprocessor directive to match the running target
//-- engine. No need to match the host applicaiton.
//-- Create a fake event object that can be passed for when
//-- the menu item is created in an loop of an array.
//-- To the reciving function it will look like:
//-- event.target.name
//-- Write a commented header
var fileContents = '//-- Created: ' + new Date () + ' by the ' + arguments.callee.name +' function.\r' ;
//-- if there is a target engine active, include this.
if ( $.engineName != undefined ) {
fileContents += '#targetengine ' + $.engineName + '\r'
}
//-- Now call the passed function and supply it with an object
//-- literal of the menu name to be used when the function
//-- is called from within a loop of an array.
//-- Version 2.67 escape any quotes in the menu item name.
fileContents += eventFunction.name + "({target:{name:\'" + menuItemName.replace (new RegExp ( "([\'\"“”‘’])",'gm'), "\\$1" ) + "\'}})" ;
//-- write the actual file.
f.open ('w') ;
f.write (fileContents) ;
f.close ();
}
}
}
catch ( err ) {
var errorObject = err ;
try {
f.close() ;
}
catch ( err ) {
var errorObject = err ;
}
}
}
}
}
//

Add a Main Menu

Below is code to add a main menu at the right side of InDesign's or InCopy's menu bar:

function makeMainMenu(menuName) {
//-------------------------------------------------------------------------
//-- M A K E M A I N M E N U
//-------------------------------------------------------------------------
//-- Generic: Yes.
//-------------------------------------------------------------------------
//-- Purpose: To add the main menu in the menu bar.
//-------------------------------------------------------------------------
//-- Parameters: menuName: A string for the name of the main menu.
//-------------------------------------------------------------------------
//-- Returns: A menu object.
//-------------------------------------------------------------------------
//-- Written by Jon S. Winters.
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
try {
//-- OPTIONAL: You may want to try to remove the menu if you are
//-- going to change it radically.
removeMenu(menuName) ;
var aMenu = app.menus.item("$ID/Main").submenus.item(menuName);
aMenu.title ; //-- this errors if the menu doesn't exist, That causes
//-- the menu to be added in the catch statement.

//-- Internal function
function removeMenu (killThisMenu) {
var mySampleScriptMenu = app.menus.item("$ID/Main").submenus.item(killThisMenu);
mySampleScriptMenu.remove();
}
}
catch ( err ) {
var aMenu = app.menus.item("$ID/Main").submenus.add(menuName);
}
return aMenu ;
}
//

2009-10-19

Pad Text

The XML encoding and decoding functions posted yesterday call a function pad() which is below.

Padding is the process of adding extra text to ( typically ) the front of a string of numbers to make the string a certain length.
This is a generic function to pad any thing that can be converted to a string to a specified count of digits.
It is unique in that it allows anything to be used as a pad character, though zero padding is the default.

Called by the of epsEntitify() function. That posting can be found here.

function pad ( orig , count , padWith ) {
//-------------------------------------------------------------------------
//-- P A D
//-------------------------------------------------------------------------
//-- Generic: Yes!
//-------------------------------------------------------------------------
//-- Purpose: to pad the 'orig' number or string to a fixed 'count'
//-- characters or digits using the 'padWith' character
//-------------------------------------------------------------------------
//-- Returns a string (even if it doesn't need to be padded)
//-------------------------------------------------------------------------
//-- Sample Calls:
//-- p = pad (7,3,0) ; // '007'
//-- p = pad (21,1) ; // '21'
//-- p = pad ('Right',10,' ') ; // ' Right'
//-- p = pad ('Left',0,' ' ) ; // 'Left'
//-------------------------------------------------------------------------
//-- Written 2009.05.01 at the PDX airport by Jon S. Winters
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
//-- Check arguments
//--default of zero as string
var padChar = "0" ;
//-- but if they sent something, use the first character of it
if ( padWith != undefined ) { padChar = String (padWith).substr (0,1) ; }
//-- Convert to a string (actually adding the "" to it would have worked.)
var withThis = String ( orig ) + '' ;
//-- Check its length, with it is long enought return. Until then keep
//-- adding the padChar
while ( count > withThis.length) {
withThis = padChar + withThis;
}
//-- Back to the caller
return withThis;
}
//