layoutbridge-light 1.0.0 → 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 (35) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.de.md +31 -2
  3. package/README.md +31 -3
  4. package/THIRD-PARTY-NOTICES.md +2 -2
  5. package/dist/src/mcp/catalog.js +9 -4
  6. package/dist/src/mcp/content-credentials.js +65 -0
  7. package/dist/src/mcp/dispatch.js +8 -1
  8. package/dist/src/mcp/license/polar-ids.js +1 -1
  9. package/dist/src/mcp/pdf-postprocess.js +798 -0
  10. package/dist/src/mcp/server.js +1 -1
  11. package/dist/src/mcp/target-folder.js +25 -0
  12. package/dist/src/mcp/tools.js +275 -20
  13. package/dist/src/mcp/typography-rules.js +1 -1
  14. package/dist/src/transport/com.js +224 -38
  15. package/dist/src/transport/transport.js +22 -0
  16. package/indesign/commands/25-export.js +258 -11
  17. package/indesign/commands/26-export-image.js +1 -1
  18. package/indesign/commands/31-view.js +100 -0
  19. package/indesign/commands/35-parents.js +44 -24
  20. package/indesign/commands/40-frames.js +35 -0
  21. package/indesign/commands/47-anchored.js +142 -0
  22. package/indesign/commands/50-modify.js +25 -3
  23. package/indesign/commands/60-images.js +28 -2
  24. package/indesign/commands/69-placeholders.js +7 -2
  25. package/indesign/commands/70-text.js +39 -4
  26. package/indesign/commands/75-tables.js +27 -1
  27. package/indesign/commands/78-frame-lines.js +59 -0
  28. package/indesign/commands/79-check-typography.js +143 -4
  29. package/indesign/commands/80-styles.js +105 -10
  30. package/indesign/commands/89-toc-index.js +61 -4
  31. package/indesign/commands/91-epub.js +92 -9
  32. package/indesign/commands/92-accessibility.js +1131 -0
  33. package/indesign/interpreter.js +51 -1
  34. package/package.json +1 -1
  35. package/rules/typography-rules.json +3 -2
