Showing posts with label InCopy. Show all posts
Showing posts with label InCopy. Show all posts

2010-05-10

Paragraphs of Selection

Handy for certain tasks
This is another function that I tend to use when needing to work with paragraphs. I recently came across it again and thought it was fine time to share.
The basic premise is that if the user has some text selected and you want to automate something with regard to the whole paragraphs that have any part selected, then you need something like this.
With all the ifs, switches, etc., this code sure does go marching off to the right, but that is just the way it is. It doesn't have to be pretty, it just has to work.
Comments?

//
function getParagraphsOfSelection () {
//----------------------------------------------------------------------------
//-- G E T P A R A G R A P H S O F S E L E C T I O N
//----------------------------------------------------------------------------
//-- Generic: Yes.
//----------------------------------------------------------------------------
//-- Purpose: To return an array of paragraphs relating to the selection
//-- or false if there is no selection or an error.
//----------------------------------------------------------------------------
//-- Written: 10 March 2009 by Jon S. Winters from a prior work.
//-- eps@electronicpublishingsupport.com
//----------------------------------------------------------------------------

//-- Determine is there in an appropriate selection.
try {
//-- Initialize the object
var myObject = false ;
//-- Check for a selection.
if(app.documents.length != 0){
if(app.selection.length != 0){
//Process the objects in the selection to create a list of
//qualifying objects (text frames).
switch(app.selection[0].constructor.name){
case "TextFrame":
myObject = ( app.selection[0].parentStory.paragraphs ) ;
break;
default:
if(app.selection.length == 1){
//If text is selected, then get the parent text frame.
switch(app.selection[0].constructor.name){
case "Text":
case "InsertionPoint":
case "Character":
case "Word":
case "Line":
case "TextStyleRange":
case "Paragraph":
case "TextColumn":
myObject = ( app.selection[0].paragraphs );
break;
} //-- end of switch
} //-- end of if selections == 1
break;
} //-- end of switch
} //-- end of app.selection.length
} //-- end of app.documents.length
} //-- end of try
catch (caughtError) {
return {} ;
}
return myObject ;
}//-- End of function
//

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-16

Get Document that Triggered Event

The correct document isn't always the one in front...

Event handlers are ways to run scripts when certain key events happen in Adobe InDesign or Adobe InCopy. Most things can be caught if there is a menu version of that function. Adobe's documentation does a decent job of explaining the common events, but not what to do with the 'event' itself, and is downright poor at the rest of the event handler issues.

But below is a very simple function that you can call inside the event handlers for 'open' and for 'close' event. Imagine you had multiple documents open. Now imagine that the windows are stacked so that you can see the close button for all the windows. Now assume you have an event handler that will do something useful (like look for overset text, preflight, adjust folios, for log a version comment). Well your event handler needs to know which document you just tried to close. That is where this function comes into play.

Event handlers always reference a function. But the function is a specified only in name. You can't pass it anything. Well your event handler will always receive a single argument -- an event. And the event has a property called .target and that .target property has another property .name. And the name of the target of the event is the name of the document.


//
function docRefFromEvent (event) {
//-------------------------------------------------------------------------
//-- D O C R E F F R O M E V E N T
//-------------------------------------------------------------------------
//-- Generic: Yes for ExtendScript CS3, CS4, etc.
//-------------------------------------------------------------------------
//-- Purpose: To return the document triggering the event handler or the
//-- default of the frontmost document.
//-------------------------------------------------------------------------
//-- Arguments: event: the event which triggered some other function which
//-- in turn called this function. Likely it would be an open or a close
//-- function so that you can catch the correct document. For example,
//-- you need this for open handlers becasue if you have two documents
//-- open, documents[0] or .activeDocument will point to the first doc
//-- and not the document you just opened. Likewise with closing files,
//-- your windows could be tiled and you can close a file other than the
//-- active or the documents[0].
//-------------------------------------------------------------------------
//-- Calls: Nothing.
//-------------------------------------------------------------------------
//-- Returns: Either a reference to the front document or the document that
//-- triggered the passed event.
//-------------------------------------------------------------------------
//-- Written: 2010.04.06 by Jon S. Winters of electronic publishing support
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------
//-- Created for version 3.52
if ((event != undefined) && event.hasOwnProperty ('target') && event.target.hasOwnProperty ('name')) {
var testDocRef = app.documents.itemByName (event.target.name) ;
if (testDocRef != null) {
return testDocRef ;
}
}
return app.documents[0] ;
}
//

2009-11-13

Gather Assigned Frames

The Text Frames associated with Adobe InCopy Assignments

On to more things with Adobe InCopy Assignments in Adobe InDesign.
The generic function below will generate a return object with two properties. One contains references to every text frame in the passed document reference that has assigned content. The second segregates the assigned frames by assignment.
Either property will allow you to locate the frames that are used in an assignment.

//
function gatherAssignedFrames ( docRef ) {
//-------------------------------------------------------------------------
//-- G A T H E R A S S I G N E D F R A M E S
//-------------------------------------------------------------------------
//-- Generic: Yes.
//-------------------------------------------------------------------------
//-- Purpose: To return an associative array of all the text frames in the
//-- passed coument that have associated Adobe InCopy Assignments
//-------------------------------------------------------------------------
//-- Arguments:
//-- docRef: An [object Document] to investigate
//-------------------------------------------------------------------------
//-- Calls: Nothing.
//-------------------------------------------------------------------------
//-- Returns: a custom object with two properties
//-- .assignmentFrames: An Associatve Array of Assignment IDs using
//-- the $ prefix as described below.
//-- Each element will contain a property for every text frame
//-- with the text frame ID with the $ prefix.
//-- The contents of these properties will be a reference
//-- to the text frame itself.
//-- .assignedTextFrames: An Associative Array of Text Frame IDs
//-- with each Text Frame ID have a $ prefix. The contents
//-- of each property is a reference to the text frame itself.
//-------------------------------------------------------------------------
//-- $ Prefix on IDs
//-- IDs are prefixed with a $. Thus Text Frame 188 will be returned as
//-- $188. This is done because objects cannot be named with numbers.
//-------------------------------------------------------------------------
//-- Sample Use:
//-- gatherAssignedFrames ( docRef )
//-------------------------------------------------------------------------
//-- Notes: Decide how you want to handled the Unassigned InCopy Content
//-- There will be references to that assignment as well. It is valid.
//-------------------------------------------------------------------------
//-- Written: 2009.11.12 by Jon S. Winters of electronic publishing support
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------

//-- Note, we are using a $ prefix on the object names in the associative
//-- array because pure numbers are not allowed.

//-- Create the return object.
var returnObject = new Object () ;
returnObject.assignmentFrames = new Object () ;
returnObject.assignedTextFrames = new Object () ;

//-- Gather all assignments.
var allAssignments = app.documents[0].assignments ;

//-- Loop through each of the assignments -- backwards for efficiency.
for ( var ai = allAssignments.length - 1 ; ai >= 0 ; ai-- ) {

//-- Get a reference to the current assignment in the loop and its ID.
var activeAssignment = allAssignments[ai] ;
var activeAssignmentID = '$' + activeAssignment.id ;
//-- Add the property to the return object for the current assignment.
returnObject.assignmentFrames[ activeAssignmentID ] = new Object () ;
//-- Get a reference to every assigned story for the assignment.
//-- There can be one or many. Most CMS's use many.
var allAssignedStories = activeAssignment.assignedStories ;

//-- Loop backwards through each story of the active assignment.
for ( var asi = allAssignedStories.length - 1 ; asi >= 0 ; asi-- ) {

//-- Get a reference to the story indicated by the loop index.
var activeStory = allAssignedStories[asi].storyReference ;
//-- Get all the text containers for the active story.
//-- There could be more than one if the text frame is
//-- manually threaded to additonal frames.
var allTextContainers = activeStory.textContainers ;

//-- Finally...
//-- Loop through all the text containers (text frames in most
//-- cases) and add the container ID with a $ prefix to both
//-- main properties of the return object.
for ( var tci = allTextContainers.length - 1 ; tci >= 0 ; tci-- ) {
//-- Get a reference to the container indicated by the loop.
var activeTextContainer = allTextContainers[tci] ;
var activeTextContainerID = '$' + activeTextContainer.id ;

//-- Add the IDs to the two objects
returnObject.assignedTextFrames[ activeTextContainerID ] = activeTextContainer ;
returnObject.assignmentFrames[ activeAssignmentID ][ activeTextContainerID ] = activeTextContainer ;
}
}
}
return returnObject ;
}
//

2009-11-12

Delete Empty Assignments

Part of a Document Cleanup Routine

I've been working with Adobe InCopy Assignments again.
When users delete frames that used to have assigned stories, the assignment remains with the Adobe InDesign document. If the assignment manages to get updated (it can happen easily) then the assignment file will contain no Adobe InCopy stories. What will happen if the user opens the Adobe InCopy story is they will get an ugly message when opening the story and they will see the page where the assigned stories used to be, but they can't edit it.
To put things back, the story needs to get reassigned and re-saved.
But if the original Adobe InDesign document still has the assignment is saved _last_ then the user sees the original page.
This function does nothing other than delete the empty assignments. To fix all ills, you could also delete the assignment file from the file server -- but the better solution is to save the page where the assignment currently exists.


//
function deleteEmptyAssignments ( docRef ) {
//-------------------------------------------------------------------------
//-- D E L E T E E M P T Y A S S I G N M E N T S
//-------------------------------------------------------------------------
//-- Generic: Yes
//-------------------------------------------------------------------------
//-- Purpose: To delete all assignments with no assignedStories
//-------------------------------------------------------------------------
//-- Arguments: docRef: a reference to the document to be processed
//-------------------------------------------------------------------------
//-- Calls: Nothing.
//-------------------------------------------------------------------------
//-- Returns: Boolean true if successful. Boolean false if an error occured
//-------------------------------------------------------------------------
//-- Sample Use: deleteEmptyAssignments ( app.documents[0] )
//-------------------------------------------------------------------------
//-- Notes: Part of a larger function.
//-------------------------------------------------------------------------
//-- Written: 2009.11.12 by Jon S. Winters of electronic publishing support
//-- at the Hilton Universal City, Los Angeles, CA
//-- eps@electronicpublishingsupport.com
//-------------------------------------------------------------------------

try {
//-- Loop through every assignment.
var allAssignments = docRef.assignments ;
for ( var aai = allAssignments.length - 1 ; aai >= 0 ; aai-- ) {
//-- if the assignment isn't the special 'Unassigned' assignmetn and if
//-- the assignment has no assigned stories, then remove it.
if ( ( allAssignments[aai].assignedStories.length == 0 ) && ( allAssignments[aai].name != 'Unassigned InCopy Content' ) ) {
allAssignments[aai].remove() ;
}
}
return true ;
}
catch ( failSilently ) {
var errorObject = failSilently ;
return false ;
}
}

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