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.
- package/CHANGELOG.md +83 -0
- package/README.de.md +31 -2
- package/README.md +31 -3
- package/THIRD-PARTY-NOTICES.md +2 -2
- package/dist/src/mcp/catalog.js +9 -4
- package/dist/src/mcp/content-credentials.js +65 -0
- package/dist/src/mcp/dispatch.js +8 -1
- package/dist/src/mcp/license/polar-ids.js +1 -1
- package/dist/src/mcp/pdf-postprocess.js +798 -0
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/mcp/target-folder.js +25 -0
- package/dist/src/mcp/tools.js +275 -20
- package/dist/src/mcp/typography-rules.js +1 -1
- package/dist/src/transport/com.js +224 -38
- package/dist/src/transport/transport.js +22 -0
- package/indesign/commands/25-export.js +258 -11
- package/indesign/commands/26-export-image.js +1 -1
- package/indesign/commands/31-view.js +100 -0
- package/indesign/commands/35-parents.js +44 -24
- package/indesign/commands/40-frames.js +35 -0
- package/indesign/commands/47-anchored.js +142 -0
- package/indesign/commands/50-modify.js +25 -3
- package/indesign/commands/60-images.js +28 -2
- package/indesign/commands/69-placeholders.js +7 -2
- package/indesign/commands/70-text.js +39 -4
- package/indesign/commands/75-tables.js +27 -1
- package/indesign/commands/78-frame-lines.js +59 -0
- package/indesign/commands/79-check-typography.js +143 -4
- package/indesign/commands/80-styles.js +105 -10
- package/indesign/commands/89-toc-index.js +61 -4
- package/indesign/commands/91-epub.js +92 -9
- package/indesign/commands/92-accessibility.js +1131 -0
- package/indesign/interpreter.js +51 -1
- package/package.json +1 -1
- package/rules/typography-rules.json +3 -2
|
@@ -274,6 +274,7 @@ IDC.register('anchor_frame_in_text', { needs_document: true, read_only: false, r
|
|
|
274
274
|
if (Number(anchoredItem.id) !== Number(args.frame_id)) {
|
|
275
275
|
ctx.warn('InDesign gave the anchored object a new id (' + Number(anchoredItem.id) + '); use it from now on.');
|
|
276
276
|
}
|
|
277
|
+
idcWarnAnchoredOutsideFrame(ctx, doc, anchoredItem);
|
|
277
278
|
if (result.story_overset === true) {
|
|
278
279
|
ctx.warn('The story overflows after anchoring (Übersatz) – the object may sit in the overset text.');
|
|
279
280
|
}
|
|
@@ -281,6 +282,27 @@ IDC.register('anchor_frame_in_text', { needs_document: true, read_only: false, r
|
|
|
281
282
|
return result;
|
|
282
283
|
});
|
|
283
284
|
|
|
285
|
+
/*
|
|
286
|
+
* A-68 Befund, Sitzung 81: Ein Objekt ueber der Zeile kann oben aus seinem
|
|
287
|
+
* Textrahmen ragen (live 11.11.2026: Rahmen oben 15 mm, Objekt bei 4,4 mm,
|
|
288
|
+
* ohne Warnung). Vergleich der Oberkanten auf derselben Seite.
|
|
289
|
+
*/
|
|
290
|
+
function idcWarnAnchoredOutsideFrame(ctx, doc, item) {
|
|
291
|
+
var parent = null, frames = null, frame, ib, fb, li, lf;
|
|
292
|
+
try { parent = item.parent; frames = parent.parentTextFrames; } catch (e0) { return; }
|
|
293
|
+
if (!frames || frames.length === 0) { return; }
|
|
294
|
+
frame = IDC.concrete(frames[0]);
|
|
295
|
+
ib = idcItemBounds(doc, item);
|
|
296
|
+
fb = idcItemBounds(doc, frame);
|
|
297
|
+
if (ib === null || fb === null) { return; }
|
|
298
|
+
li = IDC.landing(item);
|
|
299
|
+
lf = IDC.landing(frame);
|
|
300
|
+
if (li.page_id === null || li.page_id !== lf.page_id) { return; }
|
|
301
|
+
if (ib.y < fb.y - 0.05) {
|
|
302
|
+
ctx.warn('The anchored object reaches ' + IDC.round(fb.y - ib.y, 2) + ' mm above the top of its text frame ' + Number(frame.id) + ' – above the line at the start of a frame the space before and the object height do not fit; it may overlap the content above. Reduce space_before_mm or move the anchor.');
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
284
306
|
IDC.register('unanchor_frame', { needs_document: true, read_only: false, ruler: 'page' },
|
|
285
307
|
function (ctx) {
|
|
286
308
|
var args = ctx.args;
|
|
@@ -329,3 +351,123 @@ IDC.register('unanchor_frame', { needs_document: true, read_only: false, ruler:
|
|
|
329
351
|
ctx.changes = { modified_frame_ids: [Number(released.id)] };
|
|
330
352
|
return result;
|
|
331
353
|
});
|
|
354
|
+
|
|
355
|
+
/* ------------------------------------------------ set_anchored_object_options */
|
|
356
|
+
|
|
357
|
+
/*
|
|
358
|
+
* A-68 (6), Sitzung 81: Ankeroptionen eines schon verankerten Objekts
|
|
359
|
+
* aendern, ohne es zu loesen und neu zu verankern (Probe InDesign 21.6 am
|
|
360
|
+
* 11.11.2026: anchoredPosition laesst sich am verankerten Objekt von inline
|
|
361
|
+
* auf above_line und custom umstellen, das Objekt bleibt im Text;
|
|
362
|
+
* horizontalAlignment und anchorSpaceAbove wirken sofort). Parameter wie
|
|
363
|
+
* anchor_frame_in_text; was fehlt, bleibt.
|
|
364
|
+
*/
|
|
365
|
+
var IDC_ANCHOR_OPTION_KEYS = ['position', 'offset_mm', 'anchor_point', 'horizontal_reference', 'vertical_reference', 'horizontal_alignment',
|
|
366
|
+
'vertical_alignment', 'keep_within_frame', 'lock_position', 'space_before_mm', 'space_after_mm'];
|
|
367
|
+
|
|
368
|
+
IDC.register('set_anchored_object_options', { needs_document: true, read_only: false, ruler: 'page' },
|
|
369
|
+
function (ctx) {
|
|
370
|
+
var args = ctx.args;
|
|
371
|
+
var doc = ctx.doc;
|
|
372
|
+
var found = idcFrameArg(doc, args, 'frame_id');
|
|
373
|
+
var item = IDC.concrete(found.item);
|
|
374
|
+
var info = idcAnchorInfo(item);
|
|
375
|
+
var settings = null, position, positionEnum = null, given = false, i, value, ignored = [], story = null, landing, boundsMm = null, result;
|
|
376
|
+
var spaceBefore, spaceAfter, aboveAlignment = null, set = [];
|
|
377
|
+
|
|
378
|
+
if (info === null) {
|
|
379
|
+
IDC.fail('invalid_state', 'Object ' + args.frame_id + ' is not anchored in text; anchor it with anchor_frame_in_text.', { frame_id: args.frame_id, code: 'not_anchored' });
|
|
380
|
+
}
|
|
381
|
+
for (i = 0; i < IDC_ANCHOR_OPTION_KEYS.length; i++) {
|
|
382
|
+
if (idcGiven(args[IDC_ANCHOR_OPTION_KEYS[i]])) { given = true; }
|
|
383
|
+
}
|
|
384
|
+
if (!given) {
|
|
385
|
+
IDC.fail('invalid_argument', 'Pass at least one option: ' + IDC_ANCHOR_OPTION_KEYS.join(', ') + '.', null);
|
|
386
|
+
}
|
|
387
|
+
position = idcGiven(args.position) ? String(args.position) : info.position;
|
|
388
|
+
if (idcGiven(args.position)) {
|
|
389
|
+
positionEnum = idcEnumFor(IDC_ANCHOR_POSITIONS, idcEnumObject('AnchorPosition'), args.position, 'position');
|
|
390
|
+
if (positionEnum === null) { IDC.fail('invalid_state', 'This runtime has no AnchorPosition enumeration.', null); }
|
|
391
|
+
}
|
|
392
|
+
/* Alles vorher pruefen (Aufzaehlungen, Zahlen), dann schreiben. */
|
|
393
|
+
spaceBefore = idcAnchorSpaceArg(args.space_before_mm, 'space_before_mm');
|
|
394
|
+
spaceAfter = idcAnchorSpaceArg(args.space_after_mm, 'space_after_mm');
|
|
395
|
+
if (idcGiven(args.offset_mm)) { idcOffsetArg(args.offset_mm, 'offset_mm'); }
|
|
396
|
+
if (idcGiven(args.anchor_point)) { idcEnumFor(IDC_ANCHOR_POINTS, idcEnumObject('AnchorPoint'), args.anchor_point, 'anchor_point'); }
|
|
397
|
+
if (idcGiven(args.horizontal_reference)) { idcEnumFor(IDC_H_REFERENCES, idcEnumObject('AnchoredRelativeTo'), args.horizontal_reference, 'horizontal_reference'); }
|
|
398
|
+
if (idcGiven(args.vertical_reference)) { idcEnumFor(IDC_V_REFERENCES, idcEnumObject('VerticallyRelativeTo'), args.vertical_reference, 'vertical_reference'); }
|
|
399
|
+
if (idcGiven(args.horizontal_alignment)) { aboveAlignment = idcEnumFor(IDC_H_ALIGNMENTS, idcEnumObject('HorizontalAlignment'), args.horizontal_alignment, 'horizontal_alignment'); }
|
|
400
|
+
if (idcGiven(args.vertical_alignment)) { idcEnumFor(IDC_V_ALIGNMENTS, idcEnumObject('VerticalAlignment'), args.vertical_alignment, 'vertical_alignment'); }
|
|
401
|
+
if (idcGiven(args.keep_within_frame) && typeof args.keep_within_frame !== 'boolean') { IDC.fail('invalid_argument', 'keep_within_frame must be true or false.', null); }
|
|
402
|
+
if (idcGiven(args.lock_position) && typeof args.lock_position !== 'boolean') { IDC.fail('invalid_argument', 'lock_position must be true or false.', null); }
|
|
403
|
+
|
|
404
|
+
try { settings = item.anchoredObjectSettings; } catch (e0) { settings = null; }
|
|
405
|
+
if (settings === null || settings === undefined) {
|
|
406
|
+
IDC.fail('invalid_state', 'InDesign gives no anchored object settings for object ' + args.frame_id + '.', { frame_id: args.frame_id });
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
if (positionEnum !== null) { settings.anchoredPosition = positionEnum; set.push('position'); }
|
|
410
|
+
if (position === 'custom') {
|
|
411
|
+
if (idcGiven(args.anchor_point)) { settings.anchorPoint = idcEnumFor(IDC_ANCHOR_POINTS, idcEnumObject('AnchorPoint'), args.anchor_point, 'anchor_point'); set.push('anchor_point'); }
|
|
412
|
+
if (idcGiven(args.horizontal_reference)) { settings.horizontalReferencePoint = idcEnumFor(IDC_H_REFERENCES, idcEnumObject('AnchoredRelativeTo'), args.horizontal_reference, 'horizontal_reference'); set.push('horizontal_reference'); }
|
|
413
|
+
if (idcGiven(args.vertical_reference)) { settings.verticalReferencePoint = idcEnumFor(IDC_V_REFERENCES, idcEnumObject('VerticallyRelativeTo'), args.vertical_reference, 'vertical_reference'); set.push('vertical_reference'); }
|
|
414
|
+
if (aboveAlignment !== null) { settings.horizontalAlignment = aboveAlignment; set.push('horizontal_alignment'); }
|
|
415
|
+
if (idcGiven(args.vertical_alignment)) { settings.verticalAlignment = idcEnumFor(IDC_V_ALIGNMENTS, idcEnumObject('VerticalAlignment'), args.vertical_alignment, 'vertical_alignment'); set.push('vertical_alignment'); }
|
|
416
|
+
if (idcGiven(args.offset_mm)) {
|
|
417
|
+
if (idcGiven(args.offset_mm.x)) { settings.anchorXoffset = args.offset_mm.x; }
|
|
418
|
+
if (idcGiven(args.offset_mm.y)) { settings.anchorYoffset = args.offset_mm.y; }
|
|
419
|
+
set.push('offset_mm');
|
|
420
|
+
}
|
|
421
|
+
if (idcGiven(args.keep_within_frame)) { settings.pinPosition = args.keep_within_frame === true; set.push('keep_within_frame'); }
|
|
422
|
+
if (idcGiven(args.lock_position)) { settings.lockPosition = args.lock_position === true; set.push('lock_position'); }
|
|
423
|
+
if (spaceBefore !== null) { ignored.push('space_before_mm'); }
|
|
424
|
+
if (spaceAfter !== null) { ignored.push('space_after_mm'); }
|
|
425
|
+
} else if (position === 'above_line') {
|
|
426
|
+
if (aboveAlignment !== null) { settings.horizontalAlignment = aboveAlignment; set.push('horizontal_alignment'); }
|
|
427
|
+
if (spaceBefore !== null) { settings.anchorSpaceAbove = spaceBefore; set.push('space_before_mm'); }
|
|
428
|
+
if (spaceAfter !== null) { settings.anchorYoffset = spaceAfter; set.push('space_after_mm'); }
|
|
429
|
+
for (i = 0; i < IDC_ANCHOR_OPTION_KEYS.length; i++) {
|
|
430
|
+
value = IDC_ANCHOR_OPTION_KEYS[i];
|
|
431
|
+
if (value !== 'position' && value !== 'horizontal_alignment' && value !== 'space_before_mm' && value !== 'space_after_mm' && idcGiven(args[value])) { ignored.push(value); }
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
for (i = 0; i < IDC_ANCHOR_OPTION_KEYS.length; i++) {
|
|
435
|
+
value = IDC_ANCHOR_OPTION_KEYS[i];
|
|
436
|
+
if (value !== 'position' && idcGiven(args[value])) { ignored.push(value); }
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} catch (e1) {
|
|
440
|
+
if (e1 && e1.isIdcError) { throw e1; }
|
|
441
|
+
IDC.fail('script_error', 'InDesign could not change the anchored object settings of object ' + args.frame_id + ': ' + String(e1.message ? e1.message : e1),
|
|
442
|
+
{ frame_id: args.frame_id, done: set });
|
|
443
|
+
}
|
|
444
|
+
if (ignored.length > 0) {
|
|
445
|
+
ctx.warn(ignored.join(', ') + ' do not apply to position "' + position + '" and were ignored (custom: offset, reference points, alignment, keep_within_frame, lock_position; above_line: horizontal_alignment, space_before_mm, space_after_mm).');
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/* Wie anchor_frame_in_text: erst umbrechen, dann Seite und Lage lesen (A-67 Punkt 4). */
|
|
449
|
+
try { story = item.parent.parentStory; } catch (e2) { story = null; }
|
|
450
|
+
try { if (story !== null) { story.recompose(); } } catch (e3) { /* Lesen wie bisher */ }
|
|
451
|
+
landing = IDC.landing(item);
|
|
452
|
+
try { boundsMm = idcItemBounds(doc, item); } catch (e4) { boundsMm = null; }
|
|
453
|
+
result = {
|
|
454
|
+
frame_id: Number(item.id),
|
|
455
|
+
type: IDC.itemType(item),
|
|
456
|
+
story_id: info.story_id,
|
|
457
|
+
index: info.index,
|
|
458
|
+
applied: set,
|
|
459
|
+
settings: idcDescribeAnchoredSettings(item),
|
|
460
|
+
previous_position: info.position,
|
|
461
|
+
bounds_mm: boundsMm,
|
|
462
|
+
page_id: landing.page_id,
|
|
463
|
+
page_name: landing.page_name,
|
|
464
|
+
story_overset: null
|
|
465
|
+
};
|
|
466
|
+
try { if (story !== null) { result.story_overset = story.overflows === true; } } catch (e5) { /* null */ }
|
|
467
|
+
idcWarnAnchoredOutsideFrame(ctx, doc, item);
|
|
468
|
+
if (result.story_overset === true) {
|
|
469
|
+
ctx.warn('The story overflows after the change (Übersatz) – the object may sit in the overset text; autoflow_story adds pages.');
|
|
470
|
+
}
|
|
471
|
+
ctx.changes = { modified_frame_ids: [Number(item.id)] };
|
|
472
|
+
return result;
|
|
473
|
+
});
|
|
@@ -582,10 +582,21 @@ IDC.register('set_frame_inset', { needs_document: true, read_only: false, ruler:
|
|
|
582
582
|
var vj = args.vertical_justification;
|
|
583
583
|
var hasVj = vj !== undefined && vj !== null;
|
|
584
584
|
var inset = null, vjEnum = null, back = null, vjBack = null;
|
|
585
|
+
var hasFbo = idcGiven(args.first_baseline_offset), hasMin = idcGiven(args.min_first_baseline_pt), fboEnum = null, baseline = null;
|
|
585
586
|
|
|
586
587
|
/* Sitzung 24 (E-79): vertikale Ausrichtung als optionaler Parameter; dann darf inset_mm fehlen. */
|
|
587
|
-
|
|
588
|
-
|
|
588
|
+
/* A-68 (2), Sitzung 81: erste Grundlinie und Mindestwert fuer bestehende Rahmen (wie create_text_frame). */
|
|
589
|
+
if (!hasInset && !hasVj && !hasFbo && !hasMin) {
|
|
590
|
+
IDC.fail('invalid_argument', 'Pass inset_mm, vertical_justification, first_baseline_offset or min_first_baseline_pt.', null);
|
|
591
|
+
}
|
|
592
|
+
if (hasFbo) {
|
|
593
|
+
fboEnum = idcFirstBaselineFor(args.first_baseline_offset);
|
|
594
|
+
if (fboEnum === null) {
|
|
595
|
+
IDC.fail('invalid_argument', 'first_baseline_offset must be ascent, cap_height, leading, x_height, fixed or embox_top.', { first_baseline_offset: args.first_baseline_offset });
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (hasMin && (typeof args.min_first_baseline_pt !== 'number' || !isFinite(args.min_first_baseline_pt) || args.min_first_baseline_pt < 0)) {
|
|
599
|
+
IDC.fail('invalid_argument', 'min_first_baseline_pt must be a number of 0 or more (points).', { min_first_baseline_pt: args.min_first_baseline_pt });
|
|
589
600
|
}
|
|
590
601
|
if (hasInset) { inset = idcEdgeObjectArg(args.inset_mm, 'inset_mm', false); }
|
|
591
602
|
if (hasVj) {
|
|
@@ -607,10 +618,21 @@ IDC.register('set_frame_inset', { needs_document: true, read_only: false, ruler:
|
|
|
607
618
|
IDC.fail('script_error', 'InDesign could not set the vertical justification (vertikale Ausrichtung) of text frame ' + args.frame_id + ': ' + String(e1.message ? e1.message : e1),
|
|
608
619
|
{ frame_id: args.frame_id });
|
|
609
620
|
}
|
|
621
|
+
try {
|
|
622
|
+
if (fboEnum !== null) { frame.textFramePreferences.firstBaselineOffset = fboEnum; }
|
|
623
|
+
if (hasMin) { IDC.inPoints(function () { frame.textFramePreferences.minimumFirstBaselineOffset = args.min_first_baseline_pt; }); }
|
|
624
|
+
} catch (e1b) {
|
|
625
|
+
IDC.fail('script_error', 'InDesign could not set the first baseline options of text frame ' + args.frame_id + ': ' + String(e1b.message ? e1b.message : e1b),
|
|
626
|
+
{ frame_id: args.frame_id });
|
|
627
|
+
}
|
|
610
628
|
try { back = idcEdgesFromArray(frame.textFramePreferences.insetSpacing); } catch (e2) { back = null; }
|
|
611
629
|
try { vjBack = idcVerticalJustificationName(frame.textFramePreferences.verticalJustification); } catch (e3) { vjBack = null; }
|
|
630
|
+
baseline = { first_baseline_offset: null, min_first_baseline_pt: null };
|
|
631
|
+
try { baseline.first_baseline_offset = idcFirstBaselineName(frame.textFramePreferences.firstBaselineOffset); } catch (e4) { /* null */ }
|
|
632
|
+
try { IDC.inPoints(function () { baseline.min_first_baseline_pt = IDC.round(Number(frame.textFramePreferences.minimumFirstBaselineOffset), 3); }); } catch (e5) { /* null */ }
|
|
612
633
|
ctx.changes = { modified_frame_ids: [Number(frame.id)] };
|
|
613
|
-
return { frame_id: Number(frame.id), inset_mm: back, vertical_justification: vjBack
|
|
634
|
+
return { frame_id: Number(frame.id), inset_mm: back, vertical_justification: vjBack,
|
|
635
|
+
first_baseline_offset: baseline.first_baseline_offset, min_first_baseline_pt: baseline.min_first_baseline_pt };
|
|
614
636
|
});
|
|
615
637
|
|
|
616
638
|
/* ------------------------------------------------------------ set_text_wrap */
|
|
@@ -732,7 +732,7 @@ function idcLinkWhereabouts(link) {
|
|
|
732
732
|
function idcDescribeLink(doc, link) {
|
|
733
733
|
var placement = idcLinkPlacement(doc, link);
|
|
734
734
|
var entry = { link_id: null, file_name: null, file_path: null, status: null, page_id: placement.page_id, page_name: placement.page_name, frame_id: placement.frame_id };
|
|
735
|
-
var
|
|
735
|
+
var info = idcLinkWhereabouts(link), where = info.where, key;
|
|
736
736
|
try { entry.link_id = Number(link.id); } catch (e0) { entry.link_id = null; }
|
|
737
737
|
try { entry.file_name = String(link.name); } catch (e1) { entry.file_name = null; }
|
|
738
738
|
try { entry.file_path = String(link.filePath); } catch (e2) { entry.file_path = null; }
|
|
@@ -740,9 +740,28 @@ function idcDescribeLink(doc, link) {
|
|
|
740
740
|
for (key in where) {
|
|
741
741
|
if (IDC.hasOwn(where, key)) { entry[key] = where[key]; }
|
|
742
742
|
}
|
|
743
|
+
/* A-42 (Sitzung 81): Verknuepfung ohne Objekt (Waise) – weder Grafik noch Story erreichbar. */
|
|
744
|
+
entry.without_object = info.held.graphic === null && info.held.story === null;
|
|
743
745
|
return entry;
|
|
744
746
|
}
|
|
745
747
|
|
|
748
|
+
/*
|
|
749
|
+
* A-42 (Sitzung 81) – Verknuepfungen ohne Objekt, etwa Firefly-Bilder, deren
|
|
750
|
+
* Rahmen geloescht wurde. Probe an der Kopie eines Kundenhefts am
|
|
751
|
+
* 11.11.2026 (InDesign 21.6): das Link-Objekt ist gueltig, status NORMAL,
|
|
752
|
+
* aber parent wirft schon in InDesign, linkXmp ebenso; ein GenAI-Kennzeichen
|
|
753
|
+
* gibt es im Objektmodell nicht (nur Name GenAIImage_... und Ordner
|
|
754
|
+
* InDesign-GenAI-Assets). Ein IDML-Export und Wiederoeffnen entfernt genau
|
|
755
|
+
* diese Verknuepfung (61 -> 60, 44 Seiten, Preflight 0 Fehler, PDF 44 Seiten
|
|
756
|
+
* wie vorher); package_document kopiert die Datei nicht.
|
|
757
|
+
*/
|
|
758
|
+
var IDC_ORPHAN_HINT = 'it does not print and package_document does not copy its file. To remove it, export the document as IDML (File > Export > InDesign Markup) and open the IDML – that drops exactly such links and keeps pages and content (checked on a copy of a magazine, 11.11.2026); save the result under a new name and compare before replacing the original.';
|
|
759
|
+
|
|
760
|
+
function idcLooksGenerative(entry) {
|
|
761
|
+
var text = String(entry.file_name === null ? '' : entry.file_name) + '|' + String(entry.file_path === null ? '' : entry.file_path);
|
|
762
|
+
return /GenAIImage_|InDesign-GenAI-Assets/.test(text);
|
|
763
|
+
}
|
|
764
|
+
|
|
746
765
|
IDC.register('list_links', { needs_document: true, read_only: true, ruler: 'page' },
|
|
747
766
|
function (ctx) {
|
|
748
767
|
var args = ctx.args;
|
|
@@ -760,6 +779,13 @@ IDC.register('list_links', { needs_document: true, read_only: true, ruler: 'page
|
|
|
760
779
|
}
|
|
761
780
|
out.push(entry);
|
|
762
781
|
}
|
|
782
|
+
var orphans = 0, generative = 0;
|
|
783
|
+
for (i = 0; i < out.length; i++) {
|
|
784
|
+
if (out[i].without_object === true) { orphans += 1; if (idcLooksGenerative(out[i])) { generative += 1; } }
|
|
785
|
+
}
|
|
786
|
+
if (orphans > 0) {
|
|
787
|
+
ctx.warn(orphans + ' link(s) without an object (without_object true' + (generative > 0 ? '; ' + generative + ' of them a Firefly/GenAI image whose frame was deleted' : '') + '): ' + IDC_ORPHAN_HINT);
|
|
788
|
+
}
|
|
763
789
|
return { links: out, count: out.length, total: Number(doc.links.length), path_contains: filter };
|
|
764
790
|
});
|
|
765
791
|
|
|
@@ -812,7 +838,7 @@ IDC.register('delete_link', { needs_document: true, read_only: false, ruler: 'pa
|
|
|
812
838
|
{ code: 'story_link', link_id: linkId, location_before: before });
|
|
813
839
|
}
|
|
814
840
|
if (held.graphic === null) {
|
|
815
|
-
IDC.fail('invalid_state', 'Link ' + linkId + ' is not attached to any object InDesign can reach
|
|
841
|
+
IDC.fail('invalid_state', 'Link ' + linkId + ' is not attached to any object InDesign can reach (a link without an object, e.g. a Firefly image whose frame was deleted); ' + IDC_ORPHAN_HINT,
|
|
816
842
|
{ code: 'link_without_object', link_id: linkId, location_before: before });
|
|
817
843
|
}
|
|
818
844
|
onPage = where.location === 'page' || where.location === 'parent_page' || (where.location === 'anchored' && where.in_overset === false);
|
|
@@ -22,7 +22,9 @@ var IDC_PLACEHOLDERS = [
|
|
|
22
22
|
['tab', '\t'],
|
|
23
23
|
['column_break', '\u000E'],
|
|
24
24
|
['frame_break', '\u000F'],
|
|
25
|
-
['page_break', '\u0010']
|
|
25
|
+
['page_break', '\u0010'],
|
|
26
|
+
['odd_page_break', '\u0011'],
|
|
27
|
+
['even_page_break', '\u0012']
|
|
26
28
|
];
|
|
27
29
|
|
|
28
30
|
function idcPlaceholderChar(name) {
|
|
@@ -90,7 +92,7 @@ function idcPlaceholderNames() {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
/*
|
|
93
|
-
* Umbruchzeichen (Spalte 0x0E, Rahmen 0x0F, Seite 0x10) verwirft InDesign,
|
|
95
|
+
* Umbruchzeichen (Spalte 0x0E, Rahmen 0x0F, Seite 0x10, ungerade 0x11, gerade 0x12) verwirft InDesign,
|
|
94
96
|
* wenn sie ueber contents geschrieben werden (Live-Fund 05.09.2026: 110
|
|
95
97
|
* geschrieben, 107 gelesen). Sie werden deshalb aus dem Text genommen und
|
|
96
98
|
* nach dem Schreiben ueber insertionPoints als SpecialCharacters
|
|
@@ -100,6 +102,9 @@ var IDC_BREAK_ENUMS = {};
|
|
|
100
102
|
IDC_BREAK_ENUMS['\u000E'] = 'COLUMN_BREAK';
|
|
101
103
|
IDC_BREAK_ENUMS['\u000F'] = 'FRAME_BREAK';
|
|
102
104
|
IDC_BREAK_ENUMS['\u0010'] = 'PAGE_BREAK';
|
|
105
|
+
/* A-68 (4), Sitzung 81: Umbruch auf ungerade/gerade Seite – ueber contents ebenso verworfen (Probe 11.11.2026). */
|
|
106
|
+
IDC_BREAK_ENUMS['\u0011'] = 'ODD_PAGE_BREAK';
|
|
107
|
+
IDC_BREAK_ENUMS['\u0012'] = 'EVEN_PAGE_BREAK';
|
|
103
108
|
|
|
104
109
|
/* Text ohne Umbruchzeichen plus Liste {offset, name}; offset = Stelle im bereinigten Text. */
|
|
105
110
|
function idcSplitBreaks(text) {
|
|
@@ -553,6 +553,25 @@ function idcChangeInStory(story, grep) {
|
|
|
553
553
|
return grep ? text.changeGrep() : text.changeText();
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
+
/* Geht die Story ueber diesen Rahmen hinaus (Folgerahmen oder Uebersatz)? */
|
|
557
|
+
function idcStoryGoesOn(frame) {
|
|
558
|
+
var other;
|
|
559
|
+
try { if (frame.overflows === true) { return true; } } catch (e0) { /* weiter */ }
|
|
560
|
+
try { other = frame.nextTextFrame; if (other !== null && other !== undefined && other.isValid === true) { return true; } } catch (e1) { /* weiter */ }
|
|
561
|
+
try { other = frame.previousTextFrame; if (other !== null && other !== undefined && other.isValid === true) { return true; } } catch (e2) { /* weiter */ }
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* Entfernt die Ersetzung Absatzmarken? Absatzmarke im Muster (GREP \\r oder ~b, literal CR), keine in der Ersetzung. */
|
|
566
|
+
function idcRemovesParagraphMarks(find, replace, mode) {
|
|
567
|
+
var f = String(find), r = String(replace === undefined || replace === null ? '' : replace);
|
|
568
|
+
if (mode === 'grep') {
|
|
569
|
+
if (f.indexOf('\\r') < 0 && f.indexOf('~b') < 0 && f.indexOf('\r') < 0) { return false; }
|
|
570
|
+
return r.indexOf('\\r') < 0 && r.indexOf('~b') < 0 && r.indexOf('\r') < 0 && !/[$][0-9]/.test(r);
|
|
571
|
+
}
|
|
572
|
+
return f.indexOf('\r') >= 0 && r.indexOf('\r') < 0;
|
|
573
|
+
}
|
|
574
|
+
|
|
556
575
|
IDC.register('find_replace', { needs_document: true, read_only: false, ruler: 'none' },
|
|
557
576
|
function (ctx) {
|
|
558
577
|
var args = ctx.args;
|
|
@@ -573,13 +592,21 @@ IDC.register('find_replace', { needs_document: true, read_only: false, ruler: 'n
|
|
|
573
592
|
if (mode !== 'literal' && mode !== 'grep') {
|
|
574
593
|
IDC.fail('invalid_argument', 'mode must be literal or grep.', { mode: mode });
|
|
575
594
|
}
|
|
576
|
-
if (scope !== 'document' && scope !== 'frame') {
|
|
577
|
-
IDC.fail('invalid_argument', 'scope must be document or
|
|
595
|
+
if (scope !== 'document' && scope !== 'frame' && scope !== 'story') {
|
|
596
|
+
IDC.fail('invalid_argument', 'scope must be document, frame or story.', { scope: scope });
|
|
578
597
|
}
|
|
579
598
|
if (scope === 'frame') {
|
|
580
599
|
frame = idcTextFrameArg(doc, args, 'frame_id').item;
|
|
581
600
|
try { target = frame.texts.item(0); } catch (e0) { target = null; }
|
|
582
601
|
if (target === null || target === undefined) { target = idcStoryOf(frame); }
|
|
602
|
+
/* A-68 Befund (18), Sitzung 81: scope frame sucht nur im Rahmenanteil, nicht im Uebersatz und nicht in weiteren Rahmen der Kette. */
|
|
603
|
+
if (idcStoryGoesOn(frame)) {
|
|
604
|
+
ctx.warn('scope frame searches only the text of this frame - the story continues in other frames or in overset (\u00dcbersatz), which are not searched; scope story covers the whole story.');
|
|
605
|
+
}
|
|
606
|
+
} else if (scope === 'story') {
|
|
607
|
+
/* A-68 Befund (18): die ganze Story samt Uebersatz und aller Rahmen der Kette. */
|
|
608
|
+
frame = idcTextFrameArg(doc, args, 'frame_id').item;
|
|
609
|
+
target = idcStoryOf(frame);
|
|
583
610
|
} else {
|
|
584
611
|
target = doc;
|
|
585
612
|
}
|
|
@@ -634,6 +661,14 @@ IDC.register('find_replace', { needs_document: true, read_only: false, ruler: 'n
|
|
|
634
661
|
if (mode === 'literal' && / /.test(args.find)) {
|
|
635
662
|
ctx.warn('The search text contains a non-breaking space (U+00A0); literal search does not find it reliably – use mode grep with ~S.');
|
|
636
663
|
}
|
|
664
|
+
/* A-68 Befund (16), Sitzung 81: Umbruchzeichen im Lookbehind trifft InDesigns GREP nicht (live 11.11.2026: (?<=~P) 42 Fundstellen an Absatzanfaengen, keine hinter dem Seitenumbruch; ~PN dagegen richtig). */
|
|
665
|
+
if (mode === 'grep' && /\(\?<[=!][^)]*~[PMRLE]/.test(String(args.find))) {
|
|
666
|
+
ctx.warn('InDesign GREP does not match break characters (~P, ~M, ~R, ~L, ~E) inside a lookbehind (?<=…) – it reports wrong places. Match the break itself and keep it with a group instead, e.g. find (~P)X and replace $1Y.');
|
|
667
|
+
}
|
|
668
|
+
/* A-68 Befund (18): Absatzmarke entfernt - InDesign fuehrt die Absaetze mit dem Format des oberen weiter. */
|
|
669
|
+
if (!dryRun && count > 0 && idcRemovesParagraphMarks(args.find, args.replace, mode)) {
|
|
670
|
+
ctx.warn('The replacement removes paragraph marks: the merged paragraphs keep the paragraph style of the first one - reapply the intended style (apply_paragraph_style_to_range) where needed.');
|
|
671
|
+
}
|
|
637
672
|
for (i = 0; i < split.skipped.length; i++) { skippedMatches += split.skipped[i].matches; }
|
|
638
673
|
if (split.skipped.length > 0) {
|
|
639
674
|
ctx.warn(skippedMatches + ' match(es) in ' + split.skipped.length + ' locked story/stories were ' + (dryRun ? 'left out of the count' : 'not replaced') +
|
|
@@ -641,7 +676,7 @@ IDC.register('find_replace', { needs_document: true, read_only: false, ruler: 'n
|
|
|
641
676
|
}
|
|
642
677
|
if (args.include_locked_layers !== true) {
|
|
643
678
|
/* Ohne include_locked_layers sucht InDesign gesperrte Stories nicht ab: darauf hinweisen. */
|
|
644
|
-
if (scope === 'frame') {
|
|
679
|
+
if (scope === 'frame' || scope === 'story') {
|
|
645
680
|
frameLock = idcStoryLockInfo(idcStoryOf(frame));
|
|
646
681
|
if (frameLock.write_locked) { lockedInScope.push(frameLock); }
|
|
647
682
|
} else {
|
|
@@ -664,7 +699,7 @@ IDC.register('find_replace', { needs_document: true, read_only: false, ruler: 'n
|
|
|
664
699
|
dry_run: dryRun,
|
|
665
700
|
scope: scope,
|
|
666
701
|
mode: mode,
|
|
667
|
-
frame_id: scope === 'frame' ? Number(frame.id) : null,
|
|
702
|
+
frame_id: scope === 'frame' || scope === 'story' ? Number(frame.id) : null,
|
|
668
703
|
master_pages_included: args.include_master_pages === true,
|
|
669
704
|
hidden_layers_included: args.include_hidden_layers === true,
|
|
670
705
|
locked_layers_included: args.include_locked_layers === true,
|
|
@@ -202,9 +202,18 @@ function idcDescribeTable(table, story, index, count) {
|
|
|
202
202
|
try { out.story_id = Number(story.id); } catch (e1) { /* null */ }
|
|
203
203
|
try { out.width_mm = IDC.round(Number(table.width), 3); } catch (e2) { /* null */ }
|
|
204
204
|
try { out.height_mm = IDC.round(Number(table.height), 3); } catch (e3) { /* null */ }
|
|
205
|
+
/* A-80, Sitzung 82: Kopfspalten gibt es erst ab InDesign 2026 (Beleg 09/15 der Vorabpruefung); sonst null. */
|
|
206
|
+
out.header_columns = idcHeaderColumns(table);
|
|
205
207
|
return out;
|
|
206
208
|
}
|
|
207
209
|
|
|
210
|
+
function idcHeaderColumns(table) {
|
|
211
|
+
var value;
|
|
212
|
+
try { value = table.numHeaderColumns; } catch (e) { return null; }
|
|
213
|
+
if (value === undefined || value === null || isNaN(Number(value))) { return null; }
|
|
214
|
+
return Number(value);
|
|
215
|
+
}
|
|
216
|
+
|
|
208
217
|
/* Tabelle ueber frame_id (ein Rahmen der Story) und table_index (Vorgabe 0). */
|
|
209
218
|
function idcTableArg(doc, args) {
|
|
210
219
|
var found = idcTextFrameArg(doc, args, 'frame_id');
|
|
@@ -563,8 +572,20 @@ IDC.register('set_table_style', { needs_document: true, read_only: false, ruler:
|
|
|
563
572
|
}
|
|
564
573
|
if (args.header_rows !== undefined && args.header_rows !== null) { idcCountArg(args.header_rows, 'header_rows', 0, 20, 0); given += 1; }
|
|
565
574
|
if (args.footer_rows !== undefined && args.footer_rows !== null) { idcCountArg(args.footer_rows, 'footer_rows', 0, 20, 0); given += 1; }
|
|
575
|
+
if (args.header_columns !== undefined && args.header_columns !== null) {
|
|
576
|
+
idcCountArg(args.header_columns, 'header_columns', 0, 20, 0);
|
|
577
|
+
given += 1;
|
|
578
|
+
if (idcHeaderColumns(table) === null) {
|
|
579
|
+
IDC.fail('invalid_state', 'header_columns needs InDesign 2026 or later (this is ' + String(app.version) + '); older versions have no header columns.',
|
|
580
|
+
{ code: 'needs_indesign_2026', app_version: String(app.version) });
|
|
581
|
+
}
|
|
582
|
+
if (args.header_columns >= idcTableCounts(table).columns) {
|
|
583
|
+
IDC.fail('invalid_argument', 'header_columns must leave at least one other column (table has ' + idcTableCounts(table).columns + ' columns).',
|
|
584
|
+
{ header_columns: args.header_columns, columns: idcTableCounts(table).columns });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
566
587
|
if (given === 0) {
|
|
567
|
-
IDC.fail('invalid_argument', 'Pass at least one of table_style, cell_style, column_widths_mm, width_mm, header_rows, footer_rows.', null);
|
|
588
|
+
IDC.fail('invalid_argument', 'Pass at least one of table_style, cell_style, column_widths_mm, width_mm, header_rows, footer_rows, header_columns.', null);
|
|
568
589
|
}
|
|
569
590
|
|
|
570
591
|
if (tableStyle !== null) {
|
|
@@ -585,6 +606,11 @@ IDC.register('set_table_style', { needs_document: true, read_only: false, ruler:
|
|
|
585
606
|
changed = true;
|
|
586
607
|
}
|
|
587
608
|
}
|
|
609
|
+
if (args.header_columns !== undefined && args.header_columns !== null && idcHeaderColumns(table) !== args.header_columns) {
|
|
610
|
+
try { table.numHeaderColumns = args.header_columns; changed = true; } catch (eHc) {
|
|
611
|
+
IDC.fail('script_error', 'InDesign could not set header_columns: ' + String(eHc.message ? eHc.message : eHc), null);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
588
614
|
if (args.column_widths_mm !== undefined && args.column_widths_mm !== null) {
|
|
589
615
|
idcApplyColumnWidths(table, args.column_widths_mm, 'column_widths_mm');
|
|
590
616
|
changed = true;
|
|
@@ -288,6 +288,39 @@ function idcMarkSoftHyphens(lines, texts) {
|
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/*
|
|
292
|
+
* A-68 Befund, Sitzung 81: Bei einer Initiale ueber mehrere Zeilen meldet
|
|
293
|
+
* InDesign 21.6 fuer die erste Zeile des Absatzes die Grundlinie der
|
|
294
|
+
* Initiale, also die der letzten Initialzeile (live 11.11.2026: Zeile 0
|
|
295
|
+
* 35,14 mm, Zeile 1 27,38, Zeile 2 35,14 bei dreizeiliger Initiale). Liegt
|
|
296
|
+
* die erste Zeile eines Absatzes nicht ueber der folgenden Zeile desselben
|
|
297
|
+
* Absatzes, wird ihre Grundlinie am ersten Zeichen hinter der Initiale
|
|
298
|
+
* gelesen (nur dann ein Zugriff, das gebuendelte Lesen bleibt).
|
|
299
|
+
*/
|
|
300
|
+
function idcFixDropCapBaselines(textObject, entries) {
|
|
301
|
+
var i, a, b, line = null, paragraph, dropLines = 0, dropChars = 0, chars, value;
|
|
302
|
+
for (i = 0; i + 1 < entries.length; i++) {
|
|
303
|
+
a = entries[i];
|
|
304
|
+
b = entries[i + 1];
|
|
305
|
+
if (!a.is_first_line_of_paragraph || a.is_last_line_of_paragraph) { continue; }
|
|
306
|
+
if (a.paragraph_index === null || a.paragraph_index !== b.paragraph_index) { continue; }
|
|
307
|
+
if (a.baseline_mm === null || b.baseline_mm === null || a.baseline_mm < b.baseline_mm) { continue; }
|
|
308
|
+
try {
|
|
309
|
+
line = textObject.lines.item(i);
|
|
310
|
+
paragraph = line.paragraphs.item(0);
|
|
311
|
+
dropLines = Number(paragraph.dropCapLines);
|
|
312
|
+
dropChars = Number(paragraph.dropCapCharacters);
|
|
313
|
+
} catch (e0) { continue; }
|
|
314
|
+
if (!(dropLines >= 2) || !(dropChars >= 1)) { continue; }
|
|
315
|
+
try {
|
|
316
|
+
chars = line.characters;
|
|
317
|
+
if (Number(chars.length) <= dropChars) { continue; }
|
|
318
|
+
value = Number(chars.item(dropChars).baseline);
|
|
319
|
+
} catch (e1) { continue; }
|
|
320
|
+
if (isFinite(value)) { a.baseline_mm = IDC.round(value); }
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
291
324
|
function idcColumnFigures(lines) {
|
|
292
325
|
var out = {
|
|
293
326
|
line_count: lines.length,
|
|
@@ -394,6 +427,7 @@ function idcFrameLineData(doc, frame, story, includeText, maxLines, ctx, cache,
|
|
|
394
427
|
texts.push({ first: raw[i].charAt(0), last: brokenByBreak ? '' : rawLast });
|
|
395
428
|
}
|
|
396
429
|
idcMarkSoftHyphens(entries, texts);
|
|
430
|
+
idcFixDropCapBaselines(columnObjects[c], entries);
|
|
397
431
|
var figures = idcColumnFigures(entries);
|
|
398
432
|
figures.column_index = c;
|
|
399
433
|
figures.bounds_mm = bounds[c] || null;
|
|
@@ -423,6 +457,30 @@ function idcFrameLineData(doc, frame, story, includeText, maxLines, ctx, cache,
|
|
|
423
457
|
};
|
|
424
458
|
}
|
|
425
459
|
|
|
460
|
+
/*
|
|
461
|
+
* A-68 Befund, Sitzung 81: „Abstand davor“ eines Absatzes, der eine Spalte
|
|
462
|
+
* oder einen Rahmen beginnt, setzt InDesign nicht (live 11.11.2026: 30 pt
|
|
463
|
+
* davor, erste Grundlinie wie ohne). Hinweis je Spalte, ein Zugriff je
|
|
464
|
+
* Spalte, die mit einem Absatzanfang beginnt.
|
|
465
|
+
*/
|
|
466
|
+
function idcWarnSpaceBeforeAtColumnTop(ctx, frame, data) {
|
|
467
|
+
var c, column, start, space, story = null;
|
|
468
|
+
try { story = frame.parentStory; } catch (e0) { return; }
|
|
469
|
+
for (c = 0; c < data.columns.length; c++) {
|
|
470
|
+
column = data.columns[c];
|
|
471
|
+
if (!column.lines || column.lines.length === 0 || !column.lines[0].is_first_line_of_paragraph || column.lines[0].is_empty) { continue; }
|
|
472
|
+
start = column.lines[0].start_index;
|
|
473
|
+
if (start === null) { continue; }
|
|
474
|
+
space = null;
|
|
475
|
+
try {
|
|
476
|
+
space = IDC.inPoints(function () { return Number(story.characters.item(start).paragraphs.item(0).spaceBefore); });
|
|
477
|
+
} catch (e1) { space = null; }
|
|
478
|
+
if (space !== null && space > 0.01) {
|
|
479
|
+
ctx.warn('Column ' + c + ' starts with a paragraph that has space before (Abstand davor) of ' + IDC.round(space, 2) + ' pt – InDesign does not apply it at the top of a column or frame; move the frame or use the first baseline offset (set_frame_inset) instead.');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
426
484
|
IDC.register('get_frame_lines', { needs_document: true, read_only: true, ruler: 'spread' },
|
|
427
485
|
function (ctx) {
|
|
428
486
|
var args = ctx.args;
|
|
@@ -437,6 +495,7 @@ IDC.register('get_frame_lines', { needs_document: true, read_only: true, ruler:
|
|
|
437
495
|
IDC.fail('invalid_argument', 'max_lines must be a whole number between 1 and 5000.', { max_lines: args.max_lines });
|
|
438
496
|
}
|
|
439
497
|
var data = idcFrameLineData(doc, frame, story, includeText, maxLines, ctx);
|
|
498
|
+
idcWarnSpaceBeforeAtColumnTop(ctx, frame, data);
|
|
440
499
|
return {
|
|
441
500
|
frame_id: data.frame_id,
|
|
442
501
|
story_id: data.story_id,
|