Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

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 ;
}
//

2009-11-03

Remove Column of Text from Adobe InDesign or Adobe InCopy Story

Parent function from a prior post

A prior post listed the removeColumn function which can remove a delimited column from a string. This is the wrapper that calls that function to remove a column of text from a specified set of paragraphs within an Adobe InDesign or Adobe InCopy story.

If you want it to work on a set of selected paragraphs then pass it:
app.selection[0].paragraphs
which will provide the paragraphs of the selection even if the user does an imperfect (read faster ) selection. The sample as shown would do the entire story.

As an alternative, check out the paragraphs collection method which is oddly named
.itemByRange(start,stop)
as that allows you to point to a specific range of paragraphs.

As with with the removeColumn function, this is great for cleaning up sports agate text where you need to remove columns from a set of paragraphs.


//
function removeColumnFromParagraphs ( pgraphs , colToKill , delimiter ) {
//-------------------------------------------------------------------------
//-- R E M O V E C O L U M N F R O M P A R A G R A P H S
//-------------------------------------------------------------------------
//-- Generic: Yes for Adobe InDesign or Adobe InCopy CS3 or newer
//-------------------------------------------------------------------------
//-- Purpose: To remove a column from some tab delimited text in a
//-- collection of paragraphs.
//-------------------------------------------------------------------------
//-- Arguments: 3
//-- pgraphs: an Adobe InDesign or Adobe InCopy collection of
//-- paragraphs. This is can be generated by something like this:
//-- var pgraphs = story[0].paragraphs ;
//-- colToKill: An integer of zero based column of text to remove.
//-- in tab delimited columns
//-- delimiter: OPTIONAL. If not specified a tab will be used.
//-------------------------------------------------------------------------
//-- Calls: the removeColumn function -- see prior post
//-------------------------------------------------------------------------
//-- Returns: nothing, but alters the text of tha passed text.
//-------------------------------------------------------------------------
//-- Sample Use:
//~ removeColumnFromParagraphs ( app.selection[0].parentStory.paragraphs , 3 )
//~ removeColumnFromParagraphs ( app.selection[0].parentStory.paragraphs , 0 )
//~ removeColumnFromParagraphs ( app.selection[0].parentStory.paragraphs , -1 )
//-------------------------------------------------------------------------
//-- Notes: Can alter formatting of the paragraphs if they are not
//-- consistently formatted. Uses regular expressions and some array
//-- methods which are not format friendly
//-------------------------------------------------------------------------
//-- Edited: 2009.11.01 by Jon S. Winters of electronic publishing support
//-- in the business center at CLT while waiting on a flight.
//-- The basis for the posted function was taken from a long running
//-- section of code.
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
//-- check the third parameter and if not passed
if ( delimiter == undefined ) {
delimiter = '\t' ; // or ' '
}
//-- Get a count of the number of pargraphs in the collection
var numPgraphs = pgraphs.length ;
//-- Loop through every paragraph
for ( var pIndex = numPgraphs-1 ; pIndex >= 0 ; pIndex-- ) {
var textWithColumnRemoved = removeColumn ( pgraphs[pIndex].contents , colToKill , delimiter ) ;

//-- See the original had a return and if the final didn't.
//-- Note the order is important to maximize the speed
if ( ( ! new RegExp ( '\r' ).test ( textWithColumnRemoved ) ) && ( new RegExp ( '\r' ).test ( pgraphs[pIndex].contents ) ) ) {
//-- Add the return back in as the paragraph contents is replaced
pgraphs[pIndex].contents = textWithColumnRemoved + '\r' ;
}
else {
//-- Just set contents to the text without the column
pgraphs[pIndex].contents = textWithColumnRemoved ;
}
//-- continue with loop
}
}
//

2009-11-02

Remove Column of Text

Great for Sports Agate

The following function is something I regularly use for removing a column of text in sports agate cleanup scripts. It is very generic. Pass it a string, a zero based column index, and the delimiter (usually a tab specified as '\t' ) and the function will remove that column.

Note, you would need to call this with something that can process all the paragraphs. As this will only handle one paragraph at a time unless you get real fancy with the calls.

Look for another function to call this one in an upcoming post.

//
function removeColumn ( orig, col, delimiter ) {
//-------------------------------------------------------------------------
//-- R E M O V E C O L U M N
//-------------------------------------------------------------------------
//-- Generic: Yes. Should work for any ECMAScript based languages such as
//-- ExtendScript and JavaScript.
//-------------------------------------------------------------------------
//-- Purpose: To remove a 'delimiter' separated 'col'umn of text from the
//-- 'orig'inal passed string.
//-------------------------------------------------------------------------
//-- Arguments: 3
//-- orig: A string which delimited columns
//-- col: A column number. This can be negative to work from the back
//-- delimiter: a string which is used to delimit columns
//-------------------------------------------------------------------------
//-- Calls: Nothing, but requires the .split(), .splice(), and .join()
//-------------------------------------------------------------------------
//-- Returns: The original string with the specified column removed.
//-------------------------------------------------------------------------
//-- Sample Use:
//~ removeColumn ( "a\tb\tc\td\t\e\tf", -1, '\t' ) ; // "a\tb\tc\td\t\e"
//~ removeColumn ( "a\tb\tc\td\t\e\tf", 2, '\t' ) ; // "a\tb\td\t\e\tf"
//-------------------------------------------------------------------------
//-- Notes: Works great with negative column indexes. -1 is the last column
//-- If removing the last column be careful if you need the text to
//-- retain the final return or line break as this will delete it.
//-------------------------------------------------------------------------
//-- Edited: 2009.11.01 by Jon S. Winters of electronic publishing support
//-- Originally an internal function of a larger function.
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
//-- break the string into an array using the passed delimiter
var sa = orig.split(delimiter) ;
//-- Call inbuilt splice method to remove the specified element of the array
sa.splice(col,1) ;
//-- Join the array into a string using the passed delimater.
return sa.join(delimiter) ;
} //-- end of internal function.
//

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-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 ;
}
//