@@ -114,7 +114,7 @@ function idcCountPagesInRange(doc, rangeText) {
114
114
  * app.pdfExportPreferences.useDocumentBleedWithPDF gesetzt und zurueckgestellt.
115
115
  * bleed null = wie die Vorgabe.
116
116
  */
117
- function idcExportPdfOnce(doc, file, preset, rangeText, bleed) {
117
+ function idcExportPdfOnce(doc, file, preset, rangeText, bleed, access) {
118
118
  var prefs = app.pdfExportPreferences;
119
119
  var prevRange = null;
120
120
  var prevView = null;
@@ -124,21 +124,35 @@ function idcExportPdfOnce(doc, file, preset, rangeText, bleed) {
124
124
  var bleedSet = false;
125
125
  var temp = null;
126
126
  var used = preset;
127
+ var settings = idcPdfAccessSettings(access);
128
+ var prevAccess = null;
129
+ var key;
127
130
 
128
131
  try {
129
- if (bleed !== null && preset !== null) {
132
+ if ((bleed !== null || settings !== null) && preset !== null) {
130
133
  try {
131
134
  temp = preset.duplicate();
132
- temp.useDocumentBleedWithPDF = bleed;
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
+ }
133
139
  used = temp;
134
140
  } catch (eDup) {
135
- IDC.fail('script_error', 'Could not prepare the preset with use_document_bleed: ' + String(eDup.message ? eDup.message : 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),
136
142
  { preset: String(preset.name) });
137
143
  }
138
- } else if (bleed !== null) {
139
- prevBleed = prefs.useDocumentBleedWithPDF;
140
- prefs.useDocumentBleedWithPDF = bleed;
141
- bleedSet = true;
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
+ }
142
156
  }
143
157
  try {
144
158
  prevView = prefs.viewPDF;
@@ -158,6 +172,9 @@ function idcExportPdfOnce(doc, file, preset, rangeText, bleed) {
158
172
  if (rangeSet) { try { prefs.pageRange = prevRange; } catch (e1) { /* egal */ } }
159
173
  if (viewSet) { try { prefs.viewPDF = prevView; } catch (e2) { /* egal */ } }
160
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
+ }
161
178
  if (temp !== null) { try { temp.remove(); } catch (e4) { /* bleibt als Kopie stehen */ } }
162
179
  }
163
180
  /*
@@ -227,7 +244,10 @@ IDC.register('export_pdf', { needs_document: true, read_only: false, ruler: 'non
227
244
  ctx.warn('use_document_bleed is true, but the document bleed (Anschnitt) is 0 mm on every edge – set_document_bleed first.');
228
245
  }
229
246
 
230
- bytes = idcExportPdfOnce(doc, file, preset, rangeText, bleed);
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);
231
251
 
232
252
  fallbackName = args.fallback_preset === undefined || args.fallback_preset === null
233
253
  ? '' : String(args.fallback_preset);
@@ -238,9 +258,14 @@ IDC.register('export_pdf', { needs_document: true, read_only: false, ruler: 'non
238
258
  fallback = idcPdfPresetByName(fallbackName);
239
259
  if (fallback === null) {
240
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;
241
264
  } else {
242
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.');
243
- bytes = idcExportPdfOnce(doc, file, fallback, rangeText, bleed);
266
+ }
267
+ if (fallback !== null) {
268
+ bytes = idcExportPdfOnce(doc, file, fallback, rangeText, bleed, access);
244
269
  preset = fallback;
245
270
  presetUsed = fallbackName;
246
271
  if (bytes !== null && bytes > threshold) {
@@ -251,7 +276,7 @@ IDC.register('export_pdf', { needs_document: true, read_only: false, ruler: 'non
251
276
 
252
277
  ctx.changes = { exported_files: [IDC.fsPath(file)] };
253
278
 
254
- return {
279
+ var answer = {
255
280
  path: IDC.fsPath(file),
256
281
  page_count: exportedCount,
257
282
  exported_page_count: exportedCount,
@@ -262,6 +287,8 @@ IDC.register('export_pdf', { needs_document: true, read_only: false, ruler: 'non
262
287
  document_bleed_used: idcBleedInEffect(preset, bleed),
263
288
  document_bleed_mm: docBleed
264
289
  };
290
+ if (accessResult !== null) { idcAddAccessAnswer(answer, accessResult); }
291
+ return answer;
265
292
  });
266
293
 
267
294
  /* Anschnitt des Dokuments in mm (innen/links, aussen/rechts wie InDesign). */
@@ -446,3 +473,223 @@ IDC.register('preflight_document', { needs_document: true, read_only: true, rule
446
473
  findings: capped
447
474
  };
448
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
+ });
@@ -123,7 +123,7 @@ IDC.register('export_page_image', { needs_document: true, read_only: false, rule
123
123
  pages = idcPagesForExport(doc, args);
124
124
  file = IDC.fileFor(path);
125
125
  if (!IDC.fileParentExists(file)) {
126
- IDC.fail('io_error', 'The folder for ' + path + ' does not exist.', { path: path });
126
+ IDC.fail('io_error', 'The folder for ' + path + ' does not exist (pass create_folder true to create it).', { path: path });
127
127
  }
128
128
 
129
129
  prefs = format === 'png' ? app.pngExportPreferences : app.jpegExportPreferences;
@@ -208,3 +208,103 @@ IDC.register('show_page', { needs_document: true, read_only: false, ruler: 'none
208
208
  previous_view: { active_page_id: before.active_page_id, active_page_name: before.active_page_name, zoom_percentage: before.zoom_percentage }
209
209
  };
210
210
  });
211
+
212
+ /* ------------------------------------------------------- set_view_options */
213
+
214
+ /*
215
+ * A-68 (10), Sitzung 81 – Ansichtsoptionen fuer Bildschirmaufnahmen und
216
+ * Sichtpruefung. Grundlage Probe InDesign 21.6 am 11.11.2026:
217
+ * guidePreferences.guidesShown, viewPreferences.showFrameEdges und
218
+ * showTextThreads, gridPreferences.baselineGridShown und documentGridShown,
219
+ * textPreferences.showInvisibles (Dokument), LayoutWindow.screenMode
220
+ * (ScreenModeOptions PREVIEW_OFF, PREVIEW_TO_PAGE, PREVIEW_TO_BLEED,
221
+ * PREVIEW_TO_SLUG, PRESENTATION_PREVIEW). Aendert nur die Ansicht.
222
+ */
223
+ var IDC_VIEW_FLAGS = [
224
+ ['guides_shown', 'guidePreferences', 'guidesShown'],
225
+ ['frame_edges_shown', 'viewPreferences', 'showFrameEdges'],
226
+ ['text_threads_shown', 'viewPreferences', 'showTextThreads'],
227
+ ['baseline_grid_shown', 'gridPreferences', 'baselineGridShown'],
228
+ ['document_grid_shown', 'gridPreferences', 'documentGridShown'],
229
+ ['hidden_characters_shown', 'textPreferences', 'showInvisibles']
230
+ ];
231
+ var IDC_SCREEN_MODES = [
232
+ ['normal', 'PREVIEW_OFF'], ['preview', 'PREVIEW_TO_PAGE'], ['bleed', 'PREVIEW_TO_BLEED'],
233
+ ['slug', 'PREVIEW_TO_SLUG'], ['presentation', 'PRESENTATION_PREVIEW']
234
+ ];
235
+
236
+ function idcScreenModeName(value) {
237
+ var i;
238
+ if (value === undefined || value === null || typeof ScreenModeOptions === 'undefined') { return null; }
239
+ for (i = 0; i < IDC_SCREEN_MODES.length; i++) {
240
+ try { if (IDC.enumIs(value, ScreenModeOptions[IDC_SCREEN_MODES[i][1]])) { return IDC_SCREEN_MODES[i][0]; } } catch (e) { /* weiter */ }
241
+ }
242
+ return String(value);
243
+ }
244
+
245
+ function idcReadViewOptions(doc, win) {
246
+ var out = {}, i, prefs;
247
+ for (i = 0; i < IDC_VIEW_FLAGS.length; i++) {
248
+ out[IDC_VIEW_FLAGS[i][0]] = null;
249
+ try {
250
+ prefs = doc[IDC_VIEW_FLAGS[i][1]];
251
+ out[IDC_VIEW_FLAGS[i][0]] = prefs[IDC_VIEW_FLAGS[i][2]] === true;
252
+ } catch (e) { /* bleibt null */ }
253
+ }
254
+ out.screen_mode = null;
255
+ if (win !== null) {
256
+ try { out.screen_mode = idcScreenModeName(win.screenMode); } catch (e2) { /* bleibt null */ }
257
+ }
258
+ return out;
259
+ }
260
+
261
+ IDC.register('set_view_options', { needs_document: true, read_only: false, ruler: 'none' },
262
+ function (ctx) {
263
+ var args = ctx.args;
264
+ var doc = ctx.doc;
265
+ var win = idcLayoutWindowOf(doc);
266
+ var given = false, i, key, mode = null, before, after, problems = [];
267
+
268
+ for (i = 0; i < IDC_VIEW_FLAGS.length; i++) {
269
+ key = IDC_VIEW_FLAGS[i][0];
270
+ if (idcGiven(args[key])) {
271
+ given = true;
272
+ if (typeof args[key] !== 'boolean') { IDC.fail('invalid_argument', key + ' must be true or false.', { field: key, value: args[key] }); }
273
+ }
274
+ }
275
+ if (idcGiven(args.screen_mode)) {
276
+ given = true;
277
+ if (typeof ScreenModeOptions === 'undefined') { IDC.fail('invalid_state', 'This runtime has no ScreenModeOptions enumeration.', null); }
278
+ for (i = 0; i < IDC_SCREEN_MODES.length; i++) {
279
+ if (IDC_SCREEN_MODES[i][0] === args.screen_mode) { mode = ScreenModeOptions[IDC_SCREEN_MODES[i][1]]; }
280
+ }
281
+ if (mode === null) {
282
+ IDC.fail('invalid_argument', 'screen_mode must be normal, preview, bleed, slug or presentation.', { screen_mode: args.screen_mode });
283
+ }
284
+ if (win === null) {
285
+ IDC.fail('invalid_state', 'The document has no layout window (opened with show_window false); screen_mode needs one.', { code: 'no_layout_window' });
286
+ }
287
+ }
288
+ if (!given) {
289
+ IDC.fail('invalid_argument', 'Pass at least one of guides_shown, frame_edges_shown, text_threads_shown, baseline_grid_shown, document_grid_shown, hidden_characters_shown or screen_mode.', null);
290
+ }
291
+
292
+ before = idcReadViewOptions(doc, win);
293
+ for (i = 0; i < IDC_VIEW_FLAGS.length; i++) {
294
+ key = IDC_VIEW_FLAGS[i][0];
295
+ if (!idcGiven(args[key])) { continue; }
296
+ try { doc[IDC_VIEW_FLAGS[i][1]][IDC_VIEW_FLAGS[i][2]] = args[key]; } catch (e) {
297
+ problems.push(key + ': ' + String(e.message ? e.message : e));
298
+ }
299
+ }
300
+ if (mode !== null) {
301
+ try { win.screenMode = mode; } catch (e2) { problems.push('screen_mode: ' + String(e2.message ? e2.message : e2)); }
302
+ }
303
+ if (problems.length > 0) { ctx.warn('Some view options could not be set: ' + problems.join('; ')); }
304
+ after = idcReadViewOptions(doc, win);
305
+ return {
306
+ has_window: win !== null,
307
+ view_options: after,
308
+ previous: before
309
+ };
310
+ });
@@ -306,41 +306,61 @@ IDC.register('apply_parent_to_page', { needs_document: true, read_only: false, r
306
306
  function (ctx) {
307
307
  var args = ctx.args;
308
308
  var doc = ctx.doc;
309
- var found, page, spread;
309
+ var found, spread = null, ids = [], pages = [], out = [], i, j, seen, noParent;
310
310
 
311
- if (args.page_id === undefined || args.page_id === null) {
312
- IDC.fail('invalid_argument', 'page_id is required.', null);
311
+ /* A-68 (5), Sitzung 81: page_ids fuer mehrere Seiten auf einmal; page_id allein wie bisher. */
312
+ if (args.page_id !== undefined && args.page_id !== null) { ids.push(args.page_id); }
313
+ if (args.page_ids !== undefined && args.page_ids !== null) {
314
+ if (!IDC.isArray(args.page_ids) || args.page_ids.length === 0) {
315
+ IDC.fail('invalid_argument', 'page_ids must be a non-empty list of page ids.', { page_ids: args.page_ids });
316
+ }
317
+ for (i = 0; i < args.page_ids.length; i++) {
318
+ seen = false;
319
+ for (j = 0; j < ids.length; j++) { if (Number(ids[j]) === Number(args.page_ids[i])) { seen = true; } }
320
+ if (!seen) { ids.push(args.page_ids[i]); }
321
+ }
322
+ }
323
+ if (ids.length === 0) {
324
+ IDC.fail('invalid_argument', 'page_id or page_ids is required.', null);
313
325
  }
314
326
  if (args.parent_name === undefined || args.parent_name === null) {
315
327
  IDC.fail('invalid_argument', 'parent_name is required (full name such as "A-Grundlayout", or "[None]" to detach).', null);
316
328
  }
317
- found = IDC.pageById(doc, args.page_id);
318
- if (found === null) {
319
- IDC.fail('not_found', 'No page with page_id ' + args.page_id + ' in this document.', { page_id: args.page_id });
329
+ /* Erst alle Seiten und die Musterseite pruefen, dann schreiben (nichts Halbfertiges). */
330
+ for (i = 0; i < ids.length; i++) {
331
+ found = IDC.pageById(doc, ids[i]);
332
+ if (found === null) {
333
+ IDC.fail('not_found', 'No page with page_id ' + ids[i] + ' in this document.', { page_id: ids[i] });
334
+ }
335
+ pages.push(found.page);
336
+ }
337
+ noParent = idcMeansNoParent(args.parent_name);
338
+ if (!noParent) {
339
+ spread = idcMasterSpreadByName(doc, args.parent_name);
340
+ if (spread === null) {
341
+ IDC.fail('not_found', 'No master page named "' + args.parent_name + '". Use the full name with prefix.',
342
+ { parent_name: String(args.parent_name), available: idcMasterSpreadNames(doc) });
343
+ }
320
344
  }
321
- page = found.page;
322
345
 
323
- try {
324
- if (idcMeansNoParent(args.parent_name)) {
325
- page.appliedMaster = NothingEnum.NOTHING;
326
- } else {
327
- spread = idcMasterSpreadByName(doc, args.parent_name);
328
- if (spread === null) {
329
- IDC.fail('not_found', 'No master page named "' + args.parent_name + '". Use the full name with prefix.',
330
- { parent_name: String(args.parent_name), available: idcMasterSpreadNames(doc) });
331
- }
332
- page.appliedMaster = spread;
346
+ for (i = 0; i < pages.length; i++) {
347
+ try {
348
+ if (noParent) { pages[i].appliedMaster = NothingEnum.NOTHING; }
349
+ else { pages[i].appliedMaster = spread; }
350
+ } catch (e) {
351
+ IDC.fail('script_error', 'InDesign could not apply the master page: ' + String(e.message ? e.message : e),
352
+ { page_id: Number(pages[i].id), parent_name: String(args.parent_name), pages_done: i });
333
353
  }
334
- } catch (e) {
335
- if (e && e.isIdcError) { throw e; }
336
- IDC.fail('script_error', 'InDesign could not apply the master page: ' + String(e.message ? e.message : e),
337
- { page_id: args.page_id, parent_name: String(args.parent_name) });
338
354
  }
339
355
 
340
- ctx.changes = { modified_page_ids: [Number(page.id)] };
356
+ for (i = 0; i < pages.length; i++) {
357
+ out.push({ page_id: Number(pages[i].id), parent_applied: idcAppliedParentName(pages[i]) });
358
+ }
359
+ ctx.changes = { modified_page_ids: (function () { var a = [], k; for (k = 0; k < out.length; k++) { a.push(out[k].page_id); } return a; })() };
341
360
  return {
342
- page_id: Number(page.id),
343
- parent_applied: idcAppliedParentName(page)
361
+ page_id: out[0].page_id,
362
+ parent_applied: out[0].parent_applied,
363
+ pages: out
344
364
  };
345
365
  });
346
366
 
@@ -666,6 +666,35 @@ IDC.register('create_line', { needs_document: true, read_only: false, ruler: 'pa
666
666
 
667
667
  /* ---------------------------------------------------------- duplicate_frame */
668
668
 
669
+ /*
670
+ * A-73 (Sitzung 80): allPageItems liefert auch die platzierten Grafiken in
671
+ * einem Rahmen (Bild, PDF, EPS ...). Deren ID ist kein Seitenobjekt; ein
672
+ * Befehl daran (z. B. move() einer Grafik auf eine Seite) kann InDesign
673
+ * beenden (09.11.2026, InDesign 21.6). Deshalb not_found mit der gefundenen
674
+ * Objektart und der ID des Rahmens. In einen Rahmen eingefuegte Rahmen
675
+ * (Rechteck, Textrahmen, Gruppe ...) bleiben wie bisher erlaubt.
676
+ */
677
+ var IDC_FRAME_CLASSES = ['TextFrame', 'Rectangle', 'Oval', 'Polygon', 'GraphicLine', 'Group', 'Button', 'MultiStateObject', 'FormField', 'EPSText'];
678
+
679
+ function idcRejectPlacedGraphic(item, field, id) {
680
+ var className = IDC.className(item);
681
+ var parentName = '';
682
+ var parentId = null;
683
+ var i;
684
+ for (i = 0; i < IDC_FRAME_CLASSES.length; i++) {
685
+ if (IDC_FRAME_CLASSES[i] === className) { return; }
686
+ }
687
+ try { parentName = IDC.className(item.parent); } catch (e) { parentName = ''; }
688
+ if (parentName !== 'Rectangle' && parentName !== 'Oval' && parentName !== 'Polygon' && parentName !== 'GraphicLine' && parentName !== 'TextFrame') {
689
+ return;
690
+ }
691
+ try { parentId = Number(item.parent.id); } catch (e2) { parentId = null; }
692
+ IDC.fail('not_found',
693
+ 'No object with ' + field + ' ' + id + ' in this document – ' + id + ' is the ' + className +
694
+ ' placed inside frame ' + String(parentId) + '; pass the id of the frame (get_page_state lists it).',
695
+ { frame_id: id, found_class: className, container_frame_id: parentId });
696
+ }
697
+
669
698
  function idcFrameArg(doc, args, field) {
670
699
  var found;
671
700
  if (args[field] === undefined || args[field] === null) {
@@ -673,8 +702,14 @@ function idcFrameArg(doc, args, field) {
673
702
  }
674
703
  found = IDC.itemById(doc, args[field], 'any');
675
704
  if (found === null) {
705
+ if (IDC.rejectedClass !== null && IDC.rejectedClass !== undefined && IDC.rejectedClass !== '') {
706
+ /* A-73: die ID gehoert einem Objekt, das kein Seitenobjekt ist (z. B. einer Story). */
707
+ IDC.fail('not_found', 'No object with ' + field + ' ' + args[field] + ' in this document – ' + args[field] + ' is a ' + IDC.rejectedClass + ', not a frame or other page object.',
708
+ { frame_id: args[field], found_class: IDC.rejectedClass });
709
+ }
676
710
  IDC.fail('not_found', 'No object with ' + field + ' ' + args[field] + ' in this document.', { frame_id: args[field] });
677
711
  }
712
+ idcRejectPlacedGraphic(found.item, field, args[field]);
678
713
  /*
679
714
  * Schreibende Befehle scheitern an gesperrten Ebenen und gesperrten
680
715
  * Objekten – frueher mit nacktem InDesign-Fehler oder gar nicht sichtbar