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.
- package/CHANGELOG.md +105 -0
- package/LICENSE +26 -0
- package/README.de.md +367 -0
- package/README.md +353 -2
- package/THIRD-PARTY-NOTICES.md +2878 -0
- package/dist/src/mcp/catalog.js +131 -0
- package/dist/src/mcp/config.js +211 -0
- package/dist/src/mcp/content-credentials.js +65 -0
- package/dist/src/mcp/dispatch.js +171 -0
- package/dist/src/mcp/epub-check.js +331 -0
- package/dist/src/mcp/epub-orientation.js +132 -0
- package/dist/src/mcp/license/free-tools.js +16 -0
- package/dist/src/mcp/license/gate.js +210 -0
- package/dist/src/mcp/license/index.js +107 -0
- package/dist/src/mcp/license/instance-name.js +57 -0
- package/dist/src/mcp/license/key-source.js +59 -0
- package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
- package/dist/src/mcp/license/placeholder-verifier.js +10 -0
- package/dist/src/mcp/license/polar-ids.js +100 -0
- package/dist/src/mcp/license/polar-verifier.js +285 -0
- package/dist/src/mcp/license/product-ids.js +73 -0
- package/dist/src/mcp/license/state-store.js +91 -0
- package/dist/src/mcp/license/types.js +11 -0
- package/dist/src/mcp/logging.js +271 -0
- package/dist/src/mcp/pdf-postprocess.js +798 -0
- package/dist/src/mcp/product-info.js +54 -0
- package/dist/src/mcp/product.js +56 -0
- package/dist/src/mcp/server.js +253 -0
- package/dist/src/mcp/setup.js +680 -0
- package/dist/src/mcp/start.cjs +117 -0
- package/dist/src/mcp/target-folder.js +25 -0
- package/dist/src/mcp/tool-definition.js +2 -0
- package/dist/src/mcp/tools.js +2680 -0
- package/dist/src/mcp/typography-rules.js +70 -0
- package/dist/src/transport/com.js +436 -0
- package/dist/src/transport/osascript.js +171 -0
- package/dist/src/transport/queue.js +19 -0
- package/dist/src/transport/script-bundle.js +92 -0
- package/dist/src/transport/transport.js +52 -0
- package/indesign/commands/10-app.js +6 -0
- package/indesign/commands/20-documents.js +517 -0
- package/indesign/commands/22-document-setup.js +204 -0
- package/indesign/commands/23-save-package.js +258 -0
- package/indesign/commands/25-export.js +695 -0
- package/indesign/commands/26-export-image.js +202 -0
- package/indesign/commands/27-textdefaults.js +151 -0
- package/indesign/commands/28-selection.js +174 -0
- package/indesign/commands/30-pages.js +1032 -0
- package/indesign/commands/31-view.js +310 -0
- package/indesign/commands/32-sections.js +203 -0
- package/indesign/commands/35-parents.js +448 -0
- package/indesign/commands/37-layout.js +837 -0
- package/indesign/commands/40-frames.js +886 -0
- package/indesign/commands/41-shapes.js +505 -0
- package/indesign/commands/42-groups.js +117 -0
- package/indesign/commands/43-layers.js +310 -0
- package/indesign/commands/44-effects.js +274 -0
- package/indesign/commands/45-frame-options.js +247 -0
- package/indesign/commands/47-anchored.js +473 -0
- package/indesign/commands/50-modify.js +803 -0
- package/indesign/commands/52-align.js +139 -0
- package/indesign/commands/55-fit.js +106 -0
- package/indesign/commands/60-images.js +1154 -0
- package/indesign/commands/62-embed.js +120 -0
- package/indesign/commands/69-placeholders.js +132 -0
- package/indesign/commands/70-text.js +710 -0
- package/indesign/commands/71-outlines.js +92 -0
- package/indesign/commands/72-place-text.js +476 -0
- package/indesign/commands/73-story-lock.js +157 -0
- package/indesign/commands/74-stories.js +121 -0
- package/indesign/commands/75-tables.js +702 -0
- package/indesign/commands/76-text-variables.js +260 -0
- package/indesign/commands/77-story-options.js +86 -0
- package/indesign/commands/78-frame-lines.js +511 -0
- package/indesign/commands/79-check-typography.js +480 -0
- package/indesign/commands/80-styles.js +1960 -0
- package/indesign/commands/81-composition.js +355 -0
- package/indesign/commands/82-object-styles.js +263 -0
- package/indesign/commands/83-drop-caps.js +87 -0
- package/indesign/commands/83-indents-tabs.js +264 -0
- package/indesign/commands/84-import-styles.js +142 -0
- package/indesign/commands/85-swatches.js +112 -0
- package/indesign/commands/86-gradients.js +185 -0
- package/indesign/commands/87-replace-swatch.js +180 -0
- package/indesign/commands/88-replace-font.js +212 -0
- package/indesign/commands/89-toc-index.js +1002 -0
- package/indesign/commands/90-print-on-demand.js +495 -0
- package/indesign/commands/91-epub.js +629 -0
- package/indesign/commands/92-accessibility.js +1131 -0
- package/indesign/interpreter.js +829 -0
- package/package.json +41 -4
- package/rules/typography-rules.json +32 -0
|
@@ -0,0 +1,1154 @@
|
|
|
1
|
+
/* Bereich F – Bilder und Verknuepfungen. */
|
|
2
|
+
|
|
3
|
+
/* Verknuepfungsstatus als Text. longForm: LINK_MISSING/LINK_OUT_OF_DATE (list_links), sonst MISSING/OUT_OF_DATE. */
|
|
4
|
+
function idcLinkStatusName(status, longForm) {
|
|
5
|
+
if (status === null || status === undefined) { return null; }
|
|
6
|
+
if (typeof LinkStatus !== 'undefined') {
|
|
7
|
+
if (IDC.enumIs(status, LinkStatus.NORMAL)) { return 'NORMAL'; }
|
|
8
|
+
if (IDC.enumIs(status, LinkStatus.LINK_MISSING)) { return longForm ? 'LINK_MISSING' : 'MISSING'; }
|
|
9
|
+
if (IDC.enumIs(status, LinkStatus.LINK_OUT_OF_DATE)) { return longForm ? 'LINK_OUT_OF_DATE' : 'OUT_OF_DATE'; }
|
|
10
|
+
if (IDC.enumIs(status, LinkStatus.LINK_EMBEDDED)) { return 'EMBEDDED'; }
|
|
11
|
+
}
|
|
12
|
+
var text = String(status);
|
|
13
|
+
if (!longForm) { text = text.replace(/^LINK_/, ''); }
|
|
14
|
+
return text;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function idcLinkOf(graphic) {
|
|
18
|
+
var link = null;
|
|
19
|
+
try {
|
|
20
|
+
link = graphic.itemLink;
|
|
21
|
+
if (link && link.isValid !== false) { return link; }
|
|
22
|
+
} catch (e) { link = null; }
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Alle Grafiken eines Rahmens (auch in Gruppen) als Array. */
|
|
27
|
+
function idcGraphicsOf(item) {
|
|
28
|
+
var out = [];
|
|
29
|
+
var i, all;
|
|
30
|
+
try {
|
|
31
|
+
all = item.allGraphics;
|
|
32
|
+
for (i = 0; i < all.length; i++) { out.push(IDC.concrete(all[i])); }
|
|
33
|
+
if (out.length > 0) { return out; }
|
|
34
|
+
} catch (e) { /* weiter */ }
|
|
35
|
+
try {
|
|
36
|
+
all = item.graphics;
|
|
37
|
+
for (i = 0; i < all.length; i++) { out.push(IDC.concrete(all.item(i))); }
|
|
38
|
+
} catch (e2) { /* keine */ }
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Rahmen fuer Grafiken: Rechteck, Oval, Polygon – keine Gruppe, kein Text, keine Linie. */
|
|
43
|
+
function idcGraphicFrameArg(doc, args, field) {
|
|
44
|
+
var found = idcFrameArg(doc, args, field);
|
|
45
|
+
var className = IDC.className(found.item);
|
|
46
|
+
if (className !== 'Rectangle' && className !== 'Oval' && className !== 'Polygon') {
|
|
47
|
+
IDC.fail('invalid_state', 'Object ' + args[field] + ' is a ' + IDC.itemType(found.item) + ' (' + className + '), not a graphic frame.',
|
|
48
|
+
{ frame_id: args[field], type: IDC.itemType(found.item) });
|
|
49
|
+
}
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* ppi [x, y] -> { x, y, min } oder null. */
|
|
54
|
+
/*
|
|
55
|
+
* Mindestaufloesung fuer die Warnung (G-1): Parameter min_ppi, sonst 300 –
|
|
56
|
+
* der uebliche Offsetwert (doppelte 150-lpi-Rasterweite). Konfigurierbar
|
|
57
|
+
* ueber die Serverkonfiguration (E-54), die den Wert in die args legt.
|
|
58
|
+
*/
|
|
59
|
+
function idcMinPpi(args) {
|
|
60
|
+
if (args && typeof args.min_ppi === 'number' && isFinite(args.min_ppi) && args.min_ppi > 0) {
|
|
61
|
+
return args.min_ppi;
|
|
62
|
+
}
|
|
63
|
+
return 300;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function idcPpi(value) {
|
|
67
|
+
var x, y;
|
|
68
|
+
try {
|
|
69
|
+
if (IDC.isArray(value) && value.length >= 2) {
|
|
70
|
+
x = IDC.round(Number(value[0]), 1);
|
|
71
|
+
y = IDC.round(Number(value[1]), 1);
|
|
72
|
+
if (x === null || y === null) { return null; }
|
|
73
|
+
return { x: x, y: y, min: Math.min(x, y) };
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === 'number') { return { x: IDC.round(value, 1), y: IDC.round(value, 1), min: IDC.round(value, 1) }; }
|
|
76
|
+
} catch (e) { return null; }
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Rueckfallkette, wenn die gewuenschte PDF-Box fehlt (Fund 1, Hefttest Sitzung 6). */
|
|
81
|
+
function idcPdfCropFallbacks(name) {
|
|
82
|
+
if (name === 'bleed') { return ['trim', 'crop', 'media']; }
|
|
83
|
+
if (name === 'trim') { return ['crop', 'media']; }
|
|
84
|
+
if (name === 'art' || name === 'content' || name === 'crop') { return ['crop', 'media']; }
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function idcPdfCropFor(name) {
|
|
89
|
+
if (typeof PDFCrop === 'undefined') { return null; }
|
|
90
|
+
if (name === 'media') { return PDFCrop.CROP_MEDIA; }
|
|
91
|
+
if (name === 'crop') { return PDFCrop.CROP_PDF; }
|
|
92
|
+
if (name === 'trim') { return PDFCrop.CROP_TRIM; }
|
|
93
|
+
if (name === 'bleed') { return PDFCrop.CROP_BLEED; }
|
|
94
|
+
if (name === 'art') { return PDFCrop.CROP_ART; }
|
|
95
|
+
if (name === 'content') { return PDFCrop.CROP_CONTENT; }
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Name der PDF-Box zu einem PDFCrop-Wert; null, wenn keiner passt. */
|
|
100
|
+
function idcPdfCropName(value) {
|
|
101
|
+
var names = ['media', 'crop', 'trim', 'bleed', 'art', 'content'];
|
|
102
|
+
var i;
|
|
103
|
+
if (typeof PDFCrop === 'undefined' || value === null || value === undefined) { return null; }
|
|
104
|
+
for (i = 0; i < names.length; i++) {
|
|
105
|
+
if (IDC.enumIs(value, idcPdfCropFor(names[i]))) { return names[i]; }
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function idcFitOptionFor(name) {
|
|
111
|
+
if (typeof FitOptions === 'undefined') { return null; }
|
|
112
|
+
if (name === 'fill_proportionally') { return FitOptions.FILL_PROPORTIONALLY; }
|
|
113
|
+
if (name === 'fit_proportionally') { return FitOptions.PROPORTIONALLY; }
|
|
114
|
+
if (name === 'fit_content_to_frame') { return FitOptions.CONTENT_TO_FRAME; }
|
|
115
|
+
if (name === 'center_content') { return FitOptions.CENTER_CONTENT; }
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Deckt die Grafik den Rahmen bis auf 0,2 mm? */
|
|
120
|
+
function idcCoversFrame(frameBounds, graphicBounds) {
|
|
121
|
+
var t = 0.2;
|
|
122
|
+
if (!frameBounds || !graphicBounds) { return false; }
|
|
123
|
+
return graphicBounds.x <= frameBounds.x + t
|
|
124
|
+
&& graphicBounds.y <= frameBounds.y + t
|
|
125
|
+
&& graphicBounds.x + graphicBounds.width >= frameBounds.x + frameBounds.width - t
|
|
126
|
+
&& graphicBounds.y + graphicBounds.height >= frameBounds.y + frameBounds.height - t;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Beschreibung einer Grafik: Datei, Status, ppi, Farbraum, Art, Begrenzungen. */
|
|
130
|
+
function idcDescribeGraphic(doc, frame, graphic, page) {
|
|
131
|
+
var link = idcLinkOf(graphic);
|
|
132
|
+
var entry = {
|
|
133
|
+
graphic_id: null,
|
|
134
|
+
link_id: null,
|
|
135
|
+
file_name: null,
|
|
136
|
+
file_path: null,
|
|
137
|
+
link_status: null,
|
|
138
|
+
effective_ppi: null,
|
|
139
|
+
effective_ppi_xy: null,
|
|
140
|
+
actual_ppi: null,
|
|
141
|
+
actual_ppi_xy: null,
|
|
142
|
+
color_space: null,
|
|
143
|
+
image_type: IDC.className(graphic),
|
|
144
|
+
is_pdf: false,
|
|
145
|
+
frame_bounds: null,
|
|
146
|
+
graphic_bounds: null,
|
|
147
|
+
hints: []
|
|
148
|
+
};
|
|
149
|
+
var ppi;
|
|
150
|
+
try { entry.graphic_id = Number(graphic.id); } catch (e0) { entry.graphic_id = null; }
|
|
151
|
+
if (link !== null) {
|
|
152
|
+
try { entry.link_id = Number(link.id); } catch (e1) { entry.link_id = null; }
|
|
153
|
+
try { entry.file_name = String(link.name); } catch (e2) { entry.file_name = null; }
|
|
154
|
+
try { entry.file_path = String(link.filePath); } catch (e3) { entry.file_path = null; }
|
|
155
|
+
try { entry.link_status = idcLinkStatusName(link.status, false); } catch (e4) { entry.link_status = null; }
|
|
156
|
+
} else {
|
|
157
|
+
entry.hints.push('err: no link object (pasted or embedded graphic without link)');
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
if (graphic.imageTypeName !== undefined && graphic.imageTypeName !== null) { entry.image_type = String(graphic.imageTypeName); }
|
|
161
|
+
} catch (e8) { /* Klassenname bleibt */ }
|
|
162
|
+
/* Platzierte PDFs heissen "Adobe PDF" (imageTypeName) bzw. "PDF" (Klasse) – Live-Fund 04.09.2026. */
|
|
163
|
+
entry.is_pdf = /pdf/i.test(entry.image_type) || IDC.className(graphic) === 'PDF';
|
|
164
|
+
try {
|
|
165
|
+
ppi = idcPpi(graphic.effectivePpi);
|
|
166
|
+
if (ppi !== null) { entry.effective_ppi = ppi.min; entry.effective_ppi_xy = [ppi.x, ppi.y]; }
|
|
167
|
+
} catch (e5) { if (!entry.is_pdf) { entry.hints.push('err: effective ppi not available (' + String(e5.message ? e5.message : e5) + ')'); } }
|
|
168
|
+
try {
|
|
169
|
+
ppi = idcPpi(graphic.actualPpi);
|
|
170
|
+
if (ppi !== null) { entry.actual_ppi = ppi.min; entry.actual_ppi_xy = [ppi.x, ppi.y]; }
|
|
171
|
+
} catch (e6) { if (!entry.is_pdf) { entry.hints.push('err: actual ppi not available (' + String(e6.message ? e6.message : e6) + ')'); } }
|
|
172
|
+
try {
|
|
173
|
+
if (graphic.space !== undefined && graphic.space !== null) { entry.color_space = String(graphic.space).toUpperCase(); }
|
|
174
|
+
} catch (e7) { if (!entry.is_pdf) { entry.hints.push('err: colour space not available (' + String(e7.message ? e7.message : e7) + ')'); } }
|
|
175
|
+
if (entry.is_pdf && (entry.effective_ppi === null || entry.color_space === null)) {
|
|
176
|
+
entry.hints.push('err: InDesign reports neither ppi nor colour space for placed PDFs – check the file itself');
|
|
177
|
+
}
|
|
178
|
+
try { entry.frame_bounds = IDC.boundsOnPage(doc, frame, page); } catch (e9) { entry.frame_bounds = null; }
|
|
179
|
+
try { entry.graphic_bounds = IDC.boundsOnPage(doc, graphic, page); } catch (e10) { entry.graphic_bounds = null; }
|
|
180
|
+
return entry;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* -------------------------------------------------------------- place_image */
|
|
184
|
+
|
|
185
|
+
IDC.register('place_image', { needs_document: true, read_only: false, ruler: 'page' },
|
|
186
|
+
function (ctx) {
|
|
187
|
+
var args = ctx.args;
|
|
188
|
+
var doc = ctx.doc;
|
|
189
|
+
var found = idcGraphicFrameArg(doc, args, 'frame_id');
|
|
190
|
+
var frame = found.item;
|
|
191
|
+
var path = args.image_path;
|
|
192
|
+
var fit = args.fit === undefined || args.fit === null ? null : args.fit;
|
|
193
|
+
var crop = args.pdf_crop === undefined || args.pdf_crop === null ? null : args.pdf_crop;
|
|
194
|
+
var pdfPage = args.pdf_page === undefined || args.pdf_page === null ? 1 : args.pdf_page;
|
|
195
|
+
var fallback = args.pdf_crop_fallback !== false;
|
|
196
|
+
var file, isPdf, prevCrop = null, prevPage = null, cropSet = false, pageSet = false;
|
|
197
|
+
var placed, graphic, removed = 0, i, graphics, fitOption, page, frameBounds, graphicBounds, fits, entry;
|
|
198
|
+
var cropUsed = crop, attempts, attempt, lastError = null, firstErrorMessage = '', k, minPpi;
|
|
199
|
+
var presetCrop = null, presetFallbacks, triedNames;
|
|
200
|
+
|
|
201
|
+
if (typeof path !== 'string' || path === '') {
|
|
202
|
+
IDC.fail('invalid_argument', 'image_path is required: a real file path on the machine running InDesign.', null);
|
|
203
|
+
}
|
|
204
|
+
if (fit !== null && idcFitOptionFor(fit) === null && typeof FitOptions !== 'undefined') {
|
|
205
|
+
IDC.fail('invalid_argument', 'fit must be fill_proportionally, fit_proportionally, fit_content_to_frame or center_content.', { fit: fit });
|
|
206
|
+
}
|
|
207
|
+
if (crop !== null && idcPdfCropFor(crop) === null && typeof PDFCrop !== 'undefined') {
|
|
208
|
+
IDC.fail('invalid_argument', 'pdf_crop must be media, crop, trim, bleed, art or content.', { pdf_crop: crop });
|
|
209
|
+
}
|
|
210
|
+
if (typeof pdfPage !== 'number' || pdfPage < 1 || Math.floor(pdfPage) !== pdfPage) {
|
|
211
|
+
IDC.fail('invalid_argument', 'pdf_page must be a whole number of 1 or more.', { pdf_page: pdfPage });
|
|
212
|
+
}
|
|
213
|
+
file = IDC.fileFor(path);
|
|
214
|
+
if (!file.exists) {
|
|
215
|
+
IDC.fail('not_found', 'File not found: ' + path, { image_path: path });
|
|
216
|
+
}
|
|
217
|
+
if (IDC.fileSize(file) === 0) {
|
|
218
|
+
IDC.fail('io_error', 'File is empty (0 bytes) – a cloud placeholder that is not downloaded yet? ' + path, { image_path: path });
|
|
219
|
+
}
|
|
220
|
+
isPdf = /\.(pdf|ai)$/i.test(path);
|
|
221
|
+
|
|
222
|
+
/* Bestehende Grafik entfernen – erneutes Platzieren setzt Zuschnitt und Skalierung zurueck. */
|
|
223
|
+
graphics = idcGraphicsOf(frame);
|
|
224
|
+
for (i = graphics.length - 1; i >= 0; i--) {
|
|
225
|
+
try { graphics[i].remove(); removed += 1; } catch (er) { /* dann ersetzt place() */ }
|
|
226
|
+
}
|
|
227
|
+
if (removed > 0) { ctx.warn('The frame held ' + removed + ' graphic(s); they were replaced (crop and scale reset).'); }
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
if (typeof ContentType !== 'undefined') { frame.contentType = ContentType.GRAPHIC_TYPE; }
|
|
231
|
+
} catch (ec) { /* Inhaltstyp bleibt */ }
|
|
232
|
+
|
|
233
|
+
/*
|
|
234
|
+
* Platzieren mit PDF-Importvorgabe nur fuer diesen Versuch. Fehlt der PDF
|
|
235
|
+
* die gewuenschte Box (z. B. keine TrimBox in einem QR-Code-PDF), wirft
|
|
236
|
+
* InDesign; dann Rueckfall auf die naechstgroessere Box mit Warnung
|
|
237
|
+
* (pdf_crop_fallback false schaltet das ab).
|
|
238
|
+
*/
|
|
239
|
+
attempts = [crop];
|
|
240
|
+
if (isPdf && crop !== null && fallback) { attempts = attempts.concat(idcPdfCropFallbacks(crop)); }
|
|
241
|
+
/*
|
|
242
|
+
* A-67 Punkt 3 (Sitzung 72): ohne pdf_crop gilt InDesigns eingestellte
|
|
243
|
+
* Box (Platzieren-Vorgabe, z. B. "Endformat" aus dem letzten Dialog).
|
|
244
|
+
* Fehlt sie der PDF, warf place() bis 1.0.3 ohne Rueckfall ("Kann nicht
|
|
245
|
+
* auf den Endformat-Rahmen zugeschnitten werden"). Jetzt derselbe
|
|
246
|
+
* Rueckfall ab der eingestellten Box.
|
|
247
|
+
*/
|
|
248
|
+
if (isPdf && crop === null && fallback) {
|
|
249
|
+
try { presetCrop = idcPdfCropName(app.pdfPlacePreferences.pdfCrop); } catch (epc) { presetCrop = null; }
|
|
250
|
+
if (presetCrop !== null) {
|
|
251
|
+
presetFallbacks = idcPdfCropFallbacks(presetCrop);
|
|
252
|
+
for (i = 0; i < presetFallbacks.length; i++) {
|
|
253
|
+
if (presetFallbacks[i] !== presetCrop) { attempts.push(presetFallbacks[i]); }
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
placed = null;
|
|
258
|
+
for (k = 0; k < attempts.length; k++) {
|
|
259
|
+
attempt = attempts[k];
|
|
260
|
+
cropSet = false; pageSet = false;
|
|
261
|
+
try {
|
|
262
|
+
if (isPdf && typeof app.pdfPlacePreferences !== 'undefined') {
|
|
263
|
+
if (attempt !== null) {
|
|
264
|
+
prevCrop = app.pdfPlacePreferences.pdfCrop;
|
|
265
|
+
app.pdfPlacePreferences.pdfCrop = idcPdfCropFor(attempt);
|
|
266
|
+
cropSet = true;
|
|
267
|
+
}
|
|
268
|
+
prevPage = app.pdfPlacePreferences.pageNumber;
|
|
269
|
+
app.pdfPlacePreferences.pageNumber = pdfPage;
|
|
270
|
+
pageSet = true;
|
|
271
|
+
}
|
|
272
|
+
} catch (ep) {
|
|
273
|
+
ctx.warn('Could not set the PDF place preferences: ' + String(ep.message ? ep.message : ep));
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
placed = frame.place(file);
|
|
277
|
+
lastError = null;
|
|
278
|
+
} catch (e) {
|
|
279
|
+
lastError = e;
|
|
280
|
+
placed = null;
|
|
281
|
+
} finally {
|
|
282
|
+
try { if (cropSet) { app.pdfPlacePreferences.pdfCrop = prevCrop; } } catch (er1) { ctx.warn('Could not restore the PDF crop preference.'); }
|
|
283
|
+
try { if (pageSet) { app.pdfPlacePreferences.pageNumber = prevPage; } } catch (er2) { ctx.warn('Could not restore the PDF page preference.'); }
|
|
284
|
+
}
|
|
285
|
+
if (lastError === null) {
|
|
286
|
+
cropUsed = attempt;
|
|
287
|
+
if (k > 0 && crop === null) {
|
|
288
|
+
ctx.warn('InDesign\'s preset PDF box "' + presetCrop + '" failed without pdf_crop (InDesign: ' + firstErrorMessage + '); the PDF was placed with pdf_crop "' +
|
|
289
|
+
attempt + '" instead – the box is larger, check the position, or pass pdf_crop.');
|
|
290
|
+
} else if (k > 0) {
|
|
291
|
+
ctx.warn('pdf_crop "' + crop + '" failed (InDesign: ' + firstErrorMessage + '); the PDF was placed with pdf_crop "' +
|
|
292
|
+
attempt + '" instead – the box is larger, check the position.');
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
if (k === 0) { firstErrorMessage = String(lastError.message ? lastError.message : lastError); }
|
|
297
|
+
if (idcGraphicsOf(frame).length > 0) {
|
|
298
|
+
/* Trotz Fehler etwas platziert? Dann nicht nochmals. */
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (lastError !== null) {
|
|
303
|
+
triedNames = [];
|
|
304
|
+
for (i = 0; i < attempts.length; i++) { triedNames.push(attempts[i] === null ? 'preset ' + presetCrop : attempts[i]); }
|
|
305
|
+
IDC.fail('io_error', 'InDesign could not place ' + path + ': ' + String(lastError.message ? lastError.message : lastError) +
|
|
306
|
+
(attempts.length > 1 ? ' (tried pdf_crop ' + triedNames.join(', ') + ')' : ''), { image_path: path, frame_id: args.frame_id, pdf_crop_tried: attempts });
|
|
307
|
+
}
|
|
308
|
+
/*
|
|
309
|
+
* place() liefert in ExtendScript ein Array, das IDC.isArray nicht als
|
|
310
|
+
* solches erkennt (Live-Fund 04.09.2026). Deshalb zuerst die Grafik im
|
|
311
|
+
* Rahmen frisch lesen, place()-Rueckgabe nur als Rueckfall.
|
|
312
|
+
*/
|
|
313
|
+
graphic = null;
|
|
314
|
+
graphics = idcGraphicsOf(frame);
|
|
315
|
+
if (graphics.length > 0) { graphic = graphics[0]; }
|
|
316
|
+
if (graphic === null) {
|
|
317
|
+
try {
|
|
318
|
+
if (placed && typeof placed.length === 'number' && placed.id === undefined && placed.length > 0) { graphic = IDC.concrete(placed[0]); }
|
|
319
|
+
else if (placed) { graphic = IDC.concrete(placed); }
|
|
320
|
+
} catch (eg) { graphic = null; }
|
|
321
|
+
}
|
|
322
|
+
if (graphic === null) {
|
|
323
|
+
IDC.fail('io_error', 'place() returned no graphic for ' + path, { image_path: path, frame_id: args.frame_id });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (fit !== null) {
|
|
327
|
+
fitOption = idcFitOptionFor(fit);
|
|
328
|
+
try {
|
|
329
|
+
if (fitOption !== null) { frame.fit(fitOption); }
|
|
330
|
+
} catch (ef) {
|
|
331
|
+
ctx.warn('The graphic was placed but fit "' + fit + '" failed: ' + String(ef.message ? ef.message : ef));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
page = idcPageOfItem(doc, frame);
|
|
336
|
+
entry = idcDescribeGraphic(doc, frame, graphic, page);
|
|
337
|
+
frameBounds = entry.frame_bounds;
|
|
338
|
+
graphicBounds = entry.graphic_bounds;
|
|
339
|
+
fits = idcCoversFrame(frameBounds, graphicBounds);
|
|
340
|
+
if (!fits) {
|
|
341
|
+
ctx.warn('The graphic does not cover the frame (tolerance 0.2 mm): frame ' + IDC.stringify(frameBounds) + ', graphic ' + IDC.stringify(graphicBounds) +
|
|
342
|
+
'. Use fit "fill_proportionally" or scale_image_in_frame.');
|
|
343
|
+
}
|
|
344
|
+
if (entry.link_status !== null && entry.link_status !== 'NORMAL' && entry.link_status !== 'EMBEDDED') {
|
|
345
|
+
ctx.warn('Link status after placing: ' + entry.link_status + '.');
|
|
346
|
+
}
|
|
347
|
+
minPpi = idcMinPpi(args);
|
|
348
|
+
if (entry.effective_ppi !== null && entry.effective_ppi < minPpi && !entry.is_pdf) {
|
|
349
|
+
ctx.warn('Effective resolution is ' + entry.effective_ppi + ' ppi – below ' + minPpi + ' ppi (min_ppi).');
|
|
350
|
+
}
|
|
351
|
+
var h;
|
|
352
|
+
for (h = 0; h < entry.hints.length; h++) { if (!entry.is_pdf || !/ppi|colour/.test(entry.hints[h])) { ctx.warn(entry.hints[h]); } }
|
|
353
|
+
|
|
354
|
+
ctx.changes = { modified_frame_ids: [Number(frame.id)], created_graphic_ids: [entry.graphic_id] };
|
|
355
|
+
return {
|
|
356
|
+
frame_id: Number(frame.id),
|
|
357
|
+
graphic_id: entry.graphic_id,
|
|
358
|
+
link_id: entry.link_id,
|
|
359
|
+
file_name: entry.file_name,
|
|
360
|
+
link_status: entry.link_status,
|
|
361
|
+
image_type: entry.image_type,
|
|
362
|
+
is_pdf: entry.is_pdf,
|
|
363
|
+
fit: fit,
|
|
364
|
+
pdf_crop: isPdf ? crop : null,
|
|
365
|
+
pdf_crop_used: isPdf ? cropUsed : null,
|
|
366
|
+
pdf_page: isPdf ? pdfPage : null,
|
|
367
|
+
frame_bounds_mm: frameBounds,
|
|
368
|
+
graphic_bounds_mm: graphicBounds,
|
|
369
|
+
fits_frame: fits,
|
|
370
|
+
effective_ppi: entry.effective_ppi,
|
|
371
|
+
color_space: entry.color_space,
|
|
372
|
+
graphics_replaced: removed,
|
|
373
|
+
page_id: page === null ? null : Number(page.id),
|
|
374
|
+
on_parent_page: found.on_parent
|
|
375
|
+
};
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
/* ----------------------------------------------------- scale_image_in_frame */
|
|
379
|
+
|
|
380
|
+
/* Ankerpunkt {x, y} aus einer Begrenzung. */
|
|
381
|
+
function idcAnchorPoint(bounds, anchor) {
|
|
382
|
+
var x = bounds.x + bounds.width / 2;
|
|
383
|
+
var y = bounds.y + bounds.height / 2;
|
|
384
|
+
if (/left/.test(anchor)) { x = bounds.x; }
|
|
385
|
+
if (/right/.test(anchor)) { x = bounds.x + bounds.width; }
|
|
386
|
+
if (/top/.test(anchor)) { y = bounds.y; }
|
|
387
|
+
if (/bottom/.test(anchor)) { y = bounds.y + bounds.height; }
|
|
388
|
+
return { x: x, y: y };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
IDC.register('scale_image_in_frame', { needs_document: true, read_only: false, ruler: 'page' },
|
|
392
|
+
function (ctx) {
|
|
393
|
+
var args = ctx.args;
|
|
394
|
+
var doc = ctx.doc;
|
|
395
|
+
var found = idcGraphicFrameArg(doc, args, 'frame_id');
|
|
396
|
+
var frame = found.item;
|
|
397
|
+
var zoom = args.zoom_factor;
|
|
398
|
+
var anchor = args.anchor === undefined || args.anchor === null ? 'center' : args.anchor;
|
|
399
|
+
var anchors = { center: 1, top: 1, bottom: 1, left: 1, right: 1, top_left: 1, top_right: 1, bottom_left: 1, bottom_right: 1 };
|
|
400
|
+
var minPpi;
|
|
401
|
+
var graphics, graphic, page, frameBounds, before, pivot, after, rotation, dx, entry;
|
|
402
|
+
|
|
403
|
+
if (typeof zoom !== 'number' || !isFinite(zoom) || !(zoom > 0)) {
|
|
404
|
+
IDC.fail('invalid_argument', 'zoom_factor must be a number greater than 0 (1.2 = 20 % larger).', { zoom_factor: zoom });
|
|
405
|
+
}
|
|
406
|
+
if (typeof anchor !== 'string' || !IDC.hasOwn(anchors, anchor)) {
|
|
407
|
+
IDC.fail('invalid_argument', 'anchor must be center, top, bottom, left, right, top_left, top_right, bottom_left or bottom_right.', { anchor: anchor });
|
|
408
|
+
}
|
|
409
|
+
graphics = idcGraphicsOf(frame);
|
|
410
|
+
if (graphics.length === 0) {
|
|
411
|
+
IDC.fail('invalid_state', 'Frame ' + args.frame_id + ' holds no graphic.', { frame_id: args.frame_id });
|
|
412
|
+
}
|
|
413
|
+
graphic = graphics[0];
|
|
414
|
+
if (graphics.length > 1) { ctx.warn('The frame holds ' + graphics.length + ' graphics; only the first was scaled.'); }
|
|
415
|
+
rotation = idcRotationOf(graphic);
|
|
416
|
+
if (rotation !== null && rotation !== 0) { ctx.warn('The graphic is rotated by ' + rotation + ' degrees; scaling via bounds may shift it.'); }
|
|
417
|
+
|
|
418
|
+
page = idcPageOfItem(doc, frame);
|
|
419
|
+
frameBounds = IDC.boundsOnPage(doc, frame, page);
|
|
420
|
+
before = IDC.boundsOnPage(doc, graphic, page);
|
|
421
|
+
pivot = idcAnchorPoint(frameBounds, anchor);
|
|
422
|
+
after = {
|
|
423
|
+
x: pivot.x + (before.x - pivot.x) * zoom,
|
|
424
|
+
y: pivot.y + (before.y - pivot.y) * zoom,
|
|
425
|
+
width: before.width * zoom,
|
|
426
|
+
height: before.height * zoom
|
|
427
|
+
};
|
|
428
|
+
dx = page === null ? 0 : IDC.spreadOffsetOfPage(page);
|
|
429
|
+
try {
|
|
430
|
+
IDC.withRulerOrigin(doc, RulerOrigin.SPREAD_ORIGIN, function () {
|
|
431
|
+
graphic.geometricBounds = [after.y, after.x + dx, after.y + after.height, after.x + after.width + dx];
|
|
432
|
+
});
|
|
433
|
+
} catch (e) {
|
|
434
|
+
IDC.fail('script_error', 'InDesign could not scale the graphic in frame ' + args.frame_id + ': ' + String(e.message ? e.message : e),
|
|
435
|
+
{ frame_id: args.frame_id, zoom_factor: zoom });
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
entry = idcDescribeGraphic(doc, frame, graphic, page);
|
|
439
|
+
minPpi = idcMinPpi(args);
|
|
440
|
+
if (entry.effective_ppi !== null && entry.effective_ppi < minPpi && !entry.is_pdf) {
|
|
441
|
+
ctx.warn('Effective resolution after zoom is ' + entry.effective_ppi + ' ppi – below ' + minPpi + ' ppi (min_ppi).');
|
|
442
|
+
}
|
|
443
|
+
if (!idcCoversFrame(entry.frame_bounds, entry.graphic_bounds)) {
|
|
444
|
+
ctx.warn('The graphic no longer covers the whole frame (tolerance 0.2 mm).');
|
|
445
|
+
}
|
|
446
|
+
ctx.changes = { modified_frame_ids: [Number(frame.id)] };
|
|
447
|
+
return {
|
|
448
|
+
frame_id: Number(frame.id),
|
|
449
|
+
graphic_id: entry.graphic_id,
|
|
450
|
+
zoom_factor: zoom,
|
|
451
|
+
anchor: anchor,
|
|
452
|
+
graphic_bounds_mm: entry.graphic_bounds,
|
|
453
|
+
frame_bounds_mm: entry.frame_bounds,
|
|
454
|
+
graphic_bounds_before_mm: before,
|
|
455
|
+
effective_ppi: entry.effective_ppi,
|
|
456
|
+
effective_ppi_xy: entry.effective_ppi_xy,
|
|
457
|
+
fits_frame: idcCoversFrame(entry.frame_bounds, entry.graphic_bounds)
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
/* -------------------------------------------------- position_image_in_frame */
|
|
462
|
+
|
|
463
|
+
/*
|
|
464
|
+
* Sitzung 24 (Befund aus dem Praxiseinsatz, E-79): Grafik im Rahmen verschieben,
|
|
465
|
+
* ohne sie zu skalieren und ohne den Rahmen anzufassen – an einem der neun
|
|
466
|
+
* Bezugspunkte ausrichten (Bezugspunkt der Grafik auf denselben des
|
|
467
|
+
* Rahmens) und/oder um offset_mm versetzen. Schreibweg wie
|
|
468
|
+
* scale_image_in_frame: Begrenzung der Grafik unter Bogen-Nullpunkt, gleiche
|
|
469
|
+
* Groesse.
|
|
470
|
+
*/
|
|
471
|
+
IDC.register('position_image_in_frame', { needs_document: true, read_only: false, ruler: 'page' },
|
|
472
|
+
function (ctx) {
|
|
473
|
+
var args = ctx.args;
|
|
474
|
+
var doc = ctx.doc;
|
|
475
|
+
var found = idcGraphicFrameArg(doc, args, 'frame_id');
|
|
476
|
+
var frame = found.item;
|
|
477
|
+
var anchors = { center: 1, top: 1, bottom: 1, left: 1, right: 1, top_left: 1, top_right: 1, bottom_left: 1, bottom_right: 1 };
|
|
478
|
+
var align = args.align === undefined || args.align === null ? null : args.align;
|
|
479
|
+
var offset = null;
|
|
480
|
+
var graphics, graphic, page, frameBounds, before, target, frameAnchor, graphicAnchor, rotation, dxPage, entry, frameAfter;
|
|
481
|
+
|
|
482
|
+
if (align !== null && (typeof align !== 'string' || !IDC.hasOwn(anchors, align))) {
|
|
483
|
+
IDC.fail('invalid_argument', 'align must be center, top, bottom, left, right, top_left, top_right, bottom_left or bottom_right.', { align: align });
|
|
484
|
+
}
|
|
485
|
+
if (args.offset_mm !== undefined && args.offset_mm !== null) { offset = idcPointArg(args.offset_mm, 'offset_mm'); }
|
|
486
|
+
if (align === null && offset === null) {
|
|
487
|
+
IDC.fail('invalid_argument', 'Pass align, offset_mm or both.', null);
|
|
488
|
+
}
|
|
489
|
+
graphics = idcGraphicsOf(frame);
|
|
490
|
+
if (graphics.length === 0) {
|
|
491
|
+
IDC.fail('invalid_state', 'Frame ' + args.frame_id + ' holds no graphic.', { frame_id: args.frame_id });
|
|
492
|
+
}
|
|
493
|
+
graphic = graphics[0];
|
|
494
|
+
if (graphics.length > 1) { ctx.warn('The frame holds ' + graphics.length + ' graphics; only the first was positioned.'); }
|
|
495
|
+
rotation = idcRotationOf(graphic);
|
|
496
|
+
if (rotation !== null && rotation !== 0) { ctx.warn('The graphic is rotated by ' + rotation + ' degrees; alignment uses its bounding box.'); }
|
|
497
|
+
|
|
498
|
+
page = idcPageOfItem(doc, frame);
|
|
499
|
+
frameBounds = IDC.boundsOnPage(doc, frame, page);
|
|
500
|
+
before = IDC.boundsOnPage(doc, graphic, page);
|
|
501
|
+
target = { x: before.x, y: before.y };
|
|
502
|
+
if (align !== null) {
|
|
503
|
+
frameAnchor = idcAnchorPoint(frameBounds, align);
|
|
504
|
+
graphicAnchor = idcAnchorPoint(before, align);
|
|
505
|
+
target.x = before.x + (frameAnchor.x - graphicAnchor.x);
|
|
506
|
+
target.y = before.y + (frameAnchor.y - graphicAnchor.y);
|
|
507
|
+
}
|
|
508
|
+
if (offset !== null) {
|
|
509
|
+
target.x += offset.x;
|
|
510
|
+
target.y += offset.y;
|
|
511
|
+
}
|
|
512
|
+
dxPage = page === null ? 0 : IDC.spreadOffsetOfPage(page);
|
|
513
|
+
try {
|
|
514
|
+
IDC.withRulerOrigin(doc, RulerOrigin.SPREAD_ORIGIN, function () {
|
|
515
|
+
graphic.geometricBounds = [target.y, target.x + dxPage, target.y + before.height, target.x + before.width + dxPage];
|
|
516
|
+
});
|
|
517
|
+
} catch (e) {
|
|
518
|
+
IDC.fail('script_error', 'InDesign could not move the graphic in frame ' + args.frame_id + ': ' + String(e.message ? e.message : e),
|
|
519
|
+
{ frame_id: args.frame_id });
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
entry = idcDescribeGraphic(doc, frame, graphic, page);
|
|
523
|
+
frameAfter = entry.frame_bounds;
|
|
524
|
+
if (!idcCoversFrame(entry.frame_bounds, entry.graphic_bounds)) {
|
|
525
|
+
ctx.warn('The graphic no longer covers the whole frame (tolerance 0.2 mm) – part of the frame shows no picture.');
|
|
526
|
+
}
|
|
527
|
+
ctx.changes = { modified_frame_ids: [Number(frame.id)] };
|
|
528
|
+
return {
|
|
529
|
+
frame_id: Number(frame.id),
|
|
530
|
+
graphic_id: entry.graphic_id,
|
|
531
|
+
align: align,
|
|
532
|
+
offset_mm: offset,
|
|
533
|
+
moved_by_mm: { x: IDC.round(target.x - before.x), y: IDC.round(target.y - before.y) },
|
|
534
|
+
graphic_bounds_mm: entry.graphic_bounds,
|
|
535
|
+
graphic_bounds_before_mm: before,
|
|
536
|
+
frame_bounds_mm: frameAfter,
|
|
537
|
+
coordinate_space: page === null ? 'spread' : 'page',
|
|
538
|
+
page_id: page === null ? null : Number(page.id),
|
|
539
|
+
fits_frame: idcCoversFrame(entry.frame_bounds, entry.graphic_bounds)
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
/* ------------------------------------------------------ get_image_link_info */
|
|
544
|
+
|
|
545
|
+
IDC.register('get_image_link_info', { needs_document: true, read_only: true, ruler: 'page' },
|
|
546
|
+
function (ctx) {
|
|
547
|
+
var args = ctx.args;
|
|
548
|
+
var doc = ctx.doc;
|
|
549
|
+
var found = idcFrameArg(doc, args, 'frame_id');
|
|
550
|
+
var frame = found.item;
|
|
551
|
+
var graphics = idcGraphicsOf(frame);
|
|
552
|
+
var page = idcPageOfItem(doc, frame);
|
|
553
|
+
var out = [];
|
|
554
|
+
var i, entry, h;
|
|
555
|
+
|
|
556
|
+
for (i = 0; i < graphics.length; i++) {
|
|
557
|
+
entry = idcDescribeGraphic(doc, frame, graphics[i], page);
|
|
558
|
+
for (h = 0; h < entry.hints.length; h++) { ctx.warn('Graphic ' + entry.graphic_id + ': ' + entry.hints[h]); }
|
|
559
|
+
out.push({
|
|
560
|
+
graphic_id: entry.graphic_id,
|
|
561
|
+
link_id: entry.link_id,
|
|
562
|
+
file_name: entry.file_name,
|
|
563
|
+
file_path: entry.file_path,
|
|
564
|
+
link_status: entry.link_status,
|
|
565
|
+
effective_ppi: entry.effective_ppi,
|
|
566
|
+
effective_ppi_xy: entry.effective_ppi_xy,
|
|
567
|
+
actual_ppi: entry.actual_ppi,
|
|
568
|
+
actual_ppi_xy: entry.actual_ppi_xy,
|
|
569
|
+
color_space: entry.color_space,
|
|
570
|
+
image_type: entry.image_type,
|
|
571
|
+
is_pdf: entry.is_pdf,
|
|
572
|
+
frame_bounds: entry.frame_bounds,
|
|
573
|
+
graphic_bounds: entry.graphic_bounds,
|
|
574
|
+
fits_frame: idcCoversFrame(entry.frame_bounds, entry.graphic_bounds),
|
|
575
|
+
err: entry.hints.length > 0 ? entry.hints.join('; ') : null
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
if (graphics.length === 0) { ctx.warn('Frame ' + args.frame_id + ' holds no graphic.'); }
|
|
579
|
+
return {
|
|
580
|
+
frame_id: Number(frame.id),
|
|
581
|
+
type: IDC.itemType(frame),
|
|
582
|
+
count: out.length,
|
|
583
|
+
graphics: out,
|
|
584
|
+
page_id: page === null ? null : Number(page.id),
|
|
585
|
+
coordinate_space: page === null ? 'spread' : 'page',
|
|
586
|
+
on_parent_page: found.on_parent
|
|
587
|
+
};
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
/* --------------------------------------------------------------- list_links */
|
|
591
|
+
|
|
592
|
+
/* Rahmen und Seite einer Verknuepfung. */
|
|
593
|
+
function idcLinkPlacement(doc, link) {
|
|
594
|
+
var out = { frame_id: null, page_id: null, page_name: '' };
|
|
595
|
+
var graphic = null, frame = null, landing, containers;
|
|
596
|
+
try { graphic = link.parent; } catch (e0) { graphic = null; }
|
|
597
|
+
if (graphic === null || graphic === undefined) { return out; }
|
|
598
|
+
/*
|
|
599
|
+
* Sitzung 38 (A-25 Nr. 1): Link.parent kann eine Story sein (Adobe-DOM,
|
|
600
|
+
* Auszug 02.10.2026) – deren parent ist das Dokument. Rahmen und Seite
|
|
601
|
+
* dann ueber den ersten Textrahmen der Story.
|
|
602
|
+
*/
|
|
603
|
+
if (IDC.className(graphic) === 'Story') {
|
|
604
|
+
try {
|
|
605
|
+
containers = graphic.textContainers;
|
|
606
|
+
if (containers && containers.length > 0) {
|
|
607
|
+
frame = IDC.concrete(containers[0]);
|
|
608
|
+
out.frame_id = Number(frame.id);
|
|
609
|
+
landing = IDC.landing(frame);
|
|
610
|
+
out.page_id = landing.page_id;
|
|
611
|
+
out.page_name = landing.page_name;
|
|
612
|
+
}
|
|
613
|
+
} catch (eS) { /* Story ohne Rahmen: bleibt leer */ }
|
|
614
|
+
return out;
|
|
615
|
+
}
|
|
616
|
+
try { frame = graphic.parent; } catch (e1) { frame = null; }
|
|
617
|
+
try {
|
|
618
|
+
if (frame !== null && frame !== undefined && IDC.className(frame) !== 'Spread' && IDC.className(frame) !== 'MasterSpread') {
|
|
619
|
+
out.frame_id = Number(frame.id);
|
|
620
|
+
}
|
|
621
|
+
} catch (e2) { out.frame_id = null; }
|
|
622
|
+
try {
|
|
623
|
+
landing = IDC.landing(frame !== null && frame !== undefined ? frame : graphic);
|
|
624
|
+
out.page_id = landing.page_id;
|
|
625
|
+
out.page_name = landing.page_name;
|
|
626
|
+
} catch (e3) { /* bleibt leer */ }
|
|
627
|
+
return out;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/*
|
|
631
|
+
* Sitzung 41 (A-18, Anforderung aus einem Heft): wo liegt das Objekt, das
|
|
632
|
+
* eine Verknuepfung haelt? location page | parent_page | pasteboard |
|
|
633
|
+
* anchored | unknown. Der Weg geht ueber die Eltern des haltenden Objekts:
|
|
634
|
+
* eine Textstelle (Zeichen) heisst verankert, ein Musterbogen Musterseite,
|
|
635
|
+
* ein Bogen ohne parentPage Montageflaeche. Bei verankerten Objekten
|
|
636
|
+
* zusaetzlich story_id und in_overset (Ankerzeichen in keinem Rahmen).
|
|
637
|
+
*/
|
|
638
|
+
var IDC_LINK_ANCHOR_CLASSES = ['Character', 'InsertionPoint', 'Text', 'Word', 'Line', 'Paragraph', 'TextStyleRange', 'TextColumn', 'Story', 'XmlStory', 'Cell'];
|
|
639
|
+
|
|
640
|
+
function idcLinkLocation(container) {
|
|
641
|
+
var out = { location: 'unknown', spread_id: null, story_id: null, in_overset: null, anchor_index: null };
|
|
642
|
+
var current = container, step, name, story = null, anchorText = null, frames, landing;
|
|
643
|
+
if (container === null || container === undefined) { return out; }
|
|
644
|
+
for (step = 0; step < 16; step++) {
|
|
645
|
+
try { current = current.parent; } catch (e0) { return out; }
|
|
646
|
+
if (current === null || current === undefined) { return out; }
|
|
647
|
+
name = IDC.className(current);
|
|
648
|
+
if (idcInArray(IDC_LINK_ANCHOR_CLASSES, name)) {
|
|
649
|
+
anchorText = current;
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
if (name === 'MasterSpread') {
|
|
653
|
+
out.location = 'parent_page';
|
|
654
|
+
try { out.spread_id = Number(current.id); } catch (e1) { out.spread_id = null; }
|
|
655
|
+
return out;
|
|
656
|
+
}
|
|
657
|
+
if (name === 'Spread') {
|
|
658
|
+
try { out.spread_id = Number(current.id); } catch (e2) { out.spread_id = null; }
|
|
659
|
+
landing = IDC.landing(container);
|
|
660
|
+
out.location = landing.on_pasteboard ? 'pasteboard' : 'page';
|
|
661
|
+
return out;
|
|
662
|
+
}
|
|
663
|
+
if (name === 'Document' || name === 'Application') { return out; }
|
|
664
|
+
}
|
|
665
|
+
if (anchorText === null) { return out; }
|
|
666
|
+
out.location = 'anchored';
|
|
667
|
+
try { story = IDC.className(anchorText) === 'Story' ? anchorText : anchorText.parentStory; } catch (e3) { story = null; }
|
|
668
|
+
try { if (story !== null && story !== undefined) { out.story_id = Number(story.id); } } catch (e4) { out.story_id = null; }
|
|
669
|
+
try { out.anchor_index = Number(anchorText.index); } catch (e5) { out.anchor_index = null; }
|
|
670
|
+
try {
|
|
671
|
+
frames = anchorText.parentTextFrames;
|
|
672
|
+
out.in_overset = !(frames && frames.length > 0);
|
|
673
|
+
} catch (e6) { out.in_overset = null; }
|
|
674
|
+
if (out.in_overset === false) {
|
|
675
|
+
try { out.spread_id = Number(IDC.spreadOfItem(IDC.concrete(frames[0])).id); } catch (e7) { out.spread_id = null; }
|
|
676
|
+
}
|
|
677
|
+
return out;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function idcInArray(list, value) {
|
|
681
|
+
var i;
|
|
682
|
+
for (i = 0; i < list.length; i++) { if (list[i] === value) { return true; } }
|
|
683
|
+
return false;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/* Haltendes Objekt einer Verknuepfung: Rahmen der Grafik, bei Redaktionsdateien der erste Textrahmen der Story. */
|
|
687
|
+
function idcLinkContainer(link) {
|
|
688
|
+
var graphic = null, container = null, containers, name;
|
|
689
|
+
var out = { graphic: null, graphic_type: null, container: null, story: null };
|
|
690
|
+
try { graphic = link.parent; } catch (e0) { graphic = null; }
|
|
691
|
+
if (graphic === null || graphic === undefined) { return out; }
|
|
692
|
+
out.graphic = graphic;
|
|
693
|
+
name = IDC.className(graphic);
|
|
694
|
+
out.graphic_type = name === '' ? null : name;
|
|
695
|
+
if (name === 'Story') {
|
|
696
|
+
out.story = graphic;
|
|
697
|
+
try {
|
|
698
|
+
containers = graphic.textContainers;
|
|
699
|
+
if (containers && containers.length > 0) { out.container = IDC.concrete(containers[0]); }
|
|
700
|
+
} catch (eS) { out.container = null; }
|
|
701
|
+
return out;
|
|
702
|
+
}
|
|
703
|
+
try { container = graphic.parent; } catch (e1) { container = null; }
|
|
704
|
+
if (container !== null && container !== undefined) {
|
|
705
|
+
name = IDC.className(container);
|
|
706
|
+
if (name !== 'Spread' && name !== 'MasterSpread' && name !== 'Document') { out.container = IDC.concrete(container); }
|
|
707
|
+
}
|
|
708
|
+
return out;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/* Neue Felder von list_links (A-18): container_id, container_type, graphic_type, location, spread_id, story_id, in_overset. */
|
|
712
|
+
function idcLinkWhereabouts(link) {
|
|
713
|
+
var held = idcLinkContainer(link);
|
|
714
|
+
var where = { container_id: null, container_type: null, graphic_type: held.graphic_type, location: 'unknown', spread_id: null, story_id: null, in_overset: null };
|
|
715
|
+
var loc;
|
|
716
|
+
if (held.container !== null) {
|
|
717
|
+
try { where.container_id = Number(held.container.id); } catch (e0) { where.container_id = null; }
|
|
718
|
+
try { where.container_type = IDC.itemType(held.container); } catch (e1) { where.container_type = null; }
|
|
719
|
+
loc = idcLinkLocation(held.container);
|
|
720
|
+
where.location = loc.location;
|
|
721
|
+
where.spread_id = loc.spread_id;
|
|
722
|
+
where.story_id = loc.story_id;
|
|
723
|
+
where.in_overset = loc.in_overset;
|
|
724
|
+
}
|
|
725
|
+
if (held.story !== null) {
|
|
726
|
+
/* Redaktionsdatei (InCopy): die Story selbst ist verknuepft, Ort ueber ihren ersten Rahmen. */
|
|
727
|
+
try { where.story_id = Number(held.story.id); } catch (e2) { where.story_id = null; }
|
|
728
|
+
}
|
|
729
|
+
return { where: where, held: held };
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function idcDescribeLink(doc, link) {
|
|
733
|
+
var placement = idcLinkPlacement(doc, link);
|
|
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 info = idcLinkWhereabouts(link), where = info.where, key;
|
|
736
|
+
try { entry.link_id = Number(link.id); } catch (e0) { entry.link_id = null; }
|
|
737
|
+
try { entry.file_name = String(link.name); } catch (e1) { entry.file_name = null; }
|
|
738
|
+
try { entry.file_path = String(link.filePath); } catch (e2) { entry.file_path = null; }
|
|
739
|
+
try { entry.status = idcLinkStatusName(link.status, true); } catch (e3) { entry.status = null; }
|
|
740
|
+
for (key in where) {
|
|
741
|
+
if (IDC.hasOwn(where, key)) { entry[key] = where[key]; }
|
|
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;
|
|
745
|
+
return entry;
|
|
746
|
+
}
|
|
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
|
+
|
|
765
|
+
IDC.register('list_links', { needs_document: true, read_only: true, ruler: 'page' },
|
|
766
|
+
function (ctx) {
|
|
767
|
+
var args = ctx.args;
|
|
768
|
+
var doc = ctx.doc;
|
|
769
|
+
var filter = args.path_contains === undefined || args.path_contains === null ? null : String(args.path_contains).toLowerCase();
|
|
770
|
+
var out = [];
|
|
771
|
+
var i, link, entry, haystack;
|
|
772
|
+
|
|
773
|
+
for (i = 0; i < doc.links.length; i++) {
|
|
774
|
+
link = doc.links.item(i);
|
|
775
|
+
entry = idcDescribeLink(doc, link);
|
|
776
|
+
if (filter !== null) {
|
|
777
|
+
haystack = String(entry.file_path === null ? '' : entry.file_path) + '\n' + String(entry.file_name === null ? '' : entry.file_name);
|
|
778
|
+
if (haystack.toLowerCase().indexOf(filter) < 0) { continue; }
|
|
779
|
+
}
|
|
780
|
+
out.push(entry);
|
|
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
|
+
}
|
|
789
|
+
return { links: out, count: out.length, total: Number(doc.links.length), path_contains: filter };
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
/* -------------------------------------------------------------- delete_link */
|
|
793
|
+
|
|
794
|
+
/*
|
|
795
|
+
* Sitzung 41 (A-18, Anforderung aus einem Heft, E-96): Verknuepfung ueber
|
|
796
|
+
* ihre link_id entfernen – fuer Reste auf der Montageflaeche und Waisen
|
|
797
|
+
* (verankert im Uebersatz, Ort unbekannt), nicht als zweites delete_frame.
|
|
798
|
+
* Liegt das Objekt auf einer Dokument- oder Musterseite (auch verankert in
|
|
799
|
+
* sichtbarem Text), wird ohne allow_on_page abgewiesen. remove_frame
|
|
800
|
+
* (Vorgabe true) nimmt den Rahmen mit, sonst nur die Grafik.
|
|
801
|
+
*/
|
|
802
|
+
function idcLocationBefore(where, placement) {
|
|
803
|
+
return {
|
|
804
|
+
location: where.location,
|
|
805
|
+
page_id: placement.page_id,
|
|
806
|
+
page_name: placement.page_name,
|
|
807
|
+
spread_id: where.spread_id,
|
|
808
|
+
container_id: where.container_id,
|
|
809
|
+
container_type: where.container_type,
|
|
810
|
+
graphic_type: where.graphic_type,
|
|
811
|
+
story_id: where.story_id,
|
|
812
|
+
in_overset: where.in_overset
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
IDC.register('delete_link', { needs_document: true, read_only: false, ruler: 'page' },
|
|
817
|
+
function (ctx) {
|
|
818
|
+
var args = ctx.args;
|
|
819
|
+
var doc = ctx.doc;
|
|
820
|
+
var linkId = Number(args.link_id);
|
|
821
|
+
var removeFrame = args.remove_frame !== false;
|
|
822
|
+
var dryRun = args.dry_run === true;
|
|
823
|
+
var allowOnPage = args.allow_on_page === true;
|
|
824
|
+
var link = idcLinkById(doc, linkId);
|
|
825
|
+
var info, where, held, before, onPage, ids = [], linksBefore, linksAfter, anchorStory = null, lockInfo = null, target, what;
|
|
826
|
+
|
|
827
|
+
if (link === null) {
|
|
828
|
+
IDC.fail('not_found', 'No link with link_id ' + linkId + ' in this document (list_links lists them).', { link_id: linkId });
|
|
829
|
+
}
|
|
830
|
+
info = idcLinkWhereabouts(link);
|
|
831
|
+
where = info.where;
|
|
832
|
+
held = info.held;
|
|
833
|
+
before = idcLocationBefore(where, idcLinkPlacement(doc, link));
|
|
834
|
+
linksBefore = Number(doc.links.length);
|
|
835
|
+
|
|
836
|
+
if (held.story !== null) {
|
|
837
|
+
IDC.fail('invalid_state', 'Link ' + linkId + ' is a linked story file (editorial workflow), not a graphic; delete_link removes graphics only. Use delete_frame for its frames or remove it in the editorial system.',
|
|
838
|
+
{ code: 'story_link', link_id: linkId, location_before: before });
|
|
839
|
+
}
|
|
840
|
+
if (held.graphic === null) {
|
|
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,
|
|
842
|
+
{ code: 'link_without_object', link_id: linkId, location_before: before });
|
|
843
|
+
}
|
|
844
|
+
onPage = where.location === 'page' || where.location === 'parent_page' || (where.location === 'anchored' && where.in_overset === false);
|
|
845
|
+
if (onPage && !allowOnPage) {
|
|
846
|
+
IDC.fail('invalid_state', 'Link ' + linkId + ' belongs to an object on ' + (where.location === 'parent_page' ? 'a parent page' : (where.location === 'anchored' ? 'a page (anchored in visible text)' : 'page ' + before.page_name)) +
|
|
847
|
+
'; delete_link is meant for leftovers on the pasteboard and orphans. Pass allow_on_page true to remove it anyway, or use delete_frame / clear_frame_content.',
|
|
848
|
+
{ code: 'link_on_page', link_id: linkId, location_before: before });
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (held.container !== null) {
|
|
852
|
+
idcRejectLocked(doc, held.container, where.container_id, 'delete_link');
|
|
853
|
+
if (where.location === 'anchored') {
|
|
854
|
+
anchorStory = idcAnchorStoryOf(held.container);
|
|
855
|
+
lockInfo = idcRejectStoryLocked(anchorStory, where.container_id, 'delete_link');
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (removeFrame && held.container !== null) {
|
|
859
|
+
idcCollectIds(held.container, ids);
|
|
860
|
+
target = held.container;
|
|
861
|
+
what = 'object ' + where.container_id;
|
|
862
|
+
} else {
|
|
863
|
+
target = held.graphic;
|
|
864
|
+
what = 'the graphic of link ' + linkId;
|
|
865
|
+
if (removeFrame) { ctx.warn('No frame holds the graphic of link ' + linkId + '; only the graphic is removed.'); }
|
|
866
|
+
}
|
|
867
|
+
if (where.location === 'unknown') {
|
|
868
|
+
ctx.warn('The place of link ' + linkId + ' could not be determined (location unknown, graphic_type ' + where.graphic_type + ').');
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (dryRun) {
|
|
872
|
+
return {
|
|
873
|
+
dry_run: true,
|
|
874
|
+
link_id: linkId,
|
|
875
|
+
remove_frame: removeFrame,
|
|
876
|
+
would_remove_link_id: linkId,
|
|
877
|
+
would_remove_frame_ids: ids,
|
|
878
|
+
removed_link_id: null,
|
|
879
|
+
removed_frame_ids: [],
|
|
880
|
+
location_before: before,
|
|
881
|
+
links_before: linksBefore,
|
|
882
|
+
links_after: linksBefore
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
try {
|
|
887
|
+
target.remove();
|
|
888
|
+
} catch (e) {
|
|
889
|
+
IDC.fail('script_error', 'InDesign could not remove ' + what + ': ' + String(e.message ? e.message : e), { link_id: linkId, location_before: before });
|
|
890
|
+
}
|
|
891
|
+
if (idcLinkById(doc, linkId) !== null) {
|
|
892
|
+
ctx.warn('Link ' + linkId + ' is still listed after the removal; check the Links panel in InDesign.');
|
|
893
|
+
}
|
|
894
|
+
if (lockInfo !== null && lockInfo !== undefined && lockInfo.managed) {
|
|
895
|
+
ctx.warn('The object was anchored in the managed story ' + lockInfo.story_id + ' (lock_state ' + lockInfo.lock_state + ', linked to an editorial workflow); the anchor was removed from that story.');
|
|
896
|
+
}
|
|
897
|
+
linksAfter = Number(doc.links.length);
|
|
898
|
+
if (ids.length > 0) { ctx.changes = { removed_frame_ids: ids }; }
|
|
899
|
+
else if (where.container_id !== null) { ctx.changes = { modified_frame_ids: [where.container_id] }; }
|
|
900
|
+
return {
|
|
901
|
+
dry_run: false,
|
|
902
|
+
link_id: linkId,
|
|
903
|
+
remove_frame: removeFrame,
|
|
904
|
+
removed_link_id: idcLinkById(doc, linkId) === null ? linkId : null,
|
|
905
|
+
removed_frame_ids: ids,
|
|
906
|
+
location_before: before,
|
|
907
|
+
links_before: linksBefore,
|
|
908
|
+
links_after: linksAfter
|
|
909
|
+
};
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
/* --------------------------------------------------- list_pasteboard_items */
|
|
913
|
+
|
|
914
|
+
/*
|
|
915
|
+
* Sitzung 41 (A-18 Nr. 3): alle Objekte der Montageflaeche je Druckbogen –
|
|
916
|
+
* oberste Ebene der Objekte eines Bogens, deren parentPage leer ist. Masse
|
|
917
|
+
* in Bogenkoordinaten (mm, Nullpunkt links oben auf dem Bogen).
|
|
918
|
+
*/
|
|
919
|
+
function idcPasteboardItemEntry(doc, item) {
|
|
920
|
+
var entry = { item_id: null, type: null, bounds_mm: null, layer_name: null, layer_locked: null, locked: null, visible: null, text_length: null, links: [] };
|
|
921
|
+
var graphics, i, link, layer, story;
|
|
922
|
+
try { entry.item_id = Number(item.id); } catch (e0) { entry.item_id = null; }
|
|
923
|
+
entry.type = IDC.itemType(item);
|
|
924
|
+
try { entry.bounds_mm = IDC.boundsToRect(item.geometricBounds); } catch (e1) { entry.bounds_mm = null; }
|
|
925
|
+
layer = idcLayerSummary(doc, item);
|
|
926
|
+
entry.layer_name = layer.layer_name;
|
|
927
|
+
entry.layer_locked = layer.locked;
|
|
928
|
+
entry.locked = idcReadBool(item, 'locked');
|
|
929
|
+
entry.visible = idcReadBool(item, 'visible');
|
|
930
|
+
if (IDC.isTextFrame(item)) {
|
|
931
|
+
try { story = item.parentStory; entry.text_length = Number(story.characters.length); } catch (e2) { entry.text_length = null; }
|
|
932
|
+
}
|
|
933
|
+
try { graphics = item.allGraphics; } catch (e3) { graphics = []; }
|
|
934
|
+
for (i = 0; graphics && i < graphics.length; i++) {
|
|
935
|
+
link = idcLinkOf(graphics[i]);
|
|
936
|
+
if (link === null) { continue; }
|
|
937
|
+
entry.links.push({
|
|
938
|
+
link_id: Number(link.id),
|
|
939
|
+
file_name: (function () { try { return String(link.name); } catch (e4) { return null; } }()),
|
|
940
|
+
status: (function () { try { return idcLinkStatusName(link.status, true); } catch (e5) { return null; } }())
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
return entry;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function idcPasteboardOfSpread(doc, spread, isParent) {
|
|
947
|
+
var out = { spread_id: null, spread_index: IDC.spreadIndex(spread), is_parent_spread: isParent, page_names: [], items: [] };
|
|
948
|
+
var i, items, item;
|
|
949
|
+
try { out.spread_id = Number(spread.id); } catch (e0) { out.spread_id = null; }
|
|
950
|
+
try { for (i = 0; i < spread.pages.length; i++) { out.page_names.push(String(spread.pages.item(i).name)); } } catch (e1) { /* ohne Seitennamen */ }
|
|
951
|
+
try { items = spread.pageItems; } catch (e2) { return out; }
|
|
952
|
+
for (i = 0; i < items.length; i++) {
|
|
953
|
+
item = IDC.concrete(items.item(i));
|
|
954
|
+
if (!IDC.landing(item).on_pasteboard) { continue; }
|
|
955
|
+
out.items.push(idcPasteboardItemEntry(doc, item));
|
|
956
|
+
}
|
|
957
|
+
return out;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
IDC.register('list_pasteboard_items', { needs_document: true, read_only: true, ruler: 'spread' },
|
|
961
|
+
function (ctx) {
|
|
962
|
+
var args = ctx.args;
|
|
963
|
+
var doc = ctx.doc;
|
|
964
|
+
var includeParents = args.include_parent_pages === true;
|
|
965
|
+
var spreads = [], checked = 0, itemCount = 0, linkCount = 0, i, entry, j;
|
|
966
|
+
function take(spread, isParent) {
|
|
967
|
+
checked += 1;
|
|
968
|
+
entry = idcPasteboardOfSpread(doc, spread, isParent);
|
|
969
|
+
if (entry.items.length === 0) { return; }
|
|
970
|
+
itemCount += entry.items.length;
|
|
971
|
+
for (j = 0; j < entry.items.length; j++) { linkCount += entry.items[j].links.length; }
|
|
972
|
+
entry.item_count = entry.items.length;
|
|
973
|
+
spreads.push(entry);
|
|
974
|
+
}
|
|
975
|
+
for (i = 0; i < doc.spreads.length; i++) { take(doc.spreads.item(i), false); }
|
|
976
|
+
if (includeParents) {
|
|
977
|
+
for (i = 0; i < doc.masterSpreads.length; i++) { take(doc.masterSpreads.item(i), true); }
|
|
978
|
+
}
|
|
979
|
+
if (itemCount > 0) {
|
|
980
|
+
ctx.warn(itemCount + ' object(s) lie on the pasteboard; they are not printed but travel with the document (and its package).');
|
|
981
|
+
}
|
|
982
|
+
return {
|
|
983
|
+
spreads: spreads,
|
|
984
|
+
item_count: itemCount,
|
|
985
|
+
link_count: linkCount,
|
|
986
|
+
spreads_checked: checked,
|
|
987
|
+
include_parent_pages: includeParents,
|
|
988
|
+
coordinate_space: 'spread'
|
|
989
|
+
};
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
/* ------------------------------------------------------------- relink_links */
|
|
993
|
+
|
|
994
|
+
/*
|
|
995
|
+
* Gleicher Dateipfad? macOS: Gross-/Kleinschreibung egal, /private/var und
|
|
996
|
+
* /var sind derselbe Ort (Integrationssuite: temporaerer Ordner, 04.09.2026).
|
|
997
|
+
*/
|
|
998
|
+
function idcSamePath(a, b) {
|
|
999
|
+
function norm(p) {
|
|
1000
|
+
var t = String(p).toLowerCase();
|
|
1001
|
+
if (t.indexOf('/private/') === 0) { t = t.substr(8); }
|
|
1002
|
+
return t;
|
|
1003
|
+
}
|
|
1004
|
+
return norm(a) === norm(b);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function idcLinkById(doc, id) {
|
|
1008
|
+
var i, link;
|
|
1009
|
+
try {
|
|
1010
|
+
link = doc.links.itemByID(Number(id));
|
|
1011
|
+
if (link && link.isValid === true) { return link; }
|
|
1012
|
+
} catch (e) { /* weiter */ }
|
|
1013
|
+
for (i = 0; i < doc.links.length; i++) {
|
|
1014
|
+
link = doc.links.item(i);
|
|
1015
|
+
try { if (Number(link.id) === Number(id)) { return link; } } catch (e2) { /* weiter */ }
|
|
1016
|
+
}
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
IDC.register('relink_links', { needs_document: true, read_only: false, ruler: 'none' },
|
|
1021
|
+
function (ctx) {
|
|
1022
|
+
var args = ctx.args;
|
|
1023
|
+
var doc = ctx.doc;
|
|
1024
|
+
var list = args.relink;
|
|
1025
|
+
var results = [];
|
|
1026
|
+
var relinked = 0, failed = 0;
|
|
1027
|
+
var i, spec, link, file, oldPath, graphic, fresh, newId, statusAfter, entry;
|
|
1028
|
+
|
|
1029
|
+
if (!IDC.isArray(list) || list.length < 1 || list.length > 500) {
|
|
1030
|
+
IDC.fail('invalid_argument', 'relink must be an array of 1 to 500 entries { link_id, new_path }.', null);
|
|
1031
|
+
}
|
|
1032
|
+
for (i = 0; i < list.length; i++) {
|
|
1033
|
+
spec = list[i] || {};
|
|
1034
|
+
entry = { link_id: spec.link_id === undefined ? null : spec.link_id, new_link_id: null, ok: false, status_after: null, reason: null, new_path: spec.new_path === undefined ? null : String(spec.new_path) };
|
|
1035
|
+
if (typeof spec.link_id !== 'number' || typeof spec.new_path !== 'string' || spec.new_path === '') {
|
|
1036
|
+
entry.reason = 'entry needs link_id (number) and new_path (string)';
|
|
1037
|
+
failed += 1; results.push(entry); continue;
|
|
1038
|
+
}
|
|
1039
|
+
link = idcLinkById(doc, spec.link_id);
|
|
1040
|
+
if (link === null) {
|
|
1041
|
+
entry.reason = 'no link with link_id ' + spec.link_id;
|
|
1042
|
+
failed += 1; results.push(entry); continue;
|
|
1043
|
+
}
|
|
1044
|
+
file = IDC.fileFor(spec.new_path);
|
|
1045
|
+
if (!file.exists) {
|
|
1046
|
+
entry.reason = 'file not found: ' + spec.new_path;
|
|
1047
|
+
failed += 1; results.push(entry); continue;
|
|
1048
|
+
}
|
|
1049
|
+
if (IDC.fileSize(file) === 0) {
|
|
1050
|
+
entry.reason = 'file is empty (0 bytes) – cloud placeholder not downloaded? ' + spec.new_path;
|
|
1051
|
+
failed += 1; results.push(entry); continue;
|
|
1052
|
+
}
|
|
1053
|
+
oldPath = '';
|
|
1054
|
+
try { oldPath = String(link.filePath); } catch (e0) { oldPath = ''; }
|
|
1055
|
+
if (oldPath !== '' && idcSamePath(oldPath, IDC.fsPath(file))) {
|
|
1056
|
+
entry.reason = 'new path equals the current path – use update_links';
|
|
1057
|
+
failed += 1; results.push(entry); continue;
|
|
1058
|
+
}
|
|
1059
|
+
graphic = null;
|
|
1060
|
+
try { graphic = link.parent; } catch (e1) { graphic = null; }
|
|
1061
|
+
try {
|
|
1062
|
+
link.relink(file);
|
|
1063
|
+
} catch (e) {
|
|
1064
|
+
entry.reason = 'InDesign refused: ' + String(e.message ? e.message : e);
|
|
1065
|
+
failed += 1; results.push(entry); continue;
|
|
1066
|
+
}
|
|
1067
|
+
/* Nach relink() kann ein neues Link-Objekt entstehen – ueber die Grafik frisch lesen. */
|
|
1068
|
+
fresh = null;
|
|
1069
|
+
try { if (graphic !== null && graphic.isValid !== false) { fresh = graphic.itemLink; } } catch (e2) { fresh = null; }
|
|
1070
|
+
if (fresh === null || fresh === undefined) { fresh = link; }
|
|
1071
|
+
try {
|
|
1072
|
+
if (fresh.isValid !== false) {
|
|
1073
|
+
try { if (IDC.enumIs(fresh.status, LinkStatus.LINK_OUT_OF_DATE)) { fresh.update(); } } catch (eu) { /* Status bleibt */ }
|
|
1074
|
+
statusAfter = idcLinkStatusName(fresh.status, true);
|
|
1075
|
+
newId = Number(fresh.id);
|
|
1076
|
+
} else { statusAfter = null; newId = null; }
|
|
1077
|
+
} catch (e3) { statusAfter = null; newId = null; }
|
|
1078
|
+
entry.ok = true;
|
|
1079
|
+
entry.status_after = statusAfter;
|
|
1080
|
+
entry.new_link_id = newId;
|
|
1081
|
+
if (statusAfter !== null && statusAfter !== 'NORMAL') {
|
|
1082
|
+
ctx.warn('Link ' + spec.link_id + ' was relinked but reads ' + statusAfter + '.');
|
|
1083
|
+
}
|
|
1084
|
+
relinked += 1;
|
|
1085
|
+
results.push(entry);
|
|
1086
|
+
}
|
|
1087
|
+
if (relinked > 0) {
|
|
1088
|
+
ctx.changes = { modified_link_ids: (function () {
|
|
1089
|
+
var ids = [], seen = {}, k;
|
|
1090
|
+
for (k = 0; k < results.length; k++) {
|
|
1091
|
+
if (results[k].ok && !IDC.hasOwn(seen, String(results[k].link_id))) { seen[String(results[k].link_id)] = 1; ids.push(results[k].link_id); }
|
|
1092
|
+
}
|
|
1093
|
+
return ids;
|
|
1094
|
+
})() };
|
|
1095
|
+
}
|
|
1096
|
+
return { results: results, relinked: relinked, failed: failed, considered: results.length };
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
/* ------------------------------------------------------------- update_links */
|
|
1100
|
+
|
|
1101
|
+
IDC.register('update_links', { needs_document: true, read_only: false, ruler: 'none' },
|
|
1102
|
+
function (ctx) {
|
|
1103
|
+
var args = ctx.args;
|
|
1104
|
+
var doc = ctx.doc;
|
|
1105
|
+
var ids = args.link_ids;
|
|
1106
|
+
var results = [];
|
|
1107
|
+
var updated = 0, failed = 0, nothing = 0;
|
|
1108
|
+
var i, link, entry, placement, before, after, candidates = [];
|
|
1109
|
+
|
|
1110
|
+
if (ids !== undefined && ids !== null) {
|
|
1111
|
+
if (!IDC.isArray(ids) || ids.length < 1 || ids.length > 500) {
|
|
1112
|
+
IDC.fail('invalid_argument', 'link_ids must be an array of 1 to 500 link ids; leave it out to update every out-of-date link.', null);
|
|
1113
|
+
}
|
|
1114
|
+
for (i = 0; i < ids.length; i++) { candidates.push({ id: ids[i], link: idcLinkById(doc, ids[i]) }); }
|
|
1115
|
+
} else {
|
|
1116
|
+
for (i = 0; i < doc.links.length; i++) {
|
|
1117
|
+
link = doc.links.item(i);
|
|
1118
|
+
try { if (IDC.enumIs(link.status, LinkStatus.LINK_OUT_OF_DATE)) { candidates.push({ id: Number(link.id), link: link }); } } catch (e0) { /* ueberspringen */ }
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
for (i = 0; i < candidates.length; i++) {
|
|
1123
|
+
link = candidates[i].link;
|
|
1124
|
+
entry = { link_id: candidates[i].id, name: null, page_name: '', status_before: null, status_after: null, ok: false, reason: null };
|
|
1125
|
+
if (link === null) {
|
|
1126
|
+
entry.reason = 'no link with link_id ' + candidates[i].id;
|
|
1127
|
+
failed += 1; results.push(entry); continue;
|
|
1128
|
+
}
|
|
1129
|
+
try { entry.name = String(link.name); } catch (e1) { entry.name = null; }
|
|
1130
|
+
placement = idcLinkPlacement(doc, link);
|
|
1131
|
+
entry.page_name = placement.page_name;
|
|
1132
|
+
try { before = link.status; } catch (e2) { before = null; }
|
|
1133
|
+
entry.status_before = idcLinkStatusName(before, true);
|
|
1134
|
+
if (!IDC.enumIs(before, LinkStatus.LINK_OUT_OF_DATE)) {
|
|
1135
|
+
entry.ok = true;
|
|
1136
|
+
entry.status_after = entry.status_before;
|
|
1137
|
+
entry.reason = 'nothing to do';
|
|
1138
|
+
nothing += 1; results.push(entry); continue;
|
|
1139
|
+
}
|
|
1140
|
+
try {
|
|
1141
|
+
link.update();
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
entry.reason = 'InDesign refused: ' + String(e.message ? e.message : e);
|
|
1144
|
+
failed += 1; results.push(entry); continue;
|
|
1145
|
+
}
|
|
1146
|
+
try { after = link.status; } catch (e3) { after = null; }
|
|
1147
|
+
entry.status_after = idcLinkStatusName(after, true);
|
|
1148
|
+
entry.ok = true;
|
|
1149
|
+
updated += 1;
|
|
1150
|
+
results.push(entry);
|
|
1151
|
+
}
|
|
1152
|
+
if (updated > 0) { ctx.changes = { modified_link_ids: (function () { var out = [], k; for (k = 0; k < results.length; k++) { if (results[k].ok && results[k].reason === null) { out.push(results[k].link_id); } } return out; })() }; }
|
|
1153
|
+
return { results: results, updated: updated, failed: failed, nothing_to_do: nothing, considered: results.length };
|
|
1154
|
+
});
|