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,758 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* layoutbridge-spm – Befehlsinterpreter (bis 04.09.2026: indesign-connector-spm)
|
|
3
|
+
*
|
|
4
|
+
* Laeuft in InDesign. Nimmt einen Befehl {cmd, args} als JavaScript-Objekt,
|
|
5
|
+
* fuehrt ihn aus und liefert ein Ergebnisobjekt zurueck.
|
|
6
|
+
*
|
|
7
|
+
* Sprachumfang: gemeinsamer Nenner von ExtendScript (ES3) und UXP (ES6).
|
|
8
|
+
* Verboten und deshalb hier nicht benutzt: $-Objekt, #include, E4X, ScriptUI,
|
|
9
|
+
* UnitValue, globales document, JSON, Array.forEach/map/indexOf,
|
|
10
|
+
* Object.keys, String.trim, eckige Klammern auf DOM-Sammlungen,
|
|
11
|
+
* verschachtelte ?:-Ausdruecke ohne Klammern (ExtendScript klammert links).
|
|
12
|
+
*
|
|
13
|
+
* Diese Datei definiert nur den Kern. Die Handler stehen in commands/*.js und
|
|
14
|
+
* tragen sich ueber IDC.register() ein. Der Transport haengt Kern und Handler
|
|
15
|
+
* zu einem Skript zusammen; #include gibt es nicht.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
var IDC = (function () {
|
|
19
|
+
var api = {};
|
|
20
|
+
|
|
21
|
+
api.version = '0.1.0';
|
|
22
|
+
api.commands = {};
|
|
23
|
+
/* Der gerade laufende Befehlseintrag (name, read_only ...), sonst null. */
|
|
24
|
+
api.currentCommand = null;
|
|
25
|
+
|
|
26
|
+
/* ---------------------------------------------------------------- Fehler */
|
|
27
|
+
|
|
28
|
+
var KINDS = {
|
|
29
|
+
app_not_available: 1,
|
|
30
|
+
invalid_argument: 1,
|
|
31
|
+
not_found: 1,
|
|
32
|
+
invalid_state: 1,
|
|
33
|
+
io_error: 1,
|
|
34
|
+
timeout: 1,
|
|
35
|
+
script_error: 1
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function IdcError(kind, message, details) {
|
|
39
|
+
this.isIdcError = true;
|
|
40
|
+
this.kind = KINDS[kind] ? kind : 'script_error';
|
|
41
|
+
this.message = String(message);
|
|
42
|
+
this.details = details === undefined ? null : details;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
api.fail = function (kind, message, details) {
|
|
46
|
+
throw new IdcError(kind, message, details);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/* ------------------------------------------------------------- Werkzeuge */
|
|
50
|
+
|
|
51
|
+
function hasOwn(obj, key) {
|
|
52
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isArray(v) {
|
|
56
|
+
return Object.prototype.toString.call(v) === '[object Array]';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
api.hasOwn = hasOwn;
|
|
60
|
+
api.isArray = isArray;
|
|
61
|
+
|
|
62
|
+
/* Enumeratoren vergleichen: ExtendScript liefert Zahlen, UXP teils Text. */
|
|
63
|
+
function enumIs(value, expected) {
|
|
64
|
+
if (value === expected) { return true; }
|
|
65
|
+
var a = Number(value);
|
|
66
|
+
var b = Number(expected);
|
|
67
|
+
if (!isNaN(a) && !isNaN(b)) { return a === b; }
|
|
68
|
+
return String(value) === String(expected);
|
|
69
|
+
}
|
|
70
|
+
api.enumIs = enumIs;
|
|
71
|
+
|
|
72
|
+
/* Rundet Fliesskommarauschen weg (InDesign rechnet intern in Punkt). */
|
|
73
|
+
function round(value, digits) {
|
|
74
|
+
var d = digits === undefined ? 4 : digits;
|
|
75
|
+
var f = Math.pow(10, d);
|
|
76
|
+
var n = Number(value);
|
|
77
|
+
if (isNaN(n)) { return null; }
|
|
78
|
+
return Math.round(n * f) / f;
|
|
79
|
+
}
|
|
80
|
+
api.round = round;
|
|
81
|
+
|
|
82
|
+
/* --------------------------------------------------------- JSON-Ausgabe */
|
|
83
|
+
|
|
84
|
+
function pad4(hex) {
|
|
85
|
+
while (hex.length < 4) { hex = '0' + hex; }
|
|
86
|
+
return hex;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function quote(text) {
|
|
90
|
+
var out = '"';
|
|
91
|
+
var i, ch, code;
|
|
92
|
+
for (i = 0; i < text.length; i++) {
|
|
93
|
+
ch = text.charAt(i);
|
|
94
|
+
code = text.charCodeAt(i);
|
|
95
|
+
if (ch === '"') { out += '\\"'; }
|
|
96
|
+
else if (ch === '\\') { out += '\\\\'; }
|
|
97
|
+
else if (code === 8) { out += '\\b'; }
|
|
98
|
+
else if (code === 9) { out += '\\t'; }
|
|
99
|
+
else if (code === 10) { out += '\\n'; }
|
|
100
|
+
else if (code === 12) { out += '\\f'; }
|
|
101
|
+
else if (code === 13) { out += '\\r'; }
|
|
102
|
+
else if (code < 32 || code > 126) { out += '\\u' + pad4(code.toString(16)); }
|
|
103
|
+
else { out += ch; }
|
|
104
|
+
}
|
|
105
|
+
return out + '"';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* Eigener JSON-Schreiber: ES3 kennt kein JSON-Objekt. Alles ausserhalb von
|
|
110
|
+
* ASCII wird als \uXXXX geschrieben, damit die Ergebnisdatei reines ASCII
|
|
111
|
+
* ist und keine Zeichensatzfrage offen bleibt.
|
|
112
|
+
*/
|
|
113
|
+
function stringify(value) {
|
|
114
|
+
var t = typeof value;
|
|
115
|
+
var i, parts, keys, k;
|
|
116
|
+
if (value === null || value === undefined) { return 'null'; }
|
|
117
|
+
if (t === 'boolean') { return value ? 'true' : 'false'; }
|
|
118
|
+
if (t === 'number') { return isFinite(value) ? String(value) : 'null'; }
|
|
119
|
+
if (t === 'string') { return quote(value); }
|
|
120
|
+
if (isArray(value)) {
|
|
121
|
+
parts = [];
|
|
122
|
+
for (i = 0; i < value.length; i++) { parts.push(stringify(value[i])); }
|
|
123
|
+
return '[' + parts.join(',') + ']';
|
|
124
|
+
}
|
|
125
|
+
if (t === 'object') {
|
|
126
|
+
parts = [];
|
|
127
|
+
for (k in value) {
|
|
128
|
+
if (hasOwn(value, k) && typeof value[k] !== 'function') {
|
|
129
|
+
parts.push(quote(String(k)) + ':' + stringify(value[k]));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return '{' + parts.join(',') + '}';
|
|
133
|
+
}
|
|
134
|
+
return 'null';
|
|
135
|
+
}
|
|
136
|
+
api.stringify = stringify;
|
|
137
|
+
|
|
138
|
+
/* ------------------------------------------------------------ Handhabung */
|
|
139
|
+
|
|
140
|
+
/*
|
|
141
|
+
* options:
|
|
142
|
+
* needs_document true = ein Dokument muss aufloesbar sein
|
|
143
|
+
* read_only true = aendert nichts
|
|
144
|
+
* ruler 'page' (Vorgabe) | 'spread' | 'none'
|
|
145
|
+
*/
|
|
146
|
+
api.register = function (name, options, handler) {
|
|
147
|
+
var opt = options || {};
|
|
148
|
+
api.commands[name] = {
|
|
149
|
+
name: name,
|
|
150
|
+
needs_document: opt.needs_document === true,
|
|
151
|
+
read_only: opt.read_only === true,
|
|
152
|
+
ruler: opt.ruler || 'page',
|
|
153
|
+
handler: handler
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/* ------------------------------------------------- Anwendung und Dokument */
|
|
158
|
+
|
|
159
|
+
function appIsReady() {
|
|
160
|
+
try {
|
|
161
|
+
if (typeof app === 'undefined' || app === null) { return false; }
|
|
162
|
+
var probe = app.name;
|
|
163
|
+
return probe !== undefined;
|
|
164
|
+
} catch (e) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
api.documentById = function (id) {
|
|
170
|
+
var i, d;
|
|
171
|
+
for (i = 0; i < app.documents.length; i++) {
|
|
172
|
+
d = app.documents.item(i);
|
|
173
|
+
if (Number(d.id) === Number(id)) { return d; }
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
api.resolveDocument = function (args) {
|
|
179
|
+
var wanted = args && args.document_id !== undefined && args.document_id !== null
|
|
180
|
+
? args.document_id : null;
|
|
181
|
+
var doc;
|
|
182
|
+
if (wanted !== null) {
|
|
183
|
+
doc = api.documentById(wanted);
|
|
184
|
+
if (doc === null) {
|
|
185
|
+
api.fail('not_found', 'No open document with document_id ' + wanted + '.',
|
|
186
|
+
{ document_id: wanted });
|
|
187
|
+
}
|
|
188
|
+
return doc;
|
|
189
|
+
}
|
|
190
|
+
if (app.documents.length === 0) {
|
|
191
|
+
api.fail('not_found', 'No document is open. Open one first or pass document_id.', null);
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
return app.activeDocument;
|
|
195
|
+
} catch (e) {
|
|
196
|
+
return app.documents.item(0);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/* Seite (auch Musterseite) ueber ihre ID finden. */
|
|
201
|
+
api.pageById = function (doc, id) {
|
|
202
|
+
var i, j, spread, page;
|
|
203
|
+
for (i = 0; i < doc.pages.length; i++) {
|
|
204
|
+
page = doc.pages.item(i);
|
|
205
|
+
if (Number(page.id) === Number(id)) {
|
|
206
|
+
return { page: page, is_parent_page: false };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (i = 0; i < doc.masterSpreads.length; i++) {
|
|
210
|
+
spread = doc.masterSpreads.item(i);
|
|
211
|
+
for (j = 0; j < spread.pages.length; j++) {
|
|
212
|
+
page = spread.pages.item(j);
|
|
213
|
+
if (Number(page.id) === Number(id)) {
|
|
214
|
+
return { page: page, is_parent_page: true };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/* --------------------------------------------------- Masseinheiten-Klammer */
|
|
222
|
+
|
|
223
|
+
/*
|
|
224
|
+
* Fuehrt fn aus, waehrend die Skript-Masseinheit auf unit steht, und stellt
|
|
225
|
+
* sie danach zurueck. Fuer Punktwerte (Konturstaerke, Schriftgrad) genau um
|
|
226
|
+
* diese eine Zuweisung legen.
|
|
227
|
+
*/
|
|
228
|
+
api.withUnit = function (unit, fn) {
|
|
229
|
+
var prev = app.scriptPreferences.measurementUnit;
|
|
230
|
+
app.scriptPreferences.measurementUnit = unit;
|
|
231
|
+
try {
|
|
232
|
+
return fn();
|
|
233
|
+
} finally {
|
|
234
|
+
app.scriptPreferences.measurementUnit = prev;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
api.inPoints = function (fn) {
|
|
239
|
+
return api.withUnit(MeasurementUnits.POINTS, fn);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/*
|
|
243
|
+
* Konkretes Objekt zu einem Sammlungsverweis.
|
|
244
|
+
*
|
|
245
|
+
* ExtendScript liefert aus pageItems.item(i) nur einen allgemeinen
|
|
246
|
+
* "PageItem"-Verweis; erst getElements()[0] ist der TextFrame, das Rectangle
|
|
247
|
+
* usw. Ohne diesen Schritt kennt niemand die Art des Objekts. Am lebenden
|
|
248
|
+
* Dokument am 04.09.2026 beobachtet. Objekte ohne getElements() kommen
|
|
249
|
+
* unveraendert zurueck.
|
|
250
|
+
*/
|
|
251
|
+
api.concrete = function (item) {
|
|
252
|
+
var elements;
|
|
253
|
+
try {
|
|
254
|
+
if (item && item.getElements) {
|
|
255
|
+
elements = item.getElements();
|
|
256
|
+
if (elements && elements.length > 0) { return elements[0]; }
|
|
257
|
+
}
|
|
258
|
+
} catch (e) { /* dann eben der Verweis selbst */ }
|
|
259
|
+
return item;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/* ------------------------------------------------ Klassen und Objektarten */
|
|
263
|
+
|
|
264
|
+
/*
|
|
265
|
+
* Klassenname eines DOM-Objekts.
|
|
266
|
+
*
|
|
267
|
+
* ExtendScript liefert ueber constructor.name nichts Brauchbares; dort steht
|
|
268
|
+
* die Art in "[object TextFrame]". UXP kann es umgekehrt halten. Deshalb
|
|
269
|
+
* alle Wege, in dieser Reihenfolge; reflect.name ist der rein
|
|
270
|
+
* ExtendScript-seitige Rueckfall.
|
|
271
|
+
*/
|
|
272
|
+
api.className = function (item) {
|
|
273
|
+
var name = '';
|
|
274
|
+
var text, match;
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
if (item.constructor && item.constructor.name) { name = String(item.constructor.name); }
|
|
278
|
+
} catch (e) { name = ''; }
|
|
279
|
+
if (name !== '' && name !== 'Object') { return name; }
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
text = Object.prototype.toString.call(item);
|
|
283
|
+
match = text.match(/^\[object (.+)\]$/);
|
|
284
|
+
if (match) { return String(match[1]); }
|
|
285
|
+
} catch (e2) { /* naechster Versuch */ }
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
text = String(item);
|
|
289
|
+
match = text.match(/^\[object (.+)\]$/);
|
|
290
|
+
if (match) { return String(match[1]); }
|
|
291
|
+
} catch (e3) { /* naechster Versuch */ }
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
if (item.reflect && item.reflect.name) { return String(item.reflect.name); }
|
|
295
|
+
} catch (e4) { /* aufgeben */ }
|
|
296
|
+
|
|
297
|
+
return name;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/*
|
|
301
|
+
* Objektart nach Spezifikation: text|image|rectangle|line|group|other;
|
|
302
|
+
* seit A-8.9 (Sitzung 9) auch ellipse|polygon fuer leere Ovale und
|
|
303
|
+
* Polygone (mit Grafik bleiben sie image). Ein Rechteck mit Eckenoptionen
|
|
304
|
+
* bleibt rectangle.
|
|
305
|
+
*/
|
|
306
|
+
api.itemType = function (item) {
|
|
307
|
+
var className = api.className(item);
|
|
308
|
+
var hasGraphic = false;
|
|
309
|
+
|
|
310
|
+
if (className === 'TextFrame') { return 'text'; }
|
|
311
|
+
if (className === 'Group') { return 'group'; }
|
|
312
|
+
if (className === 'GraphicLine') { return 'line'; }
|
|
313
|
+
|
|
314
|
+
if (className === 'Rectangle' || className === 'Oval' || className === 'Polygon') {
|
|
315
|
+
try { hasGraphic = item.allGraphics.length > 0; } catch (e) { hasGraphic = false; }
|
|
316
|
+
if (hasGraphic) { return 'image'; }
|
|
317
|
+
if (className === 'Rectangle') { return 'rectangle'; }
|
|
318
|
+
if (className === 'Oval') { return 'ellipse'; }
|
|
319
|
+
return 'polygon';
|
|
320
|
+
}
|
|
321
|
+
return 'other';
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
api.isTextFrame = function (item) {
|
|
325
|
+
return api.className(item) === 'TextFrame';
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
/*
|
|
329
|
+
* Druckbogen (Spread oder MasterSpread), auf dem ein Objekt liegt. Der
|
|
330
|
+
* parent eines Objekts ist der Bogen, bei Gruppenmitgliedern die Gruppe,
|
|
331
|
+
* bei verankerten Objekten die Textstelle – deshalb nach oben laufen.
|
|
332
|
+
*/
|
|
333
|
+
api.spreadOfItem = function (item) {
|
|
334
|
+
var current = item;
|
|
335
|
+
var step, name;
|
|
336
|
+
for (step = 0; step < 12; step++) {
|
|
337
|
+
try {
|
|
338
|
+
name = api.className(current);
|
|
339
|
+
if (name === 'Spread' || name === 'MasterSpread') { return current; }
|
|
340
|
+
current = current.parent;
|
|
341
|
+
if (current === null || current === undefined) { return null; }
|
|
342
|
+
} catch (e) { return null; }
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
api.isOnParentSpread = function (item) {
|
|
348
|
+
var spread = api.spreadOfItem(item);
|
|
349
|
+
return spread !== null && api.className(spread) === 'MasterSpread';
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
function findInCollection(collection, wanted) {
|
|
353
|
+
var item;
|
|
354
|
+
try {
|
|
355
|
+
item = collection.itemByID(wanted);
|
|
356
|
+
if (item && item.isValid === true) { return api.concrete(item); }
|
|
357
|
+
} catch (e) { /* nicht hier */ }
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* allPageItems ist ein Array (kein DOM-Sammlungsobjekt), auch in Gruppen. */
|
|
362
|
+
function findInArray(array, wanted) {
|
|
363
|
+
var i;
|
|
364
|
+
try {
|
|
365
|
+
for (i = 0; i < array.length; i++) {
|
|
366
|
+
if (Number(array[i].id) === wanted) { return api.concrete(array[i]); }
|
|
367
|
+
}
|
|
368
|
+
} catch (e) { /* nicht hier */ }
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/*
|
|
373
|
+
* Objekt (Rahmen, Linie, Gruppe) ueber seine ID finden.
|
|
374
|
+
* where: 'any' (Vorgabe) | 'document' (nur Dokumentseiten) | 'parent'
|
|
375
|
+
* (nur Musterseiten). Liefert { item, on_parent } oder null.
|
|
376
|
+
*/
|
|
377
|
+
api.itemById = function (doc, id, where) {
|
|
378
|
+
var wanted = Number(id);
|
|
379
|
+
var scope = where || 'any';
|
|
380
|
+
var found = null;
|
|
381
|
+
var i, spread;
|
|
382
|
+
|
|
383
|
+
if (scope !== 'parent') {
|
|
384
|
+
found = findInCollection(doc.pageItems, wanted);
|
|
385
|
+
if (found === null) {
|
|
386
|
+
try { found = findInArray(doc.allPageItems, wanted); } catch (e) { found = null; }
|
|
387
|
+
}
|
|
388
|
+
if (found !== null) {
|
|
389
|
+
if (scope === 'document' && api.isOnParentSpread(found)) { return null; }
|
|
390
|
+
return { item: found, on_parent: api.isOnParentSpread(found) };
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (scope !== 'document') {
|
|
394
|
+
for (i = 0; i < doc.masterSpreads.length; i++) {
|
|
395
|
+
spread = doc.masterSpreads.item(i);
|
|
396
|
+
found = findInCollection(spread.pageItems, wanted);
|
|
397
|
+
if (found === null) {
|
|
398
|
+
try { found = findInArray(spread.allPageItems, wanted); } catch (e2) { found = null; }
|
|
399
|
+
}
|
|
400
|
+
if (found !== null) { return { item: found, on_parent: true }; }
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return null;
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
/* Absatzformat ueber den Namen, auch in Formatgruppen. */
|
|
407
|
+
api.paragraphStyleByName = function (doc, name) {
|
|
408
|
+
var style, all, i;
|
|
409
|
+
try {
|
|
410
|
+
style = doc.paragraphStyles.itemByName(name);
|
|
411
|
+
if (style && style.isValid === true) { return style; }
|
|
412
|
+
} catch (e) { /* weiter */ }
|
|
413
|
+
try {
|
|
414
|
+
all = doc.allParagraphStyles;
|
|
415
|
+
for (i = 0; i < all.length; i++) {
|
|
416
|
+
if (String(all[i].name) === String(name)) { return all[i]; }
|
|
417
|
+
}
|
|
418
|
+
} catch (e2) { /* weiter */ }
|
|
419
|
+
return null;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
/* ------------------------------------------------------ Seiten und Boegen */
|
|
423
|
+
|
|
424
|
+
api.spreadIndex = function (spread) {
|
|
425
|
+
try { return Number(spread.index); } catch (e) { return null; }
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
api.pageSide = function (page) {
|
|
429
|
+
var side;
|
|
430
|
+
try { side = page.side; } catch (e) { return 'SINGLE_SIDED'; }
|
|
431
|
+
if (enumIs(side, PageSideOptions.LEFT_HAND)) { return 'LEFT_HAND'; }
|
|
432
|
+
if (enumIs(side, PageSideOptions.RIGHT_HAND)) { return 'RIGHT_HAND'; }
|
|
433
|
+
return 'SINGLE_SIDED';
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/* Seitenzahl je Druckbogen des Dokuments. */
|
|
437
|
+
api.spreadPageCounts = function (doc) {
|
|
438
|
+
var counts = [];
|
|
439
|
+
var i;
|
|
440
|
+
for (i = 0; i < doc.spreads.length; i++) {
|
|
441
|
+
counts.push(doc.spreads.item(i).pages.length);
|
|
442
|
+
}
|
|
443
|
+
return counts;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
/*
|
|
447
|
+
* "Seiten neu anordnen" (allowPageShuffle) nur fuer die Dauer von fn
|
|
448
|
+
* umstellen. value null/undefined = unveraendert lassen.
|
|
449
|
+
*/
|
|
450
|
+
api.withShuffle = function (doc, value, fn) {
|
|
451
|
+
var prev = null;
|
|
452
|
+
var set = false;
|
|
453
|
+
if (value !== null && value !== undefined) {
|
|
454
|
+
prev = doc.documentPreferences.allowPageShuffle;
|
|
455
|
+
doc.documentPreferences.allowPageShuffle = value === true;
|
|
456
|
+
set = true;
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
return fn();
|
|
460
|
+
} finally {
|
|
461
|
+
if (set) { doc.documentPreferences.allowPageShuffle = prev; }
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
/*
|
|
466
|
+
* Lineal-Nullpunkt (Seite/Bogen) nur fuer die Dauer von fn umstellen –
|
|
467
|
+
* fuer coordinate_space "spread" innerhalb eines Aufrufs, der sonst auf
|
|
468
|
+
* "page" laeuft.
|
|
469
|
+
*/
|
|
470
|
+
api.withRulerOrigin = function (doc, origin, fn) {
|
|
471
|
+
var prev = doc.viewPreferences.rulerOrigin;
|
|
472
|
+
doc.viewPreferences.rulerOrigin = origin;
|
|
473
|
+
try {
|
|
474
|
+
return fn();
|
|
475
|
+
} finally {
|
|
476
|
+
doc.viewPreferences.rulerOrigin = prev;
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
/*
|
|
481
|
+
* Wo ist ein Objekt gelandet? parentPage ist die Seite, auf der InDesign
|
|
482
|
+
* das Objekt fuehrt (null = Montageflaeche). Liefert
|
|
483
|
+
* { page_id, page_name, on_pasteboard }.
|
|
484
|
+
*/
|
|
485
|
+
api.landing = function (item) {
|
|
486
|
+
var page = null;
|
|
487
|
+
try { page = item.parentPage; } catch (e) { page = null; }
|
|
488
|
+
var valid = false;
|
|
489
|
+
try { valid = page !== null && page !== undefined && page.isValid === true; } catch (e2) { valid = false; }
|
|
490
|
+
if (!valid) {
|
|
491
|
+
return { page_id: null, page_name: '', on_pasteboard: true };
|
|
492
|
+
}
|
|
493
|
+
return { page_id: Number(page.id), page_name: String(page.name), on_pasteboard: false };
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
/* Seitengroesse in mm ueber die Seitenbegrenzung (unter dem aktuellen Lineal). */
|
|
497
|
+
api.pageSize = function (page) {
|
|
498
|
+
var b = page.bounds;
|
|
499
|
+
return { width: round(b[3] - b[1]), height: round(b[2] - b[0]) };
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
/* Versatz einer Seite vom Anfang ihres Bogens in mm (Breiten der Seiten davor). */
|
|
503
|
+
api.spreadOffsetOfPage = function (page) {
|
|
504
|
+
var spread = null;
|
|
505
|
+
var i, other, offset = 0;
|
|
506
|
+
try { spread = page.parent; } catch (e) { return 0; }
|
|
507
|
+
if (spread === null || spread === undefined) { return 0; }
|
|
508
|
+
for (i = 0; i < spread.pages.length; i++) {
|
|
509
|
+
other = spread.pages.item(i);
|
|
510
|
+
if (Number(other.id) === Number(page.id)) { return offset; }
|
|
511
|
+
offset += api.pageSize(other).width;
|
|
512
|
+
}
|
|
513
|
+
return 0;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/*
|
|
517
|
+
* Begrenzung eines Objekts relativ zur linken oberen Ecke von page.
|
|
518
|
+
*
|
|
519
|
+
* InDesign liest geometricBounds unter Seiten-Nullpunkt relativ zur ersten
|
|
520
|
+
* Seite, die das Objekt beruehrt – ein Rahmen mit 3 mm Anschnitt auf der
|
|
521
|
+
* rechten Seite eines Bogens steht dann bei x = 207 der linken Seite (am
|
|
522
|
+
* lebenden InDesign 21.5 am 04.09.2026 gesehen). Deshalb unter Bogen-
|
|
523
|
+
* Nullpunkt lesen und selbst um den Seitenversatz verschieben. page null
|
|
524
|
+
* (Montageflaeche): Bogenkoordinaten.
|
|
525
|
+
*/
|
|
526
|
+
api.boundsOnPage = function (doc, item, page) {
|
|
527
|
+
var bounds = api.withRulerOrigin(doc, RulerOrigin.SPREAD_ORIGIN, function () {
|
|
528
|
+
return item.geometricBounds;
|
|
529
|
+
});
|
|
530
|
+
var dx = page ? api.spreadOffsetOfPage(page) : 0;
|
|
531
|
+
return {
|
|
532
|
+
x: round(bounds[1] - dx),
|
|
533
|
+
y: round(bounds[0]),
|
|
534
|
+
width: round(bounds[3] - bounds[1]),
|
|
535
|
+
height: round(bounds[2] - bounds[0])
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
/* Begrenzung relativ zur Seite, auf der InDesign das Objekt fuehrt. */
|
|
540
|
+
api.boundsOnOwnPage = function (doc, item) {
|
|
541
|
+
var page = null;
|
|
542
|
+
try { page = item.parentPage; } catch (e) { page = null; }
|
|
543
|
+
var valid = false;
|
|
544
|
+
try { valid = page !== null && page !== undefined && page.isValid === true; } catch (e2) { valid = false; }
|
|
545
|
+
return api.boundsOnPage(doc, item, valid ? page : null);
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
/* geometricBounds ist [y1, x1, y2, x2]. */
|
|
549
|
+
api.boundsToRect = function (bounds) {
|
|
550
|
+
return {
|
|
551
|
+
x: round(bounds[1]),
|
|
552
|
+
y: round(bounds[0]),
|
|
553
|
+
width: round(bounds[3] - bounds[1]),
|
|
554
|
+
height: round(bounds[2] - bounds[0])
|
|
555
|
+
};
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
/* ------------------------------------------------------------- Ausfuehrung */
|
|
559
|
+
|
|
560
|
+
function restore(state) {
|
|
561
|
+
var problems = [];
|
|
562
|
+
if (state.docRulerSet) {
|
|
563
|
+
try { state.doc.viewPreferences.rulerOrigin = state.prevRulerOrigin; }
|
|
564
|
+
catch (e1) { problems.push('ruler origin: ' + String(e1)); }
|
|
565
|
+
}
|
|
566
|
+
if (state.zeroPointSet) {
|
|
567
|
+
try { state.doc.zeroPoint = state.prevZeroPoint; }
|
|
568
|
+
catch (e1b) { problems.push('zero point: ' + String(e1b)); }
|
|
569
|
+
}
|
|
570
|
+
if (state.unitSet) {
|
|
571
|
+
try { app.scriptPreferences.measurementUnit = state.prevUnit; }
|
|
572
|
+
catch (e2) { problems.push('measurement unit: ' + String(e2)); }
|
|
573
|
+
}
|
|
574
|
+
if (state.interactionSet) {
|
|
575
|
+
try { app.scriptPreferences.userInteractionLevel = state.prevInteraction; }
|
|
576
|
+
catch (e3) { problems.push('user interaction level: ' + String(e3)); }
|
|
577
|
+
}
|
|
578
|
+
return problems;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function toError(e) {
|
|
582
|
+
if (e && e.isIdcError) {
|
|
583
|
+
return { kind: e.kind, message: e.message, details: e.details };
|
|
584
|
+
}
|
|
585
|
+
var msg;
|
|
586
|
+
try { msg = String(e && e.message ? e.message : e); } catch (x) { msg = 'unknown error'; }
|
|
587
|
+
var details = null;
|
|
588
|
+
try {
|
|
589
|
+
if (e && e.line !== undefined) {
|
|
590
|
+
details = { line: e.line, file: e.fileName ? String(e.fileName) : null };
|
|
591
|
+
}
|
|
592
|
+
} catch (y) { details = null; }
|
|
593
|
+
return { kind: 'script_error', message: msg, details: details };
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
api.execute = function (command) {
|
|
597
|
+
var state = {
|
|
598
|
+
doc: null,
|
|
599
|
+
unitSet: false,
|
|
600
|
+
interactionSet: false,
|
|
601
|
+
docRulerSet: false,
|
|
602
|
+
zeroPointSet: false,
|
|
603
|
+
prevZeroPoint: null,
|
|
604
|
+
prevUnit: null,
|
|
605
|
+
prevInteraction: null,
|
|
606
|
+
prevRulerOrigin: null
|
|
607
|
+
};
|
|
608
|
+
var warnings = [];
|
|
609
|
+
var result;
|
|
610
|
+
|
|
611
|
+
try {
|
|
612
|
+
if (!command || typeof command.cmd !== 'string') {
|
|
613
|
+
api.fail('invalid_argument', 'Command must be an object with a "cmd" string.', null);
|
|
614
|
+
}
|
|
615
|
+
if (!hasOwn(api.commands, command.cmd)) {
|
|
616
|
+
api.fail('not_found', 'Unknown command: ' + command.cmd, { cmd: command.cmd });
|
|
617
|
+
}
|
|
618
|
+
if (!appIsReady()) {
|
|
619
|
+
api.fail('app_not_available', 'InDesign is not available or not ready.', null);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
var entry = api.commands[command.cmd];
|
|
623
|
+
var args = command.args || {};
|
|
624
|
+
|
|
625
|
+
state.prevUnit = app.scriptPreferences.measurementUnit;
|
|
626
|
+
app.scriptPreferences.measurementUnit = MeasurementUnits.MILLIMETERS;
|
|
627
|
+
state.unitSet = true;
|
|
628
|
+
|
|
629
|
+
state.prevInteraction = app.scriptPreferences.userInteractionLevel;
|
|
630
|
+
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
|
|
631
|
+
state.interactionSet = true;
|
|
632
|
+
|
|
633
|
+
var doc = null;
|
|
634
|
+
if (entry.needs_document) {
|
|
635
|
+
doc = api.resolveDocument(args);
|
|
636
|
+
state.doc = doc;
|
|
637
|
+
if (entry.ruler !== 'none') {
|
|
638
|
+
state.prevRulerOrigin = doc.viewPreferences.rulerOrigin;
|
|
639
|
+
doc.viewPreferences.rulerOrigin = (entry.ruler === 'spread')
|
|
640
|
+
? RulerOrigin.SPREAD_ORIGIN
|
|
641
|
+
: RulerOrigin.PAGE_ORIGIN;
|
|
642
|
+
state.docRulerSet = true;
|
|
643
|
+
/*
|
|
644
|
+
* Der Nullpunkt kann vom Anwender verschoben sein; dann waeren alle
|
|
645
|
+
* Koordinaten um diesen Versatz falsch. Fuer die Dauer des Aufrufs
|
|
646
|
+
* auf [0, 0] und danach zurueck.
|
|
647
|
+
*/
|
|
648
|
+
try {
|
|
649
|
+
state.prevZeroPoint = doc.zeroPoint;
|
|
650
|
+
if (state.prevZeroPoint && (Number(state.prevZeroPoint[0]) !== 0 || Number(state.prevZeroPoint[1]) !== 0)) {
|
|
651
|
+
doc.zeroPoint = [0, 0];
|
|
652
|
+
state.zeroPointSet = true;
|
|
653
|
+
}
|
|
654
|
+
} catch (ez) { state.zeroPointSet = false; }
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
var ctx = {
|
|
659
|
+
args: args,
|
|
660
|
+
doc: doc,
|
|
661
|
+
warnings: warnings,
|
|
662
|
+
changes: null,
|
|
663
|
+
warn: function (text) { warnings.push(String(text)); },
|
|
664
|
+
idc: api
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
/* Fuer Helfer, die wissen muessen, ob der laufende Befehl schreibt (Ebenensperre). */
|
|
668
|
+
api.currentCommand = entry;
|
|
669
|
+
var data = entry.handler(ctx);
|
|
670
|
+
if (data === null || data === undefined) { data = {}; }
|
|
671
|
+
|
|
672
|
+
result = { ok: true, warnings: warnings };
|
|
673
|
+
var key;
|
|
674
|
+
for (key in data) {
|
|
675
|
+
if (hasOwn(data, key)) { result[key] = data[key]; }
|
|
676
|
+
}
|
|
677
|
+
if (ctx.changes !== null) { result.changes = ctx.changes; }
|
|
678
|
+
if (doc !== null && result.document_id === undefined) {
|
|
679
|
+
try { result.document_id = Number(doc.id); } catch (eid) { /* egal */ }
|
|
680
|
+
}
|
|
681
|
+
} catch (err) {
|
|
682
|
+
result = { ok: false, warnings: warnings, error: toError(err) };
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
api.currentCommand = null;
|
|
686
|
+
var restoreProblems = restore(state);
|
|
687
|
+
var r;
|
|
688
|
+
for (r = 0; r < restoreProblems.length; r++) {
|
|
689
|
+
warnings.push('Could not restore setting – ' + restoreProblems[r]);
|
|
690
|
+
}
|
|
691
|
+
result.warnings = warnings;
|
|
692
|
+
return result;
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
api.run = function (command) {
|
|
696
|
+
var out;
|
|
697
|
+
try {
|
|
698
|
+
out = api.execute(command);
|
|
699
|
+
} catch (e) {
|
|
700
|
+
out = { ok: false, warnings: [], error: toError(e) };
|
|
701
|
+
}
|
|
702
|
+
return stringify(out);
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
return api;
|
|
706
|
+
})();
|
|
707
|
+
|
|
708
|
+
/*
|
|
709
|
+
* Kompatibilitaetsschicht fuer die bekannten Unterschiede zwischen
|
|
710
|
+
* ExtendScript und UXP. Bewusst klein gehalten und an einer Stelle gebuendelt.
|
|
711
|
+
*/
|
|
712
|
+
|
|
713
|
+
/* Dateiobjekt zu einem Pfad. ExtendScript kennt File, UXP nicht. */
|
|
714
|
+
IDC.fileFor = function (path) {
|
|
715
|
+
if (typeof File !== 'undefined' && File !== null) {
|
|
716
|
+
return new File(String(path));
|
|
717
|
+
}
|
|
718
|
+
IDC.fail('invalid_state',
|
|
719
|
+
'This runtime has no ExtendScript File object; the UXP transport must hand over file entries.',
|
|
720
|
+
{ path: String(path) });
|
|
721
|
+
return null;
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
/* Ordnerobjekt zu einem Pfad. ExtendScript kennt Folder, UXP nicht. */
|
|
725
|
+
IDC.folderFor = function (path) {
|
|
726
|
+
if (typeof Folder !== 'undefined' && Folder !== null) {
|
|
727
|
+
return new Folder(String(path));
|
|
728
|
+
}
|
|
729
|
+
IDC.fail('invalid_state',
|
|
730
|
+
'This runtime has no ExtendScript Folder object; the UXP transport must hand over folder entries.',
|
|
731
|
+
{ path: String(path) });
|
|
732
|
+
return null;
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
/* Betriebssystempfad eines Dateiobjekts, leer wenn nicht ermittelbar. */
|
|
736
|
+
IDC.fsPath = function (fileObject) {
|
|
737
|
+
try {
|
|
738
|
+
if (fileObject && fileObject.fsName) { return String(fileObject.fsName); }
|
|
739
|
+
} catch (e) { /* faellt auf leer zurueck */ }
|
|
740
|
+
return '';
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
/* Groesse einer Datei in Bytes, null wenn nicht ermittelbar. */
|
|
744
|
+
IDC.fileSize = function (fileObject) {
|
|
745
|
+
try {
|
|
746
|
+
if (fileObject && fileObject.exists && fileObject.length !== undefined) {
|
|
747
|
+
return Number(fileObject.length);
|
|
748
|
+
}
|
|
749
|
+
} catch (e) { /* null */ }
|
|
750
|
+
return null;
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
/* Gibt es den Ordner, in dem die Datei liegen soll? */
|
|
754
|
+
IDC.fileParentExists = function (fileObject) {
|
|
755
|
+
try {
|
|
756
|
+
return fileObject && fileObject.parent && fileObject.parent.exists === true;
|
|
757
|
+
} catch (e) { return false; }
|
|
758
|
+
};
|