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.
Files changed (79) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/LICENSE +26 -0
  3. package/README.de.md +318 -0
  4. package/README.md +304 -4
  5. package/THIRD-PARTY-NOTICES.md +2878 -0
  6. package/dist/src/mcp/config.js +125 -0
  7. package/dist/src/mcp/dispatch.js +123 -0
  8. package/dist/src/mcp/license/free-tools.js +16 -0
  9. package/dist/src/mcp/license/gate.js +209 -0
  10. package/dist/src/mcp/license/index.js +96 -0
  11. package/dist/src/mcp/license/instance-name.js +56 -0
  12. package/dist/src/mcp/license/key-source.js +58 -0
  13. package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
  14. package/dist/src/mcp/license/placeholder-verifier.js +10 -0
  15. package/dist/src/mcp/license/polar-ids.js +63 -0
  16. package/dist/src/mcp/license/polar-verifier.js +265 -0
  17. package/dist/src/mcp/license/product-ids.js +72 -0
  18. package/dist/src/mcp/license/state-store.js +91 -0
  19. package/dist/src/mcp/license/types.js +11 -0
  20. package/dist/src/mcp/logging.js +270 -0
  21. package/dist/src/mcp/product-info.js +48 -0
  22. package/dist/src/mcp/server.js +250 -0
  23. package/dist/src/mcp/setup.js +608 -0
  24. package/dist/src/mcp/start.cjs +95 -0
  25. package/dist/src/mcp/tool-definition.js +2 -0
  26. package/dist/src/mcp/tools.js +2004 -0
  27. package/dist/src/mcp/typography-rules.js +70 -0
  28. package/dist/src/transport/com.js +250 -0
  29. package/dist/src/transport/osascript.js +171 -0
  30. package/dist/src/transport/queue.js +19 -0
  31. package/dist/src/transport/script-bundle.js +92 -0
  32. package/dist/src/transport/transport.js +30 -0
  33. package/indesign/commands/10-app.js +6 -0
  34. package/indesign/commands/20-documents.js +513 -0
  35. package/indesign/commands/22-document-setup.js +118 -0
  36. package/indesign/commands/23-save-package.js +255 -0
  37. package/indesign/commands/25-export.js +379 -0
  38. package/indesign/commands/26-export-image.js +197 -0
  39. package/indesign/commands/27-textdefaults.js +151 -0
  40. package/indesign/commands/28-selection.js +174 -0
  41. package/indesign/commands/30-pages.js +1032 -0
  42. package/indesign/commands/31-view.js +210 -0
  43. package/indesign/commands/32-sections.js +203 -0
  44. package/indesign/commands/35-parents.js +428 -0
  45. package/indesign/commands/37-layout.js +760 -0
  46. package/indesign/commands/40-frames.js +836 -0
  47. package/indesign/commands/41-shapes.js +505 -0
  48. package/indesign/commands/42-groups.js +117 -0
  49. package/indesign/commands/43-layers.js +291 -0
  50. package/indesign/commands/44-effects.js +274 -0
  51. package/indesign/commands/45-frame-options.js +247 -0
  52. package/indesign/commands/47-anchored.js +287 -0
  53. package/indesign/commands/50-modify.js +781 -0
  54. package/indesign/commands/52-align.js +139 -0
  55. package/indesign/commands/55-fit.js +106 -0
  56. package/indesign/commands/60-images.js +771 -0
  57. package/indesign/commands/62-embed.js +120 -0
  58. package/indesign/commands/69-placeholders.js +127 -0
  59. package/indesign/commands/70-text.js +567 -0
  60. package/indesign/commands/71-outlines.js +92 -0
  61. package/indesign/commands/72-place-text.js +366 -0
  62. package/indesign/commands/74-stories.js +116 -0
  63. package/indesign/commands/75-tables.js +676 -0
  64. package/indesign/commands/76-text-variables.js +260 -0
  65. package/indesign/commands/77-story-options.js +86 -0
  66. package/indesign/commands/78-frame-lines.js +304 -0
  67. package/indesign/commands/79-check-typography.js +303 -0
  68. package/indesign/commands/80-styles.js +1620 -0
  69. package/indesign/commands/81-composition.js +355 -0
  70. package/indesign/commands/82-object-styles.js +263 -0
  71. package/indesign/commands/83-indents-tabs.js +237 -0
  72. package/indesign/commands/84-import-styles.js +142 -0
  73. package/indesign/commands/85-swatches.js +112 -0
  74. package/indesign/commands/86-gradients.js +185 -0
  75. package/indesign/commands/87-replace-swatch.js +180 -0
  76. package/indesign/commands/88-replace-font.js +217 -0
  77. package/indesign/interpreter.js +758 -0
  78. package/package.json +55 -6
  79. package/rules/typography-rules.json +31 -0
