layoutbridge-light 0.0.0-stage → 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 (88) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +26 -0
  3. package/README.de.md +338 -0
  4. package/README.md +325 -2
  5. package/THIRD-PARTY-NOTICES.md +2878 -0
  6. package/dist/src/mcp/catalog.js +126 -0
  7. package/dist/src/mcp/config.js +211 -0
  8. package/dist/src/mcp/dispatch.js +164 -0
  9. package/dist/src/mcp/epub-check.js +331 -0
  10. package/dist/src/mcp/epub-orientation.js +132 -0
  11. package/dist/src/mcp/license/free-tools.js +16 -0
  12. package/dist/src/mcp/license/gate.js +210 -0
  13. package/dist/src/mcp/license/index.js +107 -0
  14. package/dist/src/mcp/license/instance-name.js +57 -0
  15. package/dist/src/mcp/license/key-source.js +59 -0
  16. package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
  17. package/dist/src/mcp/license/placeholder-verifier.js +10 -0
  18. package/dist/src/mcp/license/polar-ids.js +100 -0
  19. package/dist/src/mcp/license/polar-verifier.js +285 -0
  20. package/dist/src/mcp/license/product-ids.js +73 -0
  21. package/dist/src/mcp/license/state-store.js +91 -0
  22. package/dist/src/mcp/license/types.js +11 -0
  23. package/dist/src/mcp/logging.js +271 -0
  24. package/dist/src/mcp/product-info.js +54 -0
  25. package/dist/src/mcp/product.js +56 -0
  26. package/dist/src/mcp/server.js +253 -0
  27. package/dist/src/mcp/setup.js +680 -0
  28. package/dist/src/mcp/start.cjs +117 -0
  29. package/dist/src/mcp/tool-definition.js +2 -0
  30. package/dist/src/mcp/tools.js +2425 -0
  31. package/dist/src/mcp/typography-rules.js +70 -0
  32. package/dist/src/transport/com.js +250 -0
  33. package/dist/src/transport/osascript.js +171 -0
  34. package/dist/src/transport/queue.js +19 -0
  35. package/dist/src/transport/script-bundle.js +92 -0
  36. package/dist/src/transport/transport.js +30 -0
  37. package/indesign/commands/10-app.js +6 -0
  38. package/indesign/commands/20-documents.js +517 -0
  39. package/indesign/commands/22-document-setup.js +204 -0
  40. package/indesign/commands/23-save-package.js +258 -0
  41. package/indesign/commands/25-export.js +448 -0
  42. package/indesign/commands/26-export-image.js +202 -0
  43. package/indesign/commands/27-textdefaults.js +151 -0
  44. package/indesign/commands/28-selection.js +174 -0
  45. package/indesign/commands/30-pages.js +1032 -0
  46. package/indesign/commands/31-view.js +210 -0
  47. package/indesign/commands/32-sections.js +203 -0
  48. package/indesign/commands/35-parents.js +428 -0
  49. package/indesign/commands/37-layout.js +837 -0
  50. package/indesign/commands/40-frames.js +851 -0
  51. package/indesign/commands/41-shapes.js +505 -0
  52. package/indesign/commands/42-groups.js +117 -0
  53. package/indesign/commands/43-layers.js +310 -0
  54. package/indesign/commands/44-effects.js +274 -0
  55. package/indesign/commands/45-frame-options.js +247 -0
  56. package/indesign/commands/47-anchored.js +331 -0
  57. package/indesign/commands/50-modify.js +781 -0
  58. package/indesign/commands/52-align.js +139 -0
  59. package/indesign/commands/55-fit.js +106 -0
  60. package/indesign/commands/60-images.js +1128 -0
  61. package/indesign/commands/62-embed.js +120 -0
  62. package/indesign/commands/69-placeholders.js +127 -0
  63. package/indesign/commands/70-text.js +675 -0
  64. package/indesign/commands/71-outlines.js +92 -0
  65. package/indesign/commands/72-place-text.js +476 -0
  66. package/indesign/commands/73-story-lock.js +157 -0
  67. package/indesign/commands/74-stories.js +121 -0
  68. package/indesign/commands/75-tables.js +676 -0
  69. package/indesign/commands/76-text-variables.js +260 -0
  70. package/indesign/commands/77-story-options.js +86 -0
  71. package/indesign/commands/78-frame-lines.js +452 -0
  72. package/indesign/commands/79-check-typography.js +341 -0
  73. package/indesign/commands/80-styles.js +1865 -0
  74. package/indesign/commands/81-composition.js +355 -0
  75. package/indesign/commands/82-object-styles.js +263 -0
  76. package/indesign/commands/83-drop-caps.js +87 -0
  77. package/indesign/commands/83-indents-tabs.js +264 -0
  78. package/indesign/commands/84-import-styles.js +142 -0
  79. package/indesign/commands/85-swatches.js +112 -0
  80. package/indesign/commands/86-gradients.js +185 -0
  81. package/indesign/commands/87-replace-swatch.js +180 -0
  82. package/indesign/commands/88-replace-font.js +212 -0
  83. package/indesign/commands/89-toc-index.js +945 -0
  84. package/indesign/commands/90-print-on-demand.js +495 -0
  85. package/indesign/commands/91-epub.js +546 -0
  86. package/indesign/interpreter.js +779 -0
  87. package/package.json +41 -4
  88. package/rules/typography-rules.json +31 -0
