And 3rd attempt, this one better organised I think:
scaleSelectedItemsToMatchTargetArea(4, 'inch');
function scaleSelectedItemsToMatchTargetArea(targetAreaInSquareUnits, unitType) {
var items = app.activeDocument.selection;
for (var i = 0; i < items.length; i++) {
scaleToArea(items[i], targetAreaInSquareUnits, unitType);
}
}
function scaleToArea(item, targetAreaInSquareUnits, unitType) {
var itemAreaInSquareUnits = areaOfItem(item, unitType);
var scaleFactor = (Math.sqrt(targetAreaInSquareUnits / itemAreaInSquareUnits)) * 100;
if (scaleFactor != NaN) {
item.resize(scaleFactor, scaleFactor);
}
}
function areaOfItem(item, unitType) {
var itemAreaInSquareUnits;
var itemAreaInSquarePoints = areaOfItemInSquarePoints(item);
if (unitType == 'in' || unitType == 'inch') {
var ptsIn1inch = 72;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1inch * ptsIn1inch);
} else if (unitType == 'mm' || unitType == 'millimetres') {
var ptsIn1mm = 2.834645669;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1mm * ptsIn1mm);
} else {
// no unitType specified, use points
itemAreaInSquareUnits = itemAreaInSquarePoints;
}
return itemAreaInSquareUnits;
}
function areaOfItemInSquarePoints(item) {
var area = 0;
if (item.typename == 'PathItem') {
area = item.area;
} else if (item.typename == 'CompoundPathItem') {
for (var j = 0; j < item.pathItems.length; j++) {
area += item.pathItems[j].area;
}
} else {
// for other types of items, resort to width x height
area = item.width * item.height;
}
return Math.abs(Math.round(area * 10000) / 10000);
}
Still largely untested, but let me know how it goes! - Mark
Edit: took out my $.writelns!
4th attempt! Now handles GroupItems. Feel free to ignore attempt 3, I've left it just for learning purposes. There are two ways you may want to handle groups: 1) calculate area for each individual item inside the group, or 2) calculate the aggregate area of all the grouped items. If you want option 2) just comment out the itemsInsideGroupItems line.
Just for fun I also changed the way the parameters work for specifying the target area value and unit. I put them in a string. Example values are: '4 square inches', '4in','4 sq inch' will give same result. Other examples are:'100mm', '800 pt'. If the parameter is a number, not a string, it will be interpreted as points.
// start with the selected items
var items = app.activeDocument.selection;
// comment out the following line if you want each groups' area to be calculated, rather than the items inside each group individually
items = itemsInsideGroupItems(items);
// this is where you specify your target size ()
scaleItemsToArea(items, '4 square inches');
function scaleItemsToArea(items, targetString) {
// very simple parsing of targetString
targetString = String (targetString);
var targetAreaInSquareUnits = Number(targetString.match(/\d+/)[0]);
var unitType;
// note: only looks for 'in' or 'mm'
if (targetString.search(/(in|\")/) > -1) {
unitType = 'in';
} else if (targetString.search(/mm/) > -1) {
unitType = 'mm';
} else {
unitType = 'pt'
}
// scale each item
for (var i = 0; i < items.length; i++) {
scaleToArea(items[i], targetAreaInSquareUnits, unitType);
}
}
function scaleToArea(item, targetAreaInSquareUnits, unitType) {
var itemAreaInSquareUnits = areaOfItem(item, unitType);
var scaleFactor = (Math.sqrt(targetAreaInSquareUnits / itemAreaInSquareUnits)) * 100;
if (scaleFactor != NaN) {
item.resize(scaleFactor, scaleFactor);
}
}
function areaOfItem(item, unitType) {
var itemAreaInSquareUnits;
var itemAreaInSquarePoints = areaOfItemInSquarePoints(item);
if (unitType == 'in') {
var ptsIn1inch = 72;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1inch * ptsIn1inch);
} else if (unitType == 'mm') {
var ptsIn1mm = 2.834645669;
itemAreaInSquareUnits = itemAreaInSquarePoints / (ptsIn1mm * ptsIn1mm);
} else {
// no unitType specified, use points
itemAreaInSquareUnits = itemAreaInSquarePoints;
}
return itemAreaInSquareUnits;
}
function areaOfItemInSquarePoints(item) {
var area = 0;
if (item.typename == 'PathItem') {
area = item.area;
} else if (item.typename == 'CompoundPathItem') {
for (var j = 0; j < item.pathItems.length; j++) {
area += item.pathItems[j].area;
// $.writeln('item.pathItems[j].area = ' + item.pathItems[j].area + ' area = ' + area);
}
} else if (item.typename == 'GroupItem') {
for (var j = 0; j < item.pageItems.length; j++) {
area += areaOfItemInSquarePoints(item.pageItems[j]);
// $.writeln('item.pathItems[j].area = ' + item.pathItems[j].area + ' area = ' + area);
}
} else {
// for other types of items, resort to width x height
area = item.width * item.height;
}
return Math.abs(Math.round(area * 10000) / 10000);
}
function itemsInsideGroupItems(items) {
// returns an array containing items, including items found inside GroupItems
try {
var found = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.typename == 'GroupItem') {
found = found.concat(itemsInsideGroupItems(item.pageItems));
} else {
found.push(item);
}
}
return found;
} catch (e) {
return found;
}
}
As usual, not much testing done, so please let me know if it works as you'd expect. - Mark