- 首次过帐
- 第一次用JavaScript写作,尽管我有其他语言的经验
我在Adobe InDesign CS5.5工作。我在身份证簿中有多个文件,每个文件都包含不同数量的"章节"。这本书包括一个索引文件,主题标题以缩写形式引用章节(例如,"第125章"变为"ch 125 no 3"——注意"no x"部分无关紧要)。我的脚本的目标是创建文档间链接,当ID簿导出到PDF时,这些链接将添加重要的功能。用户将能够从索引跳到章节,反之亦然。我认为剧本和我正在处理的问题对其他人有用,但还没有找到任何帖子来解决我的问题。
特定章节("第125章")索引中的所有参考文献(如"ch 125 no 1")都有一个指向该章节标题位置的超链接。这部分脚本运行良好,运行速度快。
另一半将在每章文本的末尾插入相应的主题标题,并使这些段落链接回索引中的相应主题标题。(换句话说,它们是交叉引用,但在ID术语中不是真正的x-refs,因为我想对它们有更多的控制权,而我对这个主题的阅读告诉我要避开真正的x-ref。)这是剧本中让我头疼的部分。这本书长达数小时,却没有读完一本200章的书。请注意,出于测试目的,我只是在每一章下的所需位置插入一段文本,而不是所有的主题标题和链接。我从较小的文本集和控制台的调试打印中知道,脚本正在工作,而不是陷入无限循环。尽管如此,它运行的时间太长了,如果我打断它,InDesign没有响应,我必须杀死它,所以甚至无法查看部分结果。
基于搜索/阅读论坛:我禁用了飞行前;禁用书籍页码的自动更新;已将实时预览设置更改为延迟。我仍然怀疑速度慢可能与InDesign的开销有关,但我不知道还能尝试什么。
我对这个JS代码的风格可能有多糟糕感到尴尬,但目前我只需要它工作,然后我就可以改进它。
var myBookFilePath = File.openDialog("Choose an InDesign Book File", "Indb files: *.indb");
var myOpenBook = app.open(myBookFilePath);
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.neverInteract;
// Open up every file in the currently active Book
app.open(app.activeBook.bookContents.everyItem().fullName)
// TODO: add error handling / user interaction here -- to pick which is Index file
var strIndexFilename = "Index.indd";
var objChapHeadsWeb = {};
var myDoc = app.documents.item(strIndexFilename);
$.writeln("nn~~~ " + myDoc.name + " ~~~");
// REMOVED CODE - check for existing hyperlinks, hyperlink sources/destinations
// loop to delete any pre-existing hyperlinks & associated objects
// works w/o any problems
// Ugly GREP to find the Main heading text (all caps entry and nothing beyond) in the index file
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
/// GREP: ^[ud :;?-'"$%&!@*#,.()]+[ud](?=.|,)
app.findGrepPreferences.findWhat = "^[\u\d \:\;\?\-\'\"\$\%\&\!\@\*\#\,\.\(\)]+[\u\d](?=\.|,)";
app.findGrepPreferences.appliedParagraphStyle = "Main";
var myFound = [];
myFound = myDoc.findGrep();
$.writeln("Found " + myFound.length + " Main headings.");
for (var i = 0; i < myFound.length; i++) {
myDoc.hyperlinkTextDestinations.add(myFound[i], { name: myFound[i].contents });
}
$.writeln("There are now " + myDoc.hyperlinkTextDestinations.count() + " destinations.");
myFound.length = 0;
for (var j = app.documents.count()-1; j >= 0; j--) {
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
// set the variable to the document we are working with
myDoc = null;
myDoc = app.documents[j];
myFound.length = 0;
if (myDoc.name === strIndexFilename) {
continue; // we don't want to look for chapter heads in the Index file, so skip it
}
$.writeln("nn~~~ " + myDoc.name + " ~~~");
// REMOVED CODE - check for existing hyperlinks, hyperlink sources/destinations
// loop to delete any pre-existing hyperlinks & associated objects
// works w/o any problems
// Clear GREP prefs
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
app.findGrepPreferences.findWhat = "^CHAPTER \d+";
app.findGrepPreferences.appliedParagraphStyle = "chapter";
myFound = myDoc.findGrep();
var strTemp = "";
$.writeln("Found " + myFound.length + " chapter headings.");
for (var m = 0; m < myFound.length; m++) {
strTemp = myFound[m].contents;
objChapHeadsWeb[strTemp] = {};
objChapHeadsWeb[strTemp].withinDocName = myDoc.name;
objChapHeadsWeb[strTemp].hltdChHead =
myDoc.hyperlinkTextDestinations.add(myFound[m], {name:strTemp});
objChapHeadsWeb[strTemp].a_strIxMains = [];
objChapHeadsWeb[strTemp].a_hltdIxMains = [];
objChapHeadsWeb[strTemp].nextKeyName = "";
objChapHeadsWeb[strTemp].nextKeyName =
((m < myFound.length-1) ? myFound[m+1].contents : String(""));
}
$.writeln("There are now " + myDoc.hyperlinkTextDestinations.count() + " destinations.");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Find the "ch" (chapter) references in the index file, link them
// back to the corresponding text anchors for the chapter heads
// in the text.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
myDoc = app.documents.item(strIndexFilename); // work with the Index file
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
// GREP to find the "ch" (chapter) references in the index file
// like ch 151 no 1 OR ch 12 no 3
app.findGrepPreferences.findWhat = "(ch\s+\d+\s+no\s+\d+)";
var strExpandedChap = "";
var strWorkingMainHd = "";
var arrFoundChapRefs = [];
var myHyperlinkSource;
var myHyperlinkDest;
for (var x = 0; x < myDoc.hyperlinkTextDestinations.count(); x++) {
strWorkingMainHd = "";
arrFoundChapRefs.length = 0;
// the special case, where we are working with the ultimate hyperlinkTextDestination obj
if (x === myDoc.hyperlinkTextDestinations.count()-1) {
// This is selecting text from the start of one MAIN heading...
myDoc.hyperlinkTextDestinations[x].destinationText.select();
// This next line will extend the selection to the end of the story,
// which should also be the end of the document
myDoc.selection[0].parentStory.insertionPoints[-1].select(SelectionOptions.ADD_TO);
}
// the regular case...
else {
// This is selecting text from the start of one MAIN heading...
myDoc.hyperlinkTextDestinations[x].destinationText.select();
// ... to the start of the next MAIN heading
myDoc.hyperlinkTextDestinations[x+1].destinationText.select(SelectionOptions.ADD_TO);
}
strWorkingMainHd = myDoc.hyperlinkTextDestinations[x].name;
//arrFoundChapRefs = myDoc.selection[0].match(/(chs+)(d+)(s+nos+d+)/g); //NOTE: global flag
arrFoundChapRefs = myDoc.selection[0].findGrep();
for(y = 0; y < arrFoundChapRefs.length; y++) {
myHyperlinkSource = null;
myHyperlinkDest = null;
strExpandedChap = "";
strExpandedChap = arrFoundChapRefs[y].contents.replace(/chs+/, "CHAPTER ");
strExpandedChap = strExpandedChap.replace(/s+nos+d+/, "");
// if we found the chapter head corresponding to our chapter ref in the index
// then it is time to create a link
if (strExpandedChap in objChapHeadsWeb) {
objChapHeadsWeb[strExpandedChap].a_strIxMains.push(strWorkingMainHd);
objChapHeadsWeb[strExpandedChap].a_hltdIxMains.push(myDoc.hyperlinkTextDestinations[x]);
myHyperlinkSource = myDoc.hyperlinkTextSources.add(arrFoundChapRefs[y]);
myHyperlinkDest = objChapHeadsWeb[strExpandedChap].hltdChHead;
myDoc.hyperlinks.add(myHyperlinkSource, myHyperlinkDest);
} else {
$.writeln("Couldn't find chapter head " + strExpandedChap);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NOW TIME FOR THE HARD PART...
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
myDoc = null;
var strWorkingMainHd = "";
var nextKey = "";
var myParentStory = null;
var myCharIndex = 0;
var myCompareChar = null;
var myLeftmostBound = 0;
var myCurrentPara = null;
for (var key in objChapHeadsWeb) {
myDoc = app.documents.item(objChapHeadsWeb[key].withinDocName);
myCompareChar = null; //recent addition
$.writeln("Working on " + key + "."); //debugging
nextKey = objChapHeadsWeb[key].nextKeyName;
objChapHeadsWeb[key].hltdChHead.destinationText.select();
myLeftmostBound = myDoc.selection[0].index;
myParentStory = myDoc.selection[0].parentStory;
if( (nextKey === "") || (myDoc.name !== objChapHeadsWeb[nextKey].withinDocName) )
{
//// Need to find end of story instead of beginning of next chapter
//myDoc.selection[0].parentStory.insertionPoints[-1].select(SelectionOptions.ADD_TO);
myParentStory.insertionPoints[-1].select();
//myCharIndex = myDoc.selection[0].index; /recently commented out
myCharIndex = myDoc.selection[0].index - 1; //testing new version
myCompareChar = myParentStory.characters.item(myCharIndex); //recenttly added/relocated from below
} else {
/////
//objChapHeadsWeb[nextKey].hltdChHead.destinationText.select(SelectionOptions.ADD_TO);
objChapHeadsWeb[nextKey].hltdChHead.destinationText.select();
//myParentStory.characters.item(myDoc.selection[0].index -1).select();
myParentStory.characters.item(myDoc.selection[0].index -2).select(); //temp test *****
myCharIndex = myDoc.selection[0].index;
myCompareChar = myParentStory.characters.item(myCharIndex);
if (myCompareChar.contents === "uFEFF") {
$.writeln("Message from inside the \uFEFF check."); //debugging
myParentStory.characters.item(myDoc.selection[0].index -1).select();
myCharIndex = myDoc.selection[0].index;
myCompareChar = myParentStory.characters.item(myCharIndex);
}
if( (myCompareChar.contents !== SpecialCharacters.PAGE_BREAK) &&
(myCompareChar.contents !== SpecialCharacters.ODD_PAGE_BREAK) &&
(myCompareChar.contents !== SpecialCharacters.EVEN_PAGE_BREAK) &&
(myCompareChar.contents !== SpecialCharacters.COLUMN_BREAK) &&
(myCompareChar.contents !== SpecialCharacters.FRAME_BREAK))
{
$.writeln("Possible error finding correct insertion point for " + objChapHeadsWeb[key].hltdChHead.name + ".");
}
}
if(myCharIndex <= myLeftmostBound) { // this shouldn't ever happen
alert("Critical error finding IX Marker insertion point for " + objChapHeadsWeb[key].hltdChHead.name + ".");
}
if(myCompareChar.contents !== "r") {
myDoc.selection[0].insertionPoints[-1].contents = "r";
}
myDoc.selection[0].insertionPoints[-1].contents = "TESTING text insertion for: " + objChapHeadsWeb[key].hltdChHead.name + "r";
myDoc.selection[0].insertionPoints.previousItem(myDoc.selection[0].insertionPoints[-1]).select();
//myDoc.selection[0].insertionPoints[-1].contents = "<Now I'm here!>";
myCurrentPara = myDoc.selection[0].paragraphs[0];
myCurrentPara.appliedParagraphStyle = myDoc.paragraphStyles.item("IX Marker");
// TODO:
// need error handling for when style doesn't already exist in the document
} // end big for loop
//TODO: add error handling support to carry on if user cancels
//close each open file; user should be prompted to save changed files by default
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;
app.documents.everyItem().close();
// Cleanup
app.findGrepPreferences = NothingEnum.nothing;
app.changeGrepPreferences = NothingEnum.nothing;
尝试打开所有链接到交叉引用的文件。
我可以建议一些可能会加快速度的改进吗。首先,这里有大量的全局变量,您可以使用函数将它们集中在更少的范围内。就性能而言,拥有许多全局变量会带来巨大的成本。
话虽如此,我不会一次打开这本书的每一个文档,而是逐一处理。请注意,grep调用非常昂贵,因此您可以尝试查看您的模式。
另一个是$.writeln命令的广泛使用。避免它,尤其是在循环中。更喜欢易于设置的报表库。
最后,我试图用一种"更好"的方式重写您的代码,但很难在清楚了解您的需求的情况下构建整个脚本,而且没有文件可处理。但我希望下面的片段将帮助您开始重写代码,并说明显著的时间改进。
var debug = true;
var log = function(msg) {
var l = File (Folder.desktop+"/log.txt" );
if ( !debug ) return;
l.open('a');
l.write(msg);
l.close();
};
var main = function() {
var bookFile, uil = app.scriptPreferences.userIntercationLevel;
log("The party has started");
bookFile = File.openDialog("Choose an InDesign Book File", "Indb files: *.indb");
if (!bookFile) return;
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
try {
processBookFile ( bookFile );
}
catch(err) {
alert(err.line+"///"+err.message);
}
app.scriptPreferences.userInteractionLevel = uil;
};
function processBookFile ( bookFile ) {
var book = app.open ( bookFile ),
bks = book.bookContents,
n = bks.length;
while ( n-- ) {
File(bks[n].name)!="Index.indd" && processBookContent ( bks[n] );
}
}
function processBookContent ( bookContent ) {
var bcf = bookContent.fullName,
doc = app.open ( bcf, debug );
//DEAL WITH HEADINGS
processHeadings ( doc );
//DEAL WITH CHAPTERS
processHeadings ( doc );
//add hyperlinks
addHyperlinks( doc);
}
function processHeadings (doc){
var props = {
findWhat : "^[\u\d \:\;\?\-\'\"\$\%\&\!\@\*\#\,\.\(\)]+[\u\d](?=\.|,)",
appliedParagraphStyle : "Main"
},
found = findGrep(doc, props),
n = found.length;
while ( n-- ) {
doc.hyperlinkTextDestinations.add(doc, { name: found[i].contents });
}
};
function processChapters (doc ) {
var props = {
findWhat : "^CHAPTER \d+",
appliedParagraphStyle : "chapter"
},
found = findGrep(doc, props),
n = found.length;
while ( n-- ) {
doc.hyperlinkTextDestinations.add(found[n], found[n].contents);
}
}
function findGrep(doc, props){
app.findGrepPreferences = app.changeGrepPreferences = null;
app.findGrepPreferences.properties = props;
return doc.findGrep();
}
function addHyperlinks (doc){
//a logic of yours
};
main();