Copy link to clipboard
Copied
ABCD Name
AD Name
ABD Name
I need to align the three above.
Some people may use grids, but that feature is difficult to use and I usually don't use it. It's better to use font spacing.
However, it is troublesome to adjust the font spacing. For example, when the font size becomes larger, I have to try every Letter spacing value.
Just want to ask: What is the relationship between font size and spacing, can we calculate it?
Hi @dublove5CFE, the issue here is converting tracking units (which I assume you are using to space out the A in the second line) into ruler units. Tracking is measured in em units which relate to the font size. The conversion factor is, 1000 / font size. Some fonts have differing sized em squares but Indesign seems to calibrate them all to 1000 units in terms of the UI and scripting API. You can see how it works in this script, that attempts to solve your problem.
/**
* Letterspace Chars T
...
Hi @dublove5CFE, I have written a modified version that also sets the tab stop so that the tab character width is 5mm (you can adjust this in the script). I've also put the letterspacing into a function so it can be used for other purposes.
Try this version and see what you think.
- Mark
/**
* Letterspace Chars Before Tab
* Script expects to have a text selection that includes
* paragraphs that share a tab stop, and will and will
* adjust tracking of characters before the tab character
*
...
Hi @dublove5CFE, it sounds like it might be better in your case just to replace the tab with another character of your choice. Here is a re-write of the script that replaces tab with em space u+2003. You could choose another unicode space character.
- Mark
/**
* Letterspace Chars Before Tab
* Script expects to have a text selection that includes
* paragraphs that share a tab stop, and will adjust
* tracking of characters before the tab character
* so that the last characters before each
...
Thank you Robert.
Copy link to clipboard
Copied
And @Peter Spier has been right from the beginning with his table suggestion ...
In this case script would be much simpler - convert text to table and then set width of the column(s) and insets of the cells ...
Copy link to clipboard
Copied
Thank you Robert.
Copy link to clipboard
Copied
Ah! I think my misunderstanding is because in our demo, the text is Latin and the font proportionally-spaced which means that each character has different widths—there isn't such a thing as "one character width" in this case. But now I assume you are using Chinese characters. Here is a version of the script that just gets the tabWidth variable by measuring the width of the very first character. Perhaps this will be good for you.
- Mark
/**
* Letterspace Chars Before Tab
* Script expects to have a text selection that includes
* paragraphs that share a tab stop, and will and will
* adjust tracking of characters before the tab character
* so that the last characters in each paragraph align.
* It will set a tab stop so that the width of the tab
* character equals `tabWidth` which is set to the first
* character width.
* @author m1b
* @discussion https://community.adobe.com/t5/indesign-discussions/is-there-any-specific-algorithm-relationship-between-letter-spacing-and-font-size-in-indesign/m-p/13861581
*/
function main() {
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
var doc = app.activeDocument,
text = doc.selection[0];
if (
text == undefined
|| typeof text.findGrep !== 'function'
|| text.contents == ''
) {
alert('Please select some text and run script again.');
return;
}
// find paragraphs with tabs
app.findGrepPreferences = NothingEnum.NOTHING;
app.findGrepPreferences.findWhat = '^[^\\t]*\\t';
var found = text.findGrep();
var tabWidth = found[0].characters[0].endHorizontalOffset - found[0].characters[0].horizontalOffset;
// store the horizontal position
// of the right-most tab
var positions = [],
maxPosition = -Infinity,
tabPosition = 0
for (var i = 0; i < found.length; i++) {
found[i].tracking = 0;
positions[i] = found[i].insertionPoints[found[i].insertionPoints.length - 2].horizontalOffset;
if (positions[i] > maxPosition) {
maxPosition = positions[i];
left = found[i].insertionPoints[0].horizontalOffset;
tabPosition = found[i].insertionPoints[found[i].insertionPoints.length - 1].horizontalOffset;
}
}
for (var i = 0; i < found.length; i++) {
var tabStops = found[i].tabStops;
// remove all tab stops in alignment area
for (var j = tabStops.length - 1; j >= 0; j--)
if (tabStops[j].position <= Math.max(maxPosition + tabWidth, tabPosition))
tabStops[j].remove();
// set a tab stop to match the tabWidth
tabStops.add({ position: maxPosition - left + tabWidth });
if (
positions[i] !== maxPosition
&& found[i].characters.length > 1
)
// letterspace the characters before tab
letterspaceToWidth(found[i].characters.itemByRange(0, found[i].characters.length - 2), maxPosition - found[i].insertionPoints[0].horizontalOffset);
}
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Space Chars Before Tab");
/**
* Applies tracking to text so that
* the width of the text will match
* with `width` parameter supplied.
* Assumptions:
* - text is justified left
* - text is uniform point size
* - text doesn't span multiple lines
* @author m1b
* @version 2023-06-16
* @Param {Text} text - an Indesign text object.
* @Param {Number} width - the desired width in pts.
*/
function letterspaceToWidth(text, width) {
if (!text.hasOwnProperty('insertionPoints'))
return;
const emSquare = 1000;
var left = text.insertionPoints[0].horizontalOffset,
right = text.insertionPoints[text.insertionPoints.length - 1].horizontalOffset,
chars = text.characters.itemByRange(0, text.characters.length - 2).characters,
delta = width - (right - left);
for (var i = 0; i < chars.length; i++)
chars[i].tracking = (delta / chars.length) * (emSquare / chars[i].pointSize);
};
Copy link to clipboard
Copied
just
I explained in detail that I want the spacing to automatically be the width of one character (full or half width)
Copy link to clipboard
Copied
Did my last script do what you want?
Copy link to clipboard
Copied
I tried, but I couldn't find where to modify the interval value.
This interval should vary with font size.
Copy link to clipboard
Copied
In the last script, it uses the width of the first character (eg. the A in the demo file).
Copy link to clipboard
Copied
Difficult to control.
Can't there be three options?
Tracking=1 full width word width
or
Tracking=1 half width word
or
Tracking="5" mm
Copy link to clipboard
Copied
I replied to the end.
Copy link to clipboard
Copied
Sorry, it's a bit messy~ I replied incorrectly to another person.
Should add a version number to the script.
Perhaps this is the most applicable.
How to add the situation of ② to it, which is a colon (:)
app.findGrepPreferences.findWhat = '^[^\\t]*\\t';
There are generally two situations:
① Tab separation
② Separate with ':or :', just align the colon here
This width will change
5mm at 12pt font size
But when it comes to size 30, it needs to be adjusted to 8mm.
I hope it is automatic.
I want to keep the tab, always one full width word wide
The gap is always the width of a full width character (or half width character)
Copy link to clipboard
Copied
So, would it help to have a function that set the tabWidth based on the point size? It would be like this:
function interpolateLinear(n) {
var v = [
[12 /* font size --> */, 5 /* mm output*/],
[25 /* font size --> */, 8 /* mm output*/],
];
return v[0][1] + ((n - v[0][0]) / (v[1][0] - v[0][0])) * (v[1][1] - v[0][1]);
};
This is configured to return 5mm when given 12pt font size and 8mm when given 25pt font size. It would interpolate/extrapolate when given other values of n, eg. 30pt font size would return 9.15mm, font size 9pt would return 4.3mm.
What do you think?
- Mark
Like this:
/**
* Letterspace Chars Before Tab
* Script expects to have a text selection that includes
* paragraphs that share a tab stop, and will and will
* adjust tracking of characters before the tab character
* so that the last characters in each paragraph align.
* It will set a tab stop so that the width of the tab
* character equals `tabWidth`, which is based on the
* function `tabWidthForPointSize`.
* @author m1b
* @discussion https://community.adobe.com/t5/indesign-discussions/is-there-any-specific-algorithm-relationship-between-letter-spacing-and-font-size-in-indesign/m-p/13861581
*/
function main() {
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
var doc = app.activeDocument,
text = doc.selection[0];
if (
text == undefined
|| typeof text.findGrep !== 'function'
|| text.contents == ''
) {
alert('Please select some text and run script again.');
return;
}
// find paragraphs with tabs
app.findGrepPreferences = NothingEnum.NOTHING;
app.findGrepPreferences.findWhat = '^[^\\t]*[:\\t]';
var found = text.findGrep();
var tabWidth = new UnitValue(tabWidthForPointSize(found[0].characters[0].pointSize)).as('pt');
// store the horizontal position
// of the right-most tab
var positions = [],
maxPosition = -Infinity,
tabPosition = 0
for (var i = 0; i < found.length; i++) {
found[i].tracking = 0;
positions[i] = found[i].insertionPoints[found[i].insertionPoints.length - 2].horizontalOffset;
if (positions[i] > maxPosition) {
maxPosition = positions[i];
left = found[i].insertionPoints[0].horizontalOffset;
tabPosition = found[i].insertionPoints[found[i].insertionPoints.length - 1].horizontalOffset;
}
}
for (var i = 0; i < found.length; i++) {
var tabStops = found[i].tabStops;
// remove all tab stops in alignment area
for (var j = tabStops.length - 1; j >= 0; j--)
if (tabStops[j].position <= Math.max(maxPosition + tabWidth, tabPosition))
tabStops[j].remove();
// set a tab stop to match the tabWidth
tabStops.add({ position: maxPosition - left + tabWidth });
if (
positions[i] !== maxPosition
&& found[i].characters.length > 1
)
// letterspace the characters before tab
letterspaceToWidth(found[i].characters.itemByRange(0, found[i].characters.length - 2), maxPosition - found[i].insertionPoints[0].horizontalOffset);
}
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Space Chars Before Tab");
/**
* Applies tracking to text so that
* the width of the text will match
* with `width` parameter supplied.
* Assumptions:
* - text is justified left
* - text is uniform point size
* - text doesn't span multiple lines
* @author m1b
* @version 2023-06-16
* @Param {Text} text - an Indesign text object.
* @Param {Number} width - the desired width in pts.
*/
function letterspaceToWidth(text, width) {
if (!text.hasOwnProperty('insertionPoints'))
return;
const emSquare = 1000;
var left = text.insertionPoints[0].horizontalOffset,
right = text.insertionPoints[text.insertionPoints.length - 1].horizontalOffset,
chars = text.characters.itemByRange(0, text.characters.length - 2).characters,
delta = width - (right - left);
for (var i = 0; i < chars.length; i++)
chars[i].tracking = (delta / chars.length) * (emSquare / chars[i].pointSize);
};
/**
* Returns a width string (mm)
* given a point size (pt).
* @Param {Number} n
* @Returns {String}
*/
function tabWidthForPointSize(n) {
var v = [
[12 /* point size --> */, 5 /* mm output*/],
[25 /* point size --> */, 8 /* mm output*/],
];
return (v[0][1] + ((n - v[0][0]) / (v[1][0] - v[0][0])) * (v[1][1] - v[0][1])) + 'mm';
};
Copy link to clipboard
Copied
This is too complicated.
You still need to try different values and modify them.
The font size will not be fixed.
The font size may not be 25pt or 12pt, it is variable, I am just giving an example.
What I think is that the gap will adapt to the width of one character based on the font size (there is already a version where the tab is converted to a space, but it's a bit strange without the tab).
It would be great if the gap (1 full or half width character) could automatically adapt to the font size.
The user only needs to choose whether it is a full width or half width, and no other operations are required.
Of course, it would be more perfect to reserve the ability to set specific values.
Copy link to clipboard
Copied
Did you try that script? It does return a width based on the font size (for *any* font size).
Copy link to clipboard
Copied
hello@m1b
There are still some minor issues with this script.
Could you please take a look again?
Thank you very much
More important issues:
If you run it for the second time, it may not work. The reason is that it cannot clear the first "tab" setting. Perhaps the script should be initialized before starting to run each time.
2. Cross column, cross page operation, script error.
3. There is an empty line between the selected areas, or there is no "tab" in the line, resulting in an error in the script.
Copy link to clipboard
Copied
Hello, great M1B,
Thanks, I finally found you.
Help test this.
After running the script above, TAB did not align to the widest position.
There is no alignment before and after, it seems that it is not the width of a character.
It seems that there is a space in front of TAB, or an error occurs without a tab. The running script (two columns together) and cross -page (two pages together) will also make an error.
The script sometimes makes an error after repeated operation. It may be that the old TAB value is not cleared before each operation. At the same time, the word distance should be removed before performing the follow -up operation.
In addition, in the process of using it, I found that using TAB as a interval is more convenient, and the space -based interval is not conducive to later modification.
It is hoped that the width of the TAB defaults a full -angle word, but it can be customized and modified, such as 2 full -angle or 1 half -angle character interval.
The full -angle is targeted at Asian text and half -horn for English.
Thank you very much ~
If there is no TAB in the selection, it will not be aligned.
As if:
This situation should skip the line without TAB
I was right when I aligned the first part.
When I aligned the second part, I found that there was no reference object, so I chose the first part together, and the result could not be aligned.
Copy link to clipboard
Copied
Hi @dublove, can you please post a .indd document that shows on page 1, and example BEFORE script and on page 2 the same example AFTER script, so I can understand exactly what you want. I'm very confused.
- Mark
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Thanks @dublove but please set up a demo .indd document how I asked. I don't need any expanation, just BEFORE and AFTER. It is your job to make sure the BEFORE and AFTER accommodate all the different possibilities you want to cover. Honestly though, this is getting too complicated to warrant a free script.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Hi @dublove, I've re-written script to fit your demo document. Give it a try and let me know how it goes.
- Mark
/**
* Letterspace Chars Before Tab.js
* Script expects to have a text selection, and will adjust
* tracking of characters before the tab character so that
* the last characters before each tab align horizontally.
*
* Notes and assumptions:
* - Script assumes each paragraph is a single line.
* - Script will remove all tracking on text.
* - Tab stop is set according to maximum width of characters
* before tab, plus an em space width.
* - Some formatting will distort result, eg. first line indent.
* - Script will ignore a colon character directly before the tab.
*
* @author m1b
* @discussion https://community.adobe.com/t5/indesign-discussions/is-there-any-specific-algorithm-relationship-between-letter-spacing-and-font-size-in-indesign/m-p/13861581
*/
function main() {
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
const EM_SPACE = '\u2003';
var doc = app.activeDocument,
text = doc.selection[0];
if (
text == undefined
|| typeof text.findGrep !== 'function'
|| text.contents == ''
)
return alert('Please select some text and run script again.');
if ('TextFrame' === text.constructor.name)
text = text.texts[0];
var paras = text.paragraphs.everyItem().getElements();
// set tab stop at longest tab
var zeroPos = -Infinity,
tabStopPos = -Infinity,
justifyPos = -Infinity,
maximums = { tabStopPos: tabStopPos, justifyPos: justifyPos },
metrics = [],
findFirstTab = /\t/,
offset = paras[0].leftIndent + paras[0].firstLineIndent,
offset,
match;
for (var i = paras.length - 1; i >= 0; i--) {
match = paras[i].contents.match(findFirstTab);
if (null === match)
continue;
// remove tracking
paras[i].tracking = 0;
// remove tab stops
while (paras[i].tabStops.length)
paras[i].tabStops[0].remove();
// replace first found tab with em space
paras[i].characters[match.index].contents = EM_SPACE;
// start of line
zeroPos = paras[i].characters[match.index].lines[0].characters[0].horizontalOffset;
// position of tab stop
tabStopPos = paras[i].characters[match.index + 1].horizontalOffset;
// justify position
justifyPos = paras[i].characters[match.index].horizontalOffset;
if (maximums.tabStopPos < tabStopPos - zeroPos) {
// update maximum tab position
maximums.tabStopPos = tabStopPos - zeroPos;
maximums.justifyPos = justifyPos - zeroPos;
}
// keep some metrics for each paragraph
metrics[i] = {
tabIndex: match.index,
zeroPos: zeroPos,
justifyPos: justifyPos,
hasColon: ':' === paras[i].characters[match.index - 1].contents,
};
// return text to normal tab
paras[i].characters[match.index].contents = '\t';
}
// add a tabStop at the maximum
text.tabStops.add({
alignment: TabStopAlignment.LEFT_ALIGN,
position: maximums.tabStopPos + offset,
});
// now force justify the text before the tab
for (var i = metrics.length - 1, charactersBeforeTab; i >= 0; i--) {
if (!metrics[i] || !metrics[i].tabIndex)
continue;
rightIndex = metrics[i].tabIndex - 1;
if (metrics[i].hasColon)
rightIndex--;
charactersBeforeTab = paras[i].characters.itemByRange(0, rightIndex);
// space out the characters before the tab
letterspaceToWidth(charactersBeforeTab, maximums.justifyPos);
}
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Letterspace Characters Before Tab');
/**
* Applies tracking to text so that
* the width of the text will match
* with `width` parameter supplied.
* Assumptions:
* - text is justified left
* - text is uniform point size
* - text doesn't span multiple lines
* @author m1b
* @version 2023-06-16
* @param {Text} text - an Indesign text object.
* @param {Number} width - the desired width in pts.
*/
function letterspaceToWidth(text, width) {
if (!text.hasOwnProperty('insertionPoints'))
return;
const EM_SQUARE = 1000;
var left = text.insertionPoints[0].horizontalOffset,
right = text.insertionPoints[text.insertionPoints.length - 1].horizontalOffset,
chars = text.characters.itemByRange(0, text.characters.length - 2).characters,
delta = width - (right - left);
for (var i = 0; i < chars.length; i++)
chars[i].tracking = (delta / chars.length) * (EM_SQUARE / chars[i].pointSize);
};
EDIT 2024-06-15: added code to handle a colon character directly before the first tab (see @dublove's comment below).
Copy link to clipboard
Copied
Hi~ m1b
The 2024-6-14 version you provided, I tested it, and seemed to solve all the problems.
Too perfect!
Thank you so much!
You are too great.
The best wish for you.
I wish you good health, happy every day, congratulations to getting rich.
Copy link to clipboard
Copied
Hi m1b, I an sorry to trouble you again.
I still want this colon(:) to be directly aligned.
: Occupies one word width.
Some customers like to use colons.
When the workload is heavy, using regular still takes up a lot of time.
Thank you very much.
Copy link to clipboard
Copied
Hello, M1B
The new samples I officer are still wrong?
Copy link to clipboard
Copied
Hi @dublove that demo document is perfect. Thanks.
Copy link to clipboard
Copied
The script is perfect, It seems to solve all the problems that have been discovered.
sorry.
I suddenly remembered another situation:
Sometimes ":" as a reference.
Like below:
Find more inspiration, events, and resources on the new Adobe Community
Explore Now