layoutbridge-spm 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/LICENSE +26 -0
  3. package/README.de.md +318 -0
  4. package/README.md +304 -4
  5. package/THIRD-PARTY-NOTICES.md +2878 -0
  6. package/dist/src/mcp/config.js +125 -0
  7. package/dist/src/mcp/dispatch.js +123 -0
  8. package/dist/src/mcp/license/free-tools.js +16 -0
  9. package/dist/src/mcp/license/gate.js +209 -0
  10. package/dist/src/mcp/license/index.js +96 -0
  11. package/dist/src/mcp/license/instance-name.js +56 -0
  12. package/dist/src/mcp/license/key-source.js +58 -0
  13. package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
  14. package/dist/src/mcp/license/placeholder-verifier.js +10 -0
  15. package/dist/src/mcp/license/polar-ids.js +63 -0
  16. package/dist/src/mcp/license/polar-verifier.js +265 -0
  17. package/dist/src/mcp/license/product-ids.js +72 -0
  18. package/dist/src/mcp/license/state-store.js +91 -0
  19. package/dist/src/mcp/license/types.js +11 -0
  20. package/dist/src/mcp/logging.js +270 -0
  21. package/dist/src/mcp/product-info.js +48 -0
  22. package/dist/src/mcp/server.js +250 -0
  23. package/dist/src/mcp/setup.js +608 -0
  24. package/dist/src/mcp/start.cjs +95 -0
  25. package/dist/src/mcp/tool-definition.js +2 -0
  26. package/dist/src/mcp/tools.js +2004 -0
  27. package/dist/src/mcp/typography-rules.js +70 -0
  28. package/dist/src/transport/com.js +250 -0
  29. package/dist/src/transport/osascript.js +171 -0
  30. package/dist/src/transport/queue.js +19 -0
  31. package/dist/src/transport/script-bundle.js +92 -0
  32. package/dist/src/transport/transport.js +30 -0
  33. package/indesign/commands/10-app.js +6 -0
  34. package/indesign/commands/20-documents.js +513 -0
  35. package/indesign/commands/22-document-setup.js +118 -0
  36. package/indesign/commands/23-save-package.js +255 -0
  37. package/indesign/commands/25-export.js +379 -0
  38. package/indesign/commands/26-export-image.js +197 -0
  39. package/indesign/commands/27-textdefaults.js +151 -0
  40. package/indesign/commands/28-selection.js +174 -0
  41. package/indesign/commands/30-pages.js +1032 -0
  42. package/indesign/commands/31-view.js +210 -0
  43. package/indesign/commands/32-sections.js +203 -0
  44. package/indesign/commands/35-parents.js +428 -0
  45. package/indesign/commands/37-layout.js +760 -0
  46. package/indesign/commands/40-frames.js +836 -0
  47. package/indesign/commands/41-shapes.js +505 -0
  48. package/indesign/commands/42-groups.js +117 -0
  49. package/indesign/commands/43-layers.js +291 -0
  50. package/indesign/commands/44-effects.js +274 -0
  51. package/indesign/commands/45-frame-options.js +247 -0
  52. package/indesign/commands/47-anchored.js +287 -0
  53. package/indesign/commands/50-modify.js +781 -0
  54. package/indesign/commands/52-align.js +139 -0
  55. package/indesign/commands/55-fit.js +106 -0
  56. package/indesign/commands/60-images.js +771 -0
  57. package/indesign/commands/62-embed.js +120 -0
  58. package/indesign/commands/69-placeholders.js +127 -0
  59. package/indesign/commands/70-text.js +567 -0
  60. package/indesign/commands/71-outlines.js +92 -0
  61. package/indesign/commands/72-place-text.js +366 -0
  62. package/indesign/commands/74-stories.js +116 -0
  63. package/indesign/commands/75-tables.js +676 -0
  64. package/indesign/commands/76-text-variables.js +260 -0
  65. package/indesign/commands/77-story-options.js +86 -0
  66. package/indesign/commands/78-frame-lines.js +304 -0
  67. package/indesign/commands/79-check-typography.js +303 -0
  68. package/indesign/commands/80-styles.js +1620 -0
  69. package/indesign/commands/81-composition.js +355 -0
  70. package/indesign/commands/82-object-styles.js +263 -0
  71. package/indesign/commands/83-indents-tabs.js +237 -0
  72. package/indesign/commands/84-import-styles.js +142 -0
  73. package/indesign/commands/85-swatches.js +112 -0
  74. package/indesign/commands/86-gradients.js +185 -0
  75. package/indesign/commands/87-replace-swatch.js +180 -0
  76. package/indesign/commands/88-replace-font.js +217 -0
  77. package/indesign/interpreter.js +758 -0
  78. package/package.json +55 -6
  79. package/rules/typography-rules.json +31 -0
