m1b
Community Expert
m1b
Community Expert
Activity
Feb 24, 2025
03:15 PM
Hi @Utson_Avila2888 I see. Yes I hadn't expected anyone to fill or stroke a clipping mask, but I've updated my code above with a check for it. Should work correctly now.
- Mark
P.S. A couple of notes on posting code: (1) please use the </> button to format your code, and (2) if you are posting someone else's code publicly, it is impolite to remove their authorship notice.
... View more
Feb 23, 2025
10:02 PM
2 Upvotes
Hi @optimisticperson, as others have rightly said, there is no other way than adjusting by hand because Illustrator has no capability to judge properties such as the "visual weight" of an object. No doubt one day it will! Making disparate logos sit comfortably together is a task involving significant skill.
However, perhaps it would still save some time to give a rough approximation? I have written a script that scales the selected page items such that the area of each item's bounds matches the average area of all the items.
It won't do a great job, because the bounds of a logo isn't a good metric for the visual weight, but it might provide a starting point. Let me know if it is helpful.
- Mark
/**
* @file Scale To Average Area.js
*
* Scales the selection such that the area
* of each item's geometric bounds match
* the average area of the selected items.
*
* @author m1b
* @version 2025-02-24
* @discussion https://community.adobe.com/t5/illustrator-discussions/how-to-automatically-resize-a-bunch-of-logos-so-they-re-visually-proportional/m-p/15168811
*/
(function () {
var doc = app.activeDocument,
items = doc.selection;
if (0 === items.length)
return alert('Please select some items and try again.');
scaleItemsToAverageArea(items);
})();
/**
* Scales each page item in `items` such that the area
* of each item's geometric bounds match the average area
* of the selected items.
* @date 2025-02-24
* @param {Array<PageItem>} items - the items to scale.
*/
function scaleItemsToAverageArea(items) {
var areas = [],
areaSum = 0;
for (var i = 0, bounds, area; i < items.length; i++) {
bounds = getItemBoundsIllustrator(items[i], false);
area = (bounds[2] - bounds[0]) * -(bounds[3] - bounds[1]);
areas[i] = area;
areaSum += area;
}
var averageArea = Math.sqrt(areaSum) / items.length;
for (var i = 0, scaleFactor; i < areas.length; i++) {
scaleFactor = averageArea / Math.sqrt(areas[i]);
items[i].resize(scaleFactor * 100, scaleFactor * 100);
}
};
/**
* Returns bounds of item(s).
* @author m1b
* @version 2024-03-10
* @param {PageItem|Array<PageItem>} item - an Illustrator PageItem or array of PageItems.
* @param {Boolean} [geometric] - if false, returns visible bounds.
* @param {Array} [bounds] - private parameter, used when recursing.
* @returns {Array} - the calculated bounds.
*/
function getItemBoundsIllustrator(item, geometric, bounds) {
var newBounds = [],
boundsKey = geometric ? 'geometricBounds' : 'visibleBounds';
if (undefined == item)
return;
if (
item.typename == 'GroupItem'
|| item.constructor.name == 'Array'
) {
var children = item.typename == 'GroupItem' ? item.pageItems : item,
contentBounds = [],
isClippingGroup = (item.hasOwnProperty('clipped') && item.clipped == true),
clipBounds;
for (var i = 0, child; i < children.length; i++) {
child = children[i];
if (
child.hasOwnProperty('clipping')
&& true === child.clipping
)
// the clipping item
clipBounds = child.geometricBounds;
else
contentBounds.push(getItemBoundsIllustrator(child, geometric, bounds));
}
newBounds = combineBounds(contentBounds);
if (isClippingGroup)
newBounds = intersectionOfBounds([clipBounds, newBounds]);
}
else if (
'TextFrame' === item.constructor.name
&& TextType.AREATEXT !== item.kind
) {
// get bounds of outlined text
var dup = item.duplicate().createOutline();
newBounds = dup[boundsKey];
dup.remove();
}
else if (item.hasOwnProperty(boundsKey)) {
newBounds = item[boundsKey];
}
// `bounds` will exist if this is a recursive execution
bounds = (undefined == bounds)
? bounds = newBounds
: bounds = combineBounds([newBounds, bounds]);
return bounds;
};
/**
* Returns the combined bounds of all bounds supplied.
* Works with Illustrator or Indesign bounds.
* @author m1b
* @version 2024-03-09
* @param {Array<bounds>} boundsArray - an array of bounds [L, T, R, B] or [T, L , B, R].
* @returns {bounds?} - the combined bounds.
*/
function combineBounds(boundsArray) {
var combinedBounds = boundsArray[0],
comparator;
if (/indesign/i.test(app.name))
comparator = [Math.min, Math.min, Math.max, Math.max];
else
comparator = [Math.min, Math.max, Math.max, Math.min];
// iterate through the rest of the bounds
for (var i = 1; i < boundsArray.length; i++) {
var bounds = boundsArray[i];
combinedBounds = [
comparator[0](combinedBounds[0], bounds[0]),
comparator[1](combinedBounds[1], bounds[1]),
comparator[2](combinedBounds[2], bounds[2]),
comparator[3](combinedBounds[3], bounds[3]),
];
}
return combinedBounds;
};
/**
* Returns the overlapping rectangle
* of two or more rectangles.
* NOTE: Returns undefined if ANY
* rectangles do not intersect.
* @author m1b
* @version 2024-09-05
* @param {Array<bounds>} arrayOfBounds - an array of bounds [L, T, R, B] or [T, L , B, R].
* @returns {bounds?} - intersecting bounds.
*/
function intersectionOfBounds(arrayOfBounds) {
var comparator;
if (/indesign/i.test(app.name))
comparator = [Math.max, Math.max, Math.min, Math.min];
else
comparator = [Math.max, Math.min, Math.min, Math.max];
// sort a copy of array
var bounds = arrayOfBounds
.slice(0)
.sort(function (a, b) { return b[0] - a[0] || a[1] - b[1] });
// start with first bounds
var intersection = bounds.shift(),
b;
// compare each bounds, getting smaller
while (b = bounds.shift()) {
// if doesn't intersect, bail out
if (!boundsDoIntersect(intersection, b))
return;
intersection = [
comparator[0](intersection[0], b[0]),
comparator[1](intersection[1], b[1]),
comparator[2](intersection[2], b[2]),
comparator[3](intersection[3], b[3]),
];
}
return intersection;
};
/**
* Returns true if the two bounds intersect.
* @author m1b
* @version 2024-03-10
* @param {Array} bounds1 - bounds array.
* @param {Array} bounds2 - bounds array.
* @param {Boolean} [TLBR] - whether bounds arrays are interpreted as [t, l, b, r] or [l, t, r, b] (default: based on app).
* @returns {Boolean}
*/
function boundsDoIntersect(bounds1, bounds2, TLBR) {
if (undefined == TLBR)
TLBR = (/indesign/i.test(app.name));
return !(
TLBR
// TLBR
? (
bounds2[0] > bounds1[2]
|| bounds2[1] > bounds1[3]
|| bounds2[2] < bounds1[0]
|| bounds2[3] < bounds1[1]
)
// LTRB
: (
bounds2[0] > bounds1[2]
|| bounds2[1] < bounds1[3]
|| bounds2[2] < bounds1[0]
|| bounds2[3] > bounds1[1]
)
);
};
... View more
Feb 21, 2025
04:34 AM
perfect answer
... View more
Feb 20, 2025
02:20 AM
Oh, and, by the way, if you follow the recommendation 2. and copy cells instead of cell contents, you clearly copy all cell properties as well, inasmuch you won't need the step to transfer all cell properties. What you definitely should do is to transfer all column properties using this neat technique.
... View more
Feb 19, 2025
03:32 PM
@Robert at ID-Tasker you are no village idiot!—you are an expert like each of us. I have made many more errors than you have on this one thread—some quite embarassing—so on that metric I would win the village idiot olympics here.
We could, as time permits, go back and look over our contributions on a thread such as this, and analyze the contribution we made. Did our comments push forward the discussion, or side-track it?
I find it uncomfortable work, as I see all my mistakes, but it can be edifying.
- Mark
... View more
Feb 18, 2025
08:15 AM
Thanks bro, I like Illustrator and even more the scripts help a lot but I'm still not at your level to program. 😉
... View more
Feb 17, 2025
10:06 PM
Yes, this way I have many points of view on this problem, but the big picture is already visible, it turns out that it is possible to have scripts for this problem.
... View more
Feb 17, 2025
08:20 PM
Yes I thought so. I don't know how to do exactly what you want. There might be a clever way with Actions or graphic styles, but I don't know any. Sorry.
Good luck and let us know if you discover a good way!
- Mark
... View more
Feb 17, 2025
07:28 AM
1 Upvote
An improved version (undoable, bugfix, etc.) is now available in the IdGoodies repo: → https://github.com/indiscripts/IdGoodies/blob/master/full/SelSetFrameHeightByLineCount.jsx Note that it also supports threaded frames ↓
... View more
Feb 17, 2025
02:25 AM
Thanks for letting us know, Olivier. All the best.
... View more
Feb 17, 2025
01:25 AM
1 Upvote
🤔 For completeness sake, I think this does not preserve the previous user choice of the "Query" dropdown … Looking at the OM, I haven't found a way to get that value.
... View more
Feb 14, 2025
06:53 AM
Just check first and last cells.
When selecting Cells - either in the InDesign manually or through scripting - you can't select non-rectangual region.
So the first one - is the top-left = first row - last one - always bottom-right = last row.
... View more
Feb 12, 2025
08:08 PM
Not sure why you're looking for "\n" and remove extra spaces? Just last character should be replaced with a space - or whatever separator OP wants.
Or simply F/C can be executed on the block of Paragraphs?
But it's still less optimal - the same paragraphs are processed twice - or in this case - six times - than going through all Paragraphs and merging them at the same time.
All three types of blocks - caution warning, note - can be checked in one pass.
Unless I'm missing something?
... View more
Feb 12, 2025
06:14 PM
Hi @ep_nna, similar to the answers you already have, here is the way I would approach it.
- Mark
/**
* Demonstration of getting a Page's textFrame from a layer.
*
* @author m1b
* @version 2025-02-13
* @discussion https://community.adobe.com/t5/indesign-discussions/get-content-of-textframe-on-specific-layer-and-page/m-p/15145413
*/
function main() {
var doc = app.activeDocument,
// the layer containing the "names" text frame(s)
namesLayer = doc.layers.itemByName('Names Layer');
if (!namesLayer.isValid)
return alert('There is no "Names Layer".');
var pages = doc.pages,
names = [];
for (var i = 0; i < pages.length; i++) {
// call the function to get the name
var pageName = getFirstTextOnLayerOnPage(namesLayer, pages[i]);
if (!pageName)
// couldn't get name!
continue;
// just for demo
names.push(pageName);
// now that you have the name, you can do the export here
};
// just for demo
alert('Names:\n' + names.join('\n'));
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Do Script');
/**
* Returns contents of the first text frame on `layer` on `page`.
* @author m1b
* @version 2025-02-13
* @param {Layer} layer - the target Layer.
* @param {Page} page - the target page.
* @returns {String?}
*/
function getFirstTextOnLayerOnPage(layer, page) {
if (!layer || !page)
throw new Error('getFirstTextOnLayerOnPage: bad parameter.');
var textFrames = page.textFrames;
for (var i = 0; i < textFrames.length; i++) {
var frame = textFrames[i];
if (frame.itemLayer === layer)
return frame.contents;
}
};
... View more
Feb 08, 2025
10:40 AM
1 Upvote
Hi m1b. I tested it. var desktopPath = “~/Desktop/pdf/”; It is also recognized in windows. I tried this one you gave me and it works. var desktopPath = Folder.desktop;
var myPDFFile = new File(desktopPath +"/"+"pdf"+"/" + myFormattedDate + "-" + myFileName + ".pdf"); When you have time, research how the images fit into the column widths. For type area,and margins. Zoom in or out in equal proportion. Thank you very much.
... View more
Feb 06, 2025
01:53 PM
2 Upvotes
POSIX path of
By @Olivier-Coolgray
Yes, the support for HFS paths has been discontinued in InDesign 2025, only POSIX paths should be used.
If you ask me, Adobe didn't do a good job communicating this change to the users, which caught many of them by surprise and with no clear explanation for the errors.
... View more
Feb 06, 2025
01:58 AM
Glad to hear it helped! 🙂
... View more
Feb 06, 2025
12:51 AM
Hi @Ryan33910431r01h, here's a script that creates a hyperlink in a text frame under the placed pdf. Is this what you mean?
/**
* @file Make Download Link For Placed PDF.js
*
* Usage:
* 1. Select a linked PDF image (must have a valid URI).
* 2. Run Script
*
* Script will create a textframe containing
* a hyperlink to open the linked file's URI.
*
* @author m1b
* @version 2025-02-06
* @discussion https://community.adobe.com/t5/indesign-discussions/help-writing-a-script-to-create-a-button-that-opens-a-linked-file-s-path/m-p/15109743
*/
function main() {
var settings = {
appliedParagraphStyle: 'Download PDF Button',
overwriteExistingHyperlink: true,
}
var doc = app.activeDocument,
item = doc.selection[0];
if (
!item
|| !item.hasOwnProperty('graphics')
|| 0 === item.graphics.length
)
return alert('Please select a linked image and try again.');
var graphic = item.graphics[0],
link = graphic.itemLink,
URI = link.linkResourceURI,
bounds = [0, 0, 1, 1000];
var textFrame = item.parent.textFrames.add({
geometricBounds: bounds,
contents: 'Download PDF',
name: '"' + link.name + '" hyperlink',
});
var paraStyle = getThing(doc.allParagraphStyles, 'name', settings.appliedParagraphStyle);
if (paraStyle)
// apply a paragraph style
textFrame.parentStory.appliedParagraphStyle = paraStyle;
// adjust frame height to fit
while (textFrame.overflows) {
bounds[2] += 1;
textFrame.geometricBounds = bounds;
}
// adjust frame width to fit
var inc = bounds[3];
while (!textFrame.overflows) {
inc /= 2;
bounds[3] -= inc;
textFrame.geometricBounds = bounds;
}
bounds[3] += inc
textFrame.geometricBounds = bounds;
// position at bottom left of graphic
textFrame.move([graphic.geometricBounds[1], graphic.geometricBounds[2]]);
// create hyperlink
var hyperlinkSource = doc.hyperlinkTextSources.add(textFrame.paragraphs[0]),
hyperlinkDestination = doc.hyperlinkURLDestinations.add(URI, { hidden: true }),
hyperlink = doc.hyperlinks.add(hyperlinkSource, hyperlinkDestination),
name = link.name,
counter = 2;
if (settings.overwriteExistingHyperlink) {
// remove existing hyperlink with that name
if (doc.hyperlinks.itemByName(name).isValid)
doc.hyperlinks.itemByName(name).remove();
}
else {
// change name to avoid collision with existing hyperlink name
while (doc.hyperlinks.itemByName(name).isValid)
name = link.name + ' ' + String(counter++);
}
hyperlink.name = name;
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Make Download PDF Button');
/**
* Returns a thing with matching property.
* If `key` is undefined, evaluate the object itself.
* @author m1b
* @version 2024-04-21
* @param {Array|Collection} things - the things to look through.
* @param {String} [key] - the property name (default: undefined).
* @param {*} value - the value to match.
*/
function getThing(things, key, value) {
for (var i = 0, obj; i < things.length; i++)
if ((undefined == key ? things[i] : things[i][key]) == value)
return things[i];
};
... View more
Feb 03, 2025
09:43 AM
Actually... I use three criteria to check that I posted here... https://community.adobe.com/t5/indesign-discussions/how-to-find-and-edit-qr-codes-using-scripting/m-p/15108325#M607345 ... but yours should work. Untill Adobe gives us a rock solid way to read the data and know 100% for sure that it is indeed a QR Code, I'm taking the cautious, paranoid route.
... View more
Jan 30, 2025
12:05 PM
I'm getting this exact same issue. Works fine if each variable is linked to a single text field, but if there's multiple text fields assigned to each variable, the CSV load fails. Illustrator 29.2.1
... View more
Jan 29, 2025
10:48 PM
Thanks Robert, I'm on a Mac.
... View more
Jan 29, 2025
05:10 AM
1 Upvote
I am implementing pattern matches in several scripts once @m1b showed me their utility a while back. I've spent time on the RegEx and GREP reference materials and my patterns and group result have only grown in complexity. As they say "less is more" ... the nuances in this post are next-level: Matching a group after the main expression without including it in the result by using "lookarounds". Using "OR" and its ability to condense the pattern results. In the case of this post using the alternation produces a direct match without creating a group. Using word boundaries to match between a word character and non-word character or position. I'll be revising my patterns and surely condensing them as a result of this post.
... View more
Jan 29, 2025
03:33 AM
@Robert at ID-Tasker I think appEncoding comes into play when doScript takes a File as parameter. Also evalFile. It doesn't seem to be a factor when just running a script "directly".
... View more
Jan 28, 2025
11:09 PM
1 Upvote
Hi @fantastic_Marshmallow1587 I believe the code you've written is ExtendScript, which should be saves with a .js or .jsx file extension. I think you may have saved with an .idjs extension, which is reserved for the newer UXP code. Try changing the extension.
- Mark
P.S. If instead you *intend* to write UXP code, then you will need to search for UXP code examples and avoid ExtendScript. Sorry it is confusing situation at the moment between the two scripting systems.
... View more
Jan 28, 2025
08:45 AM
By @dianna_9432
look them up and download.
... View more
Jan 26, 2025
11:47 AM
Great! Thanks for this script. Artboard managment is so old fashioned in Illustrator, but you improoved it a lot.
... View more
Jan 26, 2025
10:42 AM
Oh my gaawd!! I never knew this alt clicking!! 😮 I have been using AI since .. 😮 I feel so shy to say this. Thank you so much.
... View more
Jan 22, 2025
03:28 PM
Glad to help. All the best!
... View more
Jan 22, 2025
10:59 AM
@brian.sipsy
Any chance you can post a screenshot of a real example - to let me better understand what you're doing?
You can click my nickname if you prefer to send it privately.
... View more