[ExtendScript] Get bounds of selected text
Hi all, I've been asked (by @danezeq) in the Indesign forum to convert a script I wrote for returning the bounds of selected text to an Illustrator version.
Examples:

The rectangles are just for demonstration purposes—you can do whatever you like with the bounds.
Here is the script. It will need some testing, so let me know if you find bugs.
- Mark
/**
* Draw rectangle showing selected text's bounds.
* @author m1b
* @discussion https://community.adobe.com/t5/illustrator-discussions/how-to-draw-a-graphic-on-hidden-character-in-illustrator/m-p/13833618
*/
(function () {
var doc = app.activeDocument,
text = doc.selection,
bounds = getTextOutlineBounds(text);
if (bounds == undefined) {
alert('Could not calculate bounds. Please select some text and try again.');
return;
}
// draw a rectangle just for showing the bounds
var rect = doc.pathItems.rectangle(bounds[1], bounds[0], bounds[2] - bounds[0], -(bounds[3] - bounds[1])); // TLWH
rect.fillColor = doc.swatches[4].color;
rect.opacity = 30;
})();
/**
* Returns bounds of Text object [L, T, R, B].
* @author m1b
* @version 2023-06-02
* @param {Text|Paragraph|Line|Word|Character} text - an Indesign text object.
* @returns {Array<Number>} - the bounds [T, L, B, R].
*/
function getTextOutlineBounds(text) {
var bounds;
if (text.constructor.name == 'Array') {
// process each element of array
for (var i = 0; i < text.length; i++)
bounds = expandBoundsForIllustrator(bounds, getTextOutlineBounds(text[i]));
return bounds;
}
else if (text.constructor.name == 'TextFrame') {
// process the text of the text frame
return getTextOutlineBounds(text.textRange);
}
else if (
!text.hasOwnProperty('characters')
|| text.characters.length == 0
|| !text.hasOwnProperty('fillColor')
)
// can't work with this
return;
var tf = text.parent.textFrames[0],
arbitraryValue = 61.803;
// we'll paint the text with this
// color so that when it's outlined,
// we can differentiate it
var markerColor = new CMYKColor();
markerColor.cyan = arbitraryValue;
markerColor.magenta = arbitraryValue;
markerColor.yellow = arbitraryValue;
markerColor.black = arbitraryValue;
var dup = tf.duplicate(),
start = text.start,
end = text.end;
// mark the selected characters
for (var i = start; i < end; i++)
dup.characters[i].fillColor = markerColor;
// a stringified version for matching purposes
var matchMarker = stringify(dup.characters[start].fillColor);
// the outlines are always compoundPathItems
var outlines = dup.createOutline().compoundPathItems;
// find the marked outlines
for (var i = 0; i < outlines.length; i++)
if (stringify(outlines[i].pathItems[0].fillColor) == matchMarker)
bounds = expandBoundsForIllustrator(bounds, outlines[i].visibleBounds);
// delete temporary items
for (var i = outlines.length - 1; i >= 0; i--)
outlines[i].remove();
return bounds;
};
/**
* Returns bounds that encompass both bounds.
* @author m1b
* @version 2022-07-24
* @param {Array} b1 - bounds array [l, t, r, b].
* @param {Array} b2 - bounds array [l, t, r, b].
* @returns {Array} - the encompassing bounds.
*/
function expandBoundsForIllustrator(b1, b2) {
if (!b1 && !b2)
return;
if (!b1 && b2)
return b2.slice();
if (!b2 && b1)
return b1.slice();
var expanded = b2.slice();
for (var i = 0; i < 4; i++) {
if (b1[i] != undefined && b2[i] == undefined) expanded[i] = b1[i];
if (b1[i] == undefined && b2[i] != undefined) expanded[i] = b2[i];
if (b1[i] == undefined && b2[i] == undefined) return;
}
if (b1[0] < b2[0]) expanded[0] = b1[0];
if (b1[1] > b2[1]) expanded[1] = b1[1];
if (b1[2] > b2[2]) expanded[2] = b1[2];
if (b1[3] < b2[3]) expanded[3] = b1[3];
return expanded;
};
/**
* Stringify tailored for the purpose
* of identifying identical Swatches.
* @author m1b
* @version 2023-04-26
* @param {Object} obj - the object to stringify.
* @returns {String}
*/
function stringify(obj) {
var str = obj.toString();
for (var key in obj) {
if (!obj.hasOwnProperty(key))
continue;
if (
key == 'spot'
|| key == 'color'
)
str += stringify(obj[key]);
else
str += (obj[key]);
}
return str;
};

