layoutbridge-light 0.0.0-stage → 1.0.1

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 (92) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/LICENSE +26 -0
  3. package/README.de.md +367 -0
  4. package/README.md +353 -2
  5. package/THIRD-PARTY-NOTICES.md +2878 -0
  6. package/dist/src/mcp/catalog.js +131 -0
  7. package/dist/src/mcp/config.js +211 -0
  8. package/dist/src/mcp/content-credentials.js +65 -0
  9. package/dist/src/mcp/dispatch.js +171 -0
  10. package/dist/src/mcp/epub-check.js +331 -0
  11. package/dist/src/mcp/epub-orientation.js +132 -0
  12. package/dist/src/mcp/license/free-tools.js +16 -0
  13. package/dist/src/mcp/license/gate.js +210 -0
  14. package/dist/src/mcp/license/index.js +107 -0
  15. package/dist/src/mcp/license/instance-name.js +57 -0
  16. package/dist/src/mcp/license/key-source.js +59 -0
  17. package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
  18. package/dist/src/mcp/license/placeholder-verifier.js +10 -0
  19. package/dist/src/mcp/license/polar-ids.js +100 -0
  20. package/dist/src/mcp/license/polar-verifier.js +285 -0
  21. package/dist/src/mcp/license/product-ids.js +73 -0
  22. package/dist/src/mcp/license/state-store.js +91 -0
  23. package/dist/src/mcp/license/types.js +11 -0
  24. package/dist/src/mcp/logging.js +271 -0
  25. package/dist/src/mcp/pdf-postprocess.js +798 -0
  26. package/dist/src/mcp/product-info.js +54 -0
  27. package/dist/src/mcp/product.js +56 -0
  28. package/dist/src/mcp/server.js +253 -0
  29. package/dist/src/mcp/setup.js +680 -0
  30. package/dist/src/mcp/start.cjs +117 -0
  31. package/dist/src/mcp/target-folder.js +25 -0
  32. package/dist/src/mcp/tool-definition.js +2 -0
  33. package/dist/src/mcp/tools.js +2680 -0
  34. package/dist/src/mcp/typography-rules.js +70 -0
  35. package/dist/src/transport/com.js +436 -0
  36. package/dist/src/transport/osascript.js +171 -0
  37. package/dist/src/transport/queue.js +19 -0
  38. package/dist/src/transport/script-bundle.js +92 -0
  39. package/dist/src/transport/transport.js +52 -0
  40. package/indesign/commands/10-app.js +6 -0
  41. package/indesign/commands/20-documents.js +517 -0
  42. package/indesign/commands/22-document-setup.js +204 -0
  43. package/indesign/commands/23-save-package.js +258 -0
  44. package/indesign/commands/25-export.js +695 -0
  45. package/indesign/commands/26-export-image.js +202 -0
  46. package/indesign/commands/27-textdefaults.js +151 -0
  47. package/indesign/commands/28-selection.js +174 -0
  48. package/indesign/commands/30-pages.js +1032 -0
  49. package/indesign/commands/31-view.js +310 -0
  50. package/indesign/commands/32-sections.js +203 -0
  51. package/indesign/commands/35-parents.js +448 -0
  52. package/indesign/commands/37-layout.js +837 -0
  53. package/indesign/commands/40-frames.js +886 -0
  54. package/indesign/commands/41-shapes.js +505 -0
  55. package/indesign/commands/42-groups.js +117 -0
  56. package/indesign/commands/43-layers.js +310 -0
  57. package/indesign/commands/44-effects.js +274 -0
  58. package/indesign/commands/45-frame-options.js +247 -0
  59. package/indesign/commands/47-anchored.js +473 -0
  60. package/indesign/commands/50-modify.js +803 -0
  61. package/indesign/commands/52-align.js +139 -0
  62. package/indesign/commands/55-fit.js +106 -0
  63. package/indesign/commands/60-images.js +1154 -0
  64. package/indesign/commands/62-embed.js +120 -0
  65. package/indesign/commands/69-placeholders.js +132 -0
  66. package/indesign/commands/70-text.js +710 -0
  67. package/indesign/commands/71-outlines.js +92 -0
  68. package/indesign/commands/72-place-text.js +476 -0
  69. package/indesign/commands/73-story-lock.js +157 -0
  70. package/indesign/commands/74-stories.js +121 -0
  71. package/indesign/commands/75-tables.js +702 -0
  72. package/indesign/commands/76-text-variables.js +260 -0
  73. package/indesign/commands/77-story-options.js +86 -0
  74. package/indesign/commands/78-frame-lines.js +511 -0
  75. package/indesign/commands/79-check-typography.js +480 -0
  76. package/indesign/commands/80-styles.js +1960 -0
  77. package/indesign/commands/81-composition.js +355 -0
  78. package/indesign/commands/82-object-styles.js +263 -0
  79. package/indesign/commands/83-drop-caps.js +87 -0
  80. package/indesign/commands/83-indents-tabs.js +264 -0
  81. package/indesign/commands/84-import-styles.js +142 -0
  82. package/indesign/commands/85-swatches.js +112 -0
  83. package/indesign/commands/86-gradients.js +185 -0
  84. package/indesign/commands/87-replace-swatch.js +180 -0
  85. package/indesign/commands/88-replace-font.js +212 -0
  86. package/indesign/commands/89-toc-index.js +1002 -0
  87. package/indesign/commands/90-print-on-demand.js +495 -0
  88. package/indesign/commands/91-epub.js +629 -0
  89. package/indesign/commands/92-accessibility.js +1131 -0
  90. package/indesign/interpreter.js +829 -0
  91. package/package.json +41 -4
  92. package/rules/typography-rules.json +32 -0