@@ -0,0 +1,185 @@
1
+ /*
2
+ * Auftrag A-8.4 (Sitzung 9) – create_gradient_swatch: Verlaufsfarbfeld
3
+ * anlegen, das an Flaechen und Konturen per fill_swatch_name /
4
+ * stroke_swatch_name verwendet wird. Adobe-DOM (Auftrag, geprueft
5
+ * 05.09.2026): Gradients.add(withProperties) -> Gradient {name, type
6
+ * (GradientType.LINEAR|RADIAL), gradientStops}; GradientStops.add() ->
7
+ * GradientStop {stopColor (Swatch-Objekt), location 0–100, midpoint 13–87}.
8
+ * Ob ein neuer Verlauf schon zwei Stopps traegt: live pruefen (Journal).
9
+ */
10
+
11
+ function idcGradientTypeFor(name) {
12
+ if (typeof GradientType === 'undefined') {
13
+ IDC.fail('invalid_state', 'This runtime has no GradientType enumeration.', null);
14
+ }
15
+ if (name === 'linear') { return GradientType.LINEAR; }
16
+ if (name === 'radial') { return GradientType.RADIAL; }
17
+ IDC.fail('invalid_argument', 'type must be linear or radial.', { type: name });
18
+ return null;
19
+ }
20
+
21
+ function idcGradientTypeName(value) {
22
+ if (value === null || value === undefined || typeof GradientType === 'undefined') { return null; }
23
+ if (IDC.enumIs(value, GradientType.LINEAR)) { return 'linear'; }
24
+ if (IDC.enumIs(value, GradientType.RADIAL)) { return 'radial'; }
25
+ return String(value);
26
+ }
27
+
28
+ /* Stopps eines Verlaufs lesen: [{index, swatch_name, location, midpoint}]. */
29
+ function idcReadGradientStops(gradient) {
30
+ var out = [], stops, i, stop, entry;
31
+ try { stops = gradient.gradientStops; } catch (e0) { return out; }
32
+ for (i = 0; i < stops.length; i++) {
33
+ entry = { index: i, swatch_name: null, location: null, midpoint: null };
34
+ try { stop = stops.item(i); } catch (e1) { continue; }
35
+ try { entry.swatch_name = String(stop.stopColor.name); } catch (e2) { /* null */ }
36
+ try { entry.location = IDC.round(Number(stop.location), 3); } catch (e3) { /* null */ }
37
+ try { entry.midpoint = IDC.round(Number(stop.midpoint), 3); } catch (e4) { /* null */ }
38
+ out.push(entry);
39
+ }
40
+ return out;
41
+ }
42
+
43
+ /* Stopp-Angabe pruefen und das Farbfeld dafuer beschaffen (Name, Hex oder CMYK). */
44
+ function idcGradientStopArg(doc, spec, index, gradientName, created) {
45
+ var ways = 0, swatch = null, kind, hexName;
46
+ if (spec === null || typeof spec !== 'object' || IDC.isArray(spec)) {
47
+ IDC.fail('invalid_argument', 'stops[' + index + '] must be an object {swatch_name | hex | cmyk, location, midpoint}.', { value: spec });
48
+ }
49
+ if (idcGiven(spec.swatch_name)) { ways += 1; }
50
+ if (idcGiven(spec.hex)) { ways += 1; }
51
+ if (idcGiven(spec.cmyk)) { ways += 1; }
52
+ if (ways !== 1) {
53
+ IDC.fail('invalid_argument', 'stops[' + index + '] needs exactly one of swatch_name, hex or cmyk.', { value: spec });
54
+ }
55
+ if (typeof spec.location !== 'number' || !isFinite(spec.location) || spec.location < 0 || spec.location > 100) {
56
+ IDC.fail('invalid_argument', 'stops[' + index + '].location must be a number between 0 and 100.', { value: spec.location });
57
+ }
58
+ if (idcGiven(spec.midpoint) && (typeof spec.midpoint !== 'number' || !isFinite(spec.midpoint) || spec.midpoint < 13 || spec.midpoint > 87)) {
59
+ IDC.fail('invalid_argument', 'stops[' + index + '].midpoint must be a number between 13 and 87.', { value: spec.midpoint });
60
+ }
61
+ if (idcGiven(spec.swatch_name)) {
62
+ swatch = idcSwatchArg(doc, spec.swatch_name, 'stops[' + index + '].swatch_name');
63
+ kind = idcSwatchKind(swatch);
64
+ if (kind !== 'color' && kind !== 'tint') {
65
+ IDC.fail('invalid_argument', 'stops[' + index + '].swatch_name must be a colour or tint swatch, not a ' + kind + '.', { swatch_name: spec.swatch_name, kind: kind });
66
+ }
67
+ } else if (idcGiven(spec.hex)) {
68
+ swatch = idcSwatchForHex(doc, spec.hex, 'stops[' + index + '].hex', created);
69
+ } else {
70
+ hexName = gradientName + ' Stopp ' + (index + 1);
71
+ swatch = idcSwatchByName(doc, hexName);
72
+ if (swatch === null) {
73
+ try {
74
+ swatch = doc.colors.add({ name: hexName, model: ColorModel.PROCESS, space: ColorSpace.CMYK, colorValue: idcCmykArg(spec.cmyk) });
75
+ created.push(Number(swatch.id));
76
+ } catch (e) {
77
+ IDC.fail('script_error', 'InDesign could not create the stop swatch "' + hexName + '": ' + String(e.message ? e.message : e), { name: hexName });
78
+ }
79
+ } else {
80
+ try { swatch.colorValue = idcCmykArg(spec.cmyk); } catch (e2) { /* Werte bleiben */ }
81
+ }
82
+ }
83
+ return { swatch: swatch, location: spec.location, midpoint: idcGiven(spec.midpoint) ? spec.midpoint : null };
84
+ }
85
+
86
+ /* Stopps in den Verlauf schreiben: vorhandene setzen, fehlende anlegen, ueberzaehlige entfernen. */
87
+ function idcWriteGradientStops(gradient, stops) {
88
+ var collection = gradient.gradientStops;
89
+ var i, stop, count;
90
+ for (i = 0; i < stops.length; i++) {
91
+ try { count = Number(collection.length); } catch (e0) { count = 0; }
92
+ if (i < count) { stop = collection.item(i); }
93
+ else { stop = collection.add(); }
94
+ stop.stopColor = stops[i].swatch;
95
+ stop.location = stops[i].location;
96
+ if (stops[i].midpoint !== null) { stop.midpoint = stops[i].midpoint; }
97
+ }
98
+ try { count = Number(collection.length); } catch (e1) { count = stops.length; }
99
+ while (count > stops.length) {
100
+ try { collection.item(count - 1).remove(); } catch (e2) { break; }
101
+ count -= 1;
102
+ }
103
+ }
104
+
105
+ IDC.register('create_gradient_swatch', { needs_document: true, read_only: false, ruler: 'none' },
106
+ function (ctx) {
107
+ var args = ctx.args;
108
+ var doc = ctx.doc;
109
+ var name = args.name;
110
+ var typeName = idcGiven(args.type) ? String(args.type) : 'linear';
111
+ var onCollision = idcCollisionArg(args.on_collision);
112
+ var created = [];
113
+ var stops = [], i, existing, existed, collision = 'none', chosenName, gradient = null, className, previous = null;
114
+
115
+ if (typeof name !== 'string' || name.length < 1 || name.length > 60) {
116
+ IDC.fail('invalid_argument', 'name is required (1–60 characters).', { name: name });
117
+ }
118
+ if (name.charAt(0) === '[') {
119
+ IDC.fail('invalid_argument', 'name must not start with "[".', { name: name });
120
+ }
121
+ idcGradientTypeFor(typeName);
122
+ if (!IDC.isArray(args.stops) || args.stops.length < 2 || args.stops.length > 20) {
123
+ IDC.fail('invalid_argument', 'stops must be an array of 2 to 20 entries in order.', { count: IDC.isArray(args.stops) ? args.stops.length : null });
124
+ }
125
+ for (i = 0; i < args.stops.length; i++) {
126
+ stops.push(idcGradientStopArg(doc, args.stops[i], i, name, created));
127
+ if (i > 0 && stops[i].location < stops[i - 1].location) {
128
+ IDC.fail('invalid_argument', 'stops must be in ascending order of location (stop ' + i + ' is at ' + stops[i].location + ', the one before at ' + stops[i - 1].location + ').', null);
129
+ }
130
+ }
131
+ if (stops[0].location !== 0) { ctx.warn('The first stop is at ' + stops[0].location + ' %, not 0 % – the gradient starts with a flat area.'); }
132
+ if (stops[stops.length - 1].location !== 100) { ctx.warn('The last stop is at ' + stops[stops.length - 1].location + ' %, not 100 % – the gradient ends with a flat area.'); }
133
+
134
+ chosenName = name;
135
+ existing = idcSwatchByName(doc, name);
136
+ existed = idcPresent(existing);
137
+ if (existed) {
138
+ className = IDC.className(existing);
139
+ if (onCollision === 'error') {
140
+ IDC.fail('invalid_state', 'Swatch "' + name + '" exists already (' + className + '). Pass on_collision "update" to change it or "version" to add "' + name + ' 2".',
141
+ { name: name, swatch_id: (function () { try { return Number(existing.id); } catch (e) { return null; } })() });
142
+ }
143
+ if (onCollision === 'update') {
144
+ if (className !== 'Gradient') {
145
+ IDC.fail('invalid_state', 'Swatch "' + name + '" is a ' + className + ', not a gradient; it cannot be updated with stops.', { name: name, type: className });
146
+ }
147
+ gradient = existing;
148
+ collision = 'updated';
149
+ previous = { type: idcGradientTypeName(gradient.type), stops: idcReadGradientStops(gradient) };
150
+ } else {
151
+ chosenName = idcFreeName(doc, name, 'swatch');
152
+ collision = 'versioned';
153
+ }
154
+ }
155
+ if (gradient === null) {
156
+ try {
157
+ gradient = doc.gradients.add({ name: chosenName, type: idcGradientTypeFor(typeName) });
158
+ } catch (e2) {
159
+ IDC.fail('script_error', 'InDesign could not create the gradient "' + chosenName + '": ' + String(e2.message ? e2.message : e2), { name: chosenName });
160
+ }
161
+ } else {
162
+ try { gradient.type = idcGradientTypeFor(typeName); } catch (e3) { /* Art bleibt */ }
163
+ }
164
+ try {
165
+ idcWriteGradientStops(gradient, stops);
166
+ } catch (e4) {
167
+ IDC.fail('script_error', 'InDesign could not write the gradient stops of "' + chosenName + '": ' + String(e4.message ? e4.message : e4), { name: chosenName });
168
+ }
169
+
170
+ var changes = { created_swatch_ids: created.slice() };
171
+ if (collision === 'updated') { changes.modified_swatch_ids = [Number(gradient.id)]; }
172
+ else { changes.created_swatch_ids.push(Number(gradient.id)); }
173
+ ctx.changes = changes;
174
+ return {
175
+ name: String(gradient.name),
176
+ swatch_id: Number(gradient.id),
177
+ type: idcGradientTypeName(gradient.type),
178
+ stops: idcReadGradientStops(gradient),
179
+ stop_count: idcReadGradientStops(gradient).length,
180
+ collision: collision,
181
+ existed: existed,
182
+ previous: previous,
183
+ swatch_ids: created
184
+ };
185
+ });
@@ -0,0 +1,180 @@
1
+ /*
2
+ * Auftrag A-9.2 (Sitzung 9) – replace_swatch: alle Verwendungen eines
3
+ * Farbfelds auf ein anderes umstellen und das alte entfernen (InDesign
4
+ * "Farbfeld loeschen ... ersetzen durch"). Adobe-DOM (geprueft 05.09.2026):
5
+ * Swatch.remove(replacingWith) – InDesign stellt Flaechen, Konturen, Text,
6
+ * Formate, Tabellen und Verlaeufe selbst um. Ohne delete_old: Text ueber
7
+ * die Suchen/Ersetzen-Klammer (FindTextPreference.fillColor), Objekte
8
+ * ueber allPageItems, Absatz- und Zeichenformate direkt; Objektformate,
9
+ * Tabellenformate und Verlaufsstopps bleiben dann beim alten Feld (Warnung).
10
+ * Verlaufsfarbfelder (A-8.4) sind als Quelle und Ziel zulaessig: InDesign
11
+ * ersetzt sie wie Farben; ein Verlauf als Ziel eines Textlaufs ist ein
12
+ * Verlaufstext.
13
+ */
14
+
15
+ function idcSwatchRef(doc, args, nameKey, idKey, what) {
16
+ var hasName = idcGiven(args[nameKey]);
17
+ var hasId = idcGiven(args[idKey]);
18
+ var swatch = null, i, list;
19
+ if (hasName === hasId) {
20
+ IDC.fail('invalid_argument', 'Pass exactly one of ' + nameKey + ' and ' + idKey + ' for the ' + what + ' swatch.', null);
21
+ }
22
+ if (hasName) {
23
+ swatch = idcSwatchByName(doc, args[nameKey]);
24
+ if (swatch === null) {
25
+ IDC.fail('not_found', 'Swatch "' + args[nameKey] + '" not found – names are exact (see list_swatches).', { swatch_name: args[nameKey] });
26
+ }
27
+ return swatch;
28
+ }
29
+ try {
30
+ list = doc.swatches;
31
+ for (i = 0; i < list.length; i++) {
32
+ if (Number(list.item(i).id) === Number(args[idKey])) { return list.item(i); }
33
+ }
34
+ } catch (e) { /* nicht gefunden */ }
35
+ IDC.fail('not_found', 'No swatch with ' + idKey + ' ' + args[idKey] + ' (see list_swatches).', { swatch_id: args[idKey] });
36
+ return null;
37
+ }
38
+
39
+ function idcDescribeSwatchShort(swatch) {
40
+ var out = { name: null, swatch_id: null, kind: null, space: null, values: [] };
41
+ try { out.name = String(swatch.name); } catch (e0) { /* null */ }
42
+ try { out.swatch_id = Number(swatch.id); } catch (e1) { /* null */ }
43
+ out.kind = idcSwatchKind(swatch);
44
+ try { out.space = idcColorSpaceName(swatch.space); } catch (e2) { out.space = null; }
45
+ try { out.values = idcNumberList(swatch.colorValue); } catch (e3) { out.values = []; }
46
+ return out;
47
+ }
48
+
49
+ function idcSwatchIs(value, swatch) {
50
+ try { return value !== null && value !== undefined && Number(value.id) === Number(swatch.id); } catch (e) { return false; }
51
+ }
52
+
53
+ /* Verwendungen eines Farbfelds zaehlen; Tabellen werden nicht gezaehlt (null). */
54
+ function idcSwatchUsage(doc, swatch, args) {
55
+ var out = { text_runs: 0, fills: 0, strokes: 0, paragraph_styles: 0, character_styles: 0, object_styles: 0, tables: null, gradients: 0 };
56
+ var items, i, item, styles, found;
57
+ try { items = doc.allPageItems; } catch (e0) { items = []; }
58
+ for (i = 0; i < items.length; i++) {
59
+ item = IDC.concrete(items[i]);
60
+ try { if (idcSwatchIs(item.fillColor, swatch)) { out.fills += 1; } } catch (e1) { /* kein fillColor */ }
61
+ try { if (idcSwatchIs(item.strokeColor, swatch)) { out.strokes += 1; } } catch (e2) { /* kein strokeColor */ }
62
+ }
63
+ styles = idcAllStyles(doc, 'paragraph');
64
+ for (i = 0; i < styles.length; i++) { try { if (idcSwatchIs(styles[i].fillColor, swatch)) { out.paragraph_styles += 1; } } catch (e3) { /* weiter */ } }
65
+ styles = idcAllStyles(doc, 'character');
66
+ for (i = 0; i < styles.length; i++) { try { if (idcSwatchIs(styles[i].fillColor, swatch)) { out.character_styles += 1; } } catch (e4) { /* weiter */ } }
67
+ try {
68
+ for (i = 0; i < doc.allObjectStyles.length; i++) {
69
+ item = doc.allObjectStyles[i];
70
+ try { if (item.enableFill === true && idcSwatchIs(item.fillColor, swatch)) { out.object_styles += 1; } } catch (e5) { /* weiter */ }
71
+ try { if (item.enableStroke === true && idcSwatchIs(item.strokeColor, swatch)) { out.object_styles += 1; } } catch (e6) { /* weiter */ }
72
+ }
73
+ } catch (e7) { /* keine Objektformate */ }
74
+ idcEachIn(doc, 'gradients', function (gradient) {
75
+ var stops, k;
76
+ try {
77
+ stops = gradient.gradientStops;
78
+ for (k = 0; k < stops.length; k++) { if (idcSwatchIs(stops.item(k).stopColor, swatch)) { out.gradients += 1; } }
79
+ } catch (e8) { /* weiter */ }
80
+ });
81
+ /* Textlaeufe ueber die Suche (dry run): fillColor in findTextPreferences. */
82
+ try {
83
+ app.findTextPreferences = NothingEnum.NOTHING;
84
+ app.changeTextPreferences = NothingEnum.NOTHING;
85
+ idcSetFindOptions(app.findChangeTextOptions, { include_master_pages: true, include_hidden_layers: true, include_locked_layers: args.include_locked_layers === true });
86
+ app.findTextPreferences.fillColor = swatch;
87
+ found = doc.findText();
88
+ out.text_runs = found.length;
89
+ } catch (e9) {
90
+ out.text_runs = null;
91
+ } finally {
92
+ try { app.findTextPreferences = NothingEnum.NOTHING; } catch (e10) { /* egal */ }
93
+ try { app.changeTextPreferences = NothingEnum.NOTHING; } catch (e11) { /* egal */ }
94
+ }
95
+ return out;
96
+ }
97
+
98
+ /* Verwendungen ohne Loeschen umstellen (delete_old false). */
99
+ function idcReassignSwatch(ctx, doc, oldSwatch, newSwatch, args) {
100
+ var items, i, item, styles, changed = { text_runs: 0, fills: 0, strokes: 0, paragraph_styles: 0, character_styles: 0 };
101
+ try { items = doc.allPageItems; } catch (e0) { items = []; }
102
+ for (i = 0; i < items.length; i++) {
103
+ item = IDC.concrete(items[i]);
104
+ try { if (idcSwatchIs(item.fillColor, oldSwatch)) { item.fillColor = newSwatch; changed.fills += 1; } } catch (e1) { /* weiter */ }
105
+ try { if (idcSwatchIs(item.strokeColor, oldSwatch)) { item.strokeColor = newSwatch; changed.strokes += 1; } } catch (e2) { /* weiter */ }
106
+ }
107
+ styles = idcAllStyles(doc, 'paragraph');
108
+ for (i = 0; i < styles.length; i++) { try { if (idcSwatchIs(styles[i].fillColor, oldSwatch)) { styles[i].fillColor = newSwatch; changed.paragraph_styles += 1; } } catch (e3) { /* weiter */ } }
109
+ styles = idcAllStyles(doc, 'character');
110
+ for (i = 0; i < styles.length; i++) { try { if (idcSwatchIs(styles[i].fillColor, oldSwatch)) { styles[i].fillColor = newSwatch; changed.character_styles += 1; } } catch (e4) { /* weiter */ } }
111
+ try {
112
+ app.findTextPreferences = NothingEnum.NOTHING;
113
+ app.changeTextPreferences = NothingEnum.NOTHING;
114
+ idcSetFindOptions(app.findChangeTextOptions, { include_master_pages: true, include_hidden_layers: true, include_locked_layers: args.include_locked_layers === true });
115
+ app.findTextPreferences.fillColor = oldSwatch;
116
+ app.changeTextPreferences.fillColor = newSwatch;
117
+ changed.text_runs = doc.changeText().length;
118
+ } catch (e5) {
119
+ ctx.warn('Text runs could not be changed by search: ' + String(e5.message ? e5.message : e5));
120
+ } finally {
121
+ try { app.findTextPreferences = NothingEnum.NOTHING; } catch (e6) { /* egal */ }
122
+ try { app.changeTextPreferences = NothingEnum.NOTHING; } catch (e7) { /* egal */ }
123
+ }
124
+ return changed;
125
+ }
126
+
127
+ IDC.register('replace_swatch', { needs_document: true, read_only: false, ruler: 'none' },
128
+ function (ctx) {
129
+ var args = ctx.args;
130
+ var doc = ctx.doc;
131
+ var deleteOld = args.delete_old !== false;
132
+ var dryRun = args.dry_run === true;
133
+ var oldSwatch = idcSwatchRef(doc, args, 'old_name', 'old_swatch_id', 'old');
134
+ var newSwatch = idcSwatchRef(doc, args, 'new_name', 'new_swatch_id', 'new');
135
+ var oldInfo = idcDescribeSwatchShort(oldSwatch);
136
+ var newInfo = idcDescribeSwatchShort(newSwatch);
137
+ var usage, deleted = false, reassigned = null, remaining = null;
138
+
139
+ if (oldInfo.name === null || oldInfo.name.charAt(0) === '[' || oldInfo.name === 'None' || oldInfo.name === 'Paper' || oldInfo.name === 'Black' || oldInfo.name === 'Registration') {
140
+ IDC.fail('invalid_argument', 'Built-in swatch "' + oldInfo.name + '" cannot be replaced.', { old: oldInfo });
141
+ }
142
+ if (oldInfo.swatch_id === newInfo.swatch_id) {
143
+ IDC.fail('invalid_argument', 'old and new name the same swatch "' + oldInfo.name + '".', { old: oldInfo });
144
+ }
145
+ if (oldInfo.space !== null && newInfo.space !== null && oldInfo.space !== newInfo.space) {
146
+ ctx.warn('Colour space changes from ' + oldInfo.space + ' to ' + newInfo.space + ' – this is no colour management; the printed colour may differ. Check the values with list_swatches.');
147
+ }
148
+ usage = idcSwatchUsage(doc, oldSwatch, args);
149
+ if (usage.text_runs === null) { ctx.warn('Text runs could not be counted by search; the other counts are complete.'); }
150
+
151
+ if (!dryRun) {
152
+ if (deleteOld) {
153
+ try {
154
+ oldSwatch.remove(newSwatch);
155
+ deleted = true;
156
+ } catch (e) {
157
+ IDC.fail('invalid_state', 'InDesign refused to remove swatch "' + oldInfo.name + '" (used as a base for a mixed ink or gradient?): ' + String(e.message ? e.message : e), { old: oldInfo });
158
+ }
159
+ } else {
160
+ reassigned = idcReassignSwatch(ctx, doc, oldSwatch, newSwatch, args);
161
+ if (usage.object_styles > 0 || usage.gradients > 0) {
162
+ ctx.warn('Without delete_old, ' + usage.object_styles + ' object style use(s) and ' + usage.gradients + ' gradient stop(s) keep the old swatch; table styles are not checked. Use delete_old true to let InDesign replace everything.');
163
+ }
164
+ }
165
+ ctx.changes = { modified_document_ids: [Number(doc.id)] };
166
+ if (deleted) { ctx.changes.removed_swatch_ids = [oldInfo.swatch_id]; }
167
+ }
168
+ try { remaining = Number(doc.swatches.length); } catch (e2) { remaining = null; }
169
+ /* "new" ist in ExtendScript auch als Eigenschaftsname verboten (Live-Fund 05.09.2026) – deshalb ueber die Klammer. */
170
+ var out = {
171
+ old: oldInfo,
172
+ deleted: deleted,
173
+ usage_before: usage,
174
+ reassigned: reassigned,
175
+ remaining_swatches: remaining,
176
+ dry_run: dryRun
177
+ };
178
+ out['new'] = newInfo;
179
+ return out;
180
+ });
@@ -0,0 +1,212 @@
1
+ /*
2
+ * Auftrag A-9.1 (Sitzung 9) – replace_font: eine Schrift dokumentweit
3
+ * ersetzen (InDesign "Schrift suchen/ersetzen") – in Absatz- und
4
+ * Zeichenformaten und in lokaler Formatierung, auf Dokument- und
5
+ * Musterseiten. Lokale Laeufe werden ueber story.textStyleRanges gelesen
6
+ * und je Lauf ueber appliedFont gesetzt (dieselbe Mechanik wie
7
+ * find_font_usage); die Zielschrift muss installiert sein. Adobe-DOM
8
+ * (geprueft 05.09.2026): FindTextPreference.appliedFont/fontStyle waeren
9
+ * der andere Weg; der Lauf ueber die Formatlaeufe zaehlt und lokalisiert
10
+ * dabei gleich und braucht keine Suchklammer.
11
+ */
12
+
13
+ /* Zielschrift: installiert? Liefert { object, family, style } oder wirft not_found. Gezielt aufgeloest, nicht ueber die ganze Schriftliste (A-59, E-105). */
14
+ function idcTargetFont(fonts, family, style) {
15
+ var name = String(family) + '\t' + String(style);
16
+ var found = fonts.find(family, style);
17
+ if (found === null) {
18
+ IDC.fail('not_found', 'Target font "' + family + ' ' + style + '" is not installed.', { to_family: family, to_style: style, available_styles: fonts.stylesOf(family) });
19
+ }
20
+ return { object: found.object, family: String(family), style: String(style), name: name };
21
+ }
22
+
23
+ function idcStoryLocked(doc, story) {
24
+ var containers, i, layer;
25
+ try { containers = story.textContainers; } catch (e0) { return false; }
26
+ for (i = 0; i < containers.length; i++) {
27
+ try { layer = containers[i].itemLayer; if (layer && layer.locked === true) { return true; } } catch (e1) { /* weiter */ }
28
+ }
29
+ return false;
30
+ }
31
+
32
+ /* Bezieht ein Lauf seine Schrift aus einem Absatz- oder Zeichenformat, das ersetzt wird? */
33
+ function idcRunFontFromChangedStyle(range, font, stylesChanged) {
34
+ var i, style, styleFont, name;
35
+ var candidates = [];
36
+ try { candidates.push(range.appliedCharacterStyle); } catch (e0) { /* keins */ }
37
+ try { candidates.push(range.appliedParagraphStyle); } catch (e1) { /* keins */ }
38
+ for (i = 0; i < candidates.length; i++) {
39
+ style = candidates[i];
40
+ if (style === null || style === undefined) { continue; }
41
+ try { name = String(style.name); } catch (e2) { continue; }
42
+ styleFont = idcReadFontLoose(style);
43
+ if (styleFont.family === null || !idcSameFamily(styleFont.family, font.family)) { continue; }
44
+ if (styleFont.style !== null && font.style !== null && !idcSameText(styleFont.style, font.style)) { continue; }
45
+ if (idcStyleInList(stylesChanged, name)) { return true; }
46
+ }
47
+ return false;
48
+ }
49
+
50
+ function idcStyleInList(list, name) {
51
+ var i;
52
+ for (i = 0; i < list.length; i++) { if (list[i].name === name) { return true; } }
53
+ return false;
54
+ }
55
+
56
+ function idcSetFontOn(target, font) {
57
+ if (font.object !== null) { target.appliedFont = font.object; }
58
+ else { target.appliedFont = font.name; }
59
+ }
60
+
61
+ IDC.register('replace_font', { needs_document: true, read_only: false, ruler: 'none' },
62
+ function (ctx) {
63
+ var args = ctx.args;
64
+ var doc = ctx.doc;
65
+ var fromFamily = args.from_family;
66
+ var fromStyle = idcGiven(args.from_style) ? String(args.from_style) : null;
67
+ var toFamily = args.to_family;
68
+ var toStyle = idcGiven(args.to_style) ? String(args.to_style) : null;
69
+ var scope = idcGiven(args.scope) ? String(args.scope) : 'all';
70
+ var includeMasters = args.include_master_pages !== false;
71
+ var includeLocked = args.include_locked_layers === true;
72
+ var dryRun = args.dry_run === true;
73
+ var fonts, targets = {}, missingStyles = [], stylesChanged = [], runsChanged = 0, charsChanged = 0, storiesTouched = 0;
74
+ var skippedLocked = [], oversetStories = [], remaining = 0, remainingWhere = [];
75
+ var kinds, k, styles, i, font, s, story, storyId, ranges, r, range, touched, length, target, styleName;
76
+
77
+ if (typeof fromFamily !== 'string' || fromFamily === '') { IDC.fail('invalid_argument', 'from_family is required.', null); }
78
+ if (typeof toFamily !== 'string' || toFamily === '') { IDC.fail('invalid_argument', 'to_family is required.', null); }
79
+ if (scope !== 'all' && scope !== 'styles_only' && scope !== 'local_only') {
80
+ IDC.fail('invalid_argument', 'scope must be all, styles_only or local_only.', { scope: scope });
81
+ }
82
+ if (fromStyle === null && toStyle !== null) {
83
+ IDC.fail('invalid_argument', 'to_style needs from_style; without from_style every style of the family is replaced by the same style name in the target family.', null);
84
+ }
85
+ if (fromStyle !== null && toStyle === null) { toStyle = fromStyle; }
86
+ if (idcSameFamily(fromFamily, toFamily) && (fromStyle === null || idcSameText(fromStyle, toStyle))) {
87
+ IDC.fail('invalid_argument', 'from and to name the same font.', { from_family: fromFamily, to_family: toFamily });
88
+ }
89
+ fonts = idcFontLookup();
90
+ if (!fonts.hasFamily(toFamily)) {
91
+ IDC.fail('not_found', 'Target font family "' + toFamily + '" is not installed.', { to_family: toFamily, available_styles: [] });
92
+ }
93
+ if (fromStyle !== null) { targets[fromStyle.toLowerCase()] = idcTargetFont(fonts, toFamily, toStyle); }
94
+
95
+ /* Zielschnitt je Quellschnitt: gleicher Name in der Zielfamilie, sonst Warnung und Auslassen. */
96
+ function targetFor(styleOfRun) {
97
+ var key = styleOfRun === null ? '' : String(styleOfRun).toLowerCase();
98
+ if (fromStyle !== null && !idcSameText(styleOfRun, fromStyle)) { return null; }
99
+ if (IDC.hasOwn(targets, key)) { return targets[key]; }
100
+ if (fonts.find(toFamily, styleOfRun) === null) {
101
+ if (!idcInList(missingStyles, String(styleOfRun))) { missingStyles.push(String(styleOfRun)); }
102
+ targets[key] = null;
103
+ return null;
104
+ }
105
+ targets[key] = idcTargetFont(fonts, toFamily, styleOfRun);
106
+ return targets[key];
107
+ }
108
+
109
+ /* Formate. */
110
+ if (scope !== 'local_only') {
111
+ kinds = ['paragraph', 'character'];
112
+ for (k = 0; k < kinds.length; k++) {
113
+ styles = idcAllStyles(doc, kinds[k]);
114
+ for (i = 0; i < styles.length; i++) {
115
+ font = idcReadFontLoose(styles[i]);
116
+ if (font.family === null || !idcSameFamily(font.family, fromFamily)) { continue; }
117
+ target = targetFor(font.style);
118
+ if (target === null) { continue; }
119
+ try { styleName = String(styles[i].name); } catch (e0) { styleName = ''; }
120
+ if (!dryRun) {
121
+ try { idcSetFontOn(styles[i], target); } catch (e1) {
122
+ IDC.fail('script_error', 'InDesign could not set the font of style "' + styleName + '": ' + String(e1.message ? e1.message : e1), { style: styleName });
123
+ }
124
+ }
125
+ stylesChanged.push({ kind: kinds[k], name: styleName, from_style: font.style, to_style: target.style });
126
+ }
127
+ }
128
+ }
129
+
130
+ /* Lokale Laeufe. */
131
+ if (scope !== 'styles_only') {
132
+ for (s = 0; s < doc.stories.length; s++) {
133
+ story = doc.stories.item(s);
134
+ try { storyId = Number(story.id); } catch (e2) { storyId = null; }
135
+ if (!includeMasters && idcStoryOnMaster(story)) { continue; }
136
+ if (!includeLocked && idcStoryLocked(doc, story)) { skippedLocked.push(storyId); continue; }
137
+ touched = false;
138
+ try { ranges = story.textStyleRanges; } catch (e3) { continue; }
139
+ for (r = 0; r < ranges.length; r++) {
140
+ try { range = ranges.item(r); } catch (e4) { continue; }
141
+ font = idcReadFontLoose(range);
142
+ if (font.family === null || !idcSameFamily(font.family, fromFamily)) { continue; }
143
+ target = targetFor(font.style);
144
+ if (target === null) { continue; }
145
+ /* dry_run: Laeufe, die ihre Schrift nur aus einem Format beziehen, das schon in styles_changed steht, zaehlen nicht (der echte Lauf aendert erst die Formate). */
146
+ if (dryRun && idcRunFontFromChangedStyle(range, font, stylesChanged)) { continue; }
147
+ try { length = Number(range.characters.length); } catch (e5) { length = 0; }
148
+ if (!dryRun) {
149
+ try { idcSetFontOn(range, target); } catch (e6) {
150
+ IDC.fail('script_error', 'InDesign could not set the font of a text run in story ' + storyId + ': ' + String(e6.message ? e6.message : e6), { story_id: storyId });
151
+ }
152
+ }
153
+ runsChanged += 1;
154
+ charsChanged += length;
155
+ touched = true;
156
+ }
157
+ if (touched) {
158
+ storiesTouched += 1;
159
+ if (!dryRun) { try { if (story.overflows === true) { oversetStories.push(storyId); } } catch (e7) { /* egal */ } }
160
+ }
161
+ }
162
+ }
163
+
164
+ /* Nachkontrolle: was von der Ausgangsschrift noch da ist (gesperrte Ebenen, Formate bei local_only ...). */
165
+ if (!dryRun) {
166
+ for (s = 0; s < doc.stories.length; s++) {
167
+ story = doc.stories.item(s);
168
+ try { ranges = story.textStyleRanges; } catch (e8) { continue; }
169
+ for (r = 0; r < ranges.length; r++) {
170
+ try { range = ranges.item(r); font = idcReadFontLoose(range); } catch (e9) { continue; }
171
+ if (font.family !== null && idcSameFamily(font.family, fromFamily) && (fromStyle === null || idcSameText(font.style, fromStyle))) {
172
+ remaining += 1;
173
+ if (remainingWhere.length < 20) {
174
+ try { remainingWhere.push({ story_id: Number(story.id), index: Number(range.characters.item(0).index), style: font.style }); } catch (e10) { /* auslassen */ }
175
+ }
176
+ }
177
+ }
178
+ }
179
+ if (remaining > 0) {
180
+ ctx.warn(remaining + ' run(s) still use "' + fromFamily + '" (locked layers, tables, anchored objects, a style outside the scope, or a style name missing in the target family) – see remaining_where.');
181
+ }
182
+ if (oversetStories.length > 0) {
183
+ ctx.warn(oversetStories.length + ' story/stories overflow after the change – InDesign recomposed every paragraph; check get_frame_lines.');
184
+ }
185
+ if (stylesChanged.length + runsChanged > 0) { ctx.changes = { modified_document_ids: [Number(doc.id)] }; }
186
+ }
187
+ for (i = 0; i < missingStyles.length; i++) {
188
+ ctx.warn('Style "' + missingStyles[i] + '" does not exist in "' + toFamily + '"; runs and styles with it were left alone. Available: ' + fonts.stylesOf(toFamily).join(', ') + '.');
189
+ }
190
+ if (skippedLocked.length > 0) {
191
+ ctx.warn(skippedLocked.length + ' story/stories on locked layers were skipped (include_locked_layers true to change them).');
192
+ }
193
+ if (stylesChanged.length + runsChanged === 0) {
194
+ ctx.warn('"' + fromFamily + (fromStyle === null ? '' : ' ' + fromStyle) + '" is not used in the given scope; nothing to replace.');
195
+ }
196
+
197
+ return {
198
+ from: { family: String(fromFamily), style: fromStyle },
199
+ to: { family: String(toFamily), style: toStyle },
200
+ scope: scope,
201
+ styles_changed: stylesChanged,
202
+ runs_changed: runsChanged,
203
+ characters_changed: charsChanged,
204
+ stories_touched: storiesTouched,
205
+ remaining_occurrences: dryRun ? null : remaining,
206
+ remaining_where: remainingWhere,
207
+ skipped_locked: skippedLocked,
208
+ styles_missing_in_target: missingStyles,
209
+ overset_stories: oversetStories,
210
+ dry_run: dryRun
211
+ };
212
+ });