layoutbridge-light 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/LICENSE +26 -0
  3. package/README.de.md +367 -0
  4. package/README.md +353 -2
  5. package/THIRD-PARTY-NOTICES.md +2878 -0
  6. package/dist/src/mcp/catalog.js +131 -0
  7. package/dist/src/mcp/config.js +211 -0
  8. package/dist/src/mcp/content-credentials.js +65 -0
  9. package/dist/src/mcp/dispatch.js +171 -0
  10. package/dist/src/mcp/epub-check.js +331 -0
  11. package/dist/src/mcp/epub-orientation.js +132 -0
  12. package/dist/src/mcp/license/free-tools.js +16 -0
  13. package/dist/src/mcp/license/gate.js +210 -0
  14. package/dist/src/mcp/license/index.js +107 -0
  15. package/dist/src/mcp/license/instance-name.js +57 -0
  16. package/dist/src/mcp/license/key-source.js +59 -0
  17. package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
  18. package/dist/src/mcp/license/placeholder-verifier.js +10 -0
  19. package/dist/src/mcp/license/polar-ids.js +100 -0
  20. package/dist/src/mcp/license/polar-verifier.js +285 -0
  21. package/dist/src/mcp/license/product-ids.js +73 -0
  22. package/dist/src/mcp/license/state-store.js +91 -0
  23. package/dist/src/mcp/license/types.js +11 -0
  24. package/dist/src/mcp/logging.js +271 -0
  25. package/dist/src/mcp/pdf-postprocess.js +798 -0
  26. package/dist/src/mcp/product-info.js +54 -0
  27. package/dist/src/mcp/product.js +56 -0
  28. package/dist/src/mcp/server.js +253 -0
  29. package/dist/src/mcp/setup.js +680 -0
  30. package/dist/src/mcp/start.cjs +117 -0
  31. package/dist/src/mcp/target-folder.js +25 -0
  32. package/dist/src/mcp/tool-definition.js +2 -0
  33. package/dist/src/mcp/tools.js +2680 -0
  34. package/dist/src/mcp/typography-rules.js +70 -0
  35. package/dist/src/transport/com.js +436 -0
  36. package/dist/src/transport/osascript.js +171 -0
  37. package/dist/src/transport/queue.js +19 -0
  38. package/dist/src/transport/script-bundle.js +92 -0
  39. package/dist/src/transport/transport.js +52 -0
  40. package/indesign/commands/10-app.js +6 -0
  41. package/indesign/commands/20-documents.js +517 -0
  42. package/indesign/commands/22-document-setup.js +204 -0
  43. package/indesign/commands/23-save-package.js +258 -0
  44. package/indesign/commands/25-export.js +695 -0
  45. package/indesign/commands/26-export-image.js +202 -0
  46. package/indesign/commands/27-textdefaults.js +151 -0
  47. package/indesign/commands/28-selection.js +174 -0
  48. package/indesign/commands/30-pages.js +1032 -0
  49. package/indesign/commands/31-view.js +310 -0
  50. package/indesign/commands/32-sections.js +203 -0
  51. package/indesign/commands/35-parents.js +448 -0
  52. package/indesign/commands/37-layout.js +837 -0
  53. package/indesign/commands/40-frames.js +886 -0
  54. package/indesign/commands/41-shapes.js +505 -0
  55. package/indesign/commands/42-groups.js +117 -0
  56. package/indesign/commands/43-layers.js +310 -0
  57. package/indesign/commands/44-effects.js +274 -0
  58. package/indesign/commands/45-frame-options.js +247 -0
  59. package/indesign/commands/47-anchored.js +473 -0
  60. package/indesign/commands/50-modify.js +803 -0
  61. package/indesign/commands/52-align.js +139 -0
  62. package/indesign/commands/55-fit.js +106 -0
  63. package/indesign/commands/60-images.js +1154 -0
  64. package/indesign/commands/62-embed.js +120 -0
  65. package/indesign/commands/69-placeholders.js +132 -0
  66. package/indesign/commands/70-text.js +710 -0
  67. package/indesign/commands/71-outlines.js +92 -0
  68. package/indesign/commands/72-place-text.js +476 -0
  69. package/indesign/commands/73-story-lock.js +157 -0
  70. package/indesign/commands/74-stories.js +121 -0
  71. package/indesign/commands/75-tables.js +702 -0
  72. package/indesign/commands/76-text-variables.js +260 -0
  73. package/indesign/commands/77-story-options.js +86 -0
  74. package/indesign/commands/78-frame-lines.js +511 -0
  75. package/indesign/commands/79-check-typography.js +480 -0
  76. package/indesign/commands/80-styles.js +1960 -0
  77. package/indesign/commands/81-composition.js +355 -0
  78. package/indesign/commands/82-object-styles.js +263 -0
  79. package/indesign/commands/83-drop-caps.js +87 -0
  80. package/indesign/commands/83-indents-tabs.js +264 -0
  81. package/indesign/commands/84-import-styles.js +142 -0
  82. package/indesign/commands/85-swatches.js +112 -0
  83. package/indesign/commands/86-gradients.js +185 -0
  84. package/indesign/commands/87-replace-swatch.js +180 -0
  85. package/indesign/commands/88-replace-font.js +212 -0
  86. package/indesign/commands/89-toc-index.js +1002 -0
  87. package/indesign/commands/90-print-on-demand.js +495 -0
  88. package/indesign/commands/91-epub.js +629 -0
  89. package/indesign/commands/92-accessibility.js +1131 -0
  90. package/indesign/interpreter.js +829 -0
  91. package/package.json +41 -4
  92. package/rules/typography-rules.json +32 -0