@@ -0,0 +1,695 @@
1
+ /* Bereich A – Export und Preflight. */
2
+
3
+ /* ------------------------------------------------------------ export_pdf */
4
+
5
+ /*
6
+ * Vorgaben ohne Angabe im Aufruf: Hauptpreset = lokalisiertes "High Quality
7
+ * Print", kein zweiter Durchgang. Hauswerte (Preset-Namen, Groessenschwelle)
8
+ * kommen aus der Serverkonfiguration und stehen dann schon in den args
9
+ * (E-54, A-6.3).
10
+ */
11
+ /* Lokalisierte Namen der mitgelieferten Vorgabe "High Quality Print". */
12
+ var IDC_HQ_PRESET_PATTERN = /high quality print|qualitativ hochwertiger druck|hochwertiger druck/i;
13
+
14
+ function idcPdfPresetByName(name) {
15
+ var preset;
16
+ if (typeof name !== 'string' || name.length === 0) { return null; }
17
+ try {
18
+ preset = app.pdfExportPresets.itemByName(name);
19
+ if (preset && preset.isValid === true) { return preset; }
20
+ } catch (e) { /* nicht vorhanden */ }
21
+ return null;
22
+ }
23
+
24
+ function idcPdfPresetNames() {
25
+ var names = [];
26
+ var i;
27
+ try {
28
+ for (i = 0; i < app.pdfExportPresets.length; i++) {
29
+ names.push(String(app.pdfExportPresets.item(i).name));
30
+ }
31
+ } catch (e) { /* leer */ }
32
+ return names;
33
+ }
34
+
35
+ function idcHighQualityPreset() {
36
+ var i, preset;
37
+ try {
38
+ for (i = 0; i < app.pdfExportPresets.length; i++) {
39
+ preset = app.pdfExportPresets.item(i);
40
+ if (IDC_HQ_PRESET_PATTERN.test(String(preset.name))) { return preset; }
41
+ }
42
+ } catch (e) { /* keine */ }
43
+ return null;
44
+ }
45
+
46
+ /* Seitenindex zu einem Namen aus einem Seitenbereich; "+3" = dritte Seite absolut. */
47
+ function idcPageIndexByName(doc, text) {
48
+ var name = text;
49
+ var i, n;
50
+ if (name.charAt(0) === '+') {
51
+ n = Number(name.substr(1));
52
+ if (isNaN(n) || n < 1 || n > doc.pages.length) { return -1; }
53
+ return n - 1;
54
+ }
55
+ for (i = 0; i < doc.pages.length; i++) {
56
+ if (String(doc.pages.item(i).name) === name) { return i; }
57
+ }
58
+ return -1;
59
+ }
60
+
61
+ function idcTrim(text) {
62
+ return String(text).replace(/^\s+/, '').replace(/\s+$/, '');
63
+ }
64
+
65
+ /*
66
+ * Seitenbereich wie in InDesigns Exportdialog ("6-8", "1, 3-5", "+2-+4")
67
+ * gegen die angezeigten Seitennamen aufloesen. Liefert die Zahl der Seiten.
68
+ */
69
+ function idcCountPagesInRange(doc, rangeText) {
70
+ var parts = String(rangeText).split(',');
71
+ var count = 0;
72
+ var p, part, single, from, to, cut, left, right, found;
73
+
74
+ for (p = 0; p < parts.length; p++) {
75
+ part = idcTrim(parts[p]);
76
+ if (part === '') { continue; }
77
+ single = idcPageIndexByName(doc, part);
78
+ if (single >= 0) { count += 1; continue; }
79
+
80
+ found = false;
81
+ for (cut = 1; cut < part.length; cut++) {
82
+ if (part.charAt(cut) !== '-') { continue; }
83
+ left = idcTrim(part.substr(0, cut));
84
+ right = idcTrim(part.substr(cut + 1));
85
+ from = idcPageIndexByName(doc, left);
86
+ to = idcPageIndexByName(doc, right);
87
+ if (from >= 0 && to >= 0) { found = true; break; }
88
+ }
89
+ if (!found) {
90
+ IDC.fail('invalid_argument',
91
+ 'page_range "' + rangeText + '": "' + part + '" names no page of this document (page names are the printed page numbers; "+3" addresses the third page).',
92
+ { page_range: String(rangeText), part: part });
93
+ }
94
+ if (to < from) {
95
+ IDC.fail('invalid_argument', 'page_range "' + rangeText + '": "' + part + '" runs backwards.',
96
+ { page_range: String(rangeText), part: part });
97
+ }
98
+ count += to - from + 1;
99
+ }
100
+ if (count === 0) {
101
+ IDC.fail('invalid_argument', 'page_range "' + rangeText + '" selects no page.', { page_range: String(rangeText) });
102
+ }
103
+ return count;
104
+ }
105
+
106
+ /*
107
+ * Dokumentanschnitt nur fuer einen Aufruf (A-43 Nr. 1, KDP-Befund 1): Die
108
+ * mitgelieferten Vorgaben ([PDF/X-1a:2001] u. a.) exportieren ohne
109
+ * Anschnitt, und sie lassen sich nicht aendern (InDesign 21.6: "Die Datei kann
110
+ * nicht erstellt werden"). Deshalb wird die Vorgabe dupliziert
111
+ * (PDFExportPreset.duplicate(), Adobe-DOM), am Duplikat
112
+ * useDocumentBleedWithPDF gesetzt, damit exportiert und das Duplikat danach
113
+ * entfernt. Ohne Vorgabe (aktuelle Exporteinstellungen) wird
114
+ * app.pdfExportPreferences.useDocumentBleedWithPDF gesetzt und zurueckgestellt.
115
+ * bleed null = wie die Vorgabe.
116
+ */
117
+ function idcExportPdfOnce(doc, file, preset, rangeText, bleed, access) {
118
+ var prefs = app.pdfExportPreferences;
119
+ var prevRange = null;
120
+ var prevView = null;
121
+ var prevBleed = null;
122
+ var rangeSet = false;
123
+ var viewSet = false;
124
+ var bleedSet = false;
125
+ var temp = null;
126
+ var used = preset;
127
+ var settings = idcPdfAccessSettings(access);
128
+ var prevAccess = null;
129
+ var key;
130
+
131
+ try {
132
+ if ((bleed !== null || settings !== null) && preset !== null) {
133
+ try {
134
+ temp = preset.duplicate();
135
+ if (bleed !== null) { temp.useDocumentBleedWithPDF = bleed; }
136
+ if (settings !== null) {
137
+ for (key in settings) { if (IDC.hasOwn(settings, key)) { temp[key] = settings[key]; } }
138
+ }
139
+ used = temp;
140
+ } catch (eDup) {
141
+ IDC.fail('script_error', 'Could not prepare the preset with ' + (bleed !== null ? 'use_document_bleed' : 'the accessibility settings') + ': ' + String(eDup.message ? eDup.message : eDup),
142
+ { preset: String(preset.name) });
143
+ }
144
+ } else {
145
+ if (bleed !== null) {
146
+ prevBleed = prefs.useDocumentBleedWithPDF;
147
+ prefs.useDocumentBleedWithPDF = bleed;
148
+ bleedSet = true;
149
+ }
150
+ if (settings !== null) {
151
+ prevAccess = {};
152
+ for (key in settings) {
153
+ if (IDC.hasOwn(settings, key)) { prevAccess[key] = prefs[key]; prefs[key] = settings[key]; }
154
+ }
155
+ }
156
+ }
157
+ try {
158
+ prevView = prefs.viewPDF;
159
+ prefs.viewPDF = false;
160
+ viewSet = true;
161
+ } catch (e0) { /* nicht jede Fassung kennt viewPDF */ }
162
+ prevRange = prefs.pageRange;
163
+ prefs.pageRange = rangeText === null ? PageRange.ALL_PAGES : rangeText;
164
+ rangeSet = true;
165
+ if (used !== null) { doc.exportFile(ExportFormat.PDF_TYPE, file, false, used); }
166
+ else { doc.exportFile(ExportFormat.PDF_TYPE, file, false); }
167
+ } catch (e) {
168
+ if (e && e.isIdcError) { throw e; }
169
+ IDC.fail('io_error', 'PDF export failed: ' + String(e.message ? e.message : e),
170
+ { path: IDC.fsPath(file), preset: preset === null ? null : String(preset.name) });
171
+ } finally {
172
+ if (rangeSet) { try { prefs.pageRange = prevRange; } catch (e1) { /* egal */ } }
173
+ if (viewSet) { try { prefs.viewPDF = prevView; } catch (e2) { /* egal */ } }
174
+ if (bleedSet) { try { prefs.useDocumentBleedWithPDF = prevBleed; } catch (e3) { /* egal */ } }
175
+ if (prevAccess !== null) {
176
+ for (key in prevAccess) { if (IDC.hasOwn(prevAccess, key)) { try { prefs[key] = prevAccess[key]; } catch (e5) { /* egal */ } } }
177
+ }
178
+ if (temp !== null) { try { temp.remove(); } catch (e4) { /* bleibt als Kopie stehen */ } }
179
+ }
180
+ /*
181
+ * Frisches File-Objekt: ExtendScript haelt length im Objekt fest, nach dem
182
+ * zweiten Export stand sonst noch die Groesse des ersten drin (am lebenden
183
+ * InDesign 21.5 am 04.09.2026 gesehen).
184
+ */
185
+ return IDC.fileSize(IDC.fileFor(IDC.fsPath(file)));
186
+ }
187
+
188
+ IDC.register('export_pdf', { needs_document: true, read_only: false, ruler: 'none' },
189
+ function (ctx) {
190
+ var args = ctx.args;
191
+ var doc = ctx.doc;
192
+ var file, rangeText, exportedCount, preset, presetUsed, bytes, fallbackName, threshold, fallback;
193
+
194
+ if (typeof args.path !== 'string' || args.path.length === 0) {
195
+ IDC.fail('invalid_argument', 'path is required (the target file path, not output_path).', null);
196
+ }
197
+ file = IDC.fileFor(args.path);
198
+ if (!IDC.fileParentExists(file)) {
199
+ IDC.fail('io_error', 'The folder for the target path does not exist: ' + args.path, { path: args.path });
200
+ }
201
+
202
+ rangeText = null;
203
+ if (args.page_range !== undefined && args.page_range !== null && idcTrim(args.page_range) !== ''
204
+ && idcTrim(args.page_range).toLowerCase() !== 'all') {
205
+ rangeText = idcTrim(args.page_range);
206
+ exportedCount = idcCountPagesInRange(doc, rangeText);
207
+ } else {
208
+ exportedCount = doc.pages.length;
209
+ }
210
+
211
+ /* Preset-Kette: Wunschpreset, sonst High Quality Print, sonst aktuelle Vorgaben. */
212
+ var wanted = null;
213
+ if (args.preset !== undefined && args.preset !== null && String(args.preset) !== '') {
214
+ wanted = String(args.preset);
215
+ }
216
+ preset = wanted === null ? null : idcPdfPresetByName(wanted);
217
+ presetUsed = wanted;
218
+ if (preset === null) {
219
+ preset = idcHighQualityPreset();
220
+ if (preset !== null) {
221
+ presetUsed = String(preset.name);
222
+ if (wanted !== null) {
223
+ ctx.warn('PDF preset "' + wanted + '" not found; used "' + presetUsed + '" instead. Available: ' + idcPdfPresetNames().join(', '));
224
+ }
225
+ } else {
226
+ presetUsed = '[current export preferences]';
227
+ if (wanted !== null) {
228
+ ctx.warn('PDF preset "' + wanted + '" not found and no High Quality Print preset either; exported with the current export preferences. Available: ' + idcPdfPresetNames().join(', '));
229
+ } else {
230
+ ctx.warn('No High Quality Print preset found; exported with the current export preferences. Available: ' + idcPdfPresetNames().join(', '));
231
+ }
232
+ }
233
+ }
234
+
235
+ var bleed = null;
236
+ if (args.use_document_bleed !== undefined && args.use_document_bleed !== null) {
237
+ if (args.use_document_bleed !== true && args.use_document_bleed !== false) {
238
+ IDC.fail('invalid_argument', 'use_document_bleed must be true or false.', { use_document_bleed: args.use_document_bleed });
239
+ }
240
+ bleed = args.use_document_bleed;
241
+ }
242
+ var docBleed = idcDocumentBleedMm(doc);
243
+ if (bleed === true && docBleed !== null && docBleed.top === 0 && docBleed.bottom === 0 && docBleed.inside_or_left === 0 && docBleed.outside_or_right === 0) {
244
+ ctx.warn('use_document_bleed is true, but the document bleed (Anschnitt) is 0 mm on every edge – set_document_bleed first.');
245
+ }
246
+
247
+ var access = idcPdfAccessArgs(args, false);
248
+ var accessResult = access.any ? idcPrepareAccessibleExport(ctx, doc, access, preset, false) : null;
249
+
250
+ bytes = idcExportPdfOnce(doc, file, preset, rangeText, bleed, access);
251
+
252
+ fallbackName = args.fallback_preset === undefined || args.fallback_preset === null
253
+ ? '' : String(args.fallback_preset);
254
+ threshold = args.fallback_over_bytes === undefined || args.fallback_over_bytes === null
255
+ ? 0 : Number(args.fallback_over_bytes);
256
+
257
+ if (fallbackName !== '' && threshold > 0 && bytes !== null && bytes > threshold) {
258
+ fallback = idcPdfPresetByName(fallbackName);
259
+ if (fallback === null) {
260
+ ctx.warn('PDF is ' + bytes + ' bytes (over ' + threshold + '), but fallback preset "' + fallbackName + '" was not found; first export kept.');
261
+ } else if (access.tagged === true && idcPresetIsAcrobat4(fallback)) {
262
+ ctx.warn('PDF is ' + bytes + ' bytes (over ' + threshold + '), but fallback preset "' + fallbackName + '" writes PDF 1.3 (Acrobat 4) without tags; the first, tagged export is kept.');
263
+ fallback = null;
264
+ } else {
265
+ ctx.warn('PDF was ' + bytes + ' bytes (over ' + threshold + '); exported again with "' + fallbackName + '". A spread preset turns single pages into spreads (Doppelseiten) – check exported_page_count.');
266
+ }
267
+ if (fallback !== null) {
268
+ bytes = idcExportPdfOnce(doc, file, fallback, rangeText, bleed, access);
269
+ preset = fallback;
270
+ presetUsed = fallbackName;
271
+ if (bytes !== null && bytes > threshold) {
272
+ ctx.warn('Still ' + bytes + ' bytes after the fallback export.');
273
+ }
274
+ }
275
+ }
276
+
277
+ ctx.changes = { exported_files: [IDC.fsPath(file)] };
278
+
279
+ var answer = {
280
+ path: IDC.fsPath(file),
281
+ page_count: exportedCount,
282
+ exported_page_count: exportedCount,
283
+ document_page_count: doc.pages.length,
284
+ bytes: bytes,
285
+ preset_used: presetUsed,
286
+ page_range_used: rangeText === null ? 'all' : rangeText,
287
+ document_bleed_used: idcBleedInEffect(preset, bleed),
288
+ document_bleed_mm: docBleed
289
+ };
290
+ if (accessResult !== null) { idcAddAccessAnswer(answer, accessResult); }
291
+ return answer;
292
+ });
293
+
294
+ /* Anschnitt des Dokuments in mm (innen/links, aussen/rechts wie InDesign). */
295
+ function idcDocumentBleedMm(doc) {
296
+ var prefs = doc.documentPreferences;
297
+ try {
298
+ return {
299
+ top: IDC.round(Number(prefs.documentBleedTopOffset)),
300
+ bottom: IDC.round(Number(prefs.documentBleedBottomOffset)),
301
+ inside_or_left: IDC.round(Number(prefs.documentBleedInsideOrLeftOffset)),
302
+ outside_or_right: IDC.round(Number(prefs.documentBleedOutsideOrRightOffset))
303
+ };
304
+ } catch (e) { return null; }
305
+ }
306
+
307
+ /* Hat der Export den Dokumentanschnitt benutzt? Aufrufwert, sonst die Vorgabe; null = unbekannt. */
308
+ function idcBleedInEffect(preset, bleed) {
309
+ if (bleed !== null) { return bleed; }
310
+ try {
311
+ if (preset !== null) { return preset.useDocumentBleedWithPDF === true; }
312
+ return app.pdfExportPreferences.useDocumentBleedWithPDF === true;
313
+ } catch (e) { return null; }
314
+ }
315
+
316
+ /* ---------------------------------------------------- preflight_document */
317
+
318
+ function idcPreflightProfileNames() {
319
+ var names = [];
320
+ var i;
321
+ try {
322
+ for (i = 0; i < app.preflightProfiles.length; i++) {
323
+ names.push(String(app.preflightProfiles.item(i).name));
324
+ }
325
+ } catch (e) { /* leer */ }
326
+ return names;
327
+ }
328
+
329
+ function idcText(value) {
330
+ if (value === null || value === undefined) { return null; }
331
+ return String(value);
332
+ }
333
+
334
+ /*
335
+ * aggregatedResults ist [Dokumentname, Profilname, Eintraege[]]. Die Form
336
+ * eines Eintrags ist in Adobes Dokumentation nur als "Array of Strings or
337
+ * Numbers or Arrays" beschrieben; deshalb werden beide beobachtete Formen
338
+ * gelesen: [Kategorie, Beschreibung, Seite, Objekt] und
339
+ * [Kategorie, Beschreibung, [[Seite, Objekt], ...]].
340
+ */
341
+ function idcParsePreflightFindings(raw) {
342
+ var out = [];
343
+ var list, i, entry, category, description, rest, j, sub;
344
+
345
+ if (!IDC.isArray(raw)) { return out; }
346
+ list = (raw.length >= 3 && IDC.isArray(raw[2])) ? raw[2] : raw;
347
+
348
+ for (i = 0; i < list.length; i++) {
349
+ entry = list[i];
350
+ if (!IDC.isArray(entry)) { continue; }
351
+ category = idcText(entry[0]);
352
+ description = idcText(entry[1]);
353
+ rest = entry.length > 2 ? entry[2] : undefined;
354
+
355
+ if (IDC.isArray(rest)) {
356
+ if (rest.length === 0) {
357
+ out.push({ category: category, description: description, page_name: null, item: null });
358
+ }
359
+ for (j = 0; j < rest.length; j++) {
360
+ sub = rest[j];
361
+ if (IDC.isArray(sub)) {
362
+ out.push({
363
+ category: category,
364
+ description: description,
365
+ page_name: sub.length > 0 ? idcText(sub[0]) : null,
366
+ item: sub.length > 1 ? idcText(sub[1]) : null
367
+ });
368
+ } else {
369
+ out.push({
370
+ category: category,
371
+ description: description,
372
+ page_name: idcText(sub),
373
+ item: entry.length > 3 ? idcText(entry[3]) : null
374
+ });
375
+ }
376
+ }
377
+ } else {
378
+ out.push({
379
+ category: category,
380
+ description: description,
381
+ page_name: rest === undefined ? null : idcText(rest),
382
+ item: entry.length > 3 ? idcText(entry[3]) : null
383
+ });
384
+ }
385
+ }
386
+ return out;
387
+ }
388
+
389
+ IDC.register('preflight_document', { needs_document: true, read_only: true, ruler: 'none' },
390
+ function (ctx) {
391
+ var args = ctx.args;
392
+ var doc = ctx.doc;
393
+ var profiles = idcPreflightProfileNames();
394
+ var maxFindings, profile, working, process, waited, summary, raw, findings, capped;
395
+
396
+ if (args.list_profiles_only === true) {
397
+ return { profiles: profiles, profile_count: profiles.length };
398
+ }
399
+
400
+ maxFindings = args.max_findings === undefined || args.max_findings === null ? 100 : Number(args.max_findings);
401
+ if (isNaN(maxFindings) || maxFindings < 1 || maxFindings > 500) {
402
+ IDC.fail('invalid_argument', 'max_findings must be between 1 and 500.', { max_findings: args.max_findings });
403
+ }
404
+
405
+ profile = null;
406
+ if (args.profile_name !== undefined && args.profile_name !== null && String(args.profile_name) !== '') {
407
+ try {
408
+ profile = app.preflightProfiles.itemByName(String(args.profile_name));
409
+ if (!profile || profile.isValid !== true) { profile = null; }
410
+ } catch (e) { profile = null; }
411
+ if (profile === null) {
412
+ IDC.fail('not_found', 'Preflight profile "' + args.profile_name + '" not found.',
413
+ { profile_name: String(args.profile_name), available: profiles });
414
+ }
415
+ } else {
416
+ /*
417
+ * Das Arbeitsprofil des Dokuments kann ein ungueltiges Objekt sein –
418
+ * oder laut Adobes DOM auch nur ein Name (String). Beides abfangen.
419
+ */
420
+ try {
421
+ working = doc.preflightOptions.preflightWorkingProfile;
422
+ if (typeof working === 'string' && working !== '') {
423
+ working = app.preflightProfiles.itemByName(working);
424
+ }
425
+ if (working && working.isValid === true) { profile = working; }
426
+ } catch (e2) { profile = null; }
427
+ if (profile === null) {
428
+ if (app.preflightProfiles.length === 0) {
429
+ IDC.fail('invalid_state', 'InDesign has no preflight profile at all.', null);
430
+ }
431
+ profile = app.preflightProfiles.item(0);
432
+ ctx.warn('The document has no valid working profile; used the first available profile "' + String(profile.name) + '".');
433
+ }
434
+ }
435
+
436
+ try {
437
+ process = app.preflightProcesses.add(doc, profile);
438
+ } catch (e3) {
439
+ IDC.fail('script_error', 'Could not start the preflight process: ' + String(e3.message ? e3.message : e3), null);
440
+ }
441
+
442
+ try {
443
+ /*
444
+ * waitForProcess(sekunden) blockiert, bis der Prozess fertig ist oder
445
+ * die Zeit um ist. Adobes Dokumentation und alle bekannten Praxisskripte
446
+ * rufen es genau einmal auf und werten den Rueckgabewert nicht aus –
447
+ * am lebenden InDesign 21.5 kam er sofort und ohne true zurueck, eine
448
+ * Warteschleife darauf drehte leer (04.09.2026). Deshalb: einmal warten,
449
+ * dann lesen.
450
+ */
451
+ var startedAt = new Date().getTime();
452
+ try { process.waitForProcess(600); } catch (eWait) { /* dann eben ohne Warten lesen */ }
453
+ waited = Math.round((new Date().getTime() - startedAt) / 1000);
454
+ if (waited >= 600) {
455
+ IDC.fail('timeout', 'Preflight did not finish within ' + waited + ' seconds.', null);
456
+ }
457
+ summary = idcTrim(String(process.processResults));
458
+ raw = process.aggregatedResults;
459
+ findings = idcParsePreflightFindings(raw);
460
+ } finally {
461
+ try { process.remove(); } catch (e4) { /* Prozess bleibt dann stehen, stoert nicht */ }
462
+ }
463
+
464
+ capped = findings.length > maxFindings ? findings.slice(0, maxFindings) : findings;
465
+ if (findings.length > maxFindings) {
466
+ ctx.warn(findings.length + ' findings, only the first ' + maxFindings + ' returned (max_findings).');
467
+ }
468
+
469
+ return {
470
+ profile_used: String(profile.name),
471
+ summary: summary,
472
+ error_count: findings.length,
473
+ findings: capped
474
+ };
475
+ });
476
+
477
+ /* ------------------------------------- Barrierefreiheit beim Export (A-80, Sitzung 82) */
478
+
479
+ /*
480
+ * Neue, optionale Parameter an export_pdf und export_interactive_pdf (E-117,
481
+ * E-120, E-121). Ohne sie bleibt jeder Aufruf wie bisher. Die Werte gelten
482
+ * nur fuer den Aufruf: an einer Kopie der Vorgabe (wie use_document_bleed)
483
+ * oder an den Exporteinstellungen, die danach zurueckgestellt werden
484
+ * (Beleg 10 der Vorabpruefung: Vorgabe und Einstellungen danach unveraendert).
485
+ */
486
+ function idcAccBoolArg(args, field) {
487
+ if (!idcGiven(args[field])) { return null; }
488
+ if (args[field] !== true && args[field] !== false) {
489
+ IDC.fail('invalid_argument', field + ' must be true or false.', { value: args[field] });
490
+ }
491
+ return args[field];
492
+ }
493
+
494
+ function idcPdfAccessArgs(args, interactive) {
495
+ var a = { any: false, tagged: null, bookmarks: null, tab_order: null, display_title: null, language: null, title: null, pdfua: false, artifact_paths: false };
496
+ a.tagged = idcAccBoolArg(args, 'tagged');
497
+ if (!interactive) { a.bookmarks = idcAccBoolArg(args, 'bookmarks'); }
498
+ if (interactive) { a.tab_order = idcAccBoolArg(args, 'tab_order_from_structure'); }
499
+ if (idcGiven(args.display_title)) {
500
+ if (args.display_title !== 'document_title' && args.display_title !== 'file_name') {
501
+ IDC.fail('invalid_argument', 'display_title must be document_title or file_name.', { value: args.display_title });
502
+ }
503
+ a.display_title = args.display_title;
504
+ }
505
+ if (idcGiven(args.pdf_language)) { a.language = idcPdfLanguageArg(args.pdf_language, 'pdf_language'); }
506
+ if (idcGiven(args.title)) {
507
+ if (typeof args.title !== 'string' || idcTrim(args.title) === '') { IDC.fail('invalid_argument', 'title must be a non-empty text.', null); }
508
+ a.title = String(args.title);
509
+ }
510
+ if (idcAccBoolArg(args, 'pdfua_identifier') === true) {
511
+ if (a.tagged === false) {
512
+ IDC.fail('invalid_argument', 'pdfua_identifier needs a tagged PDF; leave tagged out or pass true.', { code: 'pdfua_needs_tags' });
513
+ }
514
+ if (a.display_title === 'file_name') {
515
+ IDC.fail('invalid_argument', 'pdfua_identifier needs display_title document_title (PDF/UA shows the document title).', { code: 'pdfua_needs_document_title' });
516
+ }
517
+ a.tagged = true;
518
+ if (a.display_title === null) { a.display_title = 'document_title'; }
519
+ a.pdfua = true;
520
+ }
521
+ /*
522
+ * Entscheidung des Anbieters 11.11.2026 (Rueckfrage 1 b, E-120 Nachtrag): mit
523
+ * tagged true (auch ueber pdfua_identifier) schliesst der Server die
524
+ * ungetaggten Rahmen- und Tabellenkonturen automatisch als Artefakt ein;
525
+ * artifact_untagged_paths false schaltet es ab, true geht auch ohne tagged.
526
+ */
527
+ a.artifact_explicit = idcAccBoolArg(args, 'artifact_untagged_paths');
528
+ if (a.artifact_explicit === true && a.tagged === false) {
529
+ IDC.fail('invalid_argument', 'artifact_untagged_paths belongs to a tagged PDF; it is pointless with tagged false.', { code: 'artifact_paths_needs_tags' });
530
+ }
531
+ a.artifact_paths = a.artifact_explicit === true || (a.artifact_explicit === null && a.tagged === true);
532
+ if (interactive && a.tagged === true && a.tab_order === null) { a.tab_order = true; }
533
+ a.any = a.tagged !== null || a.bookmarks !== null || a.tab_order !== null || a.display_title !== null || a.language !== null || a.title !== null || a.pdfua || a.artifact_paths || a.artifact_explicit !== null;
534
+ return a;
535
+ }
536
+
537
+ function idcDisplayTitleEnum(name) {
538
+ if (typeof PdfDisplayTitleOptions === 'undefined') {
539
+ IDC.fail('invalid_state', 'This InDesign has no display title setting (PdfDisplayTitleOptions).', null);
540
+ }
541
+ return name === 'document_title' ? PdfDisplayTitleOptions.DISPLAY_DOCUMENT_TITLE : PdfDisplayTitleOptions.DISPLAY_FILE_NAME;
542
+ }
543
+
544
+ /* Einstellungen fuer den Druck-PDF-Export, null wenn keine. */
545
+ function idcPdfAccessSettings(access) {
546
+ var s = {}, any = false;
547
+ if (access === undefined || access === null || !access.any) { return null; }
548
+ if (access.tagged !== null) { s.includeStructure = access.tagged; any = true; }
549
+ if (access.bookmarks !== null) { s.includeBookmarks = access.bookmarks; any = true; }
550
+ if (access.display_title !== null) { s.pdfDisplayTitle = idcDisplayTitleEnum(access.display_title); any = true; }
551
+ if (access.language !== null) { s.defaultDocumentLanguage = access.language.value; any = true; }
552
+ return any ? s : null;
553
+ }
554
+
555
+ function idcPresetIsAcrobat4(preset) {
556
+ try {
557
+ if (typeof AcrobatCompatibility === 'undefined') { return false; }
558
+ if (preset === null) { return IDC.enumIs(app.pdfExportPreferences.acrobatCompatibility, AcrobatCompatibility.ACROBAT_4); }
559
+ return IDC.enumIs(preset.acrobatCompatibility, AcrobatCompatibility.ACROBAT_4);
560
+ } catch (e) { return false; }
561
+ }
562
+
563
+ /* Sprache, mit der exportiert wird: Aufruf, sonst Vorgabe bzw. Exporteinstellung. */
564
+ function idcExportLanguage(access, preset, interactive) {
565
+ if (access.language !== null) { return { value: access.language.value, source: 'call' }; }
566
+ try {
567
+ if (interactive) { return { value: String(app.interactivePDFExportPreferences.defaultDocumentLanguage), source: 'interactive export settings' }; }
568
+ if (preset !== null) { return { value: String(preset.defaultDocumentLanguage), source: 'preset "' + String(preset.name) + '"' }; }
569
+ return { value: String(app.pdfExportPreferences.defaultDocumentLanguage), source: 'export settings' };
570
+ } catch (e) { return { value: null, source: 'unknown' }; }
571
+ }
572
+
573
+ /*
574
+ * Vor dem Export: Kompatibilitaet, Titel, Pruefung fuer die PDF/UA-Kennung
575
+ * (Entscheidung des Anbieters 11.11.2026, Mittelweg: Kennung nur nach fehlerfreiem
576
+ * check_accessibility; sonst invalid_state mit den Befunden, E-121).
577
+ */
578
+ function idcPrepareAccessibleExport(ctx, doc, access, preset, interactive) {
579
+ var r = { access: access, title_set: null, pdfua_check: null, language: null };
580
+ var previous = null, check, errors = [], warnings = 0, i;
581
+
582
+ if (!interactive && access.tagged === true && idcPresetIsAcrobat4(preset)) {
583
+ IDC.fail('invalid_argument', 'The preset ' + (preset === null ? '(current export settings)' : '"' + String(preset.name) + '"') + ' writes PDF 1.3 (Acrobat 4), which has no tags. Choose a preset with Acrobat 5 or later for tagged true.',
584
+ { code: 'preset_without_tags', preset: preset === null ? null : String(preset.name) });
585
+ }
586
+ r.language = idcExportLanguage(access, preset, interactive);
587
+ if (access.pdfua) {
588
+ /* Live-Fund Sitzung 82: documentTitle = "" laesst InDesign 21.6 unbeachtet – deshalb erst pruefen, dann den Titel schreiben. */
589
+ check = idcAccessibilityFindings(doc, { pdf_language: r.language.value, pdf_language_source: r.language.source, artifact_untagged_paths: access.artifact_paths, planned_title: access.title });
590
+ for (i = 0; i < check.findings.length; i++) {
591
+ if (check.findings[i].severity === 'error') { errors.push(check.findings[i]); } else { warnings += 1; }
592
+ }
593
+ if (errors.length > 0) {
594
+ IDC.fail('invalid_state', 'No PDF/UA identifier: check_accessibility reports ' + errors.length + ' error(s) for this document and these export settings; nothing was exported. Fix them, or export without pdfua_identifier.',
595
+ { code: 'accessibility_check_failed', errors: errors.length, warnings: warnings, findings: errors.length > 50 ? errors.slice(0, 50) : errors });
596
+ }
597
+ r.pdfua_check = { passed: true, errors: 0, warnings: warnings };
598
+ }
599
+ if (access.title !== null) {
600
+ try { previous = String(doc.metadataPreferences.documentTitle); } catch (e0) { previous = null; }
601
+ try { doc.metadataPreferences.documentTitle = access.title; } catch (e1) {
602
+ IDC.fail('script_error', 'InDesign could not set the document title: ' + String(e1.message ? e1.message : e1), null);
603
+ }
604
+ r.title_set = { value: access.title, previous: previous };
605
+ }
606
+ if ((access.tagged === true) && doc.articles.length > 0 && idcStructureOrderIsArticles(doc) === false) {
607
+ ctx.warn('The document has articles, but the tagged PDF does not use them for the reading order (define_article use_for_tagged_pdf true).');
608
+ }
609
+ if (r.title_set !== null) { ctx.warn('title was written into the document (File Info) and stays there; save or close without saving as you wish.'); }
610
+ return r;
611
+ }
612
+
613
+ function idcAddAccessAnswer(answer, r) {
614
+ answer.tagged = r.access.tagged;
615
+ answer.display_title = r.access.display_title;
616
+ answer.pdf_language_used = r.language.value;
617
+ answer.pdf_language_source = r.language.source;
618
+ answer.title_set = r.title_set;
619
+ answer.pdfua_check = r.pdfua_check;
620
+ answer.pdfua_identifier_requested = r.access.pdfua;
621
+ answer.artifact_untagged_paths_requested = r.access.artifact_paths;
622
+ }
623
+
624
+ /* ----------------------------------------------------- export_interactive_pdf */
625
+
626
+ IDC.register('export_interactive_pdf', { needs_document: true, read_only: false, ruler: 'none' },
627
+ function (ctx) {
628
+ var args = ctx.args;
629
+ var doc = ctx.doc;
630
+ var file, rangeText, exportedCount, access, r, prefs, keys, prev = {}, set = {}, i, bytes, used = {};
631
+
632
+ if (typeof args.path !== 'string' || args.path.length === 0) {
633
+ IDC.fail('invalid_argument', 'path is required (the target file path).', null);
634
+ }
635
+ file = IDC.fileFor(args.path);
636
+ if (!IDC.fileParentExists(file)) {
637
+ IDC.fail('io_error', 'The folder for the target path does not exist: ' + args.path, { path: args.path });
638
+ }
639
+ if (typeof ExportFormat === 'undefined' || ExportFormat.INTERACTIVE_PDF === undefined) {
640
+ IDC.fail('invalid_state', 'This InDesign has no interactive PDF export.', null);
641
+ }
642
+ rangeText = null;
643
+ if (args.page_range !== undefined && args.page_range !== null && idcTrim(args.page_range) !== ''
644
+ && idcTrim(args.page_range).toLowerCase() !== 'all') {
645
+ rangeText = idcTrim(args.page_range);
646
+ exportedCount = idcCountPagesInRange(doc, rangeText);
647
+ } else {
648
+ exportedCount = doc.pages.length;
649
+ }
650
+ access = idcPdfAccessArgs(args, true);
651
+ r = idcPrepareAccessibleExport(ctx, doc, access, null, true);
652
+
653
+ prefs = app.interactivePDFExportPreferences;
654
+ set.pageRange = rangeText === null ? PageRange.ALL_PAGES : rangeText;
655
+ set.viewPDF = false;
656
+ if (access.tagged !== null) { set.includeStructure = access.tagged; }
657
+ if (access.tab_order !== null) { set.usePDFStructureForTabOrder = access.tab_order; }
658
+ if (access.display_title !== null) { set.pdfDisplayTitle = idcDisplayTitleEnum(access.display_title); }
659
+ if (access.language !== null) { set.defaultDocumentLanguage = access.language.value; }
660
+ keys = [];
661
+ for (i in set) { if (IDC.hasOwn(set, i)) { keys.push(i); } }
662
+ try {
663
+ for (i = 0; i < keys.length; i++) {
664
+ try { prev[keys[i]] = prefs[keys[i]]; } catch (eRead) { /* Eigenschaft fehlt */ }
665
+ prefs[keys[i]] = set[keys[i]];
666
+ }
667
+ try { used.tagged = prefs.includeStructure === true; } catch (eU1) { used.tagged = null; }
668
+ try { used.tab_order_from_structure = prefs.usePDFStructureForTabOrder === true; } catch (eU2) { used.tab_order_from_structure = null; }
669
+ try {
670
+ used.display_title = IDC.enumIs(prefs.pdfDisplayTitle, PdfDisplayTitleOptions.DISPLAY_DOCUMENT_TITLE) ? 'document_title' : 'file_name';
671
+ } catch (eU3) { used.display_title = null; }
672
+ try { used.pdf_language = String(prefs.defaultDocumentLanguage); } catch (eU4) { used.pdf_language = null; }
673
+ doc.exportFile(ExportFormat.INTERACTIVE_PDF, file, false);
674
+ } catch (e) {
675
+ if (e && e.isIdcError) { throw e; }
676
+ IDC.fail('io_error', 'Interactive PDF export failed: ' + String(e.message ? e.message : e), { path: IDC.fsPath(file) });
677
+ } finally {
678
+ for (i = 0; i < keys.length; i++) {
679
+ if (IDC.hasOwn(prev, keys[i])) { try { prefs[keys[i]] = prev[keys[i]]; } catch (eBack) { /* egal */ } }
680
+ }
681
+ }
682
+ bytes = IDC.fileSize(IDC.fileFor(IDC.fsPath(file)));
683
+ ctx.changes = { exported_files: [IDC.fsPath(file)] };
684
+ var answer = {
685
+ path: IDC.fsPath(file),
686
+ exported_page_count: exportedCount,
687
+ document_page_count: doc.pages.length,
688
+ bytes: bytes,
689
+ page_range_used: rangeText === null ? 'all' : rangeText,
690
+ settings_used: used
691
+ };
692
+ idcAddAccessAnswer(answer, r);
693
+ answer.tagged = used.tagged;
694
+ return answer;
695
+ });