forked from Id-Extras/Snap-Margins-to-Text-Frame
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSnapMarginsToTextFrame.jsx
252 lines (240 loc) · 11.4 KB
/
SnapMarginsToTextFrame.jsx
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
// Snap Margins to Text Frame BS"D
// Copyright (c) 2020 Ariel Walden, www.Id-Extras.com. All rights reserved.
// This work is licensed under the terms of the MIT license.
// The full text of the license can be read here: <https://opensource.org/licenses/MIT>.
// Version 1.0.0
//DESCRIPTION: Modify the document margins easily and visually instead of fiddling about with numbers in the Margins and Columns dialog. This script will modify the document's margins throughout to match the selected text frame.
//TO USE: Draw or select a text frame on a right-hand (odd-numbered) page and run the script.
// The document's margins will be modified to match the selected frame.
// Any text frames that abut the margins will be adjusted and resized as needed.
//MORE INFO: For more information about this script and how to use it, please visit https://www.id-extras.com/snap-margins-to-text-frame/
/////////////////////////////////////////////
// In Memoriam
// RAPHAEL MYER WALDEN
// 1935-2019
/////////////////////////////////////////////
gScriptName = "Snap Margins to Frame Version 1.0.0 (by www.id-extras.com)";
// The following line (a) makes the script run faster; and (b) allows it to be undone with a single click.
app.doScript(premain, undefined, undefined,UndoModes.ENTIRE_SCRIPT, gScriptName);
function premain(){
// Check that at least one document is open.
if (app.documents.length == 0){
alert("Please open a document and try again.", gScriptName);
return;
}
// Let's make sure the script units are points.
// But once the script finishes, or if any errors occur, let's make sure to set the units back to what they were.
var e, oldUnits = app.scriptPreferences.measurementUnit;
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
try{
main();
}
catch(e){
alert("Something's gone wrong. The script must now quit. Margins have not been modified.", gScriptName);
}
finally{
app.scriptPreferences.measurementUnit = oldUnits;
}
}
function main(){
var s = app.selection[0];
var allPages;
if (s instanceof TextFrame == false){
alert("Please select a text frame and try again.", gScriptName);
return;
}
if (s.rotationAngle != 0){
alert("The selected text frame is rotated. Margins cannot be rotated. Please select a non-rotated text frame and try again.", gScriptName);
return;
}
// If available, let's rely on InDesign's old Layout Adjustment feature to resize and reposition page items as we modify the margins...
var myLayoutAdjustmentStatus;
if (document.hasOwnProperty("layoutAdjustmentPreferences")){
myLayoutAdjustmentStatus = document.layoutAdjustmentPreferences.enableLayoutAdjustment;
document.layoutAdjustmentPreferences.enableLayoutAdjustment = true;
}
// But in version 2019 onwards, the old Layout Adjustment feature was replaced with the new Adjust Layout feature.
// This does not do quite the same thing, and because it relies on AI, the results are not sufficiently predictable for our use.
// Therefore, disable the feature.
if (document.hasOwnProperty("adjustLayoutPreferences")){
myLayoutAdjustmentStatus = document.adjustLayoutPreferences.properties;
document.adjustLayoutPreferences.enableAdjustLayout = false;
document.adjustLayoutPreferences.allowFontSizeAndLeadingAdjustment = false;
document.adjustLayoutPreferences.allowLockedObjectsToAdjust = false;
document.adjustLayoutPreferences.enableAutoAdjustMargins = false;
}
var b = s.geometricBounds;
var p = s.parentPage;
// Check that the selected frame is on a right-hand page (for documents with facing pages).
// Really, this is an artificial constraint. It just makes things a little bit simpler later on.
if (p.side == PageSideOptions.LEFT_HAND){
alert("Please select a text frame on a right-hand page only and try again (mirror margins will be applied).", gScriptName);
return;
}
// Remind the user what the script does, and give them a last chance to bail out!
if (! confirm("This will adjust the margins throughout the document (including on master pages) to match the selected text frame. Continue?", undefined, gScriptName)){
return;
}
var z = p.bounds;
// The top margin is the distance from the top of the selected frame to the top of the page.
b[0] -= z[0];
// The left margin, the distance from left of frame to left of page.
b[1] -= z[1];
// The bottom margin is the distance from the bottom of the page to the bottom of the frame.
b[2] = z[2] - b[2];
// And finally, the right margin is the distance from the right edge of the page to the right edge of the selected frame.
b[3] = z[3] - b[3];
if (document.hasOwnProperty("adjustLayoutPreferences")){
// If we're having to use our own layout adjustment, then we've first got to do the regular pages, then the master pages
allPages = app.activeDocument.pages.everyItem().getElements();
allPages = allPages.concat(app.activeDocument.masterSpreads.everyItem().pages.everyItem().getElements());
fit2019(allPages, b);
document.adjustLayoutPreferences.properties = myLayoutAdjustmentStatus;
}
else{
// If we're using InDesign's old layout adjustment, it's more efficient to change the master pages first.
allPages = app.activeDocument.masterSpreads.everyItem().pages.everyItem().getElements();
allPages = allPages.concat(app.activeDocument.pages.everyItem().getElements());
fit(allPages, b);
document.layoutAdjustmentPreferences.enableLayoutAdjustment = myLayoutAdjustmentStatus;
}
alert("All margins were successfully modified to match the current selection.", gScriptName);
}
// How simple life was with the old layout adjustment feature! Switch it on, modify the page margins, and let InDesign do the rest...
function fit(thePages, theBounds){
var i;
for (i = 0; i < thePages.length; i++){
with (thePages[i].marginPreferences){
top = theBounds[0];
bottom = theBounds[2];
left = theBounds[1];
right = theBounds[3];
}
}
}
// But with CC2019, the old Layout Adjustment was removed. We need to reprogram our own...
function fit2019(thePages, theBounds){
var i;
for (i = 0; i < thePages.length; i++){
adjustMargins(thePages[i], theBounds);
}
}
// Let's try to emulate InDesign's old Layout Adjustment feature.
// The idea is to keep track of the positions of all objects on the page and check if they touch the margins on any side.
// The margins are then adjusted, and finally if any of the sides of the object touched a margin, its bounds are adjusted as well.
// An added complication is that pages can have columns. So we need to check whether an item abuts not just the page margins, but any of the columns as well.
function adjustMargins(aPage, newBounds){
var i, j;
var p = aPage.marginPreferences;
var pLeft, pRight;
var pageBounds = aPage.bounds;
var oldMargins = [];
var newMargins = [];
var oldColumnPositions = [];
var newColumnPositions = [];
// Get a collection of all items on the page
var theItems = aPage.pageItems.everyItem();
// Quickly get the position of all items on the page in one go.
var itemBounds = theItems.geometricBounds;
// Now that we've collected the item bounds for all items, it's best for speed to convert the collection of page items to an array.
// For more info on this topic: http://www.indiscripts.com/post/2010/06/on-everyitem-part-1
theItems = theItems.getElements();
var theBounds;
var oldBounds;
var snap = {};
// For single-sided and right-hand pages the left and right margins are correct. But for left-hand pages they are reversed.
if (aPage.side == PageSideOptions.LEFT_HAND){
pLeft = p.right;
pRight = p.left;
}
else{
pLeft = p.left;
pRight = p.right;
}
oldMargins[0] = pageBounds[0] + p.top;
oldMargins[1] = pageBounds[1] + pLeft;
oldMargins[2] = pageBounds[2] - p.bottom;
oldMargins[3] = pageBounds[3] - pRight;
oldColumnPositions = p.columnsPositions; // Columns positions
// Avoid crashes and unexpected behavior
aPage.marginPreferences.top = newBounds[0];
aPage.marginPreferences.bottom = newBounds[2];
aPage.marginPreferences.left = newBounds[1];
aPage.marginPreferences.right = newBounds[3];
p = aPage.marginPreferences;
// For single-sided and right-hand pages the left and right margins are correct. But for left-hand pages they are reversed.
if (aPage.side == PageSideOptions.LEFT_HAND){
pLeft = p.right;
pRight = p.left;
}
else{
pLeft = p.left;
pRight = p.right;
}
newMargins[0] = pageBounds[0] + p.top;
newMargins[1] = pageBounds[1] + pLeft;
newMargins[2] = pageBounds[2] - p.bottom;
newMargins[3] = pageBounds[3] - pRight;
newColumnPositions = p.columnsPositions;
// Column positions are always measured from the visible left margin, whatever side page this is.
for (i = 0; i < oldColumnPositions.length; i++) oldColumnPositions[i] += oldMargins[1];
for (i = 0; i < newColumnPositions.length; i++) newColumnPositions[i] += newMargins[1];
// If margins and columns are the same, do nothing and return
if (String(oldMargins) == String(newMargins) && String(oldColumnPositions) == String(newColumnPositions)) return;
// Adjust page items
for (i = 0; i < theItems.length; i++){
// Ignore locked items
if (theItems[i].locked || theItems[i].itemLayer.locked) continue;
// The snap object will keep track of which edge of the object has been moved.
// Its properties will be top, bottom, left, and right, all booleans.
snap = {};
theBounds = itemBounds[i];
oldBounds = [].concat(theBounds);
// If the top edge of the page item is within 1pt of the top margin, adjust its top edge to be flush with the new margin,
// and set snap.top to true so we know that we have adjusted the top edge.
if (epsilon(theBounds[0], oldMargins[0])){
theBounds[0] = newMargins[0];
snap.top = true;
}
// Ditto bottom margin.
if (epsilon(theBounds[2], oldMargins[2])){
theBounds[2] = newMargins[2];
snap.bottom = true;
}
// Next, loop through all the column positions. If the left edge of the item was within 1pt of any of the old column positions, adjust the new position of its left edge,
// and set snap.left to true so we know that we have adjusted the left edge.
// Ditto for the right edge.
for (j = 0; j < newColumnPositions.length && j < oldColumnPositions.length; j++){
if (!snap.left && epsilon(theBounds[1], oldColumnPositions[j])){
theBounds[1] = newColumnPositions[j];
snap.left = true;
}
if (!snap.right && epsilon(theBounds[3], oldColumnPositions[j])){
theBounds[3] = newColumnPositions[j];
snap.right = true;
}
}
// The way the old Layout Adjustment feature worked was as follows:
// If only one edge of an item abutted a margin, then the entire item is moved so that it remains sitting on that margn, but its size is not changed.
// But if two opposite edges of an item abutted a margin, then the object was both resized and moved,
// Currently, theBounds array represents a resized object in all cases.
// The following lines check whether only one of a pair of edges has been modified. If so, theBounds array is adjusted so that the size of the item is not changed.
if (snap.top && !snap.bottom){
theBounds[2] = theBounds[0] + (oldBounds[2] - oldBounds[0]);
}
if (snap.bottom && !snap.top){
theBounds[0] = theBounds[2] - (oldBounds[2] - oldBounds[0]);
}
if (snap.left && !snap.right){
theBounds[3] = theBounds[1] + (oldBounds[3] - oldBounds[1]);
}
if (snap.right && !snap.left){
theBounds[1] = theBounds[3] - (oldBounds[3] - oldBounds[1]);
}
theItems[i].geometricBounds = theBounds;
}
// If the difference between a and b is smaller than 1, return true, else return false.
function epsilon(a, b){
return (Math.abs(a - b) < 1);
}
}