m1b
Community Expert
m1b
Community Expert
Activity
‎Jan 21, 2025
01:32 PM
Nice! Thanks for letting me know. 🙂
... View more
‎Jan 21, 2025
01:27 PM
1 Upvote
Yes, performance.
... View more
‎Jan 19, 2025
05:42 AM
I tried with claude.ai, and the code I received doesn't compile.
... View more
‎Jan 17, 2025
03:25 PM
@attilioL
Just in case - replace this:
function myRound(thisNumber, digits) { // By Trevor
digits = (digits) ? Math.pow(10, digits) : 1000;
return Math.round(thisNumber * digits) / digits;
}
with this:
function myRound(thisNumber, digits)
{
return thisNumber;
}
So you can always "bring back this functionality" without the need to think where it was used before.
Or in case you would like to "align to your own grid", etc.
... View more
‎Jan 17, 2025
01:44 PM
1 Upvote
Haha, yeah I didn't spot that either! Doh!
... View more
‎Jan 17, 2025
01:22 PM
Hi @bosko_5014, sure i'd be happy to help—click on my username and send me a message that way.
- Mark
P.S. here is a good tutorial on using scripts in Illustrator.
... View more
‎Jan 16, 2025
01:49 PM
Many thanks for your reply, m1b. I think I first need to understand what normal behaviour is. The first time I press SHIFT-COMMAND+R, VSCode always asks me what Adobe app I want to target (see screenshot). Once it's attached, I don't need to do that anymore and SHIFT-COMMAND+R immediately triggers Illustrator and brings it to the front. Is that normal behavior? I was hoping it would be able to find Illustrator also the first time round without me having to click the popup menu with the various Adobe apps every time. I think the debug panel looks ok (screenshot attached).
... View more
‎Jan 15, 2025
03:20 PM
1 Upvote
Hi @Luiz Vigraf there are some complexities to this issue, for example: getting the actual bounds of an item is difficult in many cases, such as text or clipping masked items. I have written a function to help with this (see in script below), which makes things much easier, and made writing this script today super quick. I also wrote a function to get the most likely artboard for a given page item, which will be handy, too. I included an option for left justifying the items (you might not need this, but I did it for fun—turn it off by editing `settings.justifyLeft` to false in the script.
Let me know if it's useful.
- Mark
/**
* @file Move Selected Items To Bottom Of Artboard.js
*
* Moves the selected items to the bottom of its artboard,
* with the option to left justify with space between.
*
* @author m1b
* @version 2025-01-16
* @discussion https://community.adobe.com/t5/illustrator-discussions/create-script-to-move-on-the-y-axis-using-artborad-as-a-reference/m-p/15091849
*/
(function () {
var settings = {
justifyLeft: true,
spaceBetween: 0,
};
var doc = app.activeDocument,
items = doc.selection,
xPositions = {};
if (settings.justifyLeft)
items.sort(sortByLeft);
for (var i = 0; i < items.length; i++) {
var item = items[i],
bounds = getItemBoundsIllustrator(item, false),
artboard = getArtboardWithMostOverlap(doc.artboards, bounds);
if (!artboard)
// item not on any artboard
continue;
var artboardBounds = artboard.artboardRect;
// keep track of x position per artboard
xPositions[artboard.name] = undefined == xPositions[artboard.name] ? artboardBounds[0] : xPositions[artboard.name];
var dx = settings.justifyLeft ? xPositions[artboard.name] - bounds[0] : 0,
dy = artboardBounds[3] - bounds[3];
// advance x position for this artboard
xPositions[artboard.name] += bounds[2] - bounds[0] + settings.spaceBetween;
// move the item into position
item.translate(dx, dy, true, true, true, true);
}
})();
/**
* Returns the artboard that most overlaps the given bounds.
* Will return nothing if no overlap at all.
* @author m1b
* @version 2025-01-16
* @param {Array<Artboard>|Artboards} artboards - the artboards to search.
* @param {Array<Number>} bounds - the bounds to check against [L, T, R, B].
* @returns {Artboard?} - the artboard with the most overlap.
*/
function getArtboardWithMostOverlap(artboards, bounds) {
if (
undefined == artboards
|| 0 === artboards.length
)
return;
var maxOverlap = 0,
bestArtboard = undefined;
for (var i = 0; i < artboards.length; i++) {
var artboard = artboards[i];
var artboardBounds = artboard.artboardRect; // [L, T, R, B]
// calculate the overlap
var overlapL = Math.max(bounds[0], artboardBounds[0]),
overlapT = Math.min(bounds[1], artboardBounds[1]),
overlapR = Math.min(bounds[2], artboardBounds[2]),
overlapB = Math.max(bounds[3], artboardBounds[3]);
// overlap area
var overlapWidth = Math.max(0, overlapR - overlapL),
overlapHeight = Math.max(0, overlapT - overlapB),
overlapArea = overlapWidth * overlapHeight;
if (overlapArea > maxOverlap) {
// the largest overlapping artboard so far
maxOverlap = overlapArea;
bestArtboard = artboard;
}
}
return bestArtboard;
};
/**
* Sort by left bounds.
* @author m1b
* @version 2025-01-16
* @param {PageItem} a - item to sort.
* @param {PageItem} b - item to sort.
* @returns {Number} - the sort code.
*/
function sortByLeft(a, b) {
return (
// left bounds value rounded to 3 decimal places
Math.round((a.visibleBounds[0] - b.visibleBounds[0]) * 1000) / 1000
// same left, so we'll sort by top bounds value
|| a.visibleBounds[1] - b.visibleBounds[1]
);
};
// from here down is just code to get a page item's bounds
/**
* 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 if (APP_IS_ILLUSTRATOR)
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 if (APP_IS_ILLUSTRATOR)
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]
)
);
};
Edit 2025-01-16: improved variable name.
... View more
‎Jan 15, 2025
04:29 AM
Interesting. Yes good advice about collecting the styles at the same loop!
... View more
‎Jan 13, 2025
06:13 PM
Good to hear!
... View more
‎Jan 13, 2025
06:08 PM
Good one!
... View more
‎Jan 11, 2025
05:02 PM
Hi @lior12, we are relying on an Object Style. Easiest way would be for you to run the script on my "demo hebrew.indd" document, and then copy an anchored text frame and paste into your real document. This will transfer my demo object style and paragraph style, which you can edit yourself.
The object style does a lot of the positioning/formatting work, eg. sets a paragraph style (font, and size etc) and also the frame height, left and right text frame insets (the distance between the small text and the normal text). If you edit the object style so that the text frame is vertically center justified it should achieve what you want when the commentary only has one word.
For this technique it is essential that you understand how to edit paragraph styles and object styles.
If there is a problem with the look or visual position of the commentary text (but it is otherwise anchored in the correct place), it is an issue to fix in the object style and/or paragraph style.
- Mark
... View more
‎Jan 11, 2025
04:07 AM
@m1b
I think that it has been already discussed enough times that Stories should be checked for overflow - NOT TextFrames?
... View more
‎Jan 10, 2025
09:04 PM
1 Upvote
If you're looking for all contractions including you'll, they're, we'll, etc.
(?i)\b[\w]+[‘’]\w{2}\b
If it's specifically words with 'll and nothing else
(?i)\b\w+['’]ll\b
Are there instances of hyphens like it'll-be-ok or anything like that
Or any other weird instances?
... View more
‎Jan 10, 2025
09:02 AM
The following function is working to find hidden conditional text and return the page number. Thanks for the help. function getConditionalText(doc, conditionName) {
var results = [];
var condition = doc.conditions.itemByName(conditionName);
var hiddenTexts = doc.stories.everyItem().hiddenTexts.everyItem().getElements();
for (i = 0; i < hiddenTexts.length; i++) {
var hiddenText = hiddenTexts[i];
if (hiddenText.texts[0].appliedConditions[0].name === conditionName) {
results.push({
text: hiddenText.texts[0].contents,
page: hiddenText.storyOffset.parentTextFrames[0].parentPage.name,
noteNumber: getFootnoteNumber(hiddenText.storyOffset)
});
}
}
return results;
}
... View more
‎Jan 09, 2025
04:13 PM
Hi @danal76941030, I don't have much experience with CC library files, but I'll try ... while we wait for someone more knowledgeable!
1. If you drag, say, "icon golf.ai" from the Library panel into a new document, does it appear normal in the Link panel and isn't "missing"?
2. Do you know how to run scripts? If so, I would like you to (a) select a "good" and a "bad" linked item, ideally both the same asset (eg. Icon Golf) then (b) run the below script. It will save a text file to your desktop listing the linked file paths. Then (c) post that text file here so I can see the differences, if any.
- Mark
The script is this:
/**
* Write selected item file paths to a text file.
* @author m1b
*/
(function () {
var location = Folder.desktop + "/test-paths.txt";
var doc = app.activeDocument,
items = doc.selection,
paths = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
paths.push(item.file
? decodeURI(item.file)
: 'No file'
)
}
var f = File(location);
f.open("w");
f.write(paths.join('\n'));
f.close();
f.execute();
})();
... View more
‎Jan 09, 2025
09:42 AM
Hi @m1b This script of yours is the equivalent of aligning the ends. My other posting, the bottom of the table aligns to the TYPE AREA to make more sense, you can definitely get creative with that. https://community.adobe.com/t5/indesign-discussions/how-to-quickly-align-a-table-to-the-bottom-of-a-type-area/m-p/15079477
... View more
‎Jan 08, 2025
03:55 PM
Nice! Well done. 🙂
... View more
‎Jan 08, 2025
02:25 AM
Hi @dublove, of course you have to set the "findWhat" greps and objectStyleNames. Just for example, to match Ananda's script, set to this:
var queries = [
{ findWhat: '\\d{3}-\\d{3}@', objectStyleName: 'AB' },
];
Adding a UI would be cool. I might do that when I have time.
- Mark
... View more
‎Jan 05, 2025
05:44 AM
There is one more way to iterate through Cells in the Table - by its name/address.
It's "safer" but requires checking for merges to avoid double processing.
If you are familiar with Excel - it's the same.
... View more
‎Jan 02, 2025
10:51 AM
OK?
... View more
‎Jan 01, 2025
05:43 PM
2 Upvotes
I have lodged a bug report. Please vote to have it fixed.
... View more
‎Dec 26, 2024
03:12 AM
Thank you, I will test and see if I can adapt something. Any result I get I return here to comment.
... View more
‎Dec 26, 2024
02:48 AM
Thanks Peter. I didn't put in my first post, but in my script there are reset instructions. app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing; Before and after the GREP code. I put the GREP as script code as calling the stored GREP didn't work in InDesign server when a style restriction is set. Seems that the loadFindChangeQuery instruction is not loading the style restriction in InDesign server. app.loadFindChangeQuery ("tempGrep", SearchModes.GREP_SEARCH); I works correctly in InDesign desktop applying the change just in the points with the specified style. Thanks for pointing me to the query manager. Good point on refactoring the find to [:-]\x20.
... View more
‎Dec 25, 2024
06:07 AM
Thank you @m1b and @Sergey Osokin for your comments. There is an action in the code above "function make_action()" that actually detects wich layers are highlighted by the user in the layers pallete and keeps those visible, and then I can get the names of those visible layers. The end goal is just to return user select layers. Does anyone know a way to accomplish this without running a coded action? I couldn't find any other solutions on my end.
... View more
‎Dec 25, 2024
12:40 AM
1 Upvote
Hi @andyf65867865, I answered a very similar question recently. Could you check out this post and see if it helps?
- Mark
... View more
‎Dec 24, 2024
10:57 AM
Oh, I know. I had worked it out in the find change panel until it worked, then pasted it over and had the issues.
... View more
‎Dec 24, 2024
01:55 AM
@rob day How to change this script to: Duplicate the currently selected rows. Then add a blank line before it. Then merge all the cells of the added row Because table headers sometimes have more than one row.
... View more
‎Dec 23, 2024
07:11 PM
1 Upvote
Well, that's great to hear it worked out well. 🙂
... View more
‎Dec 23, 2024
12:02 PM
Just include footnotes in the layout and see the result!
By @FRIdNGE
???
... View more