Skip to content

Instantly share code, notes, and snippets.

@creold
Created May 11, 2024 06:13
Show Gist options
  • Save creold/df44043c4a28119b48c05077a097ad92 to your computer and use it in GitHub Desktop.
Save creold/df44043c4a28119b48c05077a097ad92 to your computer and use it in GitHub Desktop.
Aligns the selected objects to the objects on the artboards as key objects. Adobe Illustrator script
/*
Aligns the selected objects to the objects on the artboards as key objects
Author: Sergey Osokin, email: hi@sergosokin.ru
Check my other scripts: https://github.com/creold
Donate (optional):
If you find this script helpful, you can buy me a coffee
- via Buymeacoffee: https://www.buymeacoffee.com/aiscripts
- via Donatty https://donatty.com/sergosokin
- via DonatePay https://new.donatepay.ru/en/@osokin
- via YooMoney https://yoomoney.ru/to/410011149615582
*/
//@target illustrator
preferences.setBooleanPreference('ShowExternalJSXWarning', false); // Fix drag and drop a .jsx file
function main() {
var SCRIPT = {
name: 'Align Selection To Artboard Contents',
version: 'v.0.1'
},
CFG = {
refHint: ['Top Left', 'Top', 'Top Right', 'Left', 'Center', 'Right', 'Bottom Left', 'Bottom', 'Bottom Right'],
isMac: /mac/i.test($.os),
uiOpacity: .97 // UI window opacity. Range 0-1
};
if (!isCorrectEnv('selection', 'version:16')) return;
// DIALOG
var win = new Window('dialog', SCRIPT.name + ' ' + SCRIPT.version);
win.orientation = 'column';
win.opacity = CFG.uiOpacity;
var wrapper = win.add('group');
// BOUNDS
var bndsPnl = wrapper.add('panel', undefined, 'Object Dimensions');
bndsPnl.orientation = 'column';
bndsPnl.alignChildren = ['left', 'center'];
bndsPnl.margins = [10, 15, 10, 10];
var radioBtns = bndsPnl.add('group');
var geoRb = radioBtns.add('radiobutton', undefined, 'Geometric');
geoRb.value = true;
var visRb = radioBtns.add('radiobutton', undefined, 'Visible');
bndsPnl.add('statictext', undefined, 'The visible bounds of the item\nincluding stroke width and effects', {multiline: true});
// REFERENCE POINT
var refPnl = wrapper.add('panel', undefined, 'Align Point');
refPnl.orientation = 'row';
refPnl.bounds = [0, 0, 90, 94];
// Create reference point matrix 3x3
var refArr = []; // Reference point array
var idx = 0;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
refArr[idx] = addRadiobutton(refPnl, j, i, CFG.refHint[idx]);
idx++;
}
}
refArr[4].value = true; // 4 - center. Range 0-8
// ARTBOARDS
var abPnl = win.add('panel', undefined, 'Artboards');
abPnl.orientation = 'row';
abPnl.alignChildren = ['left', 'center'];
abPnl.preferredSize.width = 320;
abPnl.margins = [10, 15, 10, 10];
var actAbRb = abPnl.add('radiobutton', undefined, 'Active Artboard');
actAbRb.value = true;
var allAbRb = abPnl.add('radiobutton', undefined, 'All ' + app.activeDocument.artboards.length + ' Artboard(s)');
allAbRb.value = false;
// OPTIONS
var optPnl = win.add('panel', undefined, 'Options');
optPnl.orientation = 'row';
optPnl.alignChildren = ['left', 'center'];
optPnl.preferredSize.width = 320;
optPnl.margins = [10, 15, 10, 10];
var isMove = optPnl.add('checkbox', undefined, 'Move in Layers');
isMove.value = false;
var isRmv = optPnl.add('checkbox', undefined, 'Remove Target Objects');
isRmv.value = false;
// Buttons
var footer = win.add('group');
footer.alignment = 'right';
var copyright = footer.add('statictext', undefined, 'Visit Github'),
cancel, ok;
if (CFG.isMac) {
cancel = footer.add('button', undefined, 'Cancel', { name: 'cancel' });
ok = footer.add('button', undefined, 'Ok', { name: 'ok' });
} else {
ok = footer.add('button', undefined, 'Ok', { name: 'ok' });
cancel = footer.add('button', undefined, 'Cancel', { name: 'cancel' });
}
copyright.addEventListener('mousedown', function () {
openURL('https://github.com/creold/');
});
cancel.onClick = win.close;
ok.onClick = okClick;
function okClick() {
deselectGuides(app.selection);
var doc = app.activeDocument;
var docSel = get(app.selection);
var items = get(app.selection);
var isGBnds = geoRb.value;
var refName = getPointName();
if (actAbRb.value) {
processAb(doc, items, refName, isGBnds, isMove.value, isRmv.value);
} else {
for (var i = 0, len = doc.artboards.length; i < len; i++) {
doc.artboards.setActiveArtboardIndex(i);
processAb(doc, items, refName, isGBnds, isMove.value, isRmv.value);
}
}
app.selection = docSel;
win.close();
}
// Get reference point name by index of active radiobutton
function getPointName() {
var str = CFG.refHint[4];
for (var j = 0; j < refPnl.children.length; j++) {
if (refPnl.children[j].value) {
str = CFG.refHint[j];
break;
}
}
return str.replace(/\s+/g, '').toUpperCase();
}
win.center();
win.show();
}
// Check the script environment
function isCorrectEnv() {
var args = ['app', 'document'];
args.push.apply(args, arguments);
for (var i = 0; i < args.length; i++) {
var arg = args[i].toString().toLowerCase();
switch (true) {
case /app/g.test(arg):
if (!/illustrator/i.test(app.name)) {
alert('Wrong application\nRun script from Adobe Illustrator', 'Script error');
return false;
}
break;
case /version/g.test(arg):
var rqdVers = parseFloat(arg.split(':')[1]);
if (parseFloat(app.version) < rqdVers) {
alert('Wrong app version\nSorry, script only works in Illustrator v.' + rqdVers + ' and later', 'Script error');
return false;
}
break;
case /document/g.test(arg):
if (!documents.length) {
alert('No documents\nOpen a document and try again', 'Script error');
return false;
}
break;
case /selection/g.test(arg):
if (!selection.length || selection.typename === 'TextRange') {
alert('Few objects are selected\nPlease, select at least one object and try again', 'Script error');
return false;
}
break;
}
}
return true;
}
// Generate radiobutton
function addRadiobutton(place, x, y, info) {
var rb = place.add('radiobutton', undefined, x),
step = 23, x0 = 10, y0 = 15, d = 14;
x = x0 + step * x;
y = y0 + step * y;
rb.bounds = [x, y, x + d, y + d];
rb.helpTip = info;
return rb;
}
// Convert collection into standard Array
function get(coll) {
var out = [];
for (var i = 0, len = coll.length; i < len; i++) {
out.push(coll[i]);
}
return out;
}
// Get paths from selection
function deselectGuides(arr) {
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (item.typename === 'GroupItem' && item.pageItems.length) {
deselectGuides(item.pageItems);
} else if ((item.hasOwnProperty('guides') && item.guides)
|| (item.typename === 'CompoundPathItem' && item.pathItems.length && item.pathItems[0].guides)) {
item.selected = false;
}
}
}
// Process artboard contents
function processAb(doc, items, refName, isGBnds, isMove, isRemove) {
app.executeMenuCommand('deselectall');
doc.selectObjectsOnActiveArtboard();
if (!app.selection.length) return;
var abItems = get(app.selection);
var type = isGBnds ? 'geometricBounds' : 'visibleBounds';
var length = Math.min(items.length, abItems.length);
for (var i = 0; i < length; i++) {
var abItemBnds = getVisibleBounds(abItems[i], type);
alignTo(items[i], abItemBnds, refName, isGBnds);
if (isMove) items[i].move(abItems[i], ElementPlacement.PLACEBEFORE);
if (isRemove) abItems[i].remove();
}
// Remove aligned objects
items.splice(0, length);
}
// Align object to artboard reference point
function alignTo(item, targetBnds, ref, isGBnds) {
var data = getMoveData(item, targetBnds, isGBnds),
delta = [];
switch (ref) {
case 'TOPLEFT':
delta = [data.left, data.top];
break;
case 'TOP':
delta = [data.centerX, data.top];
break;
case 'TOPRIGHT':
delta = [data.right, data.top];
break;
case 'LEFT':
delta = [data.left, data.centerY];
break;
case 'CENTER':
delta = [data.centerX, data.centerY];
break;
case 'RIGHT':
delta = [data.right, data.centerY];
break;
case 'BOTTOMLEFT':
delta = [data.left, data.bottom];
break;
case 'BOTTOM':
delta = [data.centerX, data.bottom];
break;
case 'BOTTOMRIGHT':
delta = [data.right, data.bottom];
break;
default:
break;
}
item.translate(delta[0], delta[1]);
}
// Get a position relative to the artboard
function getMoveData(item, abBnds, isGBnds) {
var abW = Math.abs(abBnds[2] - abBnds[0]);
var abH = Math.abs(abBnds[1] - abBnds[3]);
var type = isGBnds ? 'geometricBounds' : 'visibleBounds';
var vBnds, w, h, l, r, t, b, cx, cy;
if (item.typename === 'GroupItem' || item.typename === 'TextFrame') {
var dup = item.duplicate();
app.executeMenuCommand('deselectall');
app.selection = dup;
outlineText(dup.pageItems ? dup.pageItems : [dup]);
dup = app.selection[0];
vBnds = getVisibleBounds(dup, type);
app.executeMenuCommand('deselectall');
dup.remove();
} else {
vBnds = getVisibleBounds(item, type);
}
w = Math.abs(vBnds[2] - vBnds[0]);
h = Math.abs(vBnds[3] - vBnds[1]);
l = abBnds[0] - vBnds[0];
r = l + abW - w;
t = abBnds[1] - vBnds[1];
b = t - abH + h;
cx = l + (abW - w) / 2;
cy = t + (h - abH) / 2;
return {
left: l,
top: t,
right: r,
bottom: b,
centerX: cx,
centerY: cy,
};
}
// Create outlines
function outlineText(coll) {
for (var i = coll.length - 1; i >= 0; i--) {
var item = coll[i];
if (item.typename === 'TextFrame') {
item.createOutline();
} else if (item.typename === 'GroupItem') {
outlineText(item.pageItems);
}
}
}
// Get the actual "visible" bounds
// https://github.com/joshbduncan/illustrator-scripts/blob/main/jsx/DrawVisibleBounds.jsx
function getVisibleBounds(obj, type) {
if (arguments.length == 1 || type == undefined) type = 'geometricBounds';
var doc = app.activeDocument;
var bnds, clippedItem, tmpItem, tmpLayer;
var curItem;
if (obj.typename === 'GroupItem') {
if (obj.clipped) {
// Check all sub objects to find the clipping path
for (var i = 0; i < obj.pageItems.length; i++) {
curItem = obj.pageItems[i];
if (curItem.clipping) {
clippedItem = curItem;
break;
} else if (curItem.typename === 'CompoundPathItem') {
if (!curItem.pathItems.length) {
// Catch compound path items with no pathItems
// via William Dowling @ github.com/wdjsdev
tmpLayer = doc.layers.add();
tmpItem = curItem.duplicate(tmpLayer);
app.executeMenuCommand('deselectall');
tmpItem.selected = true;
app.executeMenuCommand('noCompoundPath');
tmpLayer.hasSelectedArtwork = true;
app.executeMenuCommand('group');
clippedItem = selection[0];
break;
} else if (curItem.pathItems[0].clipping) {
clippedItem = curItem;
break;
}
}
}
if (!clippedItem) clippedItem = obj.pageItems[0];
bnds = clippedItem[type];
if (tmpLayer) {
tmpLayer.remove();
tmpLayer = undefined;
}
} else {
// If the object is not clipped
var subObjBnds;
var allBoundPoints = [[], [], [], []];
// Get the bounds of every object in the group
for (var i = 0; i < obj.pageItems.length; i++) {
curItem = obj.pageItems[i];
subObjBnds = getVisibleBounds(curItem, type);
allBoundPoints[0].push(subObjBnds[0]);
allBoundPoints[1].push(subObjBnds[1]);
allBoundPoints[2].push(subObjBnds[2]);
allBoundPoints[3].push(subObjBnds[3]);
}
// Determine the groups bounds from it sub object bound points
bnds = [
Math.min.apply(Math, allBoundPoints[0]),
Math.max.apply(Math, allBoundPoints[1]),
Math.max.apply(Math, allBoundPoints[2]),
Math.min.apply(Math, allBoundPoints[3]),
];
}
} else {
bnds = obj[type];
}
return bnds;
}
// Open link in browser
function openURL(url) {
var html = new File(Folder.temp.absoluteURI + '/aisLink.html');
html.open('w');
var htmlBody = '<html><head><META HTTP-EQUIV=Refresh CONTENT="0; URL=' + url + '"></head><body> <p></body></html>';
html.write(htmlBody);
html.close();
html.execute();
}
try {
main();
} catch (err) {}
@creold
Copy link
Author

creold commented May 11, 2024

AlignSelectionToArtboardContents

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment