• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Illustrator script crashes at the end every time

New Here ,
Dec 14, 2017 Dec 14, 2017

Copy link to clipboard

Copied

I’ve got a script that preps art in a file for later export as an SVG. I have one step that I’d like to remove (cropTiles()) but when I do the script crashes at the very end each time. Really not sure why.

Here’s the large script (and as a more readable Gist😞

#target illustrator

// Base vars

var doc = app.activeDocument;

// Ensure the document is using the expected coordinates

with (doc) {

pageOrigin = [0, doc.height];

rulerOrigin = [0, doc.height];

}

/**

* Settings

* @type {Object}

*/

var HCo = {

// Action-wide settings

settings: {

// Mutable by user

renameBackgrounds: false,

// Immutable by user

tileCoordinates: [],

backgroundLayerName: 'background',

baseLayer: null,

baseLayerName: 'SOURCE_LAYER',

firstArtboardName: null,

baseArtboard: null,

baseArtboardName: 'SOURCE_ARTBOARD',

docRect: null,

totalTiles: null,

processSteps: 5,

tilesProcessed: 0,

// Form fields that get translated to settings

fields: {

cancelButton: null,

options: null,

progressBar: null,

progressLabel: null,

renameBackgrounds: {

no: {

text: 'I created backgrounds for each tile named “background”',

value: null

},

yes: {

text: 'Attempt to locate and rename existing objects as backgrounds',

value: true

}

},

startButton: null

}

},

/**

* Failure message

* @method failure

* @param  {String} head Header for the alert

* @param  {String} body Body message for the alert

* @return [Boolean] `false` by default

*/

failure: function(head, body) {

alert('   ' + head + '   \n' + body);

return false;

},

/**

* Check for just one artboard and layer

* @method artboardAndLayerCheck

* @return {Boolean}

*/

artboardAndLayerCheck: function() {

// Ensure there are no extra layers or artboards

if (doc.artboards.length > 1 || doc.layers.length > 1) {

alert('Failed initial check.\nThere should be just 1 artboard and 1 layer.');

return false;

}

return this;

},

/**

* Check each group for a background path. If it doesn’t exist and the user has

* requested it, try to find one to repurpose.

* @method checkBackgrounds

*/

checkBackgrounds: function() {

var layer = doc.activeLayer;

var groups = layer.groupItems;

var backgrounds = [];

var failedGroups = [];

var passedGroups = [];

var whichProcess = 'Checking for tile backgrounds…';

this.settings.totalTiles = groups.length;

// Loop through all groups in the layer

for (var i = 0; i < groups.length; i++) {

var group = groups;

var items = group.pathItems;

// Does the group contain pathItems?

if (items.length) {

// Get the last pathItem in the stack

var lastItem = items[items.length - 1];

// If the user chose to rename existing pathItems to “background”

if (HCo.settings.renameBackgrounds === true) {

lastItem.name = this.settings.backgroundLayerName;

backgrounds.push(lastItem);

passedGroups.push(group);

HCo.updateProgress(whichProcess, 1);

// If the user chose to use existing layers named “background”

} else if (HCo.settings.renameBackgrounds === false) {

// … but the last pathItem ISN’T named “background”

if (lastItem.name !== this.settings.backgroundLayerName) {

failedGroups.push(group);

} else {

backgrounds.push(lastItem);

passedGroups.push(group);

HCo.updateProgress(whichProcess, 1);

}

}

}

else failedGroups.push(group);

}

// Check lengths for both background and groups collections.

// If they don’t match, fail and show a message

if (groups.length !== backgrounds.length || failedGroups.length) {

for (var i = 0; i < passedGroups.length; i++) {

passedGroups.hidden = true;

passedGroups.locked = true;

}

return HCo.failure('Missing background layer.', 'Every group should have a bottommost layer named “background”.\n\nI’m locking and hiding all passing groups. Please fix those remaining.');

}

return this;

},

/**

* Format the name using the file name

* @method nameFromFilename

* @return {String} Formatted name

*/

nameFromFilename: function(index) {

return doc.name.replace('.ai', '').replace(' ', '-').toLowerCase() + '_' + index;

},

/**

* Select everything on the artboard and outline the text

* @method outlineText

* @return {null}

*/

outlineText: function() {

// Select only artboard

doc.artboards.setActiveArtboardIndex(0);

// Select everything on the artboard

doc.selectObjectsOnActiveArtboard();

// Find all textFrames and outline the text

var textFrames = doc.textFrames;

var textFramesCount = textFrames.length - 1;

for (var t = textFramesCount; t >= 0; t--) {

var text = textFrames;

text.createOutline();

}

HCo.updateProgress('Outlining all text…', this.settings.totalTiles);

return this;

},

/**

* Convert groups to layers

* @method convertGroupsToLayers

* @return {null}

*/

convertGroupsToLayers: function() {

var groupsCount = this.settings.baseLayer.groupItems.length;

var whichProcess = 'Converting groups to layers…';

// Cycle through all groups in the layer, create a new

// layer, and move the group to the new layer

for (var i = groupsCount - 1; i >= 0; i--) {

var group = this.settings.baseLayer.groupItems;

var layer = doc.layers.add();

var layerName = HCo.nameFromFilename(i);

if (i === 0) this.settings.firstArtboardName = layerName;

layer.name = layerName;

group.move(layer, ElementPlacement.PLACEATEND);

layer.visible = false;

HCo.updateProgress(whichProcess, 1);

}

// Remove the original, now-empty layer

this.settings.baseLayer.remove();

return this;

},

/**

* Get the “visible” bounds of the group

* @method getBounds

* @return {Array} Array containing the top, left, height, and width values

*/

getBounds: function(group) {

var bounds = [];

for (var i = group.pathItems.length - 1; i >= 0; i--) {

if (group.pathItems.typename === "PathItem" && group.pathItems.clipping) {

bounds = group.pathItems.visibleBounds;

}

}

// controlBounds is for groups without a clipping ask and

// visibleBounds is for groups with a clipping mask.

return bounds.length === 0 ? group.controlBounds : bounds;

},

/**

* Gets the position for all four group points and writes

* them to a textBox in the layer

* @method addPositionDataToGroup

* @return {Boolean}

*/

addPositionDataToGroup: function(layer) {

// Must be working in a layer with only one group

var group = layer.groupItems[0];

// Get the dimensions of the active group and push to the tileCoordinates

// array for later use with generating a background layer

var currentGroupRect = HCo.getBounds(group);

this.settings.tileCoordinates.push(currentGroupRect);

// JSON-formatted string to write

var top = Math.round(currentGroupRect[1]);

var left = Math.round(currentGroupRect[0]);

var width = Math.round(currentGroupRect[2] - currentGroupRect[0]);

var height = Math.round(currentGroupRect[1] - currentGroupRect[3]);

// Create and add the caption to the layer

var pathRef = layer.pathItems.rectangle(top - 10, left + 10, width - 20, height - 20);

var caption = layer.textFrames.areaText(pathRef);

var tileData = 'name:' + layer.name + '-top:' + Math.abs(top) + '-left:' + left + '-width:' + width + '-height:' + height;

caption.name = 'data';

caption.contents = tileData;

caption.textRange.hyphenation = false;

caption.textRange.size = 15;

caption.textRange.leading = 18;

caption.textRange.characterAttributes.textFont = app.textFonts.getByName("OperatorMonoSSm-Book");

caption.move(layer, ElementPlacement.PLACEATBEGINNING);

return true;

},

/**

* Find an artboard using its name

* @method setActiveArtboardBy

* @param  {null}

*/

setActiveArtboardBy: function(name) {

var ab = doc.artboards.getByName(name);

for (i = 0; i < doc.artboards.length; i++) {

if (doc.artboards === ab) {

doc.artboards.setActiveArtboardIndex(i);

break;

}

}

},

/**

* Convert all layers to and artboard while retaining position

* @method layersToArtboard

* @return {null}

*/

layersToArtboard: function() {

if (app.documents.length === 0) { return; }

var layerCount = doc.layers.length;

var whichProcess = 'Converting layers to artboards and adding position data…';

for (var i = 0; i < layerCount; i++) {

var layer = doc.layers;

var layerIsBackground = layer.name === this.settings.backgroundLayerName;

// Show the layer if necessary

layer.visible = true;

// Select everything in the layer

layer.hasSelectedArtwork = true;

if (doc.visibleBounds[2] === 0) {

// ignore empty layers ** Tricks by David Deraedt

continue;

}

// Add the textBox with position data

if (!layerIsBackground) HCo.addPositionDataToGroup(layer);

// Create and resize the artboard

var newArtboard = doc.artboards.add([0, 0, 100, -100]);

var indexAB = doc.artboards.getActiveArtboardIndex();

newArtboard.name = layerIsBackground ? this.settings.backgroundLayerName : HCo.nameFromFilename(i);

doc.fitArtboardToSelectedArt(indexAB);

layer.hasSelectedArtwork = false;

HCo.updateProgress(whichProcess, 1);

// Run some cleanup once the loop has completed

if (i === layerCount - 1) {

$.sleep(100);

// Remove the original now-empty artboard

this.settings.baseArtboard.remove();

HCo.setActiveArtboardBy(this.settings.firstArtboardName);

var firstAB = doc.artboards.getActiveArtboardIndex();

doc.layers[0].hasSelectedArtwork = true;

doc.fitArtboardToSelectedArt(firstAB);

layer.hasSelectedArtwork = false;

return this;

}

};

return false;

},

/**

* Creates a new artboard matching the size of the original and fills it with

* boxes that match the size and position of the original tiles.

* @method makeBackground

*/

makeBackground: function() {

// Current artboard’s dimensions

var ax1 = this.settings.docRect[0];

var ay1 = this.settings.docRect[1];

var ax2 = this.settings.docRect[2];

var ay2 = this.settings.docRect[3];

// Settings for artboard positioning

var artboardGutter = 48;

var positionDifference = (ax2 - ax1) + artboardGutter;

// New artboard’s positioning

var bx1 = ax1 - positionDifference;

var by1 = ay1;

var bx2 = ax2 - positionDifference;

var by2 = ay2;

var coordinates = [ bx1 , by1 , bx2 , by2 ];

// Add new artboard using coordinates from above

var bgArtboard = doc.artboards.add(coordinates);

bgArtboard.name = this.settings.backgroundLayerName;

// And a new layer to place the tiles on…

var bgLayer = doc.layers.add();

bgLayer.name = this.settings.backgroundLayerName;

bgLayer.move(doc, ElementPlacement.PLACEATEND);

// Loop through `tileCoordinates` and add tiles for each coordinate

var tiles = this.settings.tileCoordinates;

var totalTiles = tiles.length;

for (var i = 0; i < totalTiles; i++) {

// Shrinking the tile background by one pixel on all sides in order to

// ensure that there is no background peeking out

var top = Math.round(tiles[1] - 1);

var left = Math.round(tiles[0] - positionDifference + 1);

var width = Math.round(tiles[2] - tiles[0] - 2);

var height = Math.round(tiles[1] - tiles[3] - 2);

// Add the tile using the above coordinates

var tile = bgLayer.pathItems.rectangle(top, left, width, height);

var tileColor = new RGBColor;

tile.fillColor = tileColor;

}

return this;

},

/**

* Get all children of any type

* @method getChildAll

* @param  {Node} obj The parent node to start with

* @return [Array]

*/

getChildAll: function(obj) {

var childsArr = new Array();

if (obj.pageItems && obj.pageItems.length) {

for (var i = 0; i < obj.pageItems.length; i++) childsArr.push(obj.pageItems);

return childsArr;

}

return null;

},

/**

* Upgroup everything below the focused node

* @method ungroup

* @param  {Node} obj The node to start with

*/

ungroup: function(obj) {

var elements = HCo.getChildAll(obj);

if (elements.length < 1) {

obj.remove();

return;

} else {

for (var i = 0; i < elements.length; i++) {

try {

if (elements.parent.typename != "Layer") elements.moveBefore(obj);

if (elements.typename == "GroupItem") HCo.ungroup(elements);

} catch(e) {}

}

}

},

/**

* Toggle on or off all layers’ visibility

* @method toggleAllLayerVisibility

* @param  {Boolean} isVisible

*/

toggleAllLayerVisibility: function(isVisible) {

var layers = doc.layers;

var layerCount = layers.length;

for (var i = 0; i < layerCount; i++) layers.visible = isVisible;

},

/**

* Find the closes parent `Layer`

* @method getParentLayer

* @param  {Node} node The node to start with

* @return [Node]

*/

getParentLayer: function(node) {

var escapeHatch = 10;

while (node.parent && escapeHatch > 0) {

node = node.parent;

if (node.typename === 'Layer')

return node;

}

return null;

},

/**

* Crop all tiles with clipping masks

* @method cropTiles

*/

cropTiles: function() {

var whichProcess = 'Cropping the tiles…';

// Load the action file relative to the location of this script

var thisFile = new File($.fileName);

var basePath = thisFile.path;

app.loadAction(new File(basePath + '/actions/H&Co.aia'));

HCo.toggleAllLayerVisibility(true);

doc.selection = null;

app.executeMenuCommand("Clipping Masks menu item");

var clippingPath;

var escapeHatch = 100;

while (doc.selection.length != 0 && escapeHatch > 0) {

escapeHatch--;

// Reference the clipping mask

clippingPath = doc.selection[0];

doc.selection = null;

if (!clippingPath.clipping === true) continue;

clippingPath.name = 'clipping';

// Release the clipping mask

clippingPath.clipping = false;

// Parent layer

var layer = HCo.getParentLayer(clippingPath);

layer.selected = true;

// Ungroup all groups and subgroups

HCo.ungroup(layer);

// Create group to move all art to and place it in order above the background

var artGroup = layer.groupItems.add();

artGroup.name = 'art';

artGroup.moveAfter(clippingPath);

// Loop through all layer contents and move art into the artGroup

var items = HCo.getChildAll(layer);

var itemsLength = items.length;

for (var i = 0; i < itemsLength; i++) {

try {

var item = items;

if (item.name !== this.settings.backgroundLayerName && item.name !== 'clipping' && item.name !== 'data') {

item.move(artGroup, ElementPlacement.PLACEATBEGINNING);

}

} catch(e) {}

}

// Select the artGroup and clippingGroup

artGroup.selected = true;

clippingPath.selected = true;

app.doScript('Crop gallery tile', 'H&Co');

// Rename cropped group

doc.selection[0].name = 'art';

// Ensure everything is deselected for the next command

doc.selection = null;

// Ungroup all groups and subgroups

HCo.ungroup(layer);

// Now just make sure we prepped all clipping masks

app.executeMenuCommand("Clipping Masks menu item");

HCo.updateProgress(whichProcess, 1);

if (doc.selection.length === 0) {

app.unloadAction('H&Co','');

return this;

}

}

return false;

},

updateProgress: function(whichProcess, tilesProcessed) {

this.settings.fields.progressLabel.text = whichProcess;

this.settings.tilesProcessed = this.settings.tilesProcessed + tilesProcessed;

this.settings.fields.progressBar.value = this.settings.tilesProcessed / (this.settings.totalTiles * this.settings.processSteps) * 100;

this.dialog.update();

},

setup: function() {

this.settings.baseLayer = doc.layers[0];

this.settings.firstArtboardName = null;

this.settings.baseArtboard = doc.artboards[0];

this.settings.docRect = this.settings.baseArtboard.artboardRect;

// Set the current artboard and layer name so we can identify later

this.settings.baseArtboard.name = this.settings.baseArtboardName;

this.settings.baseLayer.name = this.settings.baseLayerName;

return this;

},

/**

* Save the options set by the user in `settingsWindow`

* @method saveSettings

*/

saveSettings: function() {

this.settings.renameBackgrounds = !this.settings.fields.renameBackgrounds.no.value;

},

/**

* Settings window presetned for user input

* @method settingsWindow

*/

settingsWindow: function() {

this.dialog = new Window('dialog', 'H&Co Gallery Prep');

var rowHeight = 30;

var labelWidth = 85;

var fieldWidth = 300;

var progressBarWidth = 400;

// Rename backgrounds

this.settings.fields.options = this.dialog.add('group', undefined, '');

this.settings.fields.options.margins = [12, 12, 12, 12];

this.settings.fields.options.orientation = 'column';

this.settings.fields.options.alignChildren = ['left', 'top'];

// Tile backgrounds label

var backgroundLabel = this.settings.fields.options.add('statictext', undefined, 'Tile backgrounds:');

backgroundLabel.size = [progressBarWidth, rowHeight];

// Tile backgrounds radio group

var radios = this.settings.fields.options.add('group', undefined, '');

radios.alignChildren = 'left';

radios.orientation = 'column';

// Tile backgrounds options

this.settings.fields.renameBackgrounds.yes = radios.add('radiobutton', undefined, this.settings.fields.renameBackgrounds.yes.text);

this.settings.fields.renameBackgrounds.yes.value = true;

this.settings.fields.renameBackgrounds.no = radios.add('radiobutton', undefined, this.settings.fields.renameBackgrounds.no.text);

// —————————————————————————————————————————————————————————————————————

// PROGRESS BAR

// —————————————————————————————————————————————————————————————————————

this.settings.fields.progressView = this.dialog.add('group', undefined, '');

this.settings.fields.progressView.orientation = 'column';

this.settings.fields.progressView.alignChildren = 'left';

this.settings.fields.progressView.maximumSize.height = 0;

// Text

this.settings.fields.progressLabel = this.settings.fields.progressView.add('statictext', undefined, '');

this.settings.fields.progressLabel.size = [progressBarWidth, rowHeight];

// Progress bar

this.settings.fields.progressBar = this.settings.fields.progressView.add('progressbar', undefined, 0, 100);

this.settings.fields.progressBar.size = [progressBarWidth, 12];

// —————————————————————————————————————————————————————————————————————

// BUTTONS

// —————————————————————————————————————————————————————————————————————

var buttons = HCo.dialog.add('group', undefined, '');

buttons.orientation = 'row';

// Cancel button

this.settings.fields.cancelButton = buttons.add('button', undefined, 'Cancel', { name: 'cancel' });

this.settings.fields.cancelButton.onClick = function() { HCo.dialog.close(); };

// Start button

this.settings.fields.startButton = buttons.add('button', undefined, 'Start', { name: 'ok' });

this.settings.fields.startButton.active = true;

this.settings.fields.startButton.onClick = function() {

HCo.saveSettings();

try {

HCo.run();

} catch(e) {

alert(e);

}

};

HCo.dialog.show();

},

/**

* Get the file preparation process running…

* @method run

*/

run: function () {

this.settings.fields.options.maximumSize.height = 0;

this.settings.fields.progressView.maximumSize.height = 100;

this.settings.fields.startButton.enabled = false;

this.dialog.layout.layout(true);

HCo

.artboardAndLayerCheck()

.checkBackgrounds()

.setup()

.outlineText()

.convertGroupsToLayers()

.layersToArtboard()

.cropTiles()

.makeBackground();

HCo.dialog.close();

},

init: function() {

this.settingsWindow();

}

}

HCo.init();

TOPICS
Scripting

Views

451

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
New Here ,
Dec 14, 2017 Dec 14, 2017

Copy link to clipboard

Copied

If I only remove the app.doScript('Crop gallery tile', 'H&Co'); line it crashes.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Valorous Hero ,
Dec 14, 2017 Dec 14, 2017

Copy link to clipboard

Copied

LATEST

The only thing I can think of right now is that the result of the action, such as an artwork structure expected by the lines further down the script, is not being formed when the doScript line is removed, causing a crash. But, still, I'd only expect an error and not a complete crash..

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines