Copy link to clipboard
Copied
The following indesign script searches full-width alphabets and numerals and converts them to half-width, but is way too low. Any suggestions for performance improvement?
(function () {
if (!app.documents.length) {
alert("No document is open.");
return;
}
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Convert Full-width Alphanumeric Characters");
function main() {
// Settings dialog
var dialog = app.dialogs.add({ name: "Convert Full-width Alphanumerics to Half-width" });
with (dialog.dialogColumns.add()) {
staticTexts.add({ staticLabel: "Please select the type of characters to convert:" });
var typeGroup = radiobuttonGroups.add();
with (typeGroup) {
radiobuttonControls.add({ staticLabel: "Alphabet only"});
radiobuttonControls.add({ staticLabel: "Numbers only" });
radiobuttonControls.add({ staticLabel: "Both alphabets and numbers", checkedState: true });
}
with(dialogRows.add()) staticTexts.add({ staticLabel: "" });
with(dialogRows.add()) staticTexts.add({ staticLabel: "Scope of conversion:" });
var scopeCheckbox = checkboxControls.add({ staticLabel: "Convert only selected text", checkedState: false });
with(dialogRows.add()) staticTexts.add({ staticLabel: "" });
with(dialogRows.add()) staticTexts.add({ staticLabel: "Show preview?" });
var previewCheckbox = checkboxControls.add({ staticLabel: "Review changes before converting", checkedState: false });
with(dialogRows.add()) staticTexts.add({ staticLabel: "" });
with(dialogRows.add()) staticTexts.add({ staticLabel: "Save log?" });
var logCheckbox = checkboxControls.add({ staticLabel: "Save conversion details to log file", checkedState: true });
}
var userChoice, selectionOnly, logging, doPreview;
if (dialog.show() === true) {
userChoice = typeGroup.selectedButton;
selectionOnly = scopeCheckbox.checkedState;
logging = logCheckbox.checkedState;
doPreview = previewCheckbox.checkedState;
dialog.destroy();
} else {
dialog.destroy();
return;
}
var targets = [];
if (selectionOnly) {
if (app.selection.length === 0) {
alert("No text is selected.");
return;
}
for (var i = 0; i < app.selection.length; i++) {
if (app.selection[i].hasOwnProperty("characters")) {
targets.push(app.selection[i]);
}
}
if (targets.length === 0) {
alert("Selected object does not contain text.");
return;
}
} else {
targets = app.activeDocument.stories.everyItem().getElements();
}
function convertChar(ch) {
var code = ch.charCodeAt(0);
if ((userChoice === 1 || userChoice === 2) && code >= 0xFF10 && code <= 0xFF19) {
return String.fromCharCode(code - 0xFEE0);
}
if ((userChoice === 0 || userChoice === 2) &&
((code >= 0xFF21 && code <= 0xFF3A) || (code >= 0xFF41 && code <= 0xFF5A))) {
return String.fromCharCode(code - 0xFEE0);
}
return null;
}
var changeLog = [];
var previewChanges = [];
// Prepare preview
for (var i = 0; i < targets.length; i++) {
var chars = targets[i].characters;
for (var j = 0; j < chars.length; j++) {
var original = String(chars[j].contents);
var replacement = convertChar(original);
if (replacement !== null) {
previewChanges.push("Story " + i + " / Character " + j + ": " + original + " → " + replacement);
}
}
}
if (previewChanges.length === 0) {
alert("No full-width alphanumerics found to convert.");
return;
}
if (doPreview) {
var previewDialog = new Window("dialog", "Conversion Preview");
previewDialog.orientation = "column";
previewDialog.alignChildren = "fill";
previewDialog.add("statictext", undefined, "The following changes will be made:");
var listBox = previewDialog.add("edittext", undefined, previewChanges.join("\r"), {
multiline: true,
scrolling: true
});
listBox.minimumSize.height = 300;
listBox.minimumSize.width = 500;
var buttonGroup = previewDialog.add("group");
buttonGroup.alignment = "center";
buttonGroup.add("button", undefined, "Execute Conversion", { name: "ok" });
buttonGroup.add("button", undefined, "Cancel", { name: "cancel" });
if (!previewDialog.show()) {
return;
}
}
// Execution
var applied = 0;
for (var i = 0; i < targets.length; i++) {
var chars = targets[i].characters;
for (var j = 0; j < chars.length; j++) {
var original = String(chars[j].contents);
var replacement = convertChar(original);
if (replacement !== null) {
chars[j].contents = replacement;
if (logging) {
changeLog.push("Story " + i + " / Character " + j + ": " + original + " → " + replacement);
}
applied++;
}
}
}
// Write log
if (logging && changeLog.length > 0) {
try {
var logFile = File.saveDialog("Please select location to save the log file", "Text File:*.txt");
if (logFile) {
logFile.encoding = "UTF-8";
logFile.open("w");
logFile.write(changeLog.join("\n"));
logFile.close();
}
} catch (e) {
alert("Failed to save the log file: " + e.message);
}
}
alert(applied + " conversions completed. Use 'Edit > Undo' to revert changes.");
}
})();
Copy link to clipboard
Copied
Hi @あれっくすか16446185 I'm not sure if this will speed things up, but I think it might. I've re-written your script so that it uses findGrep, rather than stepping through every single character. Maybe it will be faster.
I made some structural changes too, for my own practice, for example I de-coupled the UI so you can run the script without the UI if you like. But you can ignore these and go back to your script if you don't like them.
I tried to speed up the preview mode a little too, by using raw strings. Please let me know the result. 🙂
- Mark
/**
* @file Convert Full-width To Half-width.js
*
* @author あれっくすか16446185 (modified by m1b)
* @version 2025-04-24
* @discussion https://community.adobe.com/t5/indesign-discussions/full-width-to-half-width-script-performance-improvement/m-p/15285863
*/
function main() {
var settings = {
searchType: 2, // 0 = Alphabet only, 1 = Numbers, 2 = both
selectionOnly: false,
logging: true,
doPreview: false,
showUI: true,
};
// grep searches
var searches = {
FULL_WIDTH_ALPHA: '[\\x{FF21}-\\x{FF3A}\\x{FF41}-\\x{FF5A}]+',
FULL_WIDTH_NUMERIC: '[\\x{FF10}-\\x{FF19}]+',
FULL_WIDTH_ALPHANUMERIC: '[\\x{FF10}-\\x{FF19}\\x{FF21}-\\x{FF3A}\\x{FF41}-\\x{FF5A}]+',
};
if (!app.documents.length)
return alert("No document is open.");
// show the UI
if (
settings.showUI
&& !ui(settings)
) {
// user cancelled UI
return;
}
var doc = app.activeDocument;
var target = settings.selectionOnly ? doc.selection[0] : doc;
if (
!target
|| 'function' !== typeof target.findGrep
)
return alert('No text selected.');
// clear the find/change grep preferences
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
// set the find options
app.findChangeGrepOptions.includeFootnotes = false;
app.findChangeGrepOptions.includeHiddenLayers = false;
app.findChangeGrepOptions.includeLockedLayersForFind = false;
app.findChangeGrepOptions.includeLockedStoriesForFind = false;
app.findChangeGrepOptions.includeMasterPages = false;
app.findGrepPreferences.findWhat = [searches.FULL_WIDTH_ALPHA, searches.FULL_WIDTH_NUMERIC, searches.FULL_WIDTH_ALPHANUMERIC][settings.searchType];
var targets = target.findGrep();
if (settings.doPreview) {
// Prepare preview
var previewChanges = [];
for (var i = 0; i < targets.length; i++) {
var chars = targets[i].contents;
charsLoop:
for (var j = 0; j < chars.length; j++) {
original = chars[j];
replacement = String.fromCharCode(original.charCodeAt(0) - 0xFEE0);
if (replacement === null)
continue charsLoop;
previewChanges.push("Story " + i + " / Character " + j + ": " + original + " → " + replacement);
}
}
if (previewChanges.length === 0)
return alert("No full-width alphanumerics found to convert.");
var previewDialog = new Window("dialog", "Conversion Preview");
previewDialog.orientation = "column";
previewDialog.alignChildren = "fill";
previewDialog.add("statictext", undefined, "The following changes will be made:");
var listBox = previewDialog.add("edittext", undefined, previewChanges.join("\r"), {
multiline: true,
scrolling: true
});
listBox.minimumSize.height = 300;
listBox.minimumSize.width = 500;
var buttonGroup = previewDialog.add("group");
buttonGroup.alignment = "center";
buttonGroup.add("button", undefined, "Execute Conversion", { name: "ok" });
buttonGroup.add("button", undefined, "Cancel", { name: "cancel" });
if (2 === previewDialog.show())
// user cancelled
return;
}
// Execution
var applied = 0;
var changeLog = [];
for (var i = targets.length - 1, chars, original, replacement; i >= 0; i--) {
chars = targets[i].characters;
charsLoop:
for (var j = chars.length - 1; j >= 0; j--) {
original = String(chars[j].contents);
replacement = String.fromCharCode(original.charCodeAt(0) - 0xFEE0);
if (replacement === null)
continue charsLoop;
chars[j].contents = replacement;
if (settings.logging) {
changeLog.push("Story " + i + " / Character " + j + ": " + original + " → " + replacement);
}
applied++;
}
}
// Write log
if (
settings.logging
&& changeLog.length > 0
) {
try {
var logFile = File.saveDialog("Please select location to save the log file", "Text File:*.txt");
if (logFile) {
logFile.encoding = "UTF-8";
logFile.open("w");
logFile.write(changeLog.join("\n"));
logFile.close();
}
} catch (e) {
alert("Failed to save the log file: " + e.message);
}
}
alert(applied + " conversions completed. Use 'Edit > Undo' to revert changes.");
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Convert Full-width Alphanumeric Characters');
function ui(settings) {
// Settings dialog
var dialog = app.dialogs.add({ name: "Convert Full-width Alphanumerics to Half-width" });
with (dialog.dialogColumns.add()) {
staticTexts.add({ staticLabel: "Please select the type of characters to convert:" });
var typeGroup = radiobuttonGroups.add();
with (typeGroup) {
radiobuttonControls.add({ staticLabel: "Alphabet only" });
radiobuttonControls.add({ staticLabel: "Numbers only" });
radiobuttonControls.add({ staticLabel: "Both alphabets and numbers", checkedState: true });
}
with (dialogRows.add()) staticTexts.add({ staticLabel: "" });
with (dialogRows.add()) staticTexts.add({ staticLabel: "Scope of conversion:" });
var scopeCheckbox = checkboxControls.add({ staticLabel: "Convert only selected text", checkedState: false });
with (dialogRows.add()) staticTexts.add({ staticLabel: "" });
with (dialogRows.add()) staticTexts.add({ staticLabel: "Show preview?" });
var previewCheckbox = checkboxControls.add({ staticLabel: "Review changes before converting", checkedState: false });
with (dialogRows.add()) staticTexts.add({ staticLabel: "" });
with (dialogRows.add()) staticTexts.add({ staticLabel: "Save log?" });
var logCheckbox = checkboxControls.add({ staticLabel: "Save conversion details to log file", checkedState: true });
}
//update ui
typeGroup.selectedButton = settings.searchType;
scopeCheckbox.checkedState = settings.selectionOnly;
logCheckbox.checkedState = settings.logging;
previewCheckbox.checkedState = settings.doPreview;
if (dialog.show() === true) {
// update settings
settings.searchType = typeGroup.selectedButton;
settings.selectionOnly = scopeCheckbox.checkedState;
settings.logging = logCheckbox.checkedState;
settings.doPreview = previewCheckbox.checkedState;
dialog.destroy();
return true;
}
else {
dialog.destroy();
return false;
}
};
Copy link to clipboard
Copied