@@ -0,0 +1,366 @@
1
+ /* Wunschliste J2 Nr. 15 (Sitzung 7) – Manuskript einfliessen lassen: place_text_file. */
2
+
3
+ /* Namen aller Absatzformate (auch in Gruppen) als Nachschlagetabelle. */
4
+ function idcParagraphStyleNames(doc) {
5
+ var names = {};
6
+ var all, i;
7
+ try {
8
+ all = doc.allParagraphStyles;
9
+ for (i = 0; i < all.length; i++) { names[String(all[i].name)] = true; }
10
+ } catch (e) { /* leer */ }
11
+ return names;
12
+ }
13
+
14
+ /* Vorgaben eines Einstellungsobjekts merken, setzen und zurueckstellen. */
15
+ function idcWithPreferences(ctx, prefs, values, fn) {
16
+ var saved = {};
17
+ var key;
18
+ if (prefs === null || prefs === undefined) { return fn(); }
19
+ for (key in values) {
20
+ if (!IDC.hasOwn(values, key) || values[key] === null || values[key] === undefined) { continue; }
21
+ try { saved[key] = prefs[key]; } catch (e0) { continue; }
22
+ try { prefs[key] = values[key]; } catch (e1) { ctx.warn('Could not set import preference ' + key + ': ' + String(e1.message ? e1.message : e1)); }
23
+ }
24
+ try {
25
+ return fn();
26
+ } finally {
27
+ for (key in saved) {
28
+ if (IDC.hasOwn(saved, key)) {
29
+ try { prefs[key] = saved[key]; } catch (e2) { ctx.warn('Could not restore import preference ' + key + '.'); }
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ IDC.register('place_text_file', { needs_document: true, read_only: false, ruler: 'none' },
36
+ function (ctx) {
37
+ var args = ctx.args;
38
+ var doc = ctx.doc;
39
+ var found = idcTextFrameArg(doc, args, 'frame_id');
40
+ var frame = found.item;
41
+ var story = idcStoryOf(frame);
42
+ var path = args.file_path;
43
+ var mode = args.mode === undefined || args.mode === null ? 'replace' : args.mode;
44
+ var retain = args.retain_formatting === true;
45
+ var styleMap = args.style_map === undefined || args.style_map === null ? null : args.style_map;
46
+ var styleName = args.paragraph_style === undefined || args.paragraph_style === null ? null : args.paragraph_style;
47
+ var typographersQuotes = args.typographers_quotes !== false;
48
+ var file, ext, isWord, isText, lengthBefore = 0, lengthAfter = 0, insertAt, namesBefore, namesAfter, added = [];
49
+ var key, target, targetStyle, targets = {}, mapped = {}, placedRange, paragraphs, i, p, name, count = 0, insertion;
50
+
51
+ if (typeof path !== 'string' || path === '') {
52
+ IDC.fail('invalid_argument', 'file_path is required: a .docx, .doc, .rtf or .txt file on the machine running InDesign.', null);
53
+ }
54
+ if (mode !== 'replace' && mode !== 'append') {
55
+ IDC.fail('invalid_argument', 'mode must be replace (default) or append.', { mode: mode });
56
+ }
57
+ var autoflow = args.autoflow === true;
58
+ var maxPages = idcCountArg(args.max_pages, 'max_pages', 1, 200, 20);
59
+ /* Sitzung 10 (Demo-Heft-Befund 1): der Autofluss springt auf die neue Seite; Ansicht merken. */
60
+ var keepView = idcKeepViewArg(args);
61
+ var view = null, viewRestored = null;
62
+ ext = (path.match(/\.([A-Za-z0-9]+)$/) || ['', ''])[1].toLowerCase();
63
+ isWord = ext === 'docx' || ext === 'doc' || ext === 'rtf';
64
+ isText = ext === 'txt' || ext === 'text' || ext === 'md';
65
+ if (!isWord && !isText) {
66
+ IDC.fail('invalid_argument', 'file_path must end in .docx, .doc, .rtf or .txt (got .' + ext + ').', { file_path: path });
67
+ }
68
+ file = IDC.fileFor(path);
69
+ if (!file.exists) {
70
+ IDC.fail('not_found', 'File not found: ' + path, { file_path: path });
71
+ }
72
+ if (IDC.fileSize(file) === 0) {
73
+ IDC.fail('io_error', 'File is empty (0 bytes) – a cloud placeholder that is not downloaded yet? ' + path, { file_path: path });
74
+ }
75
+ if (styleMap !== null) {
76
+ if (typeof styleMap !== 'object' || IDC.isArray(styleMap)) {
77
+ IDC.fail('invalid_argument', 'style_map must be an object { "Word style name": "InDesign style name" }.', null);
78
+ }
79
+ for (key in styleMap) {
80
+ if (!IDC.hasOwn(styleMap, key)) { continue; }
81
+ target = styleMap[key];
82
+ if (typeof target !== 'string' || target === '') {
83
+ IDC.fail('invalid_argument', 'style_map values must be style names.', { key: key, value: target });
84
+ }
85
+ targetStyle = IDC.paragraphStyleByName(doc, target);
86
+ if (targetStyle === null) {
87
+ IDC.fail('not_found', 'Paragraph style "' + target + '" (target of style_map "' + key + '") does not exist.', { style_name: target });
88
+ }
89
+ targets[key] = targetStyle;
90
+ }
91
+ retain = true;
92
+ }
93
+ if (styleName !== null) {
94
+ targetStyle = IDC.paragraphStyleByName(doc, styleName);
95
+ if (targetStyle === null) {
96
+ IDC.fail('not_found', 'Paragraph style "' + styleName + '" not found.', { style_name: styleName });
97
+ }
98
+ }
99
+
100
+ try { lengthBefore = Number(story.characters.length); } catch (e0) { lengthBefore = 0; }
101
+ namesBefore = idcParagraphStyleNames(doc);
102
+
103
+ if (mode === 'replace' && lengthBefore > 0) {
104
+ try { story.contents = ''; } catch (e1) {
105
+ IDC.fail('script_error', 'Could not clear the story before placing: ' + String(e1.message ? e1.message : e1), { frame_id: args.frame_id });
106
+ }
107
+ lengthBefore = 0;
108
+ }
109
+ insertAt = lengthBefore;
110
+
111
+ var wordValues = {
112
+ removeFormatting: !retain,
113
+ preserveLocalOverrides: retain,
114
+ importUnusedStyles: false,
115
+ useTypographersQuotes: typographersQuotes,
116
+ preserveGraphics: false,
117
+ preserveTrackChanges: false,
118
+ importFootnotes: true,
119
+ importEndnotes: true,
120
+ importIndex: false,
121
+ importTOC: false
122
+ };
123
+ if (typeof ResolveStyleClash !== 'undefined') {
124
+ wordValues.resolveParagraphStyleClash = ResolveStyleClash.RESOLVE_CLASH_USE_EXISTING;
125
+ wordValues.resolveCharacterStyleClash = ResolveStyleClash.RESOLVE_CLASH_USE_EXISTING;
126
+ }
127
+ if (typeof ConvertPageBreaks !== 'undefined') { wordValues.convertPageBreaks = ConvertPageBreaks.NONE; }
128
+ var textValues = { useTypographersQuotes: typographersQuotes, stripReturnsBetweenLines: false, stripReturnsBetweenParagraphs: false };
129
+ if (typeof TextImportCharacterSet !== 'undefined') { textValues.characterSet = TextImportCharacterSet.UTF8; }
130
+
131
+ var prefs = null;
132
+ try { prefs = isWord ? app.wordRTFImportPreferences : app.textImportPreferences; } catch (e2) { prefs = null; }
133
+
134
+ idcWithPreferences(ctx, prefs, isWord ? wordValues : textValues, function () {
135
+ try {
136
+ insertion = story.insertionPoints.item(story.insertionPoints.length - 1);
137
+ placedRange = insertion.place(file);
138
+ } catch (e3) {
139
+ IDC.fail('io_error', 'InDesign could not place ' + path + ': ' + String(e3.message ? e3.message : e3), { file_path: path, frame_id: args.frame_id });
140
+ }
141
+ });
142
+
143
+ try { lengthAfter = Number(story.characters.length); } catch (e4) { lengthAfter = lengthBefore; }
144
+ namesAfter = idcParagraphStyleNames(doc);
145
+ for (name in namesAfter) {
146
+ if (IDC.hasOwn(namesAfter, name) && !namesBefore[name]) { added.push(name); }
147
+ }
148
+
149
+ /* Absatzformate: erst die Zuordnung Word -> Haus, dann ein Format ueber alles Platzierte. */
150
+ /*
151
+ * Ueber story.paragraphs mit Zeichenzaehlung – characters.itemByRange(...)
152
+ * .paragraphs lieferte am lebenden InDesign 21.5 eine leere Sammlung
153
+ * (Live-Fund 05.09.2026), deshalb wurde nichts zugeordnet.
154
+ */
155
+ try {
156
+ if (lengthAfter > insertAt) {
157
+ paragraphs = story.paragraphs;
158
+ var position = 0, plen;
159
+ for (i = 0; i < paragraphs.length; i++) {
160
+ p = paragraphs.item(i);
161
+ try { plen = Number(p.characters.length); } catch (ep) { plen = 0; }
162
+ if (position + plen <= insertAt) { position += plen; continue; }
163
+ position += plen;
164
+ count += 1;
165
+ if (styleMap !== null) {
166
+ name = idcParagraphStyleName(p);
167
+ if (name !== null && IDC.hasOwn(targets, name)) {
168
+ p.appliedParagraphStyle = targets[name];
169
+ mapped[name] = (mapped[name] || 0) + 1;
170
+ }
171
+ }
172
+ if (styleName !== null) { p.appliedParagraphStyle = targetStyle; }
173
+ }
174
+ }
175
+ } catch (e5) {
176
+ ctx.warn('The text was placed, but the paragraph styles could not be applied: ' + String(e5.message ? e5.message : e5));
177
+ }
178
+
179
+ /*
180
+ * Zugeordnete Word-Formate wieder entfernen: sie kamen erst mit diesem
181
+ * Import und sind nach der Zuordnung an keinem Absatz mehr in Gebrauch.
182
+ * remove(ersatz) haengt eventuelle Reste an das Zielformat.
183
+ */
184
+ var removed = [];
185
+ if (styleMap !== null) {
186
+ var remaining = [];
187
+ for (i = 0; i < added.length; i++) {
188
+ name = added[i];
189
+ if (IDC.hasOwn(targets, name)) {
190
+ try {
191
+ var stray = IDC.paragraphStyleByName(doc, name);
192
+ if (stray !== null) { stray.remove(targets[name]); removed.push(name); continue; }
193
+ } catch (e8) { ctx.warn('Imported style "' + name + '" could not be removed: ' + String(e8.message ? e8.message : e8)); }
194
+ }
195
+ remaining.push(name);
196
+ }
197
+ added = remaining;
198
+ }
199
+
200
+ var overset = null;
201
+ try {
202
+ var containers = story.textContainers;
203
+ overset = containers[containers.length - 1].overflows === true;
204
+ } catch (e6) { try { overset = story.overflows === true; } catch (e7) { overset = null; } }
205
+ var flow = null;
206
+ if (overset === true && autoflow) {
207
+ if (keepView) { view = idcCaptureView(doc); }
208
+ flow = idcAutoflow(ctx, doc, story, maxPages);
209
+ overset = flow.overset;
210
+ if (view !== null) { viewRestored = view.window === null ? null : idcRestoreView(view); }
211
+ }
212
+ if (overset === true) {
213
+ ctx.warn('The story overflows (Uebersatz) after placing – ' + (autoflow ? 'autoflow stopped at max_pages ' + maxPages + '; raise it or shorten the text.' : 'thread more frames, pass autoflow true, or shorten the text.'));
214
+ }
215
+ if (added.length > 0) {
216
+ ctx.warn('The import added ' + added.length + ' paragraph style(s): ' + added.join(', ') + '.');
217
+ }
218
+
219
+ ctx.changes = { modified_frame_ids: [Number(frame.id)], modified_story_ids: [Number(story.id)] };
220
+ if (flow !== null && flow.frames_added.length > 0) {
221
+ ctx.changes.created_frame_ids = [];
222
+ ctx.changes.created_page_ids = [];
223
+ for (i = 0; i < flow.frames_added.length; i++) { ctx.changes.created_frame_ids.push(flow.frames_added[i].frame_id); }
224
+ for (i = 0; i < flow.pages_added.length; i++) { ctx.changes.created_page_ids.push(flow.pages_added[i].page_id); }
225
+ }
226
+ return {
227
+ frame_id: Number(frame.id),
228
+ story_id: Number(story.id),
229
+ file_name: String(path).replace(/^.*[\/\\]/, ''),
230
+ mode: mode,
231
+ retain_formatting: retain,
232
+ placed_start_index: insertAt,
233
+ placed_end_index: lengthAfter,
234
+ inserted_chars: lengthAfter - insertAt,
235
+ story_length: lengthAfter,
236
+ paragraph_count: count,
237
+ styles_added: added,
238
+ styles_mapped: mapped,
239
+ styles_removed: removed,
240
+ paragraph_style_applied: styleName,
241
+ overset: overset,
242
+ autoflow: flow,
243
+ view_restored: viewRestored
244
+ };
245
+ });
246
+
247
+ /*
248
+ * Autoflow: solange die Story ueberlaeuft, hinter der Seite des letzten
249
+ * Rahmens eine Seite (gleiche Musterseite) anlegen, darauf einen Rahmen mit
250
+ * den Massen und Rahmenoptionen des letzten anlegen und verketten.
251
+ */
252
+ function idcAutoflow(ctx, doc, story, maxPages) {
253
+ var out = { pages_added: [], frames_added: [], overset: true, stopped: '' };
254
+ var round, containers, last, page, rect, newPage, newFrame, prefsCopied, copied;
255
+ for (round = 0; round < maxPages; round++) {
256
+ try {
257
+ containers = story.textContainers;
258
+ last = IDC.concrete(containers[containers.length - 1]);
259
+ } catch (e0) { out.stopped = 'last frame unreadable'; return out; }
260
+ try { if (last.overflows !== true) { out.overset = false; out.stopped = 'fits'; return out; } } catch (e1) { /* weiter */ }
261
+ page = null;
262
+ try { page = last.parentPage; } catch (e2) { page = null; }
263
+ if (page === null || page === undefined || page.isValid !== true) {
264
+ IDC.fail('invalid_state', 'Autoflow needs the last frame on a document page (it lies on the pasteboard or a parent page).', null);
265
+ }
266
+ if (IDC.isOnParentSpread(last)) {
267
+ IDC.fail('invalid_state', 'Autoflow does not run on parent pages.', null);
268
+ }
269
+ rect = IDC.boundsOnPage(doc, last, page);
270
+ try {
271
+ newPage = doc.pages.add(LocationOptions.AFTER, page);
272
+ } catch (e3) {
273
+ IDC.fail('script_error', 'Autoflow could not add a page: ' + String(e3.message ? e3.message : e3), null);
274
+ }
275
+ try {
276
+ if (page.appliedMaster && page.appliedMaster.isValid === true) { newPage.appliedMaster = page.appliedMaster; }
277
+ } catch (e4) { /* Vorgabe bleibt */ }
278
+ newFrame = idcAddItem(doc, newPage, 'textFrames', idcRectToBounds(rect), 'page');
279
+ /*
280
+ * Rahmenoptionen einzeln kopieren (Demo-Heft-Befund 4, 05.09.2026): die
281
+ * Zuweisung von textFramePreferences.properties liess am lebenden
282
+ * InDesign die Spalten des Ausgangsrahmens weg (neuer Rahmen einspaltig).
283
+ */
284
+ copied = idcCopyTextFramePreferences(last, newFrame);
285
+ prefsCopied = copied.failed.length === 0;
286
+ try { newFrame.itemLayer = last.itemLayer; } catch (e6) { /* aktive Ebene */ }
287
+ try {
288
+ last.nextTextFrame = newFrame;
289
+ } catch (e7) {
290
+ IDC.fail('script_error', 'Autoflow could not thread the new frame: ' + String(e7.message ? e7.message : e7), { page_id: Number(newPage.id) });
291
+ }
292
+ out.pages_added.push({ page_id: Number(newPage.id), page_name: String(newPage.name) });
293
+ out.frames_added.push({
294
+ frame_id: Number(newFrame.id),
295
+ page_id: Number(newPage.id),
296
+ bounds_mm: rect,
297
+ frame_options_copied: prefsCopied,
298
+ frame_options_failed: copied.failed,
299
+ frame_options_skipped: copied.skipped,
300
+ columns: idcReadColumnsOf(newFrame)
301
+ });
302
+ }
303
+ try {
304
+ containers = story.textContainers;
305
+ last = IDC.concrete(containers[containers.length - 1]);
306
+ out.overset = last.overflows === true;
307
+ } catch (e8) { out.overset = true; }
308
+ out.stopped = out.overset ? 'max_pages' : 'fits';
309
+ return out;
310
+ }
311
+
312
+ /*
313
+ * Rahmenoptionen eines Textrahmens auf einen anderen uebertragen – Wert fuer
314
+ * Wert, in der Reihenfolge, die InDesign verlangt (erst die Spaltenart, dann
315
+ * Zahl, Steg und feste Breite). Liefert copied[] und failed[] (Eigenschaft
316
+ * samt Meldung); Eigenschaften, die die Laufzeit nicht kennt, zaehlen nicht.
317
+ */
318
+ /* Adobe-DOM TextFramePreference (geprueft 05.09.2026): alle schreibbar. Automatische Groesse und Spaltenlinien bleiben Vorgabe. */
319
+ var IDC_TEXT_FRAME_PREFS = [
320
+ 'useFixedColumnWidth', 'useFlexibleColumnWidth', 'textColumnCount', 'textColumnGutter', 'textColumnFixedWidth',
321
+ 'textColumnMaxWidth', 'insetSpacing', 'verticalJustification', 'verticalThreshold', 'firstBaselineOffset',
322
+ 'minimumFirstBaselineOffset', 'ignoreWrap', 'verticalBalanceColumns'
323
+ ];
324
+
325
+ function idcCopyTextFramePreferences(source, target) {
326
+ var out = { copied: [], skipped: [], failed: [] };
327
+ var from, to, i, key, value, justified = false, fixedWidth = false, flexibleWidth = false;
328
+ try { from = source.textFramePreferences; to = target.textFramePreferences; } catch (e0) {
329
+ out.failed.push({ property: 'textFramePreferences', message: String(e0.message ? e0.message : e0) });
330
+ return out;
331
+ }
332
+ /*
333
+ * Live-Fund 05.09.2026: verticalThreshold laesst sich nur setzen, wenn der
334
+ * Rahmen vertikal im Blocksatz steht ("Diese Eigenschaft ist im aktuellen
335
+ * Status nicht zutreffend."); die festen/flexiblen Spaltenbreiten gelten nur
336
+ * bei der jeweiligen Spaltenart. Was nicht zutrifft, wird uebersprungen.
337
+ */
338
+ try { justified = typeof VerticalJustification !== 'undefined' && IDC.enumIs(from.verticalJustification, VerticalJustification.JUSTIFY_ALIGN); } catch (ej) { justified = false; }
339
+ try { fixedWidth = from.useFixedColumnWidth === true; } catch (ef) { fixedWidth = false; }
340
+ try { flexibleWidth = from.useFlexibleColumnWidth === true; } catch (ex) { flexibleWidth = false; }
341
+ for (i = 0; i < IDC_TEXT_FRAME_PREFS.length; i++) {
342
+ key = IDC_TEXT_FRAME_PREFS[i];
343
+ if ((key === 'verticalThreshold' && !justified) || (key === 'textColumnFixedWidth' && !fixedWidth) || (key === 'textColumnMaxWidth' && !flexibleWidth)) {
344
+ out.skipped.push(key);
345
+ continue;
346
+ }
347
+ try { value = from[key]; } catch (e1) { continue; }
348
+ if (value === undefined) { continue; }
349
+ if (IDC.isArray(value)) { value = value.slice(0); }
350
+ try {
351
+ to[key] = value;
352
+ out.copied.push(key);
353
+ } catch (e2) {
354
+ out.failed.push({ property: key, message: String(e2.message ? e2.message : e2) });
355
+ }
356
+ }
357
+ return out;
358
+ }
359
+
360
+ /* Spalten eines Textrahmens zum Gegenlesen: {count, gutter_mm}. */
361
+ function idcReadColumnsOf(frame) {
362
+ var out = { count: null, gutter_mm: null };
363
+ try { out.count = Number(frame.textFramePreferences.textColumnCount); } catch (e0) { /* bleibt null */ }
364
+ try { out.gutter_mm = IDC.round(Number(frame.textFramePreferences.textColumnGutter)); } catch (e1) { /* bleibt null */ }
365
+ return out;
366
+ }
@@ -0,0 +1,116 @@
1
+ /* Wunschliste J Nr. 10 (Sitzung 8) – Story-Uebersicht: list_stories. */
2
+
3
+ function idcStoryFrames(doc, story) {
4
+ var out = [], containers, i, frame, landing, entry;
5
+ try { containers = story.textContainers; } catch (e0) { containers = null; }
6
+ if (containers === null || containers === undefined) { return out; }
7
+ for (i = 0; i < containers.length; i++) {
8
+ try { frame = IDC.concrete(containers[i]); } catch (e1) { continue; }
9
+ landing = IDC.landing(frame);
10
+ entry = {
11
+ frame_id: null,
12
+ index: i,
13
+ page_id: landing.page_id,
14
+ page_name: landing.page_name,
15
+ on_pasteboard: landing.on_pasteboard,
16
+ on_parent_page: IDC.isOnParentSpread(frame),
17
+ class_name: IDC.className(frame),
18
+ bounds_mm: null
19
+ };
20
+ try { entry.frame_id = Number(frame.id); } catch (e2) { /* null */ }
21
+ try { entry.bounds_mm = idcItemBounds(doc, frame); } catch (e3) { entry.bounds_mm = null; }
22
+ out.push(entry);
23
+ }
24
+ return out;
25
+ }
26
+
27
+ function idcStoryOverset(story, frames) {
28
+ var last;
29
+ try { if (story.overflows === true) { return true; } } catch (e0) { /* weiter */ }
30
+ try {
31
+ last = story.textContainers[story.textContainers.length - 1];
32
+ if (last && last.overflows === true) { return true; }
33
+ } catch (e1) { /* unbekannt */ }
34
+ return frames.length === 0 ? null : false;
35
+ }
36
+
37
+ function idcDescribeStory(doc, story, snippetChars) {
38
+ var frames = idcStoryFrames(doc, story);
39
+ var ids = [], i;
40
+ var out = {
41
+ story_id: null,
42
+ frame_ids: ids,
43
+ frame_count: frames.length,
44
+ frames: frames,
45
+ character_count: null,
46
+ paragraph_count: null,
47
+ table_count: null,
48
+ overset: idcStoryOverset(story, frames),
49
+ on_parent_page: frames.length > 0 ? frames[0].on_parent_page : false,
50
+ first_page_id: frames.length > 0 ? frames[0].page_id : null,
51
+ first_page_name: frames.length > 0 ? frames[0].page_name : '',
52
+ first_paragraph_style: null,
53
+ snippet: ''
54
+ };
55
+ for (i = 0; i < frames.length; i++) { ids.push(frames[i].frame_id); }
56
+ try { out.story_id = Number(story.id); } catch (e0) { /* null */ }
57
+ try { out.character_count = Number(story.characters.length); } catch (e1) { /* null */ }
58
+ try { out.paragraph_count = Number(story.paragraphs.length); } catch (e2) { /* null */ }
59
+ try { out.table_count = Number(story.tables.length); } catch (e3) { /* null */ }
60
+ try {
61
+ if (out.paragraph_count !== null && out.paragraph_count > 0) {
62
+ out.first_paragraph_style = idcParagraphStyleName(story.paragraphs.item(0));
63
+ }
64
+ } catch (e4) { out.first_paragraph_style = null; }
65
+ try { out.snippet = idcSnippet(story.contents, snippetChars); } catch (e5) { out.snippet = ''; }
66
+ return out;
67
+ }
68
+
69
+ IDC.register('list_stories', { needs_document: true, read_only: true, ruler: 'page' },
70
+ function (ctx) {
71
+ var args = ctx.args;
72
+ var doc = ctx.doc;
73
+ var includeParents = args.include_parent_pages === true;
74
+ var includeEmpty = args.include_empty !== false;
75
+ var snippetChars = idcCountArg(args.snippet_chars, 'snippet_chars', 0, 2000, 80);
76
+ var pageId = args.page_id === undefined || args.page_id === null ? null : args.page_id;
77
+ var out = [], i, j, story, entry, oversetCount = 0, parentsSkipped = 0, emptySkipped = 0, total = 0, onPage;
78
+
79
+ if (pageId !== null && (typeof pageId !== 'number' || Math.floor(pageId) !== pageId)) {
80
+ IDC.fail('invalid_argument', 'page_id must be a whole number.', { page_id: pageId });
81
+ }
82
+ if (pageId !== null && IDC.pageById(doc, pageId) === null) {
83
+ IDC.fail('not_found', 'No page with page_id ' + pageId + ' in this document.', { page_id: pageId });
84
+ }
85
+
86
+ try { total = Number(doc.stories.length); } catch (e0) { total = 0; }
87
+ for (i = 0; i < total; i++) {
88
+ try { story = doc.stories.item(i); } catch (e1) { continue; }
89
+ entry = idcDescribeStory(doc, story, snippetChars);
90
+ if (entry.on_parent_page && !includeParents) { parentsSkipped += 1; continue; }
91
+ if (entry.character_count === 0 && !includeEmpty) { emptySkipped += 1; continue; }
92
+ if (pageId !== null) {
93
+ onPage = false;
94
+ for (j = 0; j < entry.frames.length; j++) {
95
+ if (entry.frames[j].page_id === pageId) { onPage = true; break; }
96
+ }
97
+ if (!onPage) { continue; }
98
+ }
99
+ if (entry.overset === true) { oversetCount += 1; }
100
+ out.push(entry);
101
+ }
102
+ if (oversetCount > 0) {
103
+ ctx.warn(oversetCount + ' story/stories overflow (Übersatz) – enlarge the last frame or thread another one.');
104
+ }
105
+ return {
106
+ stories: out,
107
+ count: out.length,
108
+ total_stories: total,
109
+ overset_count: oversetCount,
110
+ parent_page_stories_skipped: parentsSkipped,
111
+ empty_stories_skipped: emptySkipped,
112
+ page_id: pageId,
113
+ include_parent_pages: includeParents,
114
+ include_empty: includeEmpty
115
+ };
116
+ });