forked from johnwun/js4ai
-
Notifications
You must be signed in to change notification settings - Fork 0
/
joinTextFrames.js
306 lines (278 loc) · 10.3 KB
/
joinTextFrames.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/////////////////////////////////////////////////////////////////
//Join TextFrames v.2.1 -- CS,CS2
//>=--------------------------------------
// When Adobe came out with the new CS text engine,
// the text fields in documents saved in an older format were
// often broken into "sub" fields.
//
// This script is a rudimentary tool to help
// rejoin the broken text fields. It works best on individual lines of horizontal text.
//
// It collects text from any number of selected (non-grouped) textframes
// and aggrigates them into a single text frame.
// the order is based on relative x,y coordinates.
//
// If a selected text anchors vertical coordinate is the same as the next one,
// text areas are assumed to be on the same line.
//
// If a selected text anchors vertical coordinate is different,
// the text areas are assumed to be sequential lines,
//
//
// This script WILL NOT make any attempt to maintain visual integrety of
// character placement. It will only join the fields for easier editing,
// but sometimes, just this little bit is a HUGE time saver.
//
// As of Version 2.0 an option is given to maintain the individual styling of each letter.
// See first lines of the script for details.
//
//
// the sensitivity of what is considered "the same line" can
// be adjusted by setting the "verticalTolerance" variable
// near the top of the script (just below the style arrays).
// but please note, it is assumed that all selected text is on a horizontal plane.
// If you wish to join rotated textfields, you will have to unrotate them first.
//
//>=--------------------------------------
// JS code (c) copyright: John Wundes ( [email protected] ) www.wundes.com
//copyright full text here: http://www.wundes.com/js4ai/copyright.txt
//////////////////////////////////////////////////////////////////
var format = true;
// Notes on usage:
// When running this script with the FULL 92 item list of character style options, script execution times
// dropped to painfully slow levels.
// My fix for this was to create a subset of common formattings that one would likely be looking for.
// If you're not getting the behavior you want, you can either copy the desired attributes from
// the "fullStylesArray" to the "basicStylesArray", or if you want to copy everything all the time, you can just assign
// the "stylesArray" the value of "fullStylesArray" on (or around) line 50.
//
//
var fullStylesArray= ['Tsume','akiLeft','akiRight','alignment','alternateGlyphs','autoLeading','autoLeadingAmount','baselineDirection','baselinePosition','baselineShift','bunriKinshi','burasagariType','capitalization','characterAttributes','characterOffset','connectionForms','contents','contextualLigature','desiredGlyphScaling','desiredLetterSpacing','desiredWordSpacing','discretionaryLigature','everyLineComposer','figureStyle','fillColor','firstLineIndent','fractions','horizontalScale','hyphenateCapitalizedWords','hyphenation','hyphenationPreference','hyphenationZone','italics','justification','kerning','kerningMethod','kinsoku','kinsokuOrder','kurikaeshiMojiShori','language','leading','leadingType','leftIndent','length','ligature','maximumConsecutiveHyphens','maximumGlyphScaling','maximumLetterSpacing','maximumWordSpacing','minimumAfterHyphen','minimumBeforeHyphen','minimumGlyphScaling','minimumHyphenatedWordSize','minimumLetterSpacing','minimumWordSpacing','mojikumi','noBreak','openTypePosition','ordinals','ornaments','overprintFill','overprintStroke','paragraphAttributes','proportionalMetrics','rightIndent','romanHanging','rotation','singleWordJustification','size','spaceAfter','spaceBefore','story','strikeThrough','strokeColor','strokeWeight','stylisticAlternates','swash','tabStops=','tateChuYokoHorizontal','tateChuYokoVertical','textFont','textSelection','titling','tracking','underline','verticalScale','wariChuCharactersAfterBreak','wariChuCharactersBeforeBreak','wariChuEnabled','wariChuJustification','wariChuLineGap','wariChuLines','wariChuScale'];
//
var basicStylesArray=["alignment",'baselineDirection','baselinePosition',"baselineShift",'capitalization','characterOffset','contents','fillColor','horizontalScale','italics','justification','kerning','openTypePosition','ordinals','ornaments','strikeThrough','rotation','size','stylisticAlternates','textFont','titling','tracking','underline','verticalScale'];
//
// this sets the amount of vertical variance acceptable for anchors to be
// considered on the same line. Set to zero if you want to be exact.
var verticalTolerance = 0;
verticalTolerance = prompt ("How much variance in pixels do you want to allow for letters to be considered on the same line?",1.5); //points
//
var stylesArray = basicStylesArray;
//var exclusionList = ["characterOffset","strokeWeight"];
var allFormatedChars = new Array(0);
//above lines used for copying formatting
mainSel = activeDocument.selection;
var groupFound = 0;
//join all ungrouped TextFrames.
var lines = new Array(0);
sortAndJoin(mainSel);
//alert(allFormatedChars);
//--------------------
/*
*/
//-----------FUNCTIONS----------------
function sortAndJoin(sel){
var slen = sel.length;
if (slen >0)
{
textObjects = new Array();
// OK, I'll give you ONE level of grouping for free...
if(slen==1 && sel[0].typename == "GroupItem"){
sel=sel[0].textFrames;
slen = sel.length;
}
for (j=0;j<slen;j++ )
{
if(sel[j].typename == "TextFrame"){
textObjects.push(sel[j]);
//But if there are more groups selected, screw it...
}else if(sel[j].typename == "GroupItem"){
groupFound = 1;
}
}
if (groupFound==1)
{
alert("Please ungroup selection first.");
}else {
var tlen = textObjects.length;
//now sort the list
if(tlen > 0){
var tob=textObjects;
sort(tob);
//
//
var blab="";
for(x = 0; x < tob.length; x++) {
blab+=tob[x].contents+" "+tob[x].name+"\n";
}
//alert(blab);
//
//
var disp = "";
for (j=0;j<tlen;j++)
{
if (tob[j].name == ":hi:")
{
//add a return
tob[j].contents+="\r";
}
//alert(tob[j]);
if(format){
// ---------------create formatting array here
extractFormatting(tob[j]);
}
disp+=tob[j].contents;
}
tob[0].contents = disp;
var tobLen = tob[0].contents.length
//remove "extra" return
if(tob[0].contents[tobLen-1]=="\r"){
//alert("return found");
tob[0].contents= tob[0].contents.substr(0,tobLen-1);
}
for (j=(tlen-1);j>0;j--)
{
tob[j].remove();
}
app.redraw();
// calculated for my CPU (2.5 Ghz) Your results may vary...
format = !confirm("Individual character formatting will take roughly "+(Math.round((tobLen)*(stylesArray.length*.0115)))+" seconds to complete.\n Do you wish to skip character formatting?");
tob[0].name = tob[0].contents.substr(0,5)+"...";
// alert(allFormatedChars);
if(format){
// ---------------assign formatting here
assignFormatting(tob[0]);
//alert("formatting assigned");
}
//now kill all textfields...
///*
//*/
}
}
}
//alert("Text Objects:"+textObjects);
}
function copyText(a,b){
//Copies text from a object to b object...
b.contents = a.contents;
}
function addText(a,b){
//Copies text from a object to b object...
b.contents += ("\n"+ a.contents);
}
//anchor x,y
//position x,y
function isHiOrLeft(a,b){
//alert(a+" "+b);
// commented out version measures based on height instead of anchors.
//if(a.position[1]-a.height > b.position[1]-b.height){
if(a.anchor[1] > b.anchor[1] && !isWithin(a.anchor[1],b.anchor[1])){
a.name = ":hi:";
return true;
// commented out version measures based on height instead of anchors.
//} else if (a.position[1]-a.height == b.position[1]-b.height && a.position[0] < b.position[0]) {
} else if (isWithin(a.anchor[1],b.anchor[1]) && a.anchor[0] < b.anchor[0]) {
a.name = ":left:";
return true;
}
return false;
}
function isWithin(YposA,YposB){
if(YposA==YposB){return true};
if(findDiff(YposA,YposB)<verticalTolerance){
return true;
}
return false;
}
function findDiff(a,b){
if( a>0 && b>0 && b>a ||
a<0 && b>0 ||
a<0 && b<0 && a>b ){
return Math.abs(b-a);
}
return Math.abs(a-b);
}
function swapItems(arr,aNum,bNum){
//Copies text from a object to b object...
tmp = arr[aNum];
arr[aNum]= arr[bNum];
arr[bNum]=tmp;
}
function sort(objArr){
var x, y, holder;
// The Bubble Sort method.
obLen = objArr.length;
//for each letter in textfield:
for(x = 0; x < obLen; x++) {
//compare against each other letter
for(y = 0; y < obLen-1; y++) {
if(isHiOrLeft(objArr[y+1],objArr[y])) {
holder = objArr[y+1];
objArr[y+1] =objArr[y];
objArr[y] = holder;
}
}
}
}
//------------------------section added for handling text formatting:----------------------------------------
//following two lines moved to top of script:
//var exclusionList = ["characterOffset","strokeWeight"];
//var allFormatedChars = new Array(0);
//-------------:::USAGE:::-------
//extractFormatting(selection[0]);
//assignFormatting(selection[1]);
function extractFormatting(a){
var charsLen = a.textRange.characters.length;
for (var each=0;each < charsLen;each++){
var tempAtts = new Array(0);
//
//EVERY setting takes too long, so we define our list of things to change instead
for (everyValue in stylesArray){
//for (everyValue in a.textRange.characters[each]){
try{
//if(notExcluded(everyValue)){
tempAtts.push([stylesArray[everyValue], a.textRange.characters[each][stylesArray[everyValue]]]);
//}
}
catch(e){
//nothing...
}
}
allFormatedChars.push(tempAtts);
}
}
function assignFormatting(b){
var charsLen =allFormatedChars.length;
for (var each =0;each< charsLen;each++){
//
for (everyValuePair in allFormatedChars[each]){
try{
var att = allFormatedChars[each][everyValuePair][0]+"";// stringify...
b.textRange.characters[each][att]=allFormatedChars[each][everyValuePair][1];
}
catch(e){
//nothing
}
}
}
}
//not used
function notExcluded(item){
for(var all in exclusionList){
if(item == exclusionList[all]){
//alert(each+" was excluded");
return false;
}
}
return true;
}
//not used
function stack(objArr){
var x, y, holder;
// The Bubble Sort method.
obLen = objArr.length;
//for each letter in textfield:
for(x = 0; x < obLen; x++) {
//compare against each other letter
objArr[x].moveToBeginning(objArr[x].parent);
}
}