Copy link to clipboard
Copied
Hi -
I try to make a very "simple" Adobe Illustrator Javascript based on the below script (I found it somewhere years ago on internet, tbh can't remeber where).
The script should simply reset to zero (0°) the "rotate" value of the current selected placed item.
I tried already a lot of stuff, but can't make it works :-P:
I found information like "app.getRotationMatrix(…)". But yeah… How to make it work? 🤷🏻
Here is the source code (base), not the final script:
selection = app.activeDocument.selection;
for (i=0; i<selection.length; i++) {
if(selection[i].typename == "TextFrame" || selection[i].typename == "PlacedItem" || selection[i].typename == "RasterItem") {
get_matrix = selection[i].matrix;
if(selection[i].typename == "PlacedItem")
get_matrix = concatenateScaleMatrix(get_matrix, 100, -100);
invMatrix = invertMatrix(get_matrix);
invMatrix.mValueTX = 0;
invMatrix.mValueTY = 0;
selection[i].transform(invMatrix);
}
}
I tried something (w/ the help of ChatGPT), visually we are close to a result, but the placed item rotate value is just not reset to "zero" (0°):
function resetPlacedItemRotation() {
var myDoc_DC = app.activeDocument;
var IMG_selected = myDoc_DC.selection;
if (IMG_selected.length === 0) {
alert("Please select a placed item.");
return;
}
for (var i = 0; i < IMG_selected.length; i++) {
var item = IMG_selected[i];
if (item.typename === "PlacedItem") {
var confirmReset = confirm("Do you want to reset the rotation for the selected placed item?");
if (confirmReset) {
// Get the current transformation matrix
var currentMatrix = item.matrix;
// Calculate the current rotation angle in degrees
var currentRotation = Math.atan2(currentMatrix.mValueB, currentMatrix.mValueA) * (180 / Math.PI);
// Apply the exact opposite of the current rotation
var rotationToApply = -currentRotation; // Opposite direction
// Rotate around the center of the placed item
item.rotate(rotationToApply, true, true, true, true, Transformation.CENTER);
alert("Rotation has been reset to 0° by applying " + rotationToApply.toFixed(3) + "°.");
} else {
alert("Operation canceled.");
}
} else {
alert("Selected item is not a placed item.");
}
}
}
resetPlacedItemRotation();
Thank you,
Enjoy your day,
- Dimitri
Seulement rotation 0°
// JavaScript Document
// This script resets the rotation angle to 0 degrees for bitmap clips and Imported artwork.
var info = false;
testObjects(selection);
// -------
function testObjects(a) {
var deg,
tag,
selLen = a.length;
for (var c = 0; c < selLen; c++) {
if(a[c].typename == 'GroupItem'){
testObjects(a[c].pageItems);
} else
if(a[c].typename=='RasterItem' || a[c].typename == 'PlacedItem') {
d
...
Copy link to clipboard
Copied
Hi @dimitri_cas, here is one approach—get the rotation from the placed item's matrix, then rotate it by that negative amount:
(function () {
var doc = app.activeDocument,
placedItem = doc.selection[0];
if (!placedItem)
return alert('Please select a placed item and try again.');
var sr = getLinkScaleAndRotation(placedItem),
scale = [sr[0], sr[1]],
rotation = sr[2];
placedItem.rotate(-rotation);
})();
/**
* Return the scale, rotation and size
* of a PlacedItem or RasterItem.
* @author m1b
* @version 2023-03-09
* @param {PlacedItem|RasterItem} item - an Illustrator item.
* @param {Boolean} round - whether to round numbers to nearest integer.
* @returns {Array} [scaleX%, scaleY%, rotation°, width, height]
*/
function getLinkScaleAndRotation(item, round) {
if (item == undefined)
return;
var m = item.matrix,
rotatedAmount,
unrotatedMatrix,
scaledAmount;
var flipPlacedItem = (item.typename == 'PlacedItem') ? 1 : -1;
try {
rotatedAmount = item.tags.getByName('BBAccumRotation').value * 180 / Math.PI;
} catch (error) {
rotatedAmount = 0;
}
unrotatedMatrix = app.concatenateRotationMatrix(m, rotatedAmount * flipPlacedItem);
if (
unrotatedMatrix.mValueA == 0
&& unrotatedMatrix.mValueB !== 0
&& unrotatedMatrix.mValueC !== 0
&& unrotatedMatrix.mValueD == 0
)
scaledAmount = [unrotatedMatrix.mValueB * 100, unrotatedMatrix.mValueC * -100 * flipPlacedItem];
else
scaledAmount = [unrotatedMatrix.mValueA * 100, unrotatedMatrix.mValueD * -100 * flipPlacedItem];
if (scaledAmount[0] == 0 || scaledAmount[1] == 0)
return;
if (round)
return [round(scaledAmount[0]), round(scaledAmount[1]), round(rotatedAmount)];
else
return [scaledAmount[0], scaledAmount[1], rotatedAmount];
};
There are other ways, eg. read the rotation value from a tag:
rotatedAmount = item.tags.getByName('BBAccumRotation').value * 180 / Math.PI;
I also wrote a function that derives the rotation value by minimizing the area of the rectangular bounds of the item:
/**
* Returns the rotation amount in degrees
* that the item needs to be rotated such
* that it has a minimal bounding box area.
* Assuming that `item` is a rectangular
* object, such as a PlacedItem, RasterItem
* or a rectangular path item, the resulting
* rotation will rotate it so that the sides
* of the rectangle align to a factor of 90°.
* In other words, it will return the value
* required to "unrotate" the item.
* @author m1b
* @version 2023-08-25
* @param {PageItem} item - an Illustrator page item.
* @returns {Number}
*/
function findRotationByMinimalBounds(item) {
// we will rotate a copy and leave the original
var workingItem = item.duplicate(),
convergenceThreshold = 0.001,
inc = 45, // the starting rotation increment
rotationAmount = 0,
prevArea = area(workingItem);
while (Math.abs(inc) >= convergenceThreshold) {
workingItem.rotate(inc);
var newArea = area(workingItem);
if (newArea < prevArea) {
prevArea = newArea;
rotationAmount -= inc;
inc *= 0.5;
}
else {
workingItem.rotate(-inc); // Undo the last rotation
inc *= -0.5;
}
}
// clean up
workingItem.remove();
return round(rotationAmount, 2);
/**
* Returns area of bounding box of `item`.
* @param {PageItem} item
* @returns {Number}
*/
function area(item) {
return item.width * item.height;
};
};
/**
* Rounds `n` to `places` decimal places.
* @param {Number} n - the number to round
* @param {Number} places - number of decimal places, can be negative
* @returns {Number}
*/
function round(n, places) {
var m = Math.pow(10, places != undefined ? places : 3);
return Math.round(n * m) / m;
};
Each method has pros and cons. I suggest you try the first approach first and see how you go. It only works with an unskewed matrix. The second approach only works if the tag exists, and no rotations have been performed without updating it. The third option is probably the most robust but is a bit slower.
- Mark
Copy link to clipboard
Copied
Hi m1b -
Thanks for your reply.
First way/option seems to work perfectly 😉
It worked well the first time, I tried already in another document on another placed image and it didn't work.
The seoncd time, result is quite similar than mine… 😞
Thx again.
Enjoy your day.
- Dimitri
Copy link to clipboard
Copied
I think to get Mark's first script to run properly requires to reset the bounding box after each rotation to zero degree. You may want to modify that.
You may also have a look at this approach by Sky Chaser High which is pretty good.
/* ===============================================================================================================================================
resetToFullScale
Description
This script resets the scale to 100% and the rotation angle to 0 degrees for the linked files.
Embedded images is also supported.
Usage
Select the linked files or the embedded images, run this script from File > Scripts > Other Script...
Notes
In rare cases, if you continue to use the script, it may not work.
In that case, restart Illustrator and try again.
Requirements
Illustrator CS6 or higher
Version
1.0.1
Homepage
github.com/sky-chaser-high/adobe-illustrator-scripts
License
Released under the MIT license.
https://opensource.org/licenses/mit-license.php
=============================================================================================================================================== */
(function() {
if (app.documents.length > 0 && app.activeDocument.selection.length > 0) main();
})();
function main() {
// work around a bug
try {
app.executeMenuCommand('Adobe Update Link Shortcut'); // Link Panel > Update Link
app.redraw();
}
catch (err) { }
var items = app.activeDocument.selection;
var images = getImageItems(items);
for (var i = 0; i < images.length; i++) {
resetToFullScale(images[i]);
}
}
function resetToFullScale(item) {
var basis = {
'left': item.left,
'top': item.top,
'width': item.width,
'height': item.height
};
var filename = '';
try {
if (item.typename == 'PlacedItem') filename = item.file.name;
}
catch (err) { }
// work around a bug
var deg = getRotationAngle(item);
item.rotate(deg * -1);
var scale = getScale(item);
item.resize(1 / scale.x * 100, 1 / scale.y * 100);
item.matrix.mValueA = 1;
item.matrix.mValueB = 0;
item.matrix.mValueC = 0;
item.matrix.mValueD = (/\.eps$/i.test(filename) || item.typename == 'RasterItem') ? 1 : -1; // invert the sign except for eps file and embedded image
// workaround for the reflect object
item.left = 0;
item.top = 0;
// reposition the object to the center
item.left = basis.left - (item.width - basis.width) / 2;
item.top = basis.top + (item.height - basis.height) / 2;
}
function getRotationAngle(item) {
var matrix = item.matrix;
var rad = Math.atan2(matrix.mValueB, matrix.mValueA);
var deg = rad * 180 / Math.PI;
return (item.typename == 'PlacedItem') ? deg * -1 : deg;
}
function getScale(item) {
var matrix = item.matrix;
var x = Math.sqrt(Math.pow(matrix.mValueA, 2) + Math.pow(matrix.mValueB, 2));
var y = Math.sqrt(Math.pow(matrix.mValueC, 2) + Math.pow(matrix.mValueD, 2));
return {
x: x,
y: y
};
}
function getImageItems(items) {
var images = [];
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.typename == 'PlacedItem' || item.typename == 'RasterItem') {
images.push(item);
}
if (item.typename == 'GroupItem') {
images = images.concat(getImageItems(item.pageItems));
}
}
return images;
}
Copy link to clipboard
Copied
Bonjour à tous,
Je pense que vous devriez ajouter cela à vos scripts...
var tag = getTag(item);
if (tag != undefined) tag.value = 0;
}
//-----------
function getTag(obj)
{ // retourne le V.tag de nom "BBAccumRotation" si il existe
try {
var tag = obj.tags.getByName("BBAccumRotation");
} catch (e) {
if (info) alert( "The specified tag doesn’t exist" );
return undefined;
}
return tag;
}
René
Copy link to clipboard
Copied
Hello René -
OK, mais cela fait quoi en résumé?
Car la version de Kurt semble fonctionner correctement.
Merci pour votre intervention 🙂
- Dimitri
Copy link to clipboard
Copied
Bonjour Dimitri,
Si on prend le script de Kurt, pour la version d'Illustrator que j'utilise.
Si je sélectinne l'image après exécution.
C'est pourquoi je propose de réinitialiser le Tag à la valeur 0.
René
Copy link to clipboard
Copied
Hi Kurt 🙂
It works perfectly fine (for now).
A sh*t, It reset everything. I only need the "roate value" to be reset (nothing else).
Thanks a lot enjoy your day.
- Dimitri
Copy link to clipboard
Copied
This one doesn't work, it resets all the transformation.
Not only the "rotate value"
Copy link to clipboard
Copied
Hi…
The one from "Sky Chaser High"
OK… Pretty good, but here too, it resets all the transformations, not only the "rotate value" 😕
Copy link to clipboard
Copied
It would be interesting if you post a test file where the rotation fails, then we could possibly explain why.
Did you try each of the approaches I mentioned?
For example, I made the following technique for a job last year due to issues of other methods not being robust enough. However the downside is it will only rotate to the nearest factor of 90°. So very good for straightening, but if something rotated 91° it will rotate back by 1° only. You can compensate if you know the aspect ratio.
Anyway, here is test code. First select a placed item and run script:
(function () {
var doc = app.activeDocument,
item = doc.selection[0];
// undo the rotation
item.rotate(-findRotationByMinimalBounds(item));
})();
/**
* Returns the rotation amount in degrees
* that the item needs to be rotated such
* that it has a minimal bounding box area.
* Assuming that `item` is a rectangular
* object, such as a PlacedItem, RasterItem
* or a rectangular path item, the resulting
* rotation will rotate it so that the sides
* of the rectangle align to a factor of 90°.
* In other words, it will return the value
* required to "unrotate" the item.
* @author m1b
* @version 2023-08-25
* @Param {PageItem} item - an Illustrator page item.
* @Returns {Number}
*/
function findRotationByMinimalBounds(item) {
// we will rotate a copy and leave the original
var workingItem = item.duplicate(),
convergenceThreshold = 0.001,
inc = 45, // the starting rotation increment
rotationAmount = 0,
prevArea = area(workingItem);
while (Math.abs(inc) >= convergenceThreshold) {
workingItem.rotate(inc);
var newArea = area(workingItem);
if (newArea < prevArea) {
prevArea = newArea;
rotationAmount -= inc;
inc *= 0.5;
}
else {
workingItem.rotate(-inc); // Undo the last rotation
inc *= -0.5;
}
}
// clean up
workingItem.remove();
return round(rotationAmount, 2);
/**
* Returns area of bounding box of `item`.
* @Param {PageItem} item
* @Returns {Number}
*/
function area(item) {
return item.width * item.height;
};
};
/**
* Rounds `n` to `places` decimal places.
* @Param {Number} n - the number to round
* @Param {Number} places - number of decimal places, can be negative
* @Returns {Number}
*/
function round(n, places) {
var m = Math.pow(10, places != undefined ? places : 3);
return Math.round(n * m) / m;
};
Copy link to clipboard
Copied
Hi Mar k--The last one, the most "robust" as you said
Doesn't work too
Sorry… You written
findRotationByMinimalBounds(item)
"
But what/where the "item" in your code?
thx
Copy link to clipboard
Copied
Seulement rotation 0°
// JavaScript Document
// This script resets the rotation angle to 0 degrees for bitmap clips and Imported artwork.
var info = false;
testObjects(selection);
// -------
function testObjects(a) {
var deg,
tag,
selLen = a.length;
for (var c = 0; c < selLen; c++) {
if(a[c].typename == 'GroupItem'){
testObjects(a[c].pageItems);
} else
if(a[c].typename=='RasterItem' || a[c].typename == 'PlacedItem') {
deg = getRotationAngle(a[c]);
a[c].rotate(deg * -1);
tag = getTag(a[c]);
if (tag != undefined) tag.value = 0;
}
}
}
// -------
function getRotationAngle(item) {
var mtx = item.matrix;
var deg = Math.atan2(mtx.mValueB, mtx.mValueA) * 180 / Math.PI;
return (item.typename == 'PlacedItem') ? deg * -1 : deg;
}
// -------
function getTag(obj)
{ // retourne le V.tag de nom "BBAccumRotation" si il existe
try {
var tag = obj.tags.getByName("BBAccumRotation");
} catch (e) {
if (info) alert( "The specified tag doesn’t exist" );
return undefined;
}
return tag;
}
// -------
René
Copy link to clipboard
Copied
Salut René 🙂
Hi Mark m1b 🙂
Last script from René is exactly what I'm looking for. For now, it works like a charm (in most of the cases, see next message).
Two both of you a BIG thx (merci), for your time, explanations and your expertise.
Enjoy your day…
- Dimitri
Copy link to clipboard
Copied
Hi again 🙂
It works in most of the cases…
Now I just had a case were the value was "Rotate: 176.52°"
And the script didn't reacted correctly, placed item was like flipped vertically and not really at 0°…
- Dimitri
Copy link to clipboard
Copied
Bonjour Dimitri,
Après de multiples essais,
176.52° rotation copier et ensuite CTRL+D
et bien d'autres...
Après alignement etajout de bordures.
René
Copy link to clipboard
Copied
Hi @dimitri_cas if you discover an item that won't rotate correctly, please post a demo document (save as an Illustrator-editable pdf). That is the best way to get to the bottom of it.
- Mark
Copy link to clipboard
Copied
Hi Mark 🙂
Salut René renél80416020
Here we go, a PDF (Illustrator editable) with the wrong/incorrect behaviour of the script
Copy link to clipboard
Copied
Salut Dimitri,
Merci pour l'exemple, ma version d'Illustrator est un peu ancienne CS6, je peux cependant ouvrir le pdf peut-être a t'il subit des modifications?
Exemple l'image de gauche (sélection ?):
Si maintenant j'utilise une image sans anomalie:
Le résultat est correct.
René
Copy link to clipboard
Copied
Bonjour René 🙂
Je ne m'explique pas trop le souci.
Probablement avez vous raison…
Merci.
Copy link to clipboard
Copied
@dimitri_cas `item` is a parameter in my function, which expects a PlacedItem or a RasterItem (it will work with other things too, but if they aren't rectangular it might not give expected results). You can see my code above I call the function with item being the selected item. So: 1) select a placed item, and 2) run script.
I think you are struggling to understand something. So far, literally everyone who has commented here has given a correct answer. I suggest you post an example of a document that causes a particular script to fail.
- Mark
Copy link to clipboard
Copied
Hi @dimitri_cas, you are already well on your way to a solution, but I just remembered that I did a similar thing in a previous script I wrote. Maybe you could look at it and see if it sheds any light on the problem.
- Mark
Copy link to clipboard
Copied
Hi Mark -
Salut René renél80416020
Thanks to both of you for your help.
Strangely enough, currently I can't reproduce the "wrong behaviour" of the script (ie. That it flipped not to 0° the placed item, instead of just resetting the rotate value to 0°). And I don't know why… 😛
If I still encounter the "wrong behaviour" I'll post the file (PDF) as requested 😉
Thanks again, and
both of you, enjoy your day.
- Dimitri
Find more inspiration, events, and resources on the new Adobe Community
Explore Now