Copy link to clipboard
Copied
In a text node, different encodings of characters correspond to different widths(pt) of individual characters, how to get the width of a specified individual character when using extendscript script?ðŸ˜
Copy link to clipboard
Copied
duplicate your text frame, remove all characters except the one you need to measure, create outlines, get width
Copy link to clipboard
Copied
thank u!😉
Copy link to clipboard
Copied
Hi @wy3245464968i2, it isn't easy, as Carlos intimated. Here is a script that demonstrates a function I made to do what you ask. It returns the "fontBounds" which is the size of the glyphs bounding box(es) and the geometric bounds (calculated exactly as @CarlosCanto suggested). Note that the function is quite naive regarding the text inputs—it will certainly fail in many cases. For example, if the text is rotated, then the bounds should be correct, but the fontBounds will be wrong—it will need extra logic to calculate correctly in that case. Anyway, give it a try and see if it helps.
- Mark
/**
* Example usage of getTextDimensions function to show
* font bounds and geometry bounds of selected text.
* @author m1b
* @discussion https://community.adobe.com/t5/illustrator-discussions/how-to-get-physical-width-of-character-extendscript/m-p/14103798
*/
(function () {
var doc = app.activeDocument,
text = doc.selection,
dimensions = getTextDimensions(text);
if (dimensions == undefined) {
alert('Could not calculate dimensions. Please select some text and try again.');
return;
}
// JUST FOR EXAMPLE PURPOSES:
drawRectangle(doc, dimensions.fontBounds, makeColor([255, 0, 0]));
drawRectangle(doc, dimensions.bounds, makeColor([0, 0, 255]));
})();
/**
* Returns dimension information about the given `text`.
* @author m1b
* @version 2023-06-02
* @param {TextRange|TextFrame} text - an Illustrator text object.
* @returns {ArrayObject} - { fontBounds: bounds: left: top: width: height: }
*/
function getTextDimensions(text) {
var bounds;
if (
text.constructor.name == 'Array'
&& text.length > 0
)
return getTextDimensions(text[0]);
else if (text.constructor.name == 'TextFrame')
// process the text of the text frame
return getTextDimensions(text.textRange);
else if (
!text.hasOwnProperty('characters')
|| text.characters.length == 0
|| !text.hasOwnProperty('fillColor')
)
// can't work with this
return;
var result = {
fontBounds: undefined,
bounds: undefined,
left: undefined,
top: undefined,
width: undefined,
height: undefined,
};
var doc = getDocument(text);
// first get the dimensions by duplicate
// the text in its own frame at [0, 0]
var t = doc.activeLayer.textFrames.add(ElementPlacement.PLACEATEND);
t.position = [0, 0];
text.duplicate(t);
var fontBounds = t.geometricBounds;
var outline = t.createOutline();
var innerBounds = outline.geometricBounds;
outline.remove();
result.width = innerBounds[2] - innerBounds[0];
result.height = -(innerBounds[3] - innerBounds[1]);
// next get the dimensions in correct position
var tf = text.parent.textFrames[0];
// we'll paint the text with this
// so we can differentiate it
var markerColor = getMarkerColor();
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
dup = dup.createOutline();
var outlines = dup.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);
dup.remove();
// now correct for the difference between [0,0]
fontBounds[0] += bounds[0] - innerBounds[0];
fontBounds[1] += bounds[1] - innerBounds[1];
fontBounds[2] += bounds[2] - innerBounds[2];
fontBounds[3] += bounds[3] - innerBounds[3];
// update result
result.bounds = bounds;
result.fontBounds = fontBounds;
result.left = result.bounds[0];
result.top = result.bounds[1];
return result;
};
/**
* 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;
};
/**
* Attempt to return an unusual color for identification purposes.
* @returns {RGBColor|CMYKColor}
*/
function getMarkerColor() {
var c,
v = 61.8;
if (app.activeDocument.documentColorSpace == DocumentColorSpace.RGB) {
c = new RGBColor();
c.red = c.green = c.blue = v;
}
else {
c = new CMYKColor();
c.cyan = c.magenta = c.yellow = c.black = v;
}
return c;
};
/**
* Returns a page item's document.
* @author m1b
* @version 2023-09-13
* @param {PageItem} item - an Illustrator page item.
* @returns {Document}
*/
function getDocument(item) {
var parent = item;
while (
parent.constructor.name !== 'Document'
&& parent.hasOwnProperty('parent')
)
parent = parent.parent;
if (parent.constructor.name == 'Document')
return parent;
};
/**
* Return an RGB color.
* @param {Array<Number>} breakdown - the color breakdown.
* @returns {RGBColor}
*/
function makeColor(breakdown) {
var colr = new RGBColor();
colr.red = breakdown[0];
colr.green = breakdown[1];
colr.blue = breakdown[2];
return colr;
};
/**
* desc
* @date 2023-09-23
* @param {Document} doc - an Illustrator document.
* @param {Array<Number>} bounds - [T, L, B, R]
* @param {RGBColor|CMYKColor|GrayColor} color - the color
* @return {PathItem}
*/
function drawRectangle(doc, bounds, color) {
var rect = doc.pathItems.rectangle(bounds[1], bounds[0], bounds[2] - bounds[0], -(bounds[3] - bounds[1])); // TLWH
rect.filled = true;
rect.stroked = false;
rect.fillColor = color;
rect.opacity = 30;
return rect;
};
Copy link to clipboard
Copied
yeah covering all the scenarios is certainly not easy @m1b , your script does a lot more than I thought was needed.
thanks for sharing!
Copy link to clipboard
Copied
thank u,i will give it a try👀