@@ -0,0 +1,480 @@
1
+ /*
2
+ * Auftrag A-7.6 (Sitzung 9) – check_typography: Verstoesse gegen allgemeine
3
+ * Satzregeln finden und melden, nie aendern. Die Regeln kommen als Daten
4
+ * (args.rule_rows, aus rules/typography-rules.json, vom Server nach Sprache
5
+ * und Kennungen ausgewaehlt): kind grep = InDesign-GREP-Suche (Mechanik
6
+ * von find_replace dry_run), kind lines = Pruefung auf den Zeilen eines
7
+ * Rahmens (idcFrameLineData aus 78-frame-lines.js).
8
+ */
9
+
10
+ var IDC_SEVERITY_RANK = { info: 0, warning: 1, error: 2 };
11
+
12
+ /* Ab so vielen Textrahmen mit Zeilenregeln ohne page_range: Warnung mit Hinweis auf page_range (A-69). */
13
+ var IDC_LINE_FRAMES_WARN = 100;
14
+
15
+ /* Seite eines Rahmens als id, null auf der Montageflaeche. */
16
+ function idcFramePageId(frame) {
17
+ var page = null;
18
+ try { page = frame.parentPage; } catch (e0) { page = null; }
19
+ try { if (page !== null && page !== undefined && page.isValid !== true) { page = null; } } catch (e1) { page = null; }
20
+ if (page === null || page === undefined) { return null; }
21
+ return Number(page.id);
22
+ }
23
+
24
+ function idcRuleIdsOf(rows) {
25
+ var ids = [], i;
26
+ for (i = 0; i < rows.length; i++) {
27
+ if (!idcInList(ids, rows[i].id)) { ids.push(rows[i].id); }
28
+ }
29
+ return ids;
30
+ }
31
+
32
+ function idcInList(list, value) {
33
+ var i;
34
+ for (i = 0; i < list.length; i++) { if (list[i] === value) { return true; } }
35
+ return false;
36
+ }
37
+
38
+ /* Textrahmen des Pruefbereichs: Dokumentseiten (und auf Wunsch Musterseiten), ohne gesperrte Ebenen (auf Wunsch mit). */
39
+ function idcFramesInScope(doc, scope, frame, story, includeMasters, includeLocked) {
40
+ var out = [], i, items, item, layer, locked;
41
+ if (scope === 'frame') { return [frame]; }
42
+ if (scope === 'story') {
43
+ try {
44
+ items = story.textContainers;
45
+ for (i = 0; i < items.length; i++) { out.push(IDC.concrete(items[i])); }
46
+ } catch (e0) { out = [frame]; }
47
+ return out;
48
+ }
49
+ try { items = doc.allPageItems; } catch (e1) { items = []; }
50
+ for (i = 0; i < items.length; i++) {
51
+ item = IDC.concrete(items[i]);
52
+ if (!IDC.isTextFrame(item)) { continue; }
53
+ if (!includeMasters && IDC.isOnParentSpread(item)) { continue; }
54
+ if (!includeLocked) {
55
+ locked = false;
56
+ try { layer = item.itemLayer; locked = layer !== null && layer !== undefined && layer.locked === true; } catch (e2) { locked = false; }
57
+ if (locked) { continue; }
58
+ }
59
+ out.push(item);
60
+ }
61
+ return out;
62
+ }
63
+
64
+ /* Fundstelle einer GREP-Suche als Finding. */
65
+ function idcGrepFinding(doc, rule, match) {
66
+ var entry = idcDescribeMatch(doc, match);
67
+ var story = null, storyLength = null, from, to, snippet = entry.text, contextText;
68
+ var length = 0;
69
+ try { length = Number(match.characters.length); } catch (e0) { length = entry.text === null ? 0 : entry.text.length; }
70
+ try { story = match.parentStory; storyLength = Number(story.characters.length); } catch (e1) { story = null; }
71
+ if (story !== null && entry.index !== null && storyLength !== null) {
72
+ from = Math.max(0, entry.index - 15);
73
+ to = Math.min(storyLength - 1, entry.index + length + 14);
74
+ try {
75
+ contextText = String(story.characters.itemByRange(from, to).contents);
76
+ snippet = contextText.replace(/[\r\n]/g, '\u00b6');
77
+ } catch (e2) { snippet = entry.text; }
78
+ }
79
+ return {
80
+ rule_id: rule.id,
81
+ severity: rule.severity,
82
+ category: rule.category,
83
+ message: rule.message,
84
+ story_id: entry.story_id,
85
+ frame_id: entry.frame_id,
86
+ page_id: entry.page_id,
87
+ page_name: entry.page_name,
88
+ start_index: entry.index,
89
+ end_index: entry.index === null ? null : entry.index + length,
90
+ snippet: snippet,
91
+ suggestion: rule.suggestion
92
+ };
93
+ }
94
+
95
+ function idcMatchInTable(match) {
96
+ var parent = null, name;
97
+ try { parent = match.parent; } catch (e0) { return false; }
98
+ if (parent === null || parent === undefined) { return false; }
99
+ name = IDC.className(parent);
100
+ return name === 'Cell' || name === 'Table';
101
+ }
102
+
103
+ function idcRunGrepRule(doc, target, rule, args, findings, state) {
104
+ var found, i, finding, options = app.findChangeGrepOptions;
105
+ try {
106
+ app.findGrepPreferences = NothingEnum.NOTHING;
107
+ app.changeGrepPreferences = NothingEnum.NOTHING;
108
+ idcSetFindOptions(options, { include_master_pages: args.include_master_pages === true, include_locked_layers: args.include_locked_layers === true });
109
+ app.findGrepPreferences.findWhat = rule.pattern;
110
+ found = target.findGrep();
111
+ } catch (e) {
112
+ state.failed.push({ rule_id: rule.id, error: String(e.message ? e.message : e), pattern: rule.pattern });
113
+ try { app.findGrepPreferences = NothingEnum.NOTHING; } catch (e1) { /* egal */ }
114
+ return;
115
+ }
116
+ try { app.findGrepPreferences = NothingEnum.NOTHING; } catch (e2) { /* egal */ }
117
+ for (i = 0; i < found.length; i++) {
118
+ if (idcMatchInTable(found[i])) { state.tables_skipped += 1; continue; }
119
+ finding = idcGrepFinding(doc, rule, found[i]);
120
+ if (state.page_ids !== null && (finding.page_id === null || !state.page_ids[finding.page_id])) { continue; }
121
+ idcPush(findings, state, finding);
122
+ }
123
+ }
124
+
125
+ function idcLineFinding(rule, data, column, line, extraMessage) {
126
+ return {
127
+ rule_id: rule.id,
128
+ severity: rule.severity,
129
+ category: rule.category,
130
+ message: extraMessage ? rule.message + ' – ' + extraMessage : rule.message,
131
+ story_id: data.story_id,
132
+ frame_id: data.frame_id,
133
+ page_id: data.page_id,
134
+ page_name: data.page_name,
135
+ start_index: line === null ? null : line.start_index,
136
+ end_index: line === null ? null : line.end_index,
137
+ snippet: line === null ? null : (line.text === undefined ? null : line.text),
138
+ suggestion: rule.suggestion,
139
+ column_index: column === null ? null : column.column_index,
140
+ line_index: line === null ? null : line.index
141
+ };
142
+ }
143
+
144
+ /* Alle Regeln laufen immer; der Deckel max_findings greift erst bei der Ausgabe.
145
+ Gesammelt wird bis zur festen Obergrenze IDC_FINDINGS_CEILING, damit ein
146
+ grosses Dokument den Aufruf nicht sprengt (zaehlt weiter als dropped). */
147
+ var IDC_FINDINGS_CEILING = 5000;
148
+
149
+ function idcPush(findings, state, finding) {
150
+ if (findings.length >= IDC_FINDINGS_CEILING) { state.dropped += 1; return; }
151
+ findings.push(finding);
152
+ }
153
+
154
+ /*
155
+ * A-68 Befund, Sitzung 81: Eine einzeilige Dialog- oder Textzeile am
156
+ * Spaltenende ist keine Ueberschrift (live 11.11.2026: »Ja.« im Grundtext
157
+ * wurde gemeldet). Ausgenommen wird eine Zeile, die mit Satzzeichen oder
158
+ * schliessendem Anfuehrungszeichen endet und dasselbe Absatzformat hat wie
159
+ * der folgende Absatz; ein Zwischentitel im Grundtextformat ohne
160
+ * Schlusszeichen bleibt ein Fund. Wenige Zugriffe je Kandidat.
161
+ */
162
+ var IDC_DIALOGUE_END = /[.!?…:;»«“”‘’"')›‹]$/;
163
+
164
+ function idcDialogueLine(story, line) {
165
+ var a, b, lastChar;
166
+ if (story === null || story === undefined || line.start_index === null || line.end_index === null || line.end_index < 2) { return false; }
167
+ try {
168
+ lastChar = String(story.characters.item(line.end_index - 2).contents);
169
+ if (!IDC_DIALOGUE_END.test(lastChar)) { return false; }
170
+ a = story.characters.item(line.start_index).appliedParagraphStyle;
171
+ b = story.characters.item(line.end_index).appliedParagraphStyle;
172
+ return Number(a.id) === Number(b.id);
173
+ } catch (e) { return false; }
174
+ }
175
+
176
+ function idcRunLineRule(rule, data, args, findings, state, story) {
177
+ var c, column, lines, i, run, runStart, maxHyphens, minChars, first, last, text, prev;
178
+ maxHyphens = idcGiven(args.max_consecutive_hyphens) ? args.max_consecutive_hyphens : 3;
179
+ minChars = idcGiven(args.min_last_line_chars) ? args.min_last_line_chars : 0;
180
+
181
+ if (rule.check === 'column_first_baselines_differ') {
182
+ prev = null;
183
+ for (c = 0; c < data.columns.length; c++) {
184
+ column = data.columns[c];
185
+ if (column.line_count === 0 || column.first_baseline_mm === null) { continue; }
186
+ if (prev !== null && Math.abs(column.first_baseline_mm - prev.first_baseline_mm) > 0.1) {
187
+ idcPush(findings, state, idcLineFinding(rule, data, column, column.lines[0],
188
+ 'column ' + prev.column_index + ' starts at ' + prev.first_baseline_mm + ' mm, column ' + column.column_index + ' at ' + column.first_baseline_mm + ' mm'));
189
+ }
190
+ prev = column;
191
+ }
192
+ return;
193
+ }
194
+
195
+ for (c = 0; c < data.columns.length; c++) {
196
+ column = data.columns[c];
197
+ lines = column.lines;
198
+ if (lines.length === 0) { continue; }
199
+ first = lines[0];
200
+ last = lines[lines.length - 1];
201
+ if (rule.check === 'widow') {
202
+ if (column.starts_with_last_line_of_paragraph) { idcPush(findings, state, idcLineFinding(rule, data, column, first, null)); }
203
+ } else if (rule.check === 'orphan') {
204
+ if (column.ends_with_first_line_of_paragraph) { idcPush(findings, state, idcLineFinding(rule, data, column, last, null)); }
205
+ } else if (rule.check === 'heading_at_column_end') {
206
+ if (last.is_first_line_of_paragraph && last.is_last_line_of_paragraph && !last.is_empty
207
+ && last.paragraph_index !== null && last.paragraph_index < data.paragraph_count - 1
208
+ && !idcDialogueLine(story, last)) {
209
+ idcPush(findings, state, idcLineFinding(rule, data, column, last, null));
210
+ }
211
+ } else if (rule.check === 'empty_line_at_column_start') {
212
+ if (column.starts_with_empty_line) { idcPush(findings, state, idcLineFinding(rule, data, column, first, null)); }
213
+ } else if (rule.check === 'single_word_last_line') {
214
+ for (i = 0; i < lines.length; i++) {
215
+ if (!lines[i].is_last_line_of_paragraph || lines[i].is_empty || lines[i].paragraph_line_count === null || lines[i].paragraph_line_count < 2) { continue; }
216
+ text = lines[i].text === undefined ? '' : lines[i].text;
217
+ if (lines[i].word_count === 1 || (minChars > 0 && text.length < minChars)) {
218
+ idcPush(findings, state, idcLineFinding(rule, data, column, lines[i], lines[i].word_count === 1 ? 'single word' : text.length + ' characters'));
219
+ }
220
+ }
221
+ } else if (rule.check === 'hyphen_ladder') {
222
+ run = 0;
223
+ runStart = 0;
224
+ for (i = 0; i <= lines.length; i++) {
225
+ if (i < lines.length && lines[i].ends_with_hyphen) {
226
+ if (run === 0) { runStart = i; }
227
+ run += 1;
228
+ } else {
229
+ if (run > maxHyphens) {
230
+ idcPush(findings, state, idcLineFinding(rule, data, column, lines[runStart], run + ' hyphenated lines in a row (lines ' + runStart + '–' + (i - 1) + ')'));
231
+ }
232
+ run = 0;
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ /*
240
+ * A-78 (Sitzung 81): Regelart language – Textbereiche, deren Sprache von der
241
+ * Sprache ihres Absatzformats abweicht (etwa en-US aus einer Word-Datei in
242
+ * einem deutschen Text: falsche Trennung). Grundlage: Probe InDesign 21.6
243
+ * am 11.11.2026 – story.textStyleRanges trennen bei jedem Sprach- und
244
+ * Formatwechsel, Text in Tabellenzellen gehoert nicht dazu, everyItem()
245
+ * liest appliedLanguage, appliedParagraphStyle und index gebuendelt.
246
+ */
247
+ function idcEveryList(value, count) {
248
+ if (value !== null && value !== undefined && !IDC.isArray(value)) {
249
+ try { if (typeof value.getElements === 'function') { value = value.getElements(); } } catch (e) { return null; }
250
+ }
251
+ return idcBundled(value, count);
252
+ }
253
+
254
+ function idcLanguageName(lang) {
255
+ if (lang === null || lang === undefined) { return null; }
256
+ if (typeof lang === 'string') { return lang; }
257
+ try { return String(lang.name); } catch (e) { return null; }
258
+ }
259
+
260
+ /* Texte, deren Bereiche geprueft werden: je Story (Dokument, Story) oder der Rahmentext. */
261
+ function idcLanguageTargets(doc, scope, frame, story) {
262
+ var out = [], i, stories;
263
+ if (scope === 'frame') {
264
+ try { out.push(frame.texts.item(0)); } catch (e0) { out.push(story); }
265
+ return out;
266
+ }
267
+ if (scope === 'story') { return [story]; }
268
+ try { stories = doc.stories; } catch (e1) { return out; }
269
+ for (i = 0; i < stories.length; i++) { out.push(stories.item(i)); }
270
+ return out;
271
+ }
272
+
273
+ /* Sprache des Absatzformats, je Format einmal gelesen. */
274
+ function idcStyleLanguage(style, cache) {
275
+ var key, name;
276
+ try { key = 's' + Number(style.id); } catch (e0) { key = null; }
277
+ if (key !== null && IDC.hasOwn(cache, key)) { return cache[key]; }
278
+ try { name = idcLanguageName(style.appliedLanguage); } catch (e1) { name = null; }
279
+ if (key !== null) { cache[key] = name; }
280
+ return name;
281
+ }
282
+
283
+ function idcRunLanguageRule(doc, targets, rule, args, findings, state) {
284
+ var t, ranges, count, langs, styles, starts, i, range, lang, styleLang, style, cache = {}, text, start, frames, frame, landing, layer, entry;
285
+ for (t = 0; t < targets.length; t++) {
286
+ try { ranges = targets[t].textStyleRanges; count = Number(ranges.length); } catch (e0) { continue; }
287
+ if (count === 0) { continue; }
288
+ langs = null; styles = null; starts = null;
289
+ try {
290
+ langs = idcEveryList(ranges.everyItem().appliedLanguage, count);
291
+ styles = idcEveryList(ranges.everyItem().appliedParagraphStyle, count);
292
+ starts = idcBundled(ranges.everyItem().index, count);
293
+ } catch (e1) { langs = null; }
294
+ for (i = 0; i < count; i++) {
295
+ range = null;
296
+ try {
297
+ if (langs === null || styles === null || starts === null) {
298
+ range = ranges.item(i);
299
+ lang = idcLanguageName(range.appliedLanguage);
300
+ style = range.appliedParagraphStyle;
301
+ } else {
302
+ lang = idcLanguageName(langs[i]);
303
+ style = styles[i];
304
+ }
305
+ } catch (e2) { continue; }
306
+ styleLang = idcStyleLanguage(style, cache);
307
+ if (lang === null || styleLang === null || lang === styleLang) { continue; }
308
+ if (range === null) { try { range = ranges.item(i); } catch (e3) { continue; } }
309
+ try { text = String(range.contents); } catch (e4) { text = ''; }
310
+ if (/^[\s  -​ ]*$/.test(text)) { continue; }
311
+ if (idcMatchInTable(range)) { state.tables_skipped += 1; continue; }
312
+ frame = null;
313
+ try { frames = range.parentTextFrames; if (frames && frames.length > 0) { frame = IDC.concrete(frames[0]); } } catch (e5) { frame = null; }
314
+ if (frame !== null && args.include_master_pages !== true && IDC.isOnParentSpread(frame)) { continue; }
315
+ if (frame !== null && args.include_locked_layers !== true) {
316
+ try { layer = frame.itemLayer; if (layer !== null && layer !== undefined && layer.locked === true) { continue; } } catch (e6) { /* weiter */ }
317
+ }
318
+ entry = { frame_id: null, page_id: null, page_name: '', story_id: null };
319
+ try { entry.story_id = Number(range.parentStory.id); } catch (e7) { entry.story_id = null; }
320
+ if (frame !== null) {
321
+ entry.frame_id = Number(frame.id);
322
+ try { landing = IDC.landing(frame); entry.page_id = landing.page_id; entry.page_name = landing.page_name; } catch (e8) { /* bleibt null */ }
323
+ }
324
+ if (state.page_ids !== null && (entry.page_id === null || !state.page_ids[entry.page_id])) { continue; }
325
+ if (starts === null) { start = Number(range.index); } else { start = Number(starts[i]); }
326
+ idcPush(findings, state, {
327
+ rule_id: rule.id,
328
+ severity: rule.severity,
329
+ category: rule.category,
330
+ message: rule.message + ' – ' + lang + ' instead of ' + styleLang,
331
+ story_id: entry.story_id,
332
+ frame_id: entry.frame_id,
333
+ page_id: entry.page_id,
334
+ page_name: entry.page_name,
335
+ start_index: start,
336
+ end_index: start + text.length,
337
+ snippet: (text.length > 80 ? text.substring(0, 80) + '…' : text).replace(/[\r\n]/g, '¶'),
338
+ suggestion: rule.suggestion,
339
+ language: lang,
340
+ paragraph_style_language: styleLang
341
+ });
342
+ }
343
+ }
344
+ }
345
+
346
+ IDC.register('check_typography', { needs_document: true, read_only: true, ruler: 'spread' },
347
+ function (ctx) {
348
+ var args = ctx.args;
349
+ var doc = ctx.doc;
350
+ var scope = idcGiven(args.scope) ? String(args.scope) : 'document';
351
+ var rows = IDC.isArray(args.rule_rows) ? args.rule_rows : [];
352
+ var unknown = IDC.isArray(args.unknown_rule_ids) ? args.unknown_rule_ids : [];
353
+ var maxFindings = idcGiven(args.max_findings) ? args.max_findings : 500;
354
+ var severityMin = idcGiven(args.severity_min) ? String(args.severity_min) : 'info';
355
+ var frame = null, story = null, frames, target, i, r, rule, findings = [], state, counts = {}, out, kept = [], data, framesChecked = 0;
356
+ var grepRules = [], lineRules = [], languageRules = [];
357
+ var rangeText = null, rangePages, pageIds = null, storyCaches = {}, storyKey, frameStory;
358
+
359
+ if (scope !== 'document' && scope !== 'frame' && scope !== 'story') {
360
+ IDC.fail('invalid_argument', 'scope must be document, frame or story.', { scope: scope });
361
+ }
362
+ if (unknown.length > 0) {
363
+ IDC.fail('invalid_argument', 'Unknown rule id(s): ' + unknown.join(', ') + '.', { unknown: unknown, available: idcRuleIdsOf(rows) });
364
+ }
365
+ if (typeof maxFindings !== 'number' || maxFindings < 1 || maxFindings > 2000 || Math.floor(maxFindings) !== maxFindings) {
366
+ IDC.fail('invalid_argument', 'max_findings must be a whole number between 1 and 2000.', { max_findings: args.max_findings });
367
+ }
368
+ if (!IDC.hasOwn(IDC_SEVERITY_RANK, severityMin)) {
369
+ IDC.fail('invalid_argument', 'severity_min must be info, warning or error.', { severity_min: severityMin });
370
+ }
371
+ if (scope !== 'document') {
372
+ frame = idcTextFrameArg(doc, args, 'frame_id').item;
373
+ story = idcStoryOf(frame);
374
+ }
375
+ if (idcGiven(args.page_range) && idcTrim(args.page_range) !== '' && idcTrim(args.page_range).toLowerCase() !== 'all') {
376
+ rangeText = idcTrim(args.page_range);
377
+ rangePages = idcPagesForExport(doc, { page_range: rangeText }, 0);
378
+ pageIds = {};
379
+ for (i = 0; i < rangePages.length; i++) { pageIds[Number(rangePages[i].page.id)] = true; }
380
+ }
381
+ if (rows.length === 0) {
382
+ ctx.warn('No rule selected for locale ' + (idcGiven(args.locale) ? args.locale : 'de') + '; nothing was checked.');
383
+ }
384
+ for (r = 0; r < rows.length; r++) {
385
+ if (rows[r].kind === 'grep') { grepRules.push(rows[r]); }
386
+ else if (rows[r].kind === 'lines') { lineRules.push(rows[r]); }
387
+ else if (rows[r].kind === 'language') { languageRules.push(rows[r]); }
388
+ }
389
+
390
+ state = { max: maxFindings, truncated: false, failed: [], tables_skipped: 0, dropped: 0, page_ids: pageIds };
391
+
392
+ /* Zeichenregeln: eine GREP-Suche je Regel ueber das Ziel. */
393
+ if (grepRules.length > 0) {
394
+ if (scope === 'document') { target = doc; }
395
+ else if (scope === 'story') { target = story; }
396
+ else {
397
+ try { target = frame.texts.item(0); } catch (e0) { target = null; }
398
+ if (target === null || target === undefined) { target = story; }
399
+ }
400
+ for (r = 0; r < grepRules.length; r++) {
401
+ idcRunGrepRule(doc, target, grepRules[r], args, findings, state);
402
+ }
403
+ }
404
+
405
+ /* Sprachregel (A-78): je Story die Formatlaeufe mit der Sprache ihres Absatzformats vergleichen. */
406
+ if (languageRules.length > 0) {
407
+ var languageTargets = idcLanguageTargets(doc, scope, frame, story);
408
+ for (r = 0; r < languageRules.length; r++) {
409
+ idcRunLanguageRule(doc, languageTargets, languageRules[r], args, findings, state);
410
+ }
411
+ }
412
+
413
+ /*
414
+ * Umbruchregeln: je Rahmen die Zeilen lesen, dann jede Regel darauf.
415
+ * Absaetze und Rahmenfolge einer Story nur einmal je Story (A-69: je
416
+ * Rahmen die ganze Kette zu lesen war bei einem Buch quadratisch).
417
+ */
418
+ if (lineRules.length > 0) {
419
+ frames = idcFramesInScope(doc, scope, frame, story, args.include_master_pages === true, args.include_locked_layers === true);
420
+ if (scope === 'document' && pageIds === null && frames.length > IDC_LINE_FRAMES_WARN) {
421
+ ctx.warn('Line rules ran over ' + frames.length + ' text frames – on a long book this can take a while. Pass page_range (e.g. "1-50") to check part of the document per call, or limit rules to the character rules.');
422
+ }
423
+ for (i = 0; i < frames.length; i++) {
424
+ if (pageIds !== null) {
425
+ var framePageId = idcFramePageId(frames[i]);
426
+ if (framePageId === null || !pageIds[framePageId]) { continue; }
427
+ }
428
+ try { frameStory = frames[i].parentStory; } catch (e1) { continue; }
429
+ if (frameStory === null || frameStory === undefined) { continue; }
430
+ try { storyKey = 'k' + Number(frameStory.id); } catch (e2) { storyKey = null; }
431
+ if (storyKey === null) {
432
+ data = idcFrameLineData(doc, frames[i], frameStory, true, 5000, null, null, true);
433
+ } else {
434
+ if (!IDC.hasOwn(storyCaches, storyKey)) { storyCaches[storyKey] = idcStoryLineCache(frameStory); }
435
+ data = idcFrameLineData(doc, frames[i], frameStory, true, 5000, null, storyCaches[storyKey], true);
436
+ }
437
+ framesChecked += 1;
438
+ for (r = 0; r < lineRules.length; r++) {
439
+ idcRunLineRule(lineRules[r], data, args, findings, state, frameStory);
440
+ }
441
+ }
442
+ }
443
+
444
+ /* Schwere filtern, zaehlen (counts zaehlt alles), dann erst den Deckel anlegen. */
445
+ for (i = 0; i < findings.length; i++) {
446
+ if (IDC_SEVERITY_RANK[findings[i].severity] < IDC_SEVERITY_RANK[severityMin]) { continue; }
447
+ counts[findings[i].rule_id] = (counts[findings[i].rule_id] || 0) + 1;
448
+ if (kept.length >= maxFindings) { state.dropped += 1; continue; }
449
+ kept.push(findings[i]);
450
+ }
451
+ if (state.dropped > 0) { state.truncated = true; }
452
+ for (r = 0; r < state.failed.length; r++) {
453
+ ctx.warn('Rule ' + state.failed[r].rule_id + ' could not run – InDesign rejected the GREP pattern: ' + state.failed[r].error);
454
+ }
455
+ if (state.tables_skipped > 0) {
456
+ ctx.warn('tables_skipped: ' + state.tables_skipped + ' match(es) inside tables were not reported (table cells are not checked in this version).');
457
+ }
458
+ if (state.truncated) {
459
+ ctx.warn('Output cut at max_findings ' + maxFindings + ': ' + state.dropped + ' further finding(s) not listed (truncated true). All rules were run; counts is complete.');
460
+ }
461
+
462
+ out = {
463
+ scope: scope,
464
+ locale: idcGiven(args.locale) ? String(args.locale) : 'de',
465
+ frame_id: frame === null ? null : Number(frame.id),
466
+ story_id: story === null ? null : Number(story.id),
467
+ findings: kept,
468
+ finding_count: kept.length,
469
+ findings_dropped: state.dropped,
470
+ counts: counts,
471
+ rules_checked: idcRuleIdsOf(rows),
472
+ rules_failed: state.failed,
473
+ frames_checked: framesChecked,
474
+ tables_skipped: state.tables_skipped,
475
+ severity_min: severityMin,
476
+ page_range_used: rangeText === null ? 'all' : rangeText,
477
+ truncated: state.truncated
478
+ };
479
+ return out;
480
+ });