layoutbridge-spm 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -0
- package/LICENSE +26 -0
- package/README.de.md +318 -0
- package/README.md +304 -4
- package/THIRD-PARTY-NOTICES.md +2878 -0
- package/dist/src/mcp/config.js +125 -0
- package/dist/src/mcp/dispatch.js +123 -0
- package/dist/src/mcp/license/free-tools.js +16 -0
- package/dist/src/mcp/license/gate.js +209 -0
- package/dist/src/mcp/license/index.js +96 -0
- package/dist/src/mcp/license/instance-name.js +56 -0
- package/dist/src/mcp/license/key-source.js +58 -0
- package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
- package/dist/src/mcp/license/placeholder-verifier.js +10 -0
- package/dist/src/mcp/license/polar-ids.js +63 -0
- package/dist/src/mcp/license/polar-verifier.js +265 -0
- package/dist/src/mcp/license/product-ids.js +72 -0
- package/dist/src/mcp/license/state-store.js +91 -0
- package/dist/src/mcp/license/types.js +11 -0
- package/dist/src/mcp/logging.js +270 -0
- package/dist/src/mcp/product-info.js +48 -0
- package/dist/src/mcp/server.js +250 -0
- package/dist/src/mcp/setup.js +608 -0
- package/dist/src/mcp/start.cjs +95 -0
- package/dist/src/mcp/tool-definition.js +2 -0
- package/dist/src/mcp/tools.js +2004 -0
- package/dist/src/mcp/typography-rules.js +70 -0
- package/dist/src/transport/com.js +250 -0
- package/dist/src/transport/osascript.js +171 -0
- package/dist/src/transport/queue.js +19 -0
- package/dist/src/transport/script-bundle.js +92 -0
- package/dist/src/transport/transport.js +30 -0
- package/indesign/commands/10-app.js +6 -0
- package/indesign/commands/20-documents.js +513 -0
- package/indesign/commands/22-document-setup.js +118 -0
- package/indesign/commands/23-save-package.js +255 -0
- package/indesign/commands/25-export.js +379 -0
- package/indesign/commands/26-export-image.js +197 -0
- package/indesign/commands/27-textdefaults.js +151 -0
- package/indesign/commands/28-selection.js +174 -0
- package/indesign/commands/30-pages.js +1032 -0
- package/indesign/commands/31-view.js +210 -0
- package/indesign/commands/32-sections.js +203 -0
- package/indesign/commands/35-parents.js +428 -0
- package/indesign/commands/37-layout.js +760 -0
- package/indesign/commands/40-frames.js +836 -0
- package/indesign/commands/41-shapes.js +505 -0
- package/indesign/commands/42-groups.js +117 -0
- package/indesign/commands/43-layers.js +291 -0
- package/indesign/commands/44-effects.js +274 -0
- package/indesign/commands/45-frame-options.js +247 -0
- package/indesign/commands/47-anchored.js +287 -0
- package/indesign/commands/50-modify.js +781 -0
- package/indesign/commands/52-align.js +139 -0
- package/indesign/commands/55-fit.js +106 -0
- package/indesign/commands/60-images.js +771 -0
- package/indesign/commands/62-embed.js +120 -0
- package/indesign/commands/69-placeholders.js +127 -0
- package/indesign/commands/70-text.js +567 -0
- package/indesign/commands/71-outlines.js +92 -0
- package/indesign/commands/72-place-text.js +366 -0
- package/indesign/commands/74-stories.js +116 -0
- package/indesign/commands/75-tables.js +676 -0
- package/indesign/commands/76-text-variables.js +260 -0
- package/indesign/commands/77-story-options.js +86 -0
- package/indesign/commands/78-frame-lines.js +304 -0
- package/indesign/commands/79-check-typography.js +303 -0
- package/indesign/commands/80-styles.js +1620 -0
- package/indesign/commands/81-composition.js +355 -0
- package/indesign/commands/82-object-styles.js +263 -0
- package/indesign/commands/83-indents-tabs.js +237 -0
- package/indesign/commands/84-import-styles.js +142 -0
- package/indesign/commands/85-swatches.js +112 -0
- package/indesign/commands/86-gradients.js +185 -0
- package/indesign/commands/87-replace-swatch.js +180 -0
- package/indesign/commands/88-replace-font.js +217 -0
- package/indesign/interpreter.js +758 -0
- package/package.json +55 -6
- package/rules/typography-rules.json +31 -0
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
/* Bereich B – Seiten. */
|
|
2
|
+
|
|
3
|
+
/* Seitenseite nach Spezifikation: LEFT_HAND | RIGHT_HAND | SINGLE_SIDED. */
|
|
4
|
+
function idcPageSide(page) {
|
|
5
|
+
return IDC.pageSide(page);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Name der angewandten Musterseite, "[None]" wenn keine. */
|
|
9
|
+
function idcAppliedParentName(page) {
|
|
10
|
+
var master;
|
|
11
|
+
try { master = page.appliedMaster; } catch (e) { return '[None]'; }
|
|
12
|
+
try {
|
|
13
|
+
if (master && master.isValid) { return String(master.name); }
|
|
14
|
+
} catch (e2) { /* NothingEnum kennt kein isValid */ }
|
|
15
|
+
return '[None]';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Klassenname und Objektart kommen aus dem Kern (IDC.className, IDC.itemType). */
|
|
19
|
+
function idcClassName(item) {
|
|
20
|
+
return IDC.className(item);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function idcItemType(item) {
|
|
24
|
+
return IDC.itemType(item);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Absatzformat, wenn im ganzen Rahmen einheitlich, sonst null. */
|
|
28
|
+
function idcUniformParagraphStyle(frame) {
|
|
29
|
+
var name = null;
|
|
30
|
+
var i, current;
|
|
31
|
+
try {
|
|
32
|
+
if (frame.paragraphs.length === 0) { return null; }
|
|
33
|
+
for (i = 0; i < frame.paragraphs.length; i++) {
|
|
34
|
+
current = String(frame.paragraphs.item(i).appliedParagraphStyle.name);
|
|
35
|
+
if (name === null) { name = current; }
|
|
36
|
+
else if (name !== current) { return null; }
|
|
37
|
+
}
|
|
38
|
+
} catch (e) { return null; }
|
|
39
|
+
return name;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function idcTextSnippet(frame) {
|
|
43
|
+
var text = '';
|
|
44
|
+
try { text = String(frame.contents); } catch (e) { return ''; }
|
|
45
|
+
if (text.length > 200) { text = text.substr(0, 200); }
|
|
46
|
+
return text;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function idcDescribeItem(item, parentGroupId, doc, page) {
|
|
50
|
+
var type = idcItemType(item);
|
|
51
|
+
var entry = {
|
|
52
|
+
id: null,
|
|
53
|
+
type: type,
|
|
54
|
+
bounds_mm: null,
|
|
55
|
+
paragraph_style: null,
|
|
56
|
+
text_snippet: null,
|
|
57
|
+
child_count: 0,
|
|
58
|
+
parent_group_id: parentGroupId,
|
|
59
|
+
layer_name: idcLayerName(item)
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/* Bei "other" sagen, was es wirklich ist – sonst hilft die Angabe niemandem. */
|
|
63
|
+
if (type === 'other') {
|
|
64
|
+
entry.class_name = idcClassName(item);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try { entry.id = Number(item.id); } catch (e1) { entry.id = null; }
|
|
68
|
+
try {
|
|
69
|
+
entry.bounds_mm = doc ? IDC.boundsOnPage(doc, item, page) : IDC.boundsToRect(item.geometricBounds);
|
|
70
|
+
} catch (e2) { entry.bounds_mm = null; }
|
|
71
|
+
|
|
72
|
+
if (type === 'text') {
|
|
73
|
+
entry.paragraph_style = idcUniformParagraphStyle(item);
|
|
74
|
+
entry.text_snippet = idcTextSnippet(item);
|
|
75
|
+
}
|
|
76
|
+
if (type === 'group') {
|
|
77
|
+
try { entry.child_count = item.pageItems.length; } catch (e3) { entry.child_count = 0; }
|
|
78
|
+
}
|
|
79
|
+
return entry;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function idcCollectItems(container, out, parentGroupId, includeChildren, doc, page) {
|
|
83
|
+
var i, item, entry;
|
|
84
|
+
for (i = 0; i < container.pageItems.length; i++) {
|
|
85
|
+
item = IDC.concrete(container.pageItems.item(i));
|
|
86
|
+
entry = idcDescribeItem(item, parentGroupId, doc, page);
|
|
87
|
+
out.push(entry);
|
|
88
|
+
if (entry.type === 'group' && includeChildren) {
|
|
89
|
+
idcCollectItems(item, out, entry.id, includeChildren, doc, page);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
IDC.register('list_pages', { needs_document: true, read_only: true, ruler: 'page' },
|
|
95
|
+
function (ctx) {
|
|
96
|
+
var doc = ctx.doc;
|
|
97
|
+
var pages = [];
|
|
98
|
+
var i, page;
|
|
99
|
+
for (i = 0; i < doc.pages.length; i++) {
|
|
100
|
+
page = doc.pages.item(i);
|
|
101
|
+
pages.push({
|
|
102
|
+
page_id: Number(page.id),
|
|
103
|
+
index: Number(page.documentOffset),
|
|
104
|
+
side: idcPageSide(page),
|
|
105
|
+
parent: idcAppliedParentName(page),
|
|
106
|
+
name: String(page.name)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return { pages: pages, count: pages.length };
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
IDC.register('get_page_state', { needs_document: true, read_only: true, ruler: 'page' },
|
|
113
|
+
function (ctx) {
|
|
114
|
+
var args = ctx.args;
|
|
115
|
+
var doc = ctx.doc;
|
|
116
|
+
|
|
117
|
+
if (args.page_id === undefined || args.page_id === null) {
|
|
118
|
+
IDC.fail('invalid_argument', 'page_id is required.', null);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
var found = IDC.pageById(doc, args.page_id);
|
|
122
|
+
if (found === null) {
|
|
123
|
+
IDC.fail('not_found', 'No page with page_id ' + args.page_id + ' in this document.',
|
|
124
|
+
{ page_id: args.page_id });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var page = found.page;
|
|
128
|
+
var includeChildren = args.include_group_children === false ? false : true;
|
|
129
|
+
var frames = [];
|
|
130
|
+
/* Begrenzungen relativ zu dieser Seite, auch bei Anschnitt ueber den Bund (E-28). */
|
|
131
|
+
idcCollectItems(page, frames, null, includeChildren, doc, page);
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
* Seitenbegrenzung relativ zur Seite selbst: immer x = 0, y = 0.
|
|
135
|
+
* page.bounds liefert unter Seiten-Nullpunkt fuer die rechte Seite eines
|
|
136
|
+
* Bogens x = 210 (Live-Fund 04.09.2026), deshalb nur die Groesse lesen.
|
|
137
|
+
*/
|
|
138
|
+
var bounds = null;
|
|
139
|
+
try {
|
|
140
|
+
var size = IDC.pageSize(page);
|
|
141
|
+
bounds = { x: 0, y: 0, width: size.width, height: size.height };
|
|
142
|
+
} catch (e) { bounds = null; }
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
page_id: Number(page.id),
|
|
146
|
+
name: String(page.name),
|
|
147
|
+
is_parent_page: found.is_parent_page,
|
|
148
|
+
include_group_children: includeChildren,
|
|
149
|
+
coordinate_space: 'page',
|
|
150
|
+
page_bounds_mm: bounds,
|
|
151
|
+
frames: frames,
|
|
152
|
+
frame_count: frames.length
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/* -------------------------------------------------------------- add_page */
|
|
157
|
+
|
|
158
|
+
/* Dokumentseite (keine Musterseite) ueber die ID, sonst Fehler. */
|
|
159
|
+
function idcDocumentPageById(doc, id, field) {
|
|
160
|
+
var found;
|
|
161
|
+
if (id === undefined || id === null) {
|
|
162
|
+
IDC.fail('invalid_argument', field + ' is required.', null);
|
|
163
|
+
}
|
|
164
|
+
found = IDC.pageById(doc, id);
|
|
165
|
+
if (found === null) {
|
|
166
|
+
IDC.fail('not_found', 'No page with ' + field + ' ' + id + ' in this document.', { page_id: id });
|
|
167
|
+
}
|
|
168
|
+
if (found.is_parent_page) {
|
|
169
|
+
IDC.fail('invalid_state', field + ' ' + id + ' is a master page (Musterseite); this tool works on document pages.',
|
|
170
|
+
{ page_id: id });
|
|
171
|
+
}
|
|
172
|
+
return found.page;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function idcDescribePage(page) {
|
|
176
|
+
var spread = null;
|
|
177
|
+
try { spread = page.parent; } catch (e) { spread = null; }
|
|
178
|
+
return {
|
|
179
|
+
page_id: Number(page.id),
|
|
180
|
+
index: Number(page.documentOffset),
|
|
181
|
+
name: String(page.name),
|
|
182
|
+
side: idcPageSide(page),
|
|
183
|
+
spread_index: spread === null ? null : IDC.spreadIndex(spread)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
IDC.register('add_page', { needs_document: true, read_only: false, ruler: 'page' },
|
|
188
|
+
function (ctx) {
|
|
189
|
+
var args = ctx.args;
|
|
190
|
+
var doc = ctx.doc;
|
|
191
|
+
var at = args.at;
|
|
192
|
+
var page, ref, spread, spreadCount;
|
|
193
|
+
/* Sitzung 10 (Demo-Heft-Befund 1): pages.add() kann die Ansicht verschieben; Ansicht merken. */
|
|
194
|
+
var keepView = idcKeepViewArg(args);
|
|
195
|
+
var view = keepView ? idcCaptureView(doc) : null;
|
|
196
|
+
var viewRestored = null;
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
if (at === undefined || at === null || at === 'end') {
|
|
200
|
+
page = doc.pages.add(LocationOptions.AT_END);
|
|
201
|
+
} else if (at === 'start') {
|
|
202
|
+
page = doc.pages.add(LocationOptions.AT_BEGINNING);
|
|
203
|
+
} else if (typeof at === 'object' && at.after_page_id !== undefined) {
|
|
204
|
+
ref = idcDocumentPageById(doc, at.after_page_id, 'at.after_page_id');
|
|
205
|
+
page = doc.pages.add(LocationOptions.AFTER, ref);
|
|
206
|
+
} else if (typeof at === 'object' && at.before_page_id !== undefined) {
|
|
207
|
+
ref = idcDocumentPageById(doc, at.before_page_id, 'at.before_page_id');
|
|
208
|
+
page = doc.pages.add(LocationOptions.BEFORE, ref);
|
|
209
|
+
} else {
|
|
210
|
+
IDC.fail('invalid_argument',
|
|
211
|
+
'at must be "start", "end", { after_page_id } or { before_page_id }.', { at: at });
|
|
212
|
+
}
|
|
213
|
+
} catch (e) {
|
|
214
|
+
if (e && e.isIdcError) { throw e; }
|
|
215
|
+
IDC.fail('script_error', 'InDesign could not add the page: ' + String(e.message ? e.message : e), null);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
spread = page.parent;
|
|
219
|
+
spreadCount = spread.pages.length;
|
|
220
|
+
if (spreadCount > 2) {
|
|
221
|
+
ctx.warn('The new page landed on a spread (Druckbogen) with ' + spreadCount +
|
|
222
|
+
' pages – InDesign appends to the existing spread when "allow pages to shuffle" is off. Use move_pages_to_new_spread to repair.');
|
|
223
|
+
}
|
|
224
|
+
/*
|
|
225
|
+
* InDesign gibt der neuen Seite meist die Musterseite der Nachbarseite
|
|
226
|
+
* (am lebenden Dokument gesehen); nur wenn keine dran ist, warnen.
|
|
227
|
+
*/
|
|
228
|
+
var parentName = idcAppliedParentName(page);
|
|
229
|
+
if (parentName === '[None]') {
|
|
230
|
+
ctx.warn('The new page has no master page (Musterseite); use apply_parent_to_page.');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (view !== null) { viewRestored = view.window === null ? null : idcRestoreView(view); }
|
|
234
|
+
|
|
235
|
+
ctx.changes = { created_page_ids: [Number(page.id)] };
|
|
236
|
+
return {
|
|
237
|
+
page_id: Number(page.id),
|
|
238
|
+
index: Number(page.documentOffset),
|
|
239
|
+
name: String(page.name),
|
|
240
|
+
parent: parentName,
|
|
241
|
+
side: idcPageSide(page),
|
|
242
|
+
spread_index: IDC.spreadIndex(spread),
|
|
243
|
+
spread_page_count: spreadCount,
|
|
244
|
+
page_count: doc.pages.length,
|
|
245
|
+
view_restored: viewRestored
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
/* ----------------------------------------------------------- delete_page */
|
|
250
|
+
|
|
251
|
+
/* Ist der Rahmen mit einem anderen verkettet? NothingEnum hat kein isValid. */
|
|
252
|
+
function idcIsThreaded(frame) {
|
|
253
|
+
var next = null;
|
|
254
|
+
var prev = null;
|
|
255
|
+
try { next = frame.nextTextFrame; } catch (e1) { next = null; }
|
|
256
|
+
try { prev = frame.previousTextFrame; } catch (e2) { prev = null; }
|
|
257
|
+
function real(ref) {
|
|
258
|
+
try { return ref !== null && ref !== undefined && ref.isValid === true; } catch (e) { return false; }
|
|
259
|
+
}
|
|
260
|
+
return real(next) || real(prev);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Beurteilt ein Objekt fuer delete_page: blockiert es, und warum. */
|
|
264
|
+
function idcAssessItem(item, out) {
|
|
265
|
+
var type = idcItemType(item);
|
|
266
|
+
var entry = { id: null, type: type, blocking: false, reason: '', text_snippet: null };
|
|
267
|
+
var i, child, childEntry, text;
|
|
268
|
+
|
|
269
|
+
try { entry.id = Number(item.id); } catch (e0) { entry.id = null; }
|
|
270
|
+
|
|
271
|
+
if (type === 'text') {
|
|
272
|
+
text = '';
|
|
273
|
+
try { text = String(item.contents); } catch (e1) { text = ''; }
|
|
274
|
+
entry.text_snippet = text.length > 80 ? text.substr(0, 80) : text;
|
|
275
|
+
if (idcIsThreaded(item)) {
|
|
276
|
+
entry.blocking = true;
|
|
277
|
+
entry.reason = 'threaded text frame (verketteter Rahmen)';
|
|
278
|
+
} else if (text.length > 0) {
|
|
279
|
+
entry.blocking = true;
|
|
280
|
+
entry.reason = 'text frame with content';
|
|
281
|
+
} else {
|
|
282
|
+
entry.reason = 'empty text frame';
|
|
283
|
+
}
|
|
284
|
+
} else if (type === 'image') {
|
|
285
|
+
entry.blocking = true;
|
|
286
|
+
entry.reason = 'frame with a placed graphic';
|
|
287
|
+
} else if (type === 'rectangle' || type === 'ellipse' || type === 'polygon') {
|
|
288
|
+
entry.reason = 'empty frame';
|
|
289
|
+
} else if (type === 'line') {
|
|
290
|
+
entry.reason = 'line';
|
|
291
|
+
} else if (type === 'group') {
|
|
292
|
+
entry.reason = 'group';
|
|
293
|
+
try {
|
|
294
|
+
for (i = 0; i < item.pageItems.length; i++) {
|
|
295
|
+
child = IDC.concrete(item.pageItems.item(i));
|
|
296
|
+
childEntry = idcAssessItem(child, out);
|
|
297
|
+
if (childEntry.blocking) {
|
|
298
|
+
entry.blocking = true;
|
|
299
|
+
entry.reason = 'group with content';
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
} catch (e2) { /* Gruppe ohne lesbare Kinder */ }
|
|
303
|
+
} else {
|
|
304
|
+
entry.reason = 'other object (' + idcClassName(item) + ')';
|
|
305
|
+
}
|
|
306
|
+
out.push(entry);
|
|
307
|
+
return entry;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/*
|
|
311
|
+
* Sitzung 24 (Befund aus dem Praxiseinsatz, E-79): Objekte auf der Montageflaeche
|
|
312
|
+
* gehoeren zum Bogen, nicht zur Seite. Verschwindet mit der Seite ein Bogen,
|
|
313
|
+
* sind sie weg, ohne dass delete_page sie bisher erwaehnte. Vorhergesagt
|
|
314
|
+
* wird nur der Fall, dass die Seite die einzige ihres Bogens ist. Die
|
|
315
|
+
* fruehere Annahme (b) – bei Doppelseiten mit "Seiten neu anordnen" gehe der
|
|
316
|
+
* leere letzte Bogen samt Montageflaeche – ist am lebenden InDesign
|
|
317
|
+
* widerlegt (Nacharbeit Sitzung 24, E-80): das Objekt blieb erhalten. Nach
|
|
318
|
+
* dem Loeschen vergleicht der Handler alle Montageflaechen-Objekte des
|
|
319
|
+
* Dokuments: verloren (pasteboard_lost_ids) oder auf einen anderen Bogen
|
|
320
|
+
* bzw. eine andere Lage gewandert (pasteboard_relocated).
|
|
321
|
+
*/
|
|
322
|
+
function idcPasteboardEntry(doc, item) {
|
|
323
|
+
var entry = { frame_id: null, type: idcItemType(item), spread_index: null, bounds_mm: null, link_name: null, link_path: null, text_snippet: null };
|
|
324
|
+
var graphics, link, spread;
|
|
325
|
+
try { entry.frame_id = Number(item.id); } catch (e0) { entry.frame_id = null; }
|
|
326
|
+
spread = IDC.spreadOfItem(item);
|
|
327
|
+
if (spread !== null) { entry.spread_index = IDC.spreadIndex(spread); }
|
|
328
|
+
try { entry.bounds_mm = IDC.boundsOnPage(doc, item, null); } catch (e1) { entry.bounds_mm = null; }
|
|
329
|
+
try {
|
|
330
|
+
graphics = idcGraphicsOf(item);
|
|
331
|
+
if (graphics.length > 0) {
|
|
332
|
+
link = idcLinkOf(graphics[0]);
|
|
333
|
+
if (link !== null) {
|
|
334
|
+
try { entry.link_name = String(link.name); } catch (e2) { entry.link_name = null; }
|
|
335
|
+
try { entry.link_path = String(link.filePath); } catch (e3) { entry.link_path = null; }
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} catch (e4) { /* keine Grafik */ }
|
|
339
|
+
if (entry.type === 'text') {
|
|
340
|
+
entry.text_snippet = idcTextSnippet(item);
|
|
341
|
+
if (entry.text_snippet.length > 80) { entry.text_snippet = entry.text_snippet.substr(0, 80); }
|
|
342
|
+
}
|
|
343
|
+
return entry;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* Objekte auf der Montageflaeche eines Bogens (oberste Ebene, keine Gruppenmitglieder). */
|
|
347
|
+
function idcPasteboardItemsOf(spread) {
|
|
348
|
+
var out = [];
|
|
349
|
+
var i, item;
|
|
350
|
+
for (i = 0; i < spread.pageItems.length; i++) {
|
|
351
|
+
item = IDC.concrete(spread.pageItems.item(i));
|
|
352
|
+
if (IDC.landing(item).on_pasteboard) { out.push(item); }
|
|
353
|
+
}
|
|
354
|
+
return out;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* Boegen, die mit der Seite verschwinden (siehe oben): nur der eigene, wenn die Seite seine einzige ist. */
|
|
358
|
+
function idcSpreadsAtRisk(doc, page) {
|
|
359
|
+
var out = [];
|
|
360
|
+
var own = page.parent;
|
|
361
|
+
if (own.pages.length === 1) { out.push(own); }
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/* Seiten eines Bogens: IDs (Vergleich; Namen werden beim Loeschen neu gezaehlt) und Namen (Meldung). */
|
|
366
|
+
function idcSpreadPages(spread) {
|
|
367
|
+
var out = { ids: [], names: [] };
|
|
368
|
+
var i, page;
|
|
369
|
+
if (spread === null) { return out; }
|
|
370
|
+
for (i = 0; i < spread.pages.length; i++) {
|
|
371
|
+
page = spread.pages.item(i);
|
|
372
|
+
out.ids.push(Number(page.id));
|
|
373
|
+
out.names.push(String(page.name));
|
|
374
|
+
}
|
|
375
|
+
return out;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* Alle Montageflaechen-Objekte des Dokuments mit Bogen und Lage (vor dem Loeschen). */
|
|
379
|
+
function idcPasteboardSnapshot(doc) {
|
|
380
|
+
var out = [];
|
|
381
|
+
var i, j, spread, items, bounds;
|
|
382
|
+
for (i = 0; i < doc.spreads.length; i++) {
|
|
383
|
+
spread = doc.spreads.item(i);
|
|
384
|
+
items = idcPasteboardItemsOf(spread);
|
|
385
|
+
for (j = 0; j < items.length; j++) {
|
|
386
|
+
try { bounds = IDC.boundsOnPage(doc, items[j], null); } catch (e0) { bounds = null; }
|
|
387
|
+
out.push({ id: Number(items[j].id), spread_index: IDC.spreadIndex(spread), pages: idcSpreadPages(spread), bounds: bounds });
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function idcSameBounds(a, b) {
|
|
394
|
+
if (a === null || b === null) { return a === b; }
|
|
395
|
+
return Math.abs(a.x - b.x) < 0.01 && Math.abs(a.y - b.y) < 0.01 && Math.abs(a.width - b.width) < 0.01 && Math.abs(a.height - b.height) < 0.01;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* on_pasteboard_items: warn (Vorgabe) | abort | move. */
|
|
399
|
+
function idcPasteboardModeArg(args) {
|
|
400
|
+
var mode = args.on_pasteboard_items;
|
|
401
|
+
if (mode === undefined || mode === null) { return 'warn'; }
|
|
402
|
+
if (mode !== 'warn' && mode !== 'abort' && mode !== 'move') {
|
|
403
|
+
IDC.fail('invalid_argument', 'on_pasteboard_items must be "warn", "abort" or "move".', { on_pasteboard_items: mode });
|
|
404
|
+
}
|
|
405
|
+
return mode;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/*
|
|
409
|
+
* Objekt auf die Montageflaeche des Bogens von target bringen: gleiche Lage
|
|
410
|
+
* zum Bogen; was rechts neben dem alten Bogen lag, liegt rechts neben dem
|
|
411
|
+
* neuen (gleicher Abstand zur rechten Seitenkante).
|
|
412
|
+
*/
|
|
413
|
+
function idcMoveToPasteboardOf(ctx, doc, item, target) {
|
|
414
|
+
var before = IDC.boundsOnPage(doc, item, null);
|
|
415
|
+
var sourceWidth = 0, targetWidth, spread, i, x, current, moved;
|
|
416
|
+
spread = IDC.spreadOfItem(item);
|
|
417
|
+
for (i = 0; i < spread.pages.length; i++) { sourceWidth += IDC.pageSize(spread.pages.item(i)).width; }
|
|
418
|
+
targetWidth = idcSpreadWidthOfPage(target);
|
|
419
|
+
x = before.x;
|
|
420
|
+
if (before.x >= sourceWidth) { x = targetWidth + (before.x - sourceWidth); }
|
|
421
|
+
moved = item.move(target);
|
|
422
|
+
if (IDC.isArray(moved)) { moved = moved[0]; }
|
|
423
|
+
if (moved !== undefined && moved !== null) { item = IDC.concrete(moved); }
|
|
424
|
+
/* Unter Bogen-Nullpunkt gelesen: Bogenkoordinaten, gleich ob auf einer Seite oder daneben. */
|
|
425
|
+
current = IDC.boundsOnPage(doc, item, null);
|
|
426
|
+
idcMoveBy(doc, item, x - current.x, before.y - current.y);
|
|
427
|
+
if (!IDC.landing(item).on_pasteboard) {
|
|
428
|
+
ctx.warn('Object ' + Number(item.id) + ' was moved to the spread of page ' + String(target.name) + ', but lands on page ' +
|
|
429
|
+
IDC.landing(item).page_name + ' there – move it with move_frame_to_page (pasteboard).');
|
|
430
|
+
}
|
|
431
|
+
return Number(item.id);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
IDC.register('delete_page', { needs_document: true, read_only: false, ruler: 'page' },
|
|
435
|
+
function (ctx) {
|
|
436
|
+
var args = ctx.args;
|
|
437
|
+
var doc = ctx.doc;
|
|
438
|
+
var page, summary, blocking, i, j, item, formerIndex, name, pageId, boilerplate, removedFrameIds;
|
|
439
|
+
var pasteboardMode, atRisk, riskItems, pasteboardEntries, target, movedIds, lostIds, allBefore, riskText, onSpread;
|
|
440
|
+
var relocated, still, stillSpread, stillPages, stillBounds, entryText, lostEntry;
|
|
441
|
+
|
|
442
|
+
page = idcDocumentPageById(doc, args.page_id, 'page_id');
|
|
443
|
+
if (doc.pages.length <= 1) {
|
|
444
|
+
IDC.fail('invalid_state', 'The only page of a document cannot be deleted.', { page_id: args.page_id });
|
|
445
|
+
}
|
|
446
|
+
pasteboardMode = idcPasteboardModeArg(args);
|
|
447
|
+
target = null;
|
|
448
|
+
if (pasteboardMode === 'move') {
|
|
449
|
+
if (args.pasteboard_target_page_id === undefined || args.pasteboard_target_page_id === null) {
|
|
450
|
+
IDC.fail('invalid_argument', 'on_pasteboard_items "move" needs pasteboard_target_page_id (a page whose spread receives the objects).', null);
|
|
451
|
+
}
|
|
452
|
+
target = idcDocumentPageById(doc, args.pasteboard_target_page_id, 'pasteboard_target_page_id');
|
|
453
|
+
if (Number(target.id) === Number(page.id)) {
|
|
454
|
+
IDC.fail('invalid_argument', 'pasteboard_target_page_id must be another page than the one deleted.', { pasteboard_target_page_id: args.pasteboard_target_page_id });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
summary = [];
|
|
459
|
+
blocking = [];
|
|
460
|
+
for (i = 0; i < page.pageItems.length; i++) {
|
|
461
|
+
item = IDC.concrete(page.pageItems.item(i));
|
|
462
|
+
idcAssessItem(item, summary);
|
|
463
|
+
}
|
|
464
|
+
for (i = 0; i < summary.length; i++) {
|
|
465
|
+
if (summary[i].blocking) { blocking.push(summary[i]); }
|
|
466
|
+
}
|
|
467
|
+
boilerplate = 0;
|
|
468
|
+
try { boilerplate = page.masterPageItems.length; } catch (e1) { boilerplate = 0; }
|
|
469
|
+
|
|
470
|
+
if (blocking.length > 0 && args.force !== true) {
|
|
471
|
+
IDC.fail('invalid_state',
|
|
472
|
+
'Page ' + String(page.name) + ' holds ' + blocking.length +
|
|
473
|
+
' object(s) with content (text, images or threaded frames). Nothing was deleted. Pass force: true to delete anyway – there is no undo.',
|
|
474
|
+
{ page_id: Number(page.id), blocking_items: blocking, item_summary: summary, boilerplate_ignored: boilerplate });
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Montageflaeche: gefaehrdete Boegen und ihre Objekte, vor jeder Aenderung. */
|
|
478
|
+
atRisk = idcSpreadsAtRisk(doc, page);
|
|
479
|
+
riskItems = [];
|
|
480
|
+
pasteboardEntries = [];
|
|
481
|
+
for (i = 0; i < atRisk.length; i++) {
|
|
482
|
+
onSpread = idcPasteboardItemsOf(atRisk[i]);
|
|
483
|
+
for (j = 0; j < onSpread.length; j++) {
|
|
484
|
+
riskItems.push(onSpread[j]);
|
|
485
|
+
pasteboardEntries.push(idcPasteboardEntry(doc, onSpread[j]));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (target !== null) {
|
|
489
|
+
for (i = 0; i < atRisk.length; i++) {
|
|
490
|
+
if (IDC.spreadIndex(atRisk[i]) === IDC.spreadIndex(target.parent)) {
|
|
491
|
+
IDC.fail('invalid_argument', 'pasteboard_target_page_id lies on a spread that disappears with this page; choose a page on another spread.',
|
|
492
|
+
{ pasteboard_target_page_id: Number(target.id), spread_index: IDC.spreadIndex(target.parent) });
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (riskItems.length > 0 && pasteboardMode === 'abort') {
|
|
497
|
+
IDC.fail('invalid_state',
|
|
498
|
+
'Deleting page ' + String(page.name) + ' removes a spread (Druckbogen) with ' + riskItems.length +
|
|
499
|
+
' object(s) on its pasteboard (Montageflaeche). Nothing was deleted. Move them first (move_frame_to_page) or pass on_pasteboard_items "move" with pasteboard_target_page_id.',
|
|
500
|
+
{ code: 'pasteboard_items_present', page_id: Number(page.id), pasteboard_items: pasteboardEntries });
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
pageId = Number(page.id);
|
|
504
|
+
name = String(page.name);
|
|
505
|
+
formerIndex = Number(page.documentOffset);
|
|
506
|
+
removedFrameIds = [];
|
|
507
|
+
for (i = 0; i < summary.length; i++) {
|
|
508
|
+
if (summary[i].id !== null) { removedFrameIds.push(summary[i].id); }
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
movedIds = [];
|
|
512
|
+
if (riskItems.length > 0 && pasteboardMode === 'move') {
|
|
513
|
+
for (i = 0; i < riskItems.length; i++) {
|
|
514
|
+
try {
|
|
515
|
+
movedIds.push(idcMoveToPasteboardOf(ctx, doc, riskItems[i], target));
|
|
516
|
+
} catch (eM) {
|
|
517
|
+
IDC.fail('script_error', 'InDesign could not move pasteboard object ' + pasteboardEntries[i].frame_id + ' to the spread of page ' +
|
|
518
|
+
String(target.name) + ': ' + String(eM.message ? eM.message : eM) + '. The page was not deleted.',
|
|
519
|
+
{ page_id: pageId, moved_frame_ids: movedIds, pasteboard_items: pasteboardEntries });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
allBefore = idcPasteboardSnapshot(doc);
|
|
524
|
+
|
|
525
|
+
try {
|
|
526
|
+
page.remove();
|
|
527
|
+
} catch (e2) {
|
|
528
|
+
IDC.fail('script_error', 'InDesign could not delete the page: ' + String(e2.message ? e2.message : e2),
|
|
529
|
+
{ page_id: pageId });
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/* Nach dem Loeschen: was ist verloren, was liegt jetzt an einem anderen Bogen oder anders? */
|
|
533
|
+
lostIds = [];
|
|
534
|
+
relocated = [];
|
|
535
|
+
for (i = 0; i < allBefore.length; i++) {
|
|
536
|
+
still = IDC.itemById(doc, allBefore[i].id, 'document');
|
|
537
|
+
if (still === null) { lostIds.push(allBefore[i].id); continue; }
|
|
538
|
+
still = still.item;
|
|
539
|
+
stillSpread = IDC.spreadOfItem(still);
|
|
540
|
+
stillPages = idcSpreadPages(stillSpread);
|
|
541
|
+
try { stillBounds = IDC.boundsOnPage(doc, still, null); } catch (e3) { stillBounds = null; }
|
|
542
|
+
if (stillPages.ids.join(',') !== allBefore[i].pages.ids.join(',') || !idcSameBounds(stillBounds, allBefore[i].bounds)) {
|
|
543
|
+
relocated.push({
|
|
544
|
+
frame_id: allBefore[i].id,
|
|
545
|
+
spread_page_ids_before: allBefore[i].pages.ids,
|
|
546
|
+
spread_page_ids: stillPages.ids,
|
|
547
|
+
spread_page_names_before: allBefore[i].pages.names,
|
|
548
|
+
spread_page_names: stillPages.names,
|
|
549
|
+
spread_index: stillSpread === null ? null : IDC.spreadIndex(stillSpread),
|
|
550
|
+
bounds_mm_before: allBefore[i].bounds,
|
|
551
|
+
bounds_mm: stillBounds,
|
|
552
|
+
on_pasteboard: IDC.landing(still).on_pasteboard === true
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (lostIds.length > 0) {
|
|
557
|
+
riskText = [];
|
|
558
|
+
for (i = 0; i < lostIds.length; i++) {
|
|
559
|
+
removedFrameIds.push(lostIds[i]);
|
|
560
|
+
entryText = String(lostIds[i]);
|
|
561
|
+
for (j = 0; j < pasteboardEntries.length; j++) {
|
|
562
|
+
lostEntry = pasteboardEntries[j];
|
|
563
|
+
if (lostEntry.frame_id === lostIds[i]) {
|
|
564
|
+
entryText += ' (' + lostEntry.type + (lostEntry.link_name !== null ? ', ' + lostEntry.link_name : '') + ')';
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
riskText.push(entryText);
|
|
568
|
+
}
|
|
569
|
+
ctx.warn('A spread (Druckbogen) went with page ' + name + '; ' + lostIds.length + ' object(s) on its pasteboard (Montageflaeche) were deleted: ' +
|
|
570
|
+
riskText.join(', ') + '. See pasteboard_items and pasteboard_lost_ids; on_pasteboard_items "abort" or "move" prevents this.');
|
|
571
|
+
}
|
|
572
|
+
if (relocated.length > 0) {
|
|
573
|
+
riskText = [];
|
|
574
|
+
for (i = 0; i < relocated.length; i++) {
|
|
575
|
+
riskText.push(relocated[i].frame_id + ' now beside [' + relocated[i].spread_page_names.join(' ') + ']' +
|
|
576
|
+
(relocated[i].spread_page_ids_before.join(',') !== relocated[i].spread_page_ids.join(',') ? ' (was [' + relocated[i].spread_page_names_before.join(' ') + '] before deleting)' : ' (position changed)'));
|
|
577
|
+
}
|
|
578
|
+
ctx.warn('InDesign regrouped the spreads; ' + relocated.length + ' pasteboard object(s) (Montageflaeche) now lie elsewhere: ' + riskText.join(', ') + '. See pasteboard_relocated.');
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (boilerplate > 0) {
|
|
582
|
+
ctx.warn(boilerplate + ' master page object(s) (Musterseitenobjekte) were on the page; they belong to the master and were not counted.');
|
|
583
|
+
}
|
|
584
|
+
if (blocking.length > 0) {
|
|
585
|
+
ctx.warn('Deleted with force: ' + blocking.length + ' object(s) with content are gone.');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
ctx.changes = movedIds.length > 0
|
|
589
|
+
? { removed_page_ids: [pageId], removed_frame_ids: removedFrameIds, modified_frame_ids: movedIds }
|
|
590
|
+
: { removed_page_ids: [pageId], removed_frame_ids: removedFrameIds };
|
|
591
|
+
return {
|
|
592
|
+
page_id: pageId,
|
|
593
|
+
name: name,
|
|
594
|
+
former_index: formerIndex,
|
|
595
|
+
items_deleted: summary.length,
|
|
596
|
+
boilerplate_ignored: boilerplate,
|
|
597
|
+
item_summary: summary,
|
|
598
|
+
page_count: doc.pages.length,
|
|
599
|
+
on_pasteboard_items: pasteboardMode,
|
|
600
|
+
pasteboard_items: pasteboardEntries,
|
|
601
|
+
pasteboard_moved_ids: movedIds,
|
|
602
|
+
pasteboard_lost_ids: lostIds,
|
|
603
|
+
pasteboard_relocated: relocated
|
|
604
|
+
};
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
/* ------------------------------------------------------------ move_pages */
|
|
608
|
+
|
|
609
|
+
/* Seitenliste pruefen: IDs vorhanden, Dokumentseiten, direkte Nachbarn; sortiert nach Position. */
|
|
610
|
+
function idcAdjacentPages(doc, ids, field, minCount, maxCount) {
|
|
611
|
+
var pages = [];
|
|
612
|
+
var i, j, tmp, seen = {};
|
|
613
|
+
|
|
614
|
+
if (!IDC.isArray(ids) || ids.length < minCount || ids.length > maxCount) {
|
|
615
|
+
IDC.fail('invalid_argument', field + ' must be an array of ' + minCount + ' to ' + maxCount + ' page ids.',
|
|
616
|
+
{ count: IDC.isArray(ids) ? ids.length : null });
|
|
617
|
+
}
|
|
618
|
+
for (i = 0; i < ids.length; i++) {
|
|
619
|
+
if (seen[String(ids[i])]) {
|
|
620
|
+
IDC.fail('invalid_argument', field + ' names page ' + ids[i] + ' twice.', { page_id: ids[i] });
|
|
621
|
+
}
|
|
622
|
+
seen[String(ids[i])] = true;
|
|
623
|
+
pages.push(idcDocumentPageById(doc, ids[i], field));
|
|
624
|
+
}
|
|
625
|
+
/* nach documentOffset sortieren (Einfuegesortierung, ES3-sicher) */
|
|
626
|
+
for (i = 1; i < pages.length; i++) {
|
|
627
|
+
tmp = pages[i];
|
|
628
|
+
j = i - 1;
|
|
629
|
+
while (j >= 0 && Number(pages[j].documentOffset) > Number(tmp.documentOffset)) {
|
|
630
|
+
pages[j + 1] = pages[j];
|
|
631
|
+
j -= 1;
|
|
632
|
+
}
|
|
633
|
+
pages[j + 1] = tmp;
|
|
634
|
+
}
|
|
635
|
+
for (i = 1; i < pages.length; i++) {
|
|
636
|
+
if (Number(pages[i].documentOffset) !== Number(pages[i - 1].documentOffset) + 1) {
|
|
637
|
+
IDC.fail('invalid_argument', field + ' must be direct neighbours in the page order (' +
|
|
638
|
+
String(pages[i - 1].name) + ' and ' + String(pages[i].name) + ' are not).',
|
|
639
|
+
{ page_ids: ids });
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return pages;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function idcSnapshotSides(doc) {
|
|
646
|
+
var out = {};
|
|
647
|
+
var i, page;
|
|
648
|
+
for (i = 0; i < doc.pages.length; i++) {
|
|
649
|
+
page = doc.pages.item(i);
|
|
650
|
+
out[String(Number(page.id))] = idcPageSide(page);
|
|
651
|
+
}
|
|
652
|
+
return out;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function idcDescribeAllPages(doc) {
|
|
656
|
+
var out = [];
|
|
657
|
+
var i;
|
|
658
|
+
for (i = 0; i < doc.pages.length; i++) {
|
|
659
|
+
out.push(idcDescribePage(doc.pages.item(i)));
|
|
660
|
+
}
|
|
661
|
+
return out;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/*
|
|
665
|
+
* Sitzung 24 (Befund aus dem Praxiseinsatz, E-79/E-80): InDesign haelt hoechstens
|
|
666
|
+
* zehn Seiten auf einem Bogen. Ohne "Seiten neu anordnen" (oder mit einem
|
|
667
|
+
* Zielbogen, der selbst nicht neu angeordnet wird) landen die verschobenen
|
|
668
|
+
* Seiten auf dem Bogen der Bezugsseite – vorher pruefen, damit InDesign nicht
|
|
669
|
+
* mitten im Verschieben abbricht.
|
|
670
|
+
*/
|
|
671
|
+
var IDC_SPREAD_PAGE_LIMIT = 10;
|
|
672
|
+
|
|
673
|
+
function idcPredictTargetSpreadCount(doc, pages, ref, shuffleArg) {
|
|
674
|
+
var target = ref.parent;
|
|
675
|
+
var shuffle, island = false, joining = 0, i;
|
|
676
|
+
if (shuffleArg === true || shuffleArg === false) { shuffle = shuffleArg; }
|
|
677
|
+
else {
|
|
678
|
+
try { shuffle = doc.documentPreferences.allowPageShuffle === true; } catch (e0) { shuffle = true; }
|
|
679
|
+
}
|
|
680
|
+
try { island = target.allowPageShuffle === false; } catch (e1) { island = false; }
|
|
681
|
+
if (shuffle && !island) { return null; }
|
|
682
|
+
for (i = 0; i < pages.length; i++) {
|
|
683
|
+
if (IDC.spreadIndex(pages[i].parent) !== IDC.spreadIndex(target)) { joining += 1; }
|
|
684
|
+
}
|
|
685
|
+
return { spread_index: IDC.spreadIndex(target), count: target.pages.length + joining };
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* Bogenaufteilung als Text: [1] [2 3] [4 5 6] … (Seitennamen). */
|
|
689
|
+
function idcSpreadDivisionText(doc) {
|
|
690
|
+
var parts = [];
|
|
691
|
+
var i, j, spread, names;
|
|
692
|
+
for (i = 0; i < doc.spreads.length; i++) {
|
|
693
|
+
spread = doc.spreads.item(i);
|
|
694
|
+
names = [];
|
|
695
|
+
for (j = 0; j < spread.pages.length; j++) { names.push(String(spread.pages.item(j).name)); }
|
|
696
|
+
parts.push('[' + names.join(' ') + ']');
|
|
697
|
+
}
|
|
698
|
+
return parts.join(' ');
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/*
|
|
702
|
+
* keep_spreads: die Boegen zwischen alter und neuer Stelle wieder als Paare
|
|
703
|
+
* bilden – bei Doppelseiten linke und rechte Seite zusammen, beginnend wie
|
|
704
|
+
* die erste Seite des Dokuments (rechts: erste Seite allein), sonst eine
|
|
705
|
+
* Seite je Bogen. Gruppen, die schon genau einen Bogen bilden und deren
|
|
706
|
+
* Seiten links/rechts richtig stehen, bleiben; fuer die anderen entsteht ein
|
|
707
|
+
* neuer Bogen vor dem Bogen ihrer ersten Seite (ohne Neuanordnen).
|
|
708
|
+
*
|
|
709
|
+
* Nacharbeit 2 zu Sitzung 24 (Lauf S24 am 22.09.2026): ein Bogen ohne
|
|
710
|
+
* Neuanordnen behaelt seinen Bund. Der neue Bogen kommt mit zwei
|
|
711
|
+
* Platzhaltern (links | rechts); wer mit AT_END angehaengt wird, liegt
|
|
712
|
+
* rechts vom Bund, und nach dem Entfernen der Platzhalter standen alle
|
|
713
|
+
* Seiten auf RIGHT_HAND. Darum: die linke Seite der Gruppe an den Anfang
|
|
714
|
+
* (vor den linken Platzhalter), die rechte an das Ende, dann genau die
|
|
715
|
+
* Platzhalter entfernen. Danach wird jede Seite gegen die erwartete Seite
|
|
716
|
+
* geprueft (idcExpectedSide); Abweichungen meldet der Aufrufer als Warnung.
|
|
717
|
+
* Liefert { created, mismatched: [Seitennamen], failures: [Meldungen] }.
|
|
718
|
+
*/
|
|
719
|
+
function idcExpectedSide(offset, facing, firstRight) {
|
|
720
|
+
if (!facing) { return 'SINGLE_SIDED'; }
|
|
721
|
+
if ((offset % 2 === 0) === firstRight) { return 'RIGHT_HAND'; }
|
|
722
|
+
return 'LEFT_HAND';
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/*
|
|
726
|
+
* Nacharbeit 3 zu Sitzung 24 (Lauf S24 am 22.09.2026): ein Seitenverweis
|
|
727
|
+
* gilt in InDesign nur, solange die Seite auf dem Bogen liegt, auf dem er
|
|
728
|
+
* geholt wurde; nach move/spreads.add/remove auf einem anderen Bogen
|
|
729
|
+
* scheitert jeder Zugriff („Objekt existiert nicht mehr“, Zeile 3464 im
|
|
730
|
+
* Buendel: Name ueber einen alten Verweis gelesen). Darum wird jede Seite
|
|
731
|
+
* unmittelbar vor ihrer Verwendung per id neu geholt (idcFreshPage), die
|
|
732
|
+
* Platzhalter aller neuen Boegen werden erst am Ende entfernt, und ein
|
|
733
|
+
* Scheitern bei einer Gruppe haelt die uebrigen nicht auf.
|
|
734
|
+
*/
|
|
735
|
+
function idcFreshPage(doc, id) {
|
|
736
|
+
var found = IDC.pageById(doc, id);
|
|
737
|
+
if (found === null || found.is_parent_page) { return null; }
|
|
738
|
+
return found.page;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function idcRebuildSpreadPairs(doc, lo, hi, firstRight) {
|
|
742
|
+
var facing = false, groups = [], current = null, currentKey = null;
|
|
743
|
+
var i, j, g, spread, page, offset, key, created = 0, mismatched = [], failures = [], placeholderIds = [];
|
|
744
|
+
try { facing = doc.documentPreferences.facingPages === true; } catch (e0) { facing = false; }
|
|
745
|
+
for (i = lo; i <= hi; i++) {
|
|
746
|
+
spread = doc.spreads.item(i);
|
|
747
|
+
for (j = 0; j < spread.pages.length; j++) {
|
|
748
|
+
page = spread.pages.item(j);
|
|
749
|
+
offset = Number(page.documentOffset);
|
|
750
|
+
if (!facing) { key = offset; }
|
|
751
|
+
else if (firstRight) { key = Math.floor((offset + 1) / 2); }
|
|
752
|
+
else { key = Math.floor(offset / 2); }
|
|
753
|
+
if (current === null || key !== currentKey) {
|
|
754
|
+
current = [];
|
|
755
|
+
currentKey = key;
|
|
756
|
+
groups.push(current);
|
|
757
|
+
}
|
|
758
|
+
current.push({ id: Number(page.id), side: idcExpectedSide(offset, facing, firstRight) });
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
IDC.withShuffle(doc, false, function () {
|
|
762
|
+
var k, entry, first, whole, newSpread, newSpreadId, moved, target;
|
|
763
|
+
for (g = 0; g < groups.length; g++) {
|
|
764
|
+
try {
|
|
765
|
+
first = idcFreshPage(doc, groups[g][0].id);
|
|
766
|
+
if (first === null) { throw new Error('page ' + groups[g][0].id + ' not found'); }
|
|
767
|
+
spread = first.parent;
|
|
768
|
+
whole = spread.pages.length === groups[g].length;
|
|
769
|
+
for (k = 0; whole && k < groups[g].length; k++) {
|
|
770
|
+
page = spread.pages.item(k);
|
|
771
|
+
if (Number(page.id) !== groups[g][k].id) { whole = false; }
|
|
772
|
+
else if (idcPageSide(page) !== groups[g][k].side) { whole = false; }
|
|
773
|
+
}
|
|
774
|
+
if (whole) { continue; }
|
|
775
|
+
newSpread = doc.spreads.add(LocationOptions.BEFORE, spread);
|
|
776
|
+
newSpread.allowPageShuffle = false;
|
|
777
|
+
newSpreadId = Number(newSpread.id);
|
|
778
|
+
for (k = 0; k < newSpread.pages.length; k++) { placeholderIds.push(Number(newSpread.pages.item(k).id)); }
|
|
779
|
+
/* linke Seiten rueckwaerts an den Anfang (Reihenfolge bleibt), rechte der Reihe nach an das Ende */
|
|
780
|
+
moved = [];
|
|
781
|
+
for (k = groups[g].length - 1; k >= 0; k--) {
|
|
782
|
+
entry = groups[g][k];
|
|
783
|
+
if (entry.side === 'LEFT_HAND') { moved.push({ entry: entry, at: LocationOptions.AT_BEGINNING }); }
|
|
784
|
+
}
|
|
785
|
+
for (k = 0; k < groups[g].length; k++) {
|
|
786
|
+
entry = groups[g][k];
|
|
787
|
+
if (entry.side !== 'LEFT_HAND') { moved.push({ entry: entry, at: LocationOptions.AT_END }); }
|
|
788
|
+
}
|
|
789
|
+
for (k = 0; k < moved.length; k++) {
|
|
790
|
+
page = idcFreshPage(doc, moved[k].entry.id);
|
|
791
|
+
target = doc.spreads.itemByID(newSpreadId);
|
|
792
|
+
if (page === null) { throw new Error('page ' + moved[k].entry.id + ' not found'); }
|
|
793
|
+
page.move(moved[k].at, target);
|
|
794
|
+
}
|
|
795
|
+
created += 1;
|
|
796
|
+
} catch (eG) {
|
|
797
|
+
failures.push(String(eG.message ? eG.message : eG));
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
/* Platzhalter erst jetzt entfernen – jeden einzeln neu geholt */
|
|
801
|
+
for (k = 0; k < placeholderIds.length; k++) {
|
|
802
|
+
try {
|
|
803
|
+
page = idcFreshPage(doc, placeholderIds[k]);
|
|
804
|
+
if (page !== null) { page.remove(); }
|
|
805
|
+
} catch (eP) {
|
|
806
|
+
failures.push(String(eP.message ? eP.message : eP));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
for (g = 0; g < groups.length; g++) {
|
|
811
|
+
for (i = 0; i < groups[g].length; i++) {
|
|
812
|
+
page = idcFreshPage(doc, groups[g][i].id);
|
|
813
|
+
if (page !== null && idcPageSide(page) !== groups[g][i].side) {
|
|
814
|
+
mismatched.push(String(page.name) + ' (' + idcPageSide(page) + ', expected ' + groups[g][i].side + ')');
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return { created: created, mismatched: mismatched, failures: failures };
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
IDC.register('move_pages', { needs_document: true, read_only: false, ruler: 'page' },
|
|
822
|
+
function (ctx) {
|
|
823
|
+
var args = ctx.args;
|
|
824
|
+
var doc = ctx.doc;
|
|
825
|
+
var pages, ref, position, i, movedIds, namesBefore, namesAfter, sidesBefore, sidesAfter, flipped, counts, key, page;
|
|
826
|
+
var keepSpreads, predicted, neighbourIds, firstRight, touched, lo, hi, found, rebuilt, rebuiltInfo, overfull, refId, index;
|
|
827
|
+
|
|
828
|
+
pages = idcAdjacentPages(doc, args.page_ids, 'page_ids', 1, 48);
|
|
829
|
+
ref = idcDocumentPageById(doc, args.reference_page_id, 'reference_page_id');
|
|
830
|
+
for (i = 0; i < pages.length; i++) {
|
|
831
|
+
if (Number(pages[i].id) === Number(ref.id)) {
|
|
832
|
+
IDC.fail('invalid_argument', 'reference_page_id must lie outside the pages being moved.',
|
|
833
|
+
{ reference_page_id: args.reference_page_id });
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
position = args.position;
|
|
837
|
+
if (position !== 'before' && position !== 'after') {
|
|
838
|
+
IDC.fail('invalid_argument', 'position must be "before" or "after".', { position: position });
|
|
839
|
+
}
|
|
840
|
+
if (args.keep_spreads !== undefined && args.keep_spreads !== null && typeof args.keep_spreads !== 'boolean') {
|
|
841
|
+
IDC.fail('invalid_argument', 'keep_spreads must be true or false.', { keep_spreads: args.keep_spreads });
|
|
842
|
+
}
|
|
843
|
+
keepSpreads = args.keep_spreads === true;
|
|
844
|
+
|
|
845
|
+
/* 10-Seiten-Grenze je Bogen vor dem Verschieben (E-80). */
|
|
846
|
+
predicted = idcPredictTargetSpreadCount(doc, pages, ref, args.allow_page_shuffle);
|
|
847
|
+
if (predicted !== null && predicted.count > IDC_SPREAD_PAGE_LIMIT) {
|
|
848
|
+
IDC.fail('invalid_state',
|
|
849
|
+
'Moving ' + pages.length + ' page(s) next to page ' + String(ref.name) + ' would put ' + predicted.count +
|
|
850
|
+
' pages on one spread (Druckbogen); InDesign allows at most ' + IDC_SPREAD_PAGE_LIMIT + '. Nothing was moved. Move fewer pages at once, or pass allow_page_shuffle true.',
|
|
851
|
+
{ code: 'spread_page_limit', spread_index: predicted.spread_index, predicted_page_count: predicted.count, limit: IDC_SPREAD_PAGE_LIMIT });
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/* Fuer keep_spreads: Nachbarn des Blocks und Seite der ersten Dokumentseite vor dem Verschieben. */
|
|
855
|
+
neighbourIds = [Number(ref.id)];
|
|
856
|
+
if (Number(pages[0].documentOffset) > 0) { neighbourIds.push(Number(doc.pages.item(Number(pages[0].documentOffset) - 1).id)); }
|
|
857
|
+
if (Number(pages[pages.length - 1].documentOffset) < doc.pages.length - 1) {
|
|
858
|
+
neighbourIds.push(Number(doc.pages.item(Number(pages[pages.length - 1].documentOffset) + 1).id));
|
|
859
|
+
}
|
|
860
|
+
firstRight = idcPageSide(doc.pages.item(0)) !== 'LEFT_HAND';
|
|
861
|
+
|
|
862
|
+
movedIds = [];
|
|
863
|
+
namesBefore = [];
|
|
864
|
+
for (i = 0; i < pages.length; i++) {
|
|
865
|
+
movedIds.push(Number(pages[i].id));
|
|
866
|
+
namesBefore.push(String(pages[i].name));
|
|
867
|
+
}
|
|
868
|
+
sidesBefore = idcSnapshotSides(doc);
|
|
869
|
+
|
|
870
|
+
/* Verweise vor jedem Schritt per id neu holen (Nacharbeit 3 zu Sitzung 24, idcFreshPage). */
|
|
871
|
+
refId = Number(ref.id);
|
|
872
|
+
IDC.withShuffle(doc, args.allow_page_shuffle, function () {
|
|
873
|
+
var k;
|
|
874
|
+
try {
|
|
875
|
+
if (position === 'after') {
|
|
876
|
+
/* rueckwaerts: jede Seite direkt hinter die Referenz, Reihenfolge bleibt */
|
|
877
|
+
for (k = movedIds.length - 1; k >= 0; k--) { idcFreshPage(doc, movedIds[k]).move(LocationOptions.AFTER, idcFreshPage(doc, refId)); }
|
|
878
|
+
} else {
|
|
879
|
+
for (k = 0; k < movedIds.length; k++) { idcFreshPage(doc, movedIds[k]).move(LocationOptions.BEFORE, idcFreshPage(doc, refId)); }
|
|
880
|
+
}
|
|
881
|
+
} catch (e) {
|
|
882
|
+
IDC.fail('script_error', 'InDesign could not move the pages: ' + String(e.message ? e.message : e),
|
|
883
|
+
{ page_ids: movedIds });
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
rebuilt = 0;
|
|
888
|
+
if (keepSpreads) {
|
|
889
|
+
try {
|
|
890
|
+
touched = [];
|
|
891
|
+
for (i = 0; i < movedIds.length; i++) { touched.push(movedIds[i]); }
|
|
892
|
+
for (i = 0; i < neighbourIds.length; i++) { touched.push(neighbourIds[i]); }
|
|
893
|
+
lo = null;
|
|
894
|
+
hi = null;
|
|
895
|
+
for (i = 0; i < touched.length; i++) {
|
|
896
|
+
found = idcFreshPage(doc, touched[i]);
|
|
897
|
+
index = found === null ? null : IDC.spreadIndex(found.parent);
|
|
898
|
+
if (index === null) { continue; }
|
|
899
|
+
if (lo === null || index < lo) { lo = index; }
|
|
900
|
+
if (hi === null || index > hi) { hi = index; }
|
|
901
|
+
}
|
|
902
|
+
if (lo === null) { throw new Error('the moved pages were not found'); }
|
|
903
|
+
rebuiltInfo = idcRebuildSpreadPairs(doc, lo, hi, firstRight);
|
|
904
|
+
rebuilt = rebuiltInfo.created;
|
|
905
|
+
if (rebuiltInfo.failures.length > 0) {
|
|
906
|
+
ctx.warn('The pages were moved, but ' + rebuiltInfo.failures.length + ' step(s) of rebuilding the spreads as pairs failed: ' +
|
|
907
|
+
rebuiltInfo.failures.join('; ') + ' – repair with move_pages_to_new_spread.');
|
|
908
|
+
}
|
|
909
|
+
if (rebuiltInfo.mismatched.length > 0) {
|
|
910
|
+
ctx.warn('The spreads were rebuilt as pairs, but ' + rebuiltInfo.mismatched.length +
|
|
911
|
+
' page(s) are not on the expected side (left/right): ' + rebuiltInfo.mismatched.join(', ') +
|
|
912
|
+
'. Check the spreads in InDesign.');
|
|
913
|
+
}
|
|
914
|
+
} catch (eR) {
|
|
915
|
+
ctx.warn('The pages were moved, but the spreads could not be rebuilt as pairs: ' + String(eR.message ? eR.message : eR) +
|
|
916
|
+
' – repair with move_pages_to_new_spread.');
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
namesAfter = [];
|
|
921
|
+
for (i = 0; i < movedIds.length; i++) {
|
|
922
|
+
page = idcFreshPage(doc, movedIds[i]);
|
|
923
|
+
namesAfter.push(page === null ? '' : String(page.name));
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
sidesAfter = idcSnapshotSides(doc);
|
|
927
|
+
flipped = [];
|
|
928
|
+
for (key in sidesAfter) {
|
|
929
|
+
if (IDC.hasOwn(sidesAfter, key) && IDC.hasOwn(sidesBefore, key) && sidesAfter[key] !== sidesBefore[key]) {
|
|
930
|
+
page = IDC.pageById(doc, Number(key));
|
|
931
|
+
flipped.push(page === null ? key : String(page.page.name));
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
if (flipped.length > 0) {
|
|
935
|
+
ctx.warn('Page side changed (left/right swapped) for ' + flipped.length + ' page(s): ' + flipped.join(', ') +
|
|
936
|
+
'. Moving an odd number of pages flips every page in between.');
|
|
937
|
+
}
|
|
938
|
+
counts = IDC.spreadPageCounts(doc);
|
|
939
|
+
overfull = false;
|
|
940
|
+
for (i = 0; i < counts.length; i++) {
|
|
941
|
+
if (counts[i] > 2) {
|
|
942
|
+
overfull = true;
|
|
943
|
+
ctx.warn('Spread (Druckbogen) ' + i + ' now holds ' + counts[i] + ' pages.');
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (overfull) {
|
|
947
|
+
ctx.warn('New spread division (Bogenaufteilung): ' + idcSpreadDivisionText(doc) +
|
|
948
|
+
(keepSpreads ? '' : ' – pass keep_spreads true to rebuild the spreads as pairs, or repair with move_pages_to_new_spread.'));
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
ctx.changes = { moved_page_ids: movedIds };
|
|
952
|
+
return {
|
|
953
|
+
moved_page_ids: movedIds,
|
|
954
|
+
names_before: namesBefore,
|
|
955
|
+
names_after: namesAfter,
|
|
956
|
+
page_count: doc.pages.length,
|
|
957
|
+
spread_count: doc.spreads.length,
|
|
958
|
+
spread_page_counts: counts,
|
|
959
|
+
pages: idcDescribeAllPages(doc),
|
|
960
|
+
keep_spreads: keepSpreads,
|
|
961
|
+
spreads_rebuilt: rebuilt
|
|
962
|
+
};
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
/* ------------------------------------------------ move_pages_to_new_spread */
|
|
966
|
+
|
|
967
|
+
IDC.register('move_pages_to_new_spread', { needs_document: true, read_only: false, ruler: 'page' },
|
|
968
|
+
function (ctx) {
|
|
969
|
+
var args = ctx.args;
|
|
970
|
+
var doc = ctx.doc;
|
|
971
|
+
var pages, source, sourceIndex, i, movedIds, names, newSpread, placeholder, sourceCountBefore;
|
|
972
|
+
|
|
973
|
+
pages = idcAdjacentPages(doc, args.page_ids, 'page_ids', 1, 24);
|
|
974
|
+
source = pages[0].parent;
|
|
975
|
+
sourceIndex = IDC.spreadIndex(source);
|
|
976
|
+
for (i = 1; i < pages.length; i++) {
|
|
977
|
+
if (IDC.spreadIndex(pages[i].parent) !== sourceIndex) {
|
|
978
|
+
IDC.fail('invalid_argument', 'All page_ids must lie on the same spread (Druckbogen).',
|
|
979
|
+
{ page_ids: args.page_ids });
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
sourceCountBefore = source.pages.length;
|
|
983
|
+
if (pages.length >= sourceCountBefore) {
|
|
984
|
+
IDC.fail('invalid_argument',
|
|
985
|
+
'page_ids names every page of the spread; leave at least one page behind.',
|
|
986
|
+
{ page_ids: args.page_ids, spread_page_count: sourceCountBefore });
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
movedIds = [];
|
|
990
|
+
names = [];
|
|
991
|
+
for (i = 0; i < pages.length; i++) {
|
|
992
|
+
movedIds.push(Number(pages[i].id));
|
|
993
|
+
names.push(String(pages[i].name));
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
IDC.withShuffle(doc, false, function () {
|
|
997
|
+
var k;
|
|
998
|
+
try {
|
|
999
|
+
/*
|
|
1000
|
+
* Ein neuer Bogen kommt nicht leer: in einem Doppelseiten-Dokument
|
|
1001
|
+
* mit zwei Platzhalterseiten, sonst mit einer (am lebenden InDesign
|
|
1002
|
+
* 21.5 am 04.09.2026 gesehen). Erst die Seiten anhaengen, dann alle
|
|
1003
|
+
* Platzhalter vorn entfernen.
|
|
1004
|
+
*/
|
|
1005
|
+
newSpread = doc.spreads.add(LocationOptions.AFTER, source);
|
|
1006
|
+
newSpread.allowPageShuffle = false;
|
|
1007
|
+
placeholder = newSpread.pages.length;
|
|
1008
|
+
for (k = 0; k < pages.length; k++) {
|
|
1009
|
+
pages[k].move(LocationOptions.AT_END, newSpread);
|
|
1010
|
+
}
|
|
1011
|
+
while (placeholder > 0 && newSpread.pages.length > pages.length) {
|
|
1012
|
+
newSpread.pages.item(0).remove();
|
|
1013
|
+
placeholder -= 1;
|
|
1014
|
+
}
|
|
1015
|
+
} catch (e) {
|
|
1016
|
+
IDC.fail('script_error', 'InDesign could not build the new spread: ' + String(e.message ? e.message : e),
|
|
1017
|
+
{ page_ids: movedIds });
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
ctx.changes = { moved_page_ids: movedIds, created_spread_indexes: [IDC.spreadIndex(newSpread)] };
|
|
1022
|
+
return {
|
|
1023
|
+
moved_page_ids: movedIds,
|
|
1024
|
+
names: names,
|
|
1025
|
+
new_spread_index: IDC.spreadIndex(newSpread),
|
|
1026
|
+
new_spread_page_count: newSpread.pages.length,
|
|
1027
|
+
source_spread_index: sourceIndex,
|
|
1028
|
+
source_spread_page_count: source.pages.length,
|
|
1029
|
+
spread_count: doc.spreads.length,
|
|
1030
|
+
page_count: doc.pages.length
|
|
1031
|
+
};
|
|
1032
|
+
});
|