@@ -0,0 +1,505 @@
1
+ /*
2
+ * Auftrag A-8 (Sitzung 9) – Gestaltungswerkzeuge, Teil Formen und Ecken:
3
+ * create_ellipse (A-8.1), create_polygon (A-8.2), set_corner_options
4
+ * (A-8.3) und die gemeinsamen Helfer fuer Flaeche, Kontur, Farbton und
5
+ * Verlaufswinkel (A-8.5), die auch create_rectangle, create_line und
6
+ * set_frame_appearance benutzen.
7
+ *
8
+ * Adobe-DOM (Auftrag, geprueft 05.09.2026): Ovals.add(layer, at,
9
+ * reference, withProperties); Polygons.add(layer, numberOfSides,
10
+ * insetPercentage, at, reference, withProperties); Path.entirePath;
11
+ * fillTint/strokeTint (-1 = kein Ton); gradientFillAngle,
12
+ * gradientStrokeAngle, gradientFillStart, gradientFillLength;
13
+ * topLeftCornerOption ... bottomRightCornerRadius. Die Werte von
14
+ * CornerOptions sind nicht belegbar (404) – live bestaetigt (Journal).
15
+ */
16
+
17
+ /* ------------------------------------------------------- Eckenoptionen */
18
+
19
+ var IDC_CORNER_TYPES = [
20
+ ['none', 'NONE'],
21
+ ['rounded', 'ROUNDED_CORNER'],
22
+ ['inverse_rounded', 'INVERSE_ROUNDED_CORNER'],
23
+ ['bevel', 'BEVEL_CORNER'],
24
+ ['inset', 'INSET_CORNER'],
25
+ ['fancy', 'FANCY_CORNER']
26
+ ];
27
+
28
+ var IDC_CORNER_NAMES = ['top_left', 'top_right', 'bottom_left', 'bottom_right'];
29
+ var IDC_CORNER_PROPS = {
30
+ top_left: ['topLeftCornerOption', 'topLeftCornerRadius'],
31
+ top_right: ['topRightCornerOption', 'topRightCornerRadius'],
32
+ bottom_left: ['bottomLeftCornerOption', 'bottomLeftCornerRadius'],
33
+ bottom_right: ['bottomRightCornerOption', 'bottomRightCornerRadius']
34
+ };
35
+
36
+ function idcCornerTypeFor(name, field) {
37
+ var i;
38
+ if (typeof CornerOptions === 'undefined') {
39
+ IDC.fail('invalid_state', 'This runtime has no CornerOptions enumeration.', null);
40
+ }
41
+ for (i = 0; i < IDC_CORNER_TYPES.length; i++) {
42
+ if (IDC_CORNER_TYPES[i][0] === name) {
43
+ if (CornerOptions[IDC_CORNER_TYPES[i][1]] === undefined) {
44
+ IDC.fail('invalid_state', 'This InDesign version has no corner type "' + name + '" (CornerOptions.' + IDC_CORNER_TYPES[i][1] + ').', { value: name });
45
+ }
46
+ return CornerOptions[IDC_CORNER_TYPES[i][1]];
47
+ }
48
+ }
49
+ IDC.fail('invalid_argument', field + ' must be one of: none, rounded, inverse_rounded, bevel, inset, fancy.', { value: name });
50
+ return null;
51
+ }
52
+
53
+ function idcCornerTypeName(value) {
54
+ var i;
55
+ if (value === null || value === undefined || typeof CornerOptions === 'undefined') { return null; }
56
+ for (i = 0; i < IDC_CORNER_TYPES.length; i++) {
57
+ try {
58
+ if (CornerOptions[IDC_CORNER_TYPES[i][1]] !== undefined && IDC.enumIs(value, CornerOptions[IDC_CORNER_TYPES[i][1]])) { return IDC_CORNER_TYPES[i][0]; }
59
+ } catch (e) { /* weiter */ }
60
+ }
61
+ return String(value);
62
+ }
63
+
64
+ /* Ecken eines Objekts lesen: {top_left {type, radius_mm}, ...} oder null (Linien, Objekte ohne Ecken). */
65
+ function idcReadCorners(item) {
66
+ var out = {}, i, name, props, type, radius, any = false;
67
+ for (i = 0; i < IDC_CORNER_NAMES.length; i++) {
68
+ name = IDC_CORNER_NAMES[i];
69
+ props = IDC_CORNER_PROPS[name];
70
+ type = null;
71
+ radius = null;
72
+ try { type = idcCornerTypeName(item[props[0]]); any = true; } catch (e0) { type = null; }
73
+ try { radius = IDC.round(Number(item[props[1]])); } catch (e1) { radius = null; }
74
+ out[name] = { type: type, radius_mm: radius };
75
+ }
76
+ if (!any) { return null; }
77
+ out.uniform = out.top_left.type === out.top_right.type && out.top_left.type === out.bottom_left.type && out.top_left.type === out.bottom_right.type
78
+ && out.top_left.radius_mm === out.top_right.radius_mm && out.top_left.radius_mm === out.bottom_left.radius_mm && out.top_left.radius_mm === out.bottom_right.radius_mm;
79
+ return out;
80
+ }
81
+
82
+ /* Eine Ecke setzen; radius null = unveraendert. */
83
+ function idcSetCorner(item, name, typeName, radius) {
84
+ var props = IDC_CORNER_PROPS[name];
85
+ if (typeName !== null) { item[props[0]] = idcCornerTypeFor(typeName, 'corner type'); }
86
+ if (radius !== null) { item[props[1]] = radius; }
87
+ }
88
+
89
+ function idcRadiusArg(value, field) {
90
+ if (value === undefined || value === null) { return null; }
91
+ if (typeof value !== 'number' || !isFinite(value) || value < 0) {
92
+ IDC.fail('invalid_argument', field + ' must be a number of 0 or more (mm).', { value: value });
93
+ }
94
+ return value;
95
+ }
96
+
97
+ /* Gemeinsam fuer create_rectangle (corner_type + corner_radius_mm) und set_corner_options. */
98
+ function idcApplyCornerArgs(item, args) {
99
+ var applied = false, i, name, spec, type, radius;
100
+ var cornerType = args.corner_type === undefined || args.corner_type === null ? null : String(args.corner_type);
101
+ var uniformRadius = idcRadiusArg(args.radius_mm !== undefined ? args.radius_mm : args.corner_radius_mm, args.radius_mm !== undefined ? 'radius_mm' : 'corner_radius_mm');
102
+ if (cornerType === null && uniformRadius !== null && uniformRadius > 0) { cornerType = 'rounded'; }
103
+ if (cornerType !== null || uniformRadius !== null) {
104
+ for (i = 0; i < IDC_CORNER_NAMES.length; i++) { idcSetCorner(item, IDC_CORNER_NAMES[i], cornerType, uniformRadius); }
105
+ applied = true;
106
+ }
107
+ if (args.corners !== undefined && args.corners !== null) {
108
+ if (typeof args.corners !== 'object' || IDC.isArray(args.corners)) {
109
+ IDC.fail('invalid_argument', 'corners must be an object with top_left, top_right, bottom_left, bottom_right.', { value: args.corners });
110
+ }
111
+ for (name in args.corners) {
112
+ if (!IDC.hasOwn(args.corners, name)) { continue; }
113
+ if (!IDC.hasOwn(IDC_CORNER_PROPS, name)) {
114
+ IDC.fail('invalid_argument', 'corners.' + name + ' is not a corner; use top_left, top_right, bottom_left, bottom_right.', { value: name });
115
+ }
116
+ spec = args.corners[name];
117
+ if (spec === null || typeof spec !== 'object') {
118
+ IDC.fail('invalid_argument', 'corners.' + name + ' must be {type, radius_mm}.', { value: spec });
119
+ }
120
+ type = spec.type === undefined || spec.type === null ? null : String(spec.type);
121
+ radius = idcRadiusArg(spec.radius_mm, 'corners.' + name + '.radius_mm');
122
+ if (type === null && radius === null) { continue; }
123
+ idcSetCorner(item, name, type, radius);
124
+ applied = true;
125
+ }
126
+ }
127
+ return applied;
128
+ }
129
+
130
+ /* -------------------------------------------- Flaeche, Kontur, Farbton */
131
+
132
+ function idcTintArg(value, field) {
133
+ if (value === undefined || value === null) { return null; }
134
+ if (typeof value !== 'number' || !isFinite(value) || value < 0 || value > 100) {
135
+ IDC.fail('invalid_argument', field + ' must be a number between 0 and 100 (percent).', { value: value });
136
+ }
137
+ return value;
138
+ }
139
+
140
+ function idcAngleArg(value, field) {
141
+ if (value === undefined || value === null) { return null; }
142
+ if (typeof value !== 'number' || !isFinite(value) || value < -180 || value > 180) {
143
+ IDC.fail('invalid_argument', field + ' must be a number between -180 and 180 (degrees).', { value: value });
144
+ }
145
+ return value;
146
+ }
147
+
148
+ /* Art eines Farbfelds am Objekt: none|color|tint|gradient|mixed_ink. */
149
+ function idcSwatchKind(swatch) {
150
+ var name = '', cls = '';
151
+ if (swatch === null || swatch === undefined) { return null; }
152
+ try { name = String(swatch.name); } catch (e0) { name = ''; }
153
+ if (name === 'None' || name === '[None]') { return 'none'; }
154
+ cls = IDC.className(swatch);
155
+ if (cls === 'Gradient') { return 'gradient'; }
156
+ if (cls === 'Tint') { return 'tint'; }
157
+ if (cls === 'MixedInk') { return 'mixed_ink'; }
158
+ return 'color';
159
+ }
160
+
161
+ /* Kontur und Flaeche mit Art, Farbton und Verlaufswinkel (A-8.5); ergaenzt idcAppearance. */
162
+ function idcAppearanceFull(item) {
163
+ var out = idcAppearance(item);
164
+ var tint, angle, start, length;
165
+ try { out.fill_kind = idcSwatchKind(item.fillColor); } catch (e0) { out.fill_kind = null; }
166
+ try { out.stroke_kind = idcSwatchKind(item.strokeColor); } catch (e1) { out.stroke_kind = null; }
167
+ out.fill_tint_percent = null;
168
+ out.stroke_tint_percent = null;
169
+ out.gradient_fill_angle_deg = null;
170
+ out.gradient_stroke_angle_deg = null;
171
+ out.gradient_fill_start_mm = null;
172
+ out.gradient_fill_length_mm = null;
173
+ /* InDesign meldet -1, wenn kein Ton gesetzt ist – als null zurueckgeben (Auftrag A-8.5). */
174
+ try { tint = Number(item.fillTint); if (isFinite(tint) && tint >= 0) { out.fill_tint_percent = IDC.round(tint, 3); } } catch (e2) { /* null */ }
175
+ try { tint = Number(item.strokeTint); if (isFinite(tint) && tint >= 0) { out.stroke_tint_percent = IDC.round(tint, 3); } } catch (e3) { /* null */ }
176
+ if (out.fill_kind === 'gradient') {
177
+ try { angle = Number(item.gradientFillAngle); if (isFinite(angle)) { out.gradient_fill_angle_deg = IDC.round(angle, 3); } } catch (e4) { /* null */ }
178
+ try {
179
+ start = item.gradientFillStart;
180
+ if (start && start.length >= 2) { out.gradient_fill_start_mm = { x: IDC.round(Number(start[0])), y: IDC.round(Number(start[1])) }; }
181
+ } catch (e5) { /* null */ }
182
+ try { length = Number(item.gradientFillLength); if (isFinite(length)) { out.gradient_fill_length_mm = IDC.round(length); } } catch (e6) { /* null */ }
183
+ }
184
+ if (out.stroke_kind === 'gradient') {
185
+ try { angle = Number(item.gradientStrokeAngle); if (isFinite(angle)) { out.gradient_stroke_angle_deg = IDC.round(angle, 3); } } catch (e7) { /* null */ }
186
+ }
187
+ return out;
188
+ }
189
+
190
+ /*
191
+ * Flaeche, Kontur, Farbton und Verlaufswinkel aus den Aufrufparametern auf
192
+ * ein neues Objekt schreiben (create_ellipse, create_polygon,
193
+ * create_rectangle). Reihenfolge Staerke, Konturfarbe, Flaeche (Live-Fund
194
+ * Sitzung 3). Liefert nichts; Farbfeld-IDs neuer RGB-Felder in swatchIds.
195
+ */
196
+ function idcStyleNewShape(ctx, doc, item, args, swatchIds) {
197
+ var fill = null, stroke = null, weight = null, none;
198
+ var fillTint = idcTintArg(args.fill_tint_percent, 'fill_tint_percent');
199
+ var strokeTint = idcTintArg(args.stroke_tint_percent, 'stroke_tint_percent');
200
+ var fillAngle = idcAngleArg(args.gradient_fill_angle_deg, 'gradient_fill_angle_deg');
201
+ var strokeAngle = idcAngleArg(args.gradient_stroke_angle_deg, 'gradient_stroke_angle_deg');
202
+
203
+ idcBothGiven(args, 'fill_hex', 'fill_swatch_name');
204
+ idcBothGiven(args, 'stroke_hex', 'stroke_swatch_name');
205
+ if (args.fill_hex !== undefined && args.fill_hex !== null) { fill = idcSwatchForHex(doc, args.fill_hex, 'fill_hex', swatchIds); }
206
+ if (args.fill_swatch_name !== undefined && args.fill_swatch_name !== null) { fill = idcSwatchArg(doc, args.fill_swatch_name, 'fill_swatch_name'); }
207
+ if (args.stroke_hex !== undefined && args.stroke_hex !== null) { stroke = idcSwatchForHex(doc, args.stroke_hex, 'stroke_hex', swatchIds); }
208
+ if (args.stroke_swatch_name !== undefined && args.stroke_swatch_name !== null) { stroke = idcSwatchArg(doc, args.stroke_swatch_name, 'stroke_swatch_name'); }
209
+ if (args.stroke_weight_pt !== undefined && args.stroke_weight_pt !== null) {
210
+ if (typeof args.stroke_weight_pt !== 'number' || !isFinite(args.stroke_weight_pt) || args.stroke_weight_pt < 0) {
211
+ IDC.fail('invalid_argument', 'stroke_weight_pt must be a number of 0 or more.', { stroke_weight_pt: args.stroke_weight_pt });
212
+ }
213
+ weight = args.stroke_weight_pt;
214
+ }
215
+
216
+ none = idcNoneSwatch(doc);
217
+ var strokeSwatch = null, strokeWeight = 0;
218
+ if (stroke !== null) {
219
+ strokeSwatch = stroke;
220
+ if (weight === null) { strokeWeight = 1; ctx.warn('A stroke colour without stroke_weight_pt was given; 1 pt was applied.'); }
221
+ else { strokeWeight = weight; }
222
+ } else if (weight !== null && weight > 0) {
223
+ strokeSwatch = idcBlackSwatch(doc) || none;
224
+ strokeWeight = weight;
225
+ } else {
226
+ strokeSwatch = none;
227
+ strokeWeight = 0;
228
+ }
229
+ idcSetStrokeWeightPt(item, strokeWeight);
230
+ if (strokeSwatch !== null) { item.strokeColor = strokeSwatch; }
231
+ if (fill !== null) { item.fillColor = fill; }
232
+ else if (none !== null) { item.fillColor = none; }
233
+
234
+ idcApplyTintsAndAngles(ctx, item, fillTint, strokeTint, fillAngle, strokeAngle);
235
+ }
236
+
237
+ /* Farbton und Verlaufswinkel setzen; Winkel ohne Verlauf: Warnung, Wert wird trotzdem geschrieben. */
238
+ function idcApplyTintsAndAngles(ctx, item, fillTint, strokeTint, fillAngle, strokeAngle) {
239
+ if (fillTint !== null) { item.fillTint = fillTint; }
240
+ if (strokeTint !== null) { item.strokeTint = strokeTint; }
241
+ if (fillAngle !== null) {
242
+ if (idcSwatchKindSafe(item, 'fillColor') !== 'gradient') { ctx.warn('gradient_fill_angle_deg was set, but the fill is no gradient swatch – the angle has no visible effect until one is applied.'); }
243
+ item.gradientFillAngle = fillAngle;
244
+ }
245
+ if (strokeAngle !== null) {
246
+ if (idcSwatchKindSafe(item, 'strokeColor') !== 'gradient') { ctx.warn('gradient_stroke_angle_deg was set, but the stroke is no gradient swatch – the angle has no visible effect until one is applied.'); }
247
+ item.gradientStrokeAngle = strokeAngle;
248
+ }
249
+ }
250
+
251
+ function idcSwatchKindSafe(item, property) {
252
+ try { return idcSwatchKind(item[property]); } catch (e) { return null; }
253
+ }
254
+
255
+ /* Ebene fuer neue Objekte: layer_name (muss existieren), sonst die aktive. */
256
+ function idcLayerForNew(doc, args) {
257
+ if (args.layer_name === undefined || args.layer_name === null) { return undefined; }
258
+ return idcLayerArg(doc, { layer_name: args.layer_name }).layer;
259
+ }
260
+
261
+ function idcNewShapeResult(ctx, doc, item, page, found, space, swatchIds, type, what) {
262
+ var landing = idcCheckLanding(ctx, item, page, what);
263
+ var wrap = idcTextWrapName(item);
264
+ if (wrap !== null && wrap !== 'none' && (ctx.args.text_wrap === undefined || ctx.args.text_wrap === null)) {
265
+ ctx.warn('The ' + what + ' carries text wrap (Textumfluss) "' + wrap + '" from the document defaults. Pass text_wrap: "none" or use set_text_wrap.');
266
+ }
267
+ var look = idcAppearanceFull(item);
268
+ var layerName = null;
269
+ try { layerName = String(item.itemLayer.name); } catch (e0) { layerName = null; }
270
+ return {
271
+ frame_id: Number(item.id),
272
+ type: type,
273
+ page_id: Number(page.id),
274
+ coordinate_space: space,
275
+ bounds_mm: idcItemBounds(doc, item),
276
+ swatch_ids: swatchIds,
277
+ stroke_weight_pt: look.stroke_weight_pt,
278
+ stroke_color: look.stroke_color,
279
+ stroke_kind: look.stroke_kind,
280
+ fill_color: look.fill_color,
281
+ fill_kind: look.fill_kind,
282
+ fill_tint_percent: look.fill_tint_percent,
283
+ stroke_tint_percent: look.stroke_tint_percent,
284
+ gradient_fill_angle_deg: look.gradient_fill_angle_deg,
285
+ gradient_stroke_angle_deg: look.gradient_stroke_angle_deg,
286
+ landed_on_page_id: landing.page_id,
287
+ landed_on_page_name: landing.page_name,
288
+ on_parent_page: found.is_parent_page,
289
+ text_wrap: wrap,
290
+ text_wrap_mode: wrap,
291
+ layer_name: layerName,
292
+ name: (function () { try { return String(item.label); } catch (e1) { return ''; } })()
293
+ };
294
+ }
295
+
296
+ /* Textumfluss beim Anlegen: none|bounding_box|object_shape (A-8.1); ohne Angabe Dokumentvorgabe. */
297
+ function idcApplyCreateTextWrap(item, args) {
298
+ var mode = args.text_wrap;
299
+ if (mode === undefined || mode === null) { return; }
300
+ if (typeof TextWrapModes === 'undefined') { return; }
301
+ if (mode === 'none') { item.textWrapPreferences.textWrapMode = TextWrapModes.NONE; return; }
302
+ if (mode === 'bounding_box') { item.textWrapPreferences.textWrapMode = TextWrapModes.BOUNDING_BOX_TEXT_WRAP; return; }
303
+ if (mode === 'object_shape') { item.textWrapPreferences.textWrapMode = TextWrapModes.CONTOUR; return; }
304
+ IDC.fail('invalid_argument', 'text_wrap must be none, bounding_box or object_shape.', { text_wrap: mode });
305
+ }
306
+
307
+ function idcApplyName(item, args) {
308
+ if (args.name === undefined || args.name === null) { return; }
309
+ if (typeof args.name !== 'string') { IDC.fail('invalid_argument', 'name must be a string.', { name: args.name }); }
310
+ item.label = args.name;
311
+ }
312
+
313
+ /* ---------------------------------------------------------- create_ellipse */
314
+
315
+ IDC.register('create_ellipse', { needs_document: true, read_only: false, ruler: 'page' },
316
+ function (ctx) {
317
+ var args = ctx.args;
318
+ var doc = ctx.doc;
319
+ var found = idcAnyPageById(doc, args.page_id);
320
+ var page = found.page;
321
+ var rect = idcBoundsArg(args.bounds_mm, 'bounds_mm');
322
+ var space = idcCoordinateSpace(args);
323
+ var swatchIds = [];
324
+ var layer = idcLayerForNew(doc, args);
325
+ var item;
326
+
327
+ if (rect.width <= 0 || rect.height <= 0) {
328
+ IDC.fail('invalid_argument', 'bounds_mm.width and height must be greater than 0.', { bounds_mm: args.bounds_mm });
329
+ }
330
+ item = idcAddShape(doc, page, 'ovals', layer, idcRectToBounds(rect), space, null);
331
+ try {
332
+ idcStyleNewShape(ctx, doc, item, args, swatchIds);
333
+ idcApplyCreateTextWrap(item, args);
334
+ idcApplyName(item, args);
335
+ } catch (e) {
336
+ if (e && e.isIdcError) { throw e; }
337
+ IDC.fail('script_error', 'The ellipse was created (frame_id ' + Number(item.id) + ') but could not be styled: ' + String(e.message ? e.message : e), { frame_id: Number(item.id) });
338
+ }
339
+ ctx.changes = { created_frame_ids: [Number(item.id)], created_swatch_ids: swatchIds };
340
+ return idcNewShapeResult(ctx, doc, item, page, found, space, swatchIds, 'ellipse', 'ellipse');
341
+ });
342
+
343
+ /*
344
+ * Objekt anlegen wie idcAddItem, aber mit Ebene und – fuer Polygone – der
345
+ * anderen add-Signatur (layer, numberOfSides, insetPercentage, at,
346
+ * reference, withProperties).
347
+ */
348
+ function idcAddShape(doc, page, collectionName, layer, bounds, space, polygonSpec) {
349
+ var item;
350
+ var spreadBounds = idcToSpreadBounds(page, bounds, space);
351
+ try {
352
+ item = IDC.withRulerOrigin(doc, RulerOrigin.SPREAD_ORIGIN, function () {
353
+ var created;
354
+ if (polygonSpec !== null) {
355
+ created = page[collectionName].add(layer, polygonSpec.sides, polygonSpec.inset, undefined, undefined, { geometricBounds: spreadBounds });
356
+ } else {
357
+ created = page[collectionName].add(layer, undefined, undefined, { geometricBounds: spreadBounds });
358
+ }
359
+ return IDC.concrete(created);
360
+ });
361
+ } catch (e) {
362
+ IDC.fail('script_error', 'InDesign could not create the object: ' + String(e.message ? e.message : e),
363
+ { page_id: Number(page.id), bounds: bounds });
364
+ }
365
+ return item;
366
+ }
367
+
368
+ /* ---------------------------------------------------------- create_polygon */
369
+
370
+ function idcPointsArg(points, field) {
371
+ var out = [], i, p;
372
+ if (!IDC.isArray(points) || points.length < 3 || points.length > 200) {
373
+ IDC.fail('invalid_argument', field + ' must be an array of 3 to 200 points {x, y}.', { count: IDC.isArray(points) ? points.length : null });
374
+ }
375
+ for (i = 0; i < points.length; i++) {
376
+ p = points[i];
377
+ if (p === null || typeof p !== 'object' || typeof p.x !== 'number' || typeof p.y !== 'number' || !isFinite(p.x) || !isFinite(p.y)) {
378
+ IDC.fail('invalid_argument', field + '[' + i + '] must be {x, y} in mm.', { point: p });
379
+ }
380
+ out.push({ x: p.x, y: p.y });
381
+ }
382
+ return out;
383
+ }
384
+
385
+ /* Ankerpunkte eines Pfads relativ zur Seite lesen (nur Ankerpunkte, keine Griffe). */
386
+ function idcReadPathPoints(doc, item, page) {
387
+ var out = [], path, raw, i, entry, dx;
388
+ try { path = item.paths.item(0); raw = path.entirePath; } catch (e0) { return out; }
389
+ dx = page === null ? 0 : IDC.spreadOffsetOfPage(page);
390
+ for (i = 0; i < raw.length; i++) {
391
+ entry = raw[i];
392
+ /* Gekruemmt: [[links], [anker], [rechts]]; gerade: [x, y]. */
393
+ if (IDC.isArray(entry) && entry.length === 3 && IDC.isArray(entry[1])) { entry = entry[1]; }
394
+ if (IDC.isArray(entry) && entry.length >= 2) {
395
+ out.push({ x: IDC.round(Number(entry[0]) - dx), y: IDC.round(Number(entry[1])) });
396
+ }
397
+ }
398
+ return out;
399
+ }
400
+
401
+ IDC.register('create_polygon', { needs_document: true, read_only: false, ruler: 'page' },
402
+ function (ctx) {
403
+ var args = ctx.args;
404
+ var doc = ctx.doc;
405
+ var found = idcAnyPageById(doc, args.page_id);
406
+ var page = found.page;
407
+ var space = idcCoordinateSpace(args);
408
+ var swatchIds = [];
409
+ var layer = idcLayerForNew(doc, args);
410
+ var hasBounds = args.bounds_mm !== undefined && args.bounds_mm !== null;
411
+ var hasSides = args.sides !== undefined && args.sides !== null;
412
+ var hasPoints = args.points_mm !== undefined && args.points_mm !== null;
413
+ var item, rect, sides, inset = 0, points, spreadPath, i, dx, result;
414
+
415
+ if (hasPoints && (hasBounds || hasSides || (args.star_inset_percent !== undefined && args.star_inset_percent !== null))) {
416
+ IDC.fail('invalid_argument', 'Pass either points_mm (free shape) or bounds_mm with sides (regular polygon or star), not both.', null);
417
+ }
418
+ if (!hasPoints && !(hasBounds && hasSides)) {
419
+ IDC.fail('invalid_argument', 'create_polygon needs bounds_mm and sides (3–100), or points_mm (3–200 points).', null);
420
+ }
421
+ if (hasPoints) {
422
+ points = idcPointsArg(args.points_mm, 'points_mm');
423
+ dx = IDC.spreadOffsetOfPage(page);
424
+ spreadPath = [];
425
+ for (i = 0; i < points.length; i++) { spreadPath.push([points[i].x + (space === 'spread' ? 0 : dx), points[i].y]); }
426
+ item = idcAddShape(doc, page, 'polygons', layer, idcRectToBounds({ x: points[0].x, y: points[0].y, width: 1, height: 1 }), space, { sides: 3, inset: 0 });
427
+ try {
428
+ IDC.withRulerOrigin(doc, RulerOrigin.SPREAD_ORIGIN, function () {
429
+ item.paths.item(0).entirePath = spreadPath;
430
+ });
431
+ } catch (e0) {
432
+ try { item.remove(); } catch (e0b) { /* bleibt liegen */ }
433
+ IDC.fail('script_error', 'InDesign rejected the path points: ' + String(e0.message ? e0.message : e0), { points_mm: points });
434
+ }
435
+ } else {
436
+ rect = idcBoundsArg(args.bounds_mm, 'bounds_mm');
437
+ sides = args.sides;
438
+ if (typeof sides !== 'number' || Math.floor(sides) !== sides || sides < 3 || sides > 100) {
439
+ IDC.fail('invalid_argument', 'sides must be a whole number between 3 and 100.', { sides: sides });
440
+ }
441
+ if (args.star_inset_percent !== undefined && args.star_inset_percent !== null) {
442
+ inset = args.star_inset_percent;
443
+ if (typeof inset !== 'number' || !isFinite(inset) || inset < 0 || inset > 100) {
444
+ IDC.fail('invalid_argument', 'star_inset_percent must be a number between 0 and 100.', { star_inset_percent: inset });
445
+ }
446
+ }
447
+ if (rect.width <= 0 || rect.height <= 0) {
448
+ IDC.fail('invalid_argument', 'bounds_mm.width and height must be greater than 0.', { bounds_mm: args.bounds_mm });
449
+ }
450
+ item = idcAddShape(doc, page, 'polygons', layer, idcRectToBounds(rect), space, { sides: sides, inset: inset });
451
+ }
452
+ try {
453
+ idcStyleNewShape(ctx, doc, item, args, swatchIds);
454
+ idcApplyCreateTextWrap(item, args);
455
+ idcApplyName(item, args);
456
+ } catch (e) {
457
+ if (e && e.isIdcError) { throw e; }
458
+ IDC.fail('script_error', 'The polygon was created (frame_id ' + Number(item.id) + ') but could not be styled: ' + String(e.message ? e.message : e), { frame_id: Number(item.id) });
459
+ }
460
+ ctx.changes = { created_frame_ids: [Number(item.id)], created_swatch_ids: swatchIds };
461
+ result = idcNewShapeResult(ctx, doc, item, page, found, space, swatchIds, 'polygon', 'polygon');
462
+ result.sides = hasPoints ? null : sides;
463
+ result.star_inset_percent = hasPoints ? null : inset;
464
+ result.points_mm = idcReadPathPoints(doc, item, IDC.landing(item).on_pasteboard ? null : (function () { try { return item.parentPage; } catch (e2) { return null; } })());
465
+ result.point_count = result.points_mm.length;
466
+ return result;
467
+ });
468
+
469
+ /* ------------------------------------------------------ set_corner_options */
470
+
471
+ IDC.register('set_corner_options', { needs_document: true, read_only: false, ruler: 'page' },
472
+ function (ctx) {
473
+ var args = ctx.args;
474
+ var doc = ctx.doc;
475
+ var found = idcFrameArg(doc, args, 'frame_id');
476
+ var item = found.item;
477
+ var type = IDC.itemType(item);
478
+ var applied, corners;
479
+
480
+ if (type === 'line' || type === 'group') {
481
+ IDC.fail('invalid_state', 'Object ' + args.frame_id + ' is a ' + type + '; corner options need a frame or polygon.', { frame_id: args.frame_id, type: type });
482
+ }
483
+ if ((args.corner_type === undefined || args.corner_type === null) && (args.radius_mm === undefined || args.radius_mm === null) && (args.corners === undefined || args.corners === null)) {
484
+ IDC.fail('invalid_argument', 'Pass corner_type and/or radius_mm for all four corners, or corners {top_left, top_right, bottom_left, bottom_right}.', null);
485
+ }
486
+ try {
487
+ applied = idcApplyCornerArgs(item, args);
488
+ } catch (e) {
489
+ if (e && e.isIdcError) { throw e; }
490
+ IDC.fail('script_error', 'InDesign could not set the corner options of object ' + args.frame_id + ': ' + String(e.message ? e.message : e), { frame_id: args.frame_id });
491
+ }
492
+ corners = idcReadCorners(item);
493
+ if (corners === null) {
494
+ IDC.fail('invalid_state', 'Object ' + args.frame_id + ' (' + type + ') has no corner options.', { frame_id: args.frame_id, type: type });
495
+ }
496
+ if (!applied) { ctx.warn('Nothing to set: every given corner was empty.'); }
497
+ else { ctx.changes = { modified_frame_ids: [Number(item.id)] }; }
498
+ return {
499
+ frame_id: Number(item.id),
500
+ type: type,
501
+ corners: { top_left: corners.top_left, top_right: corners.top_right, bottom_left: corners.bottom_left, bottom_right: corners.bottom_right },
502
+ uniform: corners.uniform,
503
+ bounds_mm: idcItemBounds(doc, item)
504
+ };
505
+ });
@@ -0,0 +1,117 @@
1
+ /* Wunschliste J Nr. 3 (Sitzung 7) – Gruppen: group_frames, ungroup_frame. */
2
+
3
+ /* IDs der direkten Mitglieder einer Gruppe. */
4
+ function idcGroupMemberIds(group) {
5
+ var ids = [];
6
+ var i, items;
7
+ try {
8
+ items = group.pageItems;
9
+ for (i = 0; i < items.length; i++) { ids.push(Number(items.item(i).id)); }
10
+ } catch (e) { /* leer */ }
11
+ return ids;
12
+ }
13
+
14
+ IDC.register('group_frames', { needs_document: true, read_only: false, ruler: 'page' },
15
+ function (ctx) {
16
+ var args = ctx.args;
17
+ var doc = ctx.doc;
18
+ var ids = args.frame_ids;
19
+ var items = [];
20
+ var seen = {};
21
+ var i, found, item, spread = null, other, group, landing, memberIds;
22
+
23
+ if (!IDC.isArray(ids) || ids.length < 2) {
24
+ IDC.fail('invalid_argument', 'frame_ids must list at least two object ids.', { frame_ids: ids });
25
+ }
26
+ if (ids.length > 500) {
27
+ IDC.fail('invalid_argument', 'frame_ids may list at most 500 objects.', { count: ids.length });
28
+ }
29
+ for (i = 0; i < ids.length; i++) {
30
+ if (typeof ids[i] !== 'number' || Math.floor(ids[i]) !== ids[i]) {
31
+ IDC.fail('invalid_argument', 'frame_ids must contain whole numbers.', { frame_ids: ids });
32
+ }
33
+ if (seen[ids[i]]) {
34
+ IDC.fail('invalid_argument', 'frame_ids lists object ' + ids[i] + ' twice.', { frame_id: ids[i] });
35
+ }
36
+ seen[ids[i]] = true;
37
+ found = IDC.itemById(doc, ids[i], 'any');
38
+ if (found === null) {
39
+ IDC.fail('not_found', 'No object with frame_id ' + ids[i] + ' in this document.', { frame_id: ids[i] });
40
+ }
41
+ item = found.item;
42
+ idcRejectGroupMember(item, ids[i], 'group_frames');
43
+ idcRejectLocked(doc, item, ids[i], 'group_frames');
44
+ other = IDC.spreadOfItem(item);
45
+ if (other === null) {
46
+ IDC.fail('invalid_state', 'Object ' + ids[i] + ' is not on a spread.', { frame_id: ids[i] });
47
+ }
48
+ if (spread === null) { spread = other; }
49
+ else if (!idcSameObject(spread, other)) {
50
+ IDC.fail('invalid_state', 'All objects must lie on the same spread (Druckbogen); object ' + ids[i] + ' lies on another one.',
51
+ { frame_id: ids[i] });
52
+ }
53
+ items.push(item);
54
+ }
55
+
56
+ try {
57
+ group = IDC.concrete(doc.groups.add(items));
58
+ } catch (e) {
59
+ IDC.fail('script_error', 'InDesign could not group the objects: ' + String(e.message ? e.message : e), { frame_ids: ids });
60
+ }
61
+
62
+ landing = IDC.landing(group);
63
+ memberIds = idcGroupMemberIds(group);
64
+ ctx.changes = { created_frame_ids: [Number(group.id)], modified_frame_ids: memberIds };
65
+ return {
66
+ frame_id: Number(group.id),
67
+ type: 'group',
68
+ member_ids: memberIds,
69
+ member_count: memberIds.length,
70
+ bounds_mm: idcItemBounds(doc, group),
71
+ landed_on_page_id: landing.page_id,
72
+ landed_on_page_name: landing.page_name,
73
+ on_parent_page: IDC.isOnParentSpread(group)
74
+ };
75
+ });
76
+
77
+ IDC.register('ungroup_frame', { needs_document: true, read_only: false, ruler: 'page' },
78
+ function (ctx) {
79
+ var args = ctx.args;
80
+ var doc = ctx.doc;
81
+ var found = idcFrameArg(doc, args, 'frame_id');
82
+ var group = found.item;
83
+ var memberIds, i, member, landing = null, survivors = [];
84
+
85
+ if (IDC.itemType(group) !== 'group') {
86
+ IDC.fail('invalid_state', 'Object ' + args.frame_id + ' is not a group (it is a ' + IDC.itemType(group) + ').',
87
+ { frame_id: args.frame_id, type: IDC.itemType(group) });
88
+ }
89
+ idcRejectGroupMember(group, args.frame_id, 'ungroup_frame');
90
+ memberIds = idcGroupMemberIds(group);
91
+
92
+ try {
93
+ group.ungroup();
94
+ } catch (e) {
95
+ IDC.fail('script_error', 'InDesign could not ungroup object ' + args.frame_id + ': ' + String(e.message ? e.message : e),
96
+ { frame_id: args.frame_id });
97
+ }
98
+
99
+ for (i = 0; i < memberIds.length; i++) {
100
+ member = IDC.itemById(doc, memberIds[i], 'any');
101
+ if (member !== null) {
102
+ survivors.push(memberIds[i]);
103
+ if (landing === null) { landing = IDC.landing(member.item); }
104
+ }
105
+ }
106
+ if (survivors.length !== memberIds.length) {
107
+ ctx.warn('After ungrouping, ' + (memberIds.length - survivors.length) + ' member id(s) could not be found again; read get_page_state.');
108
+ }
109
+ ctx.changes = { removed_frame_ids: [Number(args.frame_id)], modified_frame_ids: survivors };
110
+ return {
111
+ frame_id: Number(args.frame_id),
112
+ member_ids: survivors,
113
+ member_count: survivors.length,
114
+ landed_on_page_id: landing === null ? null : landing.page_id,
115
+ landed_on_page_name: landing === null ? '' : landing.page_name
116
+ };
117
+ });