layoutbridge-light 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.de.md +31 -2
- package/README.md +31 -3
- package/THIRD-PARTY-NOTICES.md +2 -2
- package/dist/src/mcp/catalog.js +9 -4
- package/dist/src/mcp/content-credentials.js +65 -0
- package/dist/src/mcp/dispatch.js +8 -1
- package/dist/src/mcp/license/polar-ids.js +1 -1
- package/dist/src/mcp/pdf-postprocess.js +798 -0
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/mcp/target-folder.js +25 -0
- package/dist/src/mcp/tools.js +275 -20
- package/dist/src/mcp/typography-rules.js +1 -1
- package/dist/src/transport/com.js +224 -38
- package/dist/src/transport/transport.js +22 -0
- package/indesign/commands/25-export.js +258 -11
- package/indesign/commands/26-export-image.js +1 -1
- package/indesign/commands/31-view.js +100 -0
- package/indesign/commands/35-parents.js +44 -24
- package/indesign/commands/40-frames.js +35 -0
- package/indesign/commands/47-anchored.js +142 -0
- package/indesign/commands/50-modify.js +25 -3
- package/indesign/commands/60-images.js +28 -2
- package/indesign/commands/69-placeholders.js +7 -2
- package/indesign/commands/70-text.js +39 -4
- package/indesign/commands/75-tables.js +27 -1
- package/indesign/commands/78-frame-lines.js +59 -0
- package/indesign/commands/79-check-typography.js +143 -4
- package/indesign/commands/80-styles.js +105 -10
- package/indesign/commands/89-toc-index.js +61 -4
- package/indesign/commands/91-epub.js +92 -9
- package/indesign/commands/92-accessibility.js +1131 -0
- package/indesign/interpreter.js +51 -1
- package/package.json +1 -1
- package/rules/typography-rules.json +3 -2
|
@@ -0,0 +1,1131 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Barrierefreie PDFs (A-71 = A-79, A-80, A-81; Sitzung 82; E-117, E-118 bis E-121).
|
|
3
|
+
*
|
|
4
|
+
* Grundlage: Vorabpruefung Sitzung 78 (Bericht
|
|
5
|
+
* docs/sitzungen/Sitzung-78_2026-11-11_Bericht-Barrierefreiheit-A-71.md):
|
|
6
|
+
* Alternativtext, ActualText und Tag-Art haengen an objectExportOptions des
|
|
7
|
+
* Seitenobjekts (nicht an der Grafik); PDF-Tags der Formate an
|
|
8
|
+
* styleExportTagMaps (nur P, H, H1-H6, Artifact); die Lesereihenfolge ueber
|
|
9
|
+
* doc.articles und taggedPDFPreferences.structureOrder.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ------------------------------------------------------------ Aufzaehlungen */
|
|
13
|
+
|
|
14
|
+
/* Quelle des Alternativtexts: Name im Werkzeug -> Name des Enumerators SourceType. */
|
|
15
|
+
var IDC_ALT_SOURCES = [
|
|
16
|
+
['custom', 'SOURCE_CUSTOM'],
|
|
17
|
+
['xmp_title', 'SOURCE_XMP_TITLE'],
|
|
18
|
+
['xmp_description', 'SOURCE_XMP_DESCRIPTION'],
|
|
19
|
+
['xmp_headline', 'SOURCE_XMP_HEADLINE'],
|
|
20
|
+
['xmp_alt_text', 'SOURCE_XMP_ALT_TEXT'],
|
|
21
|
+
['xmp_extended_description', 'SOURCE_XMP_EXTENDED_DESCRIPTION'],
|
|
22
|
+
['xmp_other', 'SOURCE_XMP_OTHER'],
|
|
23
|
+
['structure', 'SOURCE_XML_STRUCTURE'],
|
|
24
|
+
['decorative', 'SOURCE_DECORATIVE_IMAGE']
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
var IDC_TAG_TYPES = [
|
|
28
|
+
['from_structure', 'TAG_FROM_STRUCTURE'],
|
|
29
|
+
['artifact', 'TAG_ARTIFACT'],
|
|
30
|
+
['based_on_object', 'TAG_BASED_ON_OBJECT']
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/* Zulaessige PDF-Tags der Formatzuordnung (InDesign 21.6, Beleg 06 der Vorabpruefung). */
|
|
34
|
+
var IDC_PDF_TAGS = ['P', 'H', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Artifact', ''];
|
|
35
|
+
|
|
36
|
+
/* Klassen platzierter Grafiken (Inhalt eines Rahmens, kein eigenes Seitenobjekt). */
|
|
37
|
+
var IDC_GRAPHIC_CLASSES = { Image: 1, EPS: 1, PDF: 1, PICT: 1, WMF: 1, ImportedPage: 1, SVG: 1, Graphic: 1 };
|
|
38
|
+
|
|
39
|
+
function idcEnumFrom(table, enumObject, name, field) {
|
|
40
|
+
var i;
|
|
41
|
+
if (enumObject === undefined || enumObject === null) {
|
|
42
|
+
IDC.fail('invalid_state', 'This InDesign has no enumeration for ' + field + '.', { field: field });
|
|
43
|
+
}
|
|
44
|
+
for (i = 0; i < table.length; i++) {
|
|
45
|
+
if (table[i][0] === name) { return enumObject[table[i][1]]; }
|
|
46
|
+
}
|
|
47
|
+
var names = [];
|
|
48
|
+
for (i = 0; i < table.length; i++) { names.push(table[i][0]); }
|
|
49
|
+
IDC.fail('invalid_argument', field + ' must be one of ' + names.join(', ') + '.', { value: name, allowed: names });
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function idcAccEnumName(table, enumObject, value) {
|
|
54
|
+
var i;
|
|
55
|
+
if (enumObject === undefined || enumObject === null) { return null; }
|
|
56
|
+
for (i = 0; i < table.length; i++) {
|
|
57
|
+
try {
|
|
58
|
+
if (IDC.enumIs(value, enumObject[table[i][1]])) { return table[i][0]; }
|
|
59
|
+
} catch (e) { /* naechster */ }
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function idcSourceTypeEnum() { return typeof SourceType === 'undefined' ? null : SourceType; }
|
|
65
|
+
function idcTagTypeEnum() { return typeof TagType === 'undefined' ? null : TagType; }
|
|
66
|
+
|
|
67
|
+
/* --------------------------------------------------------------- PDF-Tags */
|
|
68
|
+
|
|
69
|
+
/* PDF-Tag eines Formats: "" ohne Zuordnung (automatisch), null wenn nicht lesbar. */
|
|
70
|
+
function idcReadPdfTag(style) {
|
|
71
|
+
var maps, i, map;
|
|
72
|
+
try { maps = style.styleExportTagMaps; } catch (e) { return null; }
|
|
73
|
+
if (!maps) { return null; }
|
|
74
|
+
try {
|
|
75
|
+
for (i = 0; i < maps.length; i++) {
|
|
76
|
+
map = maps.item(i);
|
|
77
|
+
if (String(map.exportType) === 'PDF') { return String(map.exportTag); }
|
|
78
|
+
}
|
|
79
|
+
} catch (e2) { return null; }
|
|
80
|
+
return '';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* pdf_tag setzen: "" entfernt die PDF-Zuordnung; add ersetzt eine vorhandene (Beleg 05). */
|
|
84
|
+
function idcPdfTagArg(tag) {
|
|
85
|
+
var i, known = false;
|
|
86
|
+
for (i = 0; i < IDC_PDF_TAGS.length; i++) {
|
|
87
|
+
if (IDC_PDF_TAGS[i] === tag) { known = true; }
|
|
88
|
+
}
|
|
89
|
+
if (typeof tag !== 'string' || !known) {
|
|
90
|
+
IDC.fail('invalid_argument', 'pdf_tag must be one of P, H, H1 to H6, Artifact or "" (no tag). InDesign accepts no other tag for a style mapping.',
|
|
91
|
+
{ pdf_tag: tag, allowed: IDC_PDF_TAGS });
|
|
92
|
+
}
|
|
93
|
+
return tag;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function idcApplyPdfTag(style, tag) {
|
|
97
|
+
var maps, i, map;
|
|
98
|
+
idcPdfTagArg(tag);
|
|
99
|
+
try { maps = style.styleExportTagMaps; } catch (e) { maps = null; }
|
|
100
|
+
if (!maps) { IDC.fail('invalid_state', 'This InDesign has no export tag mapping for styles (styleExportTagMaps).', null); }
|
|
101
|
+
if (tag === '') {
|
|
102
|
+
for (i = maps.length - 1; i >= 0; i--) {
|
|
103
|
+
map = maps.item(i);
|
|
104
|
+
if (String(map.exportType) === 'PDF') { map.remove(); }
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
maps.add('PDF', tag, '', '');
|
|
110
|
+
} catch (e2) {
|
|
111
|
+
IDC.fail('invalid_argument', 'InDesign rejects pdf_tag "' + tag + '": ' + String(e2.message ? e2.message : e2), { pdf_tag: tag });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ------------------------------------------------------------ Seitenobjekte */
|
|
116
|
+
|
|
117
|
+
function idcAncestorClasses(item) {
|
|
118
|
+
var out = [];
|
|
119
|
+
var current, step, name;
|
|
120
|
+
try { current = item.parent; } catch (e) { return out; }
|
|
121
|
+
for (step = 0; step < 16; step++) {
|
|
122
|
+
if (current === null || current === undefined) { break; }
|
|
123
|
+
name = IDC.className(current);
|
|
124
|
+
out.push({ name: name, item: current });
|
|
125
|
+
if (name === 'Spread' || name === 'MasterSpread' || name === 'Document' || name === 'Page') { break; }
|
|
126
|
+
try { current = current.parent; } catch (e2) { break; }
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Verankert: der Weg nach oben fuehrt ueber eine Textstelle (Character) vor dem Druckbogen. */
|
|
132
|
+
function idcIsAnchored(item) {
|
|
133
|
+
var chain = idcAncestorClasses(item);
|
|
134
|
+
var i;
|
|
135
|
+
for (i = 0; i < chain.length; i++) {
|
|
136
|
+
if (chain[i].name === 'Character' || chain[i].name === 'InsertionPoint' || chain[i].name === 'Story') { return true; }
|
|
137
|
+
if (chain[i].name === 'Spread' || chain[i].name === 'MasterSpread') { return false; }
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function idcIsInGroup(item) {
|
|
143
|
+
try { return IDC.className(item.parent) === 'Group'; } catch (e) { return false; }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function idcHasGraphic(item) {
|
|
147
|
+
try { return item.allGraphics.length > 0; } catch (e) { return false; }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function idcIsGraphicFrame(item) {
|
|
151
|
+
var name = IDC.className(item);
|
|
152
|
+
return (name === 'Rectangle' || name === 'Oval' || name === 'Polygon') && idcHasGraphic(item);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function idcItemPageName(item) {
|
|
156
|
+
var page;
|
|
157
|
+
try { page = item.parentPage; } catch (e) { page = null; }
|
|
158
|
+
try {
|
|
159
|
+
if (page !== null && page !== undefined && page.isValid !== false) { return String(page.name); }
|
|
160
|
+
} catch (e2) { /* kein Name */ }
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function idcExportOptions(item) {
|
|
165
|
+
try { return item.objectExportOptions; } catch (e) { return null; }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Gruppe mit eigenem Alternativtext ueber dem Objekt (sie verdeckt es im PDF), sonst null. */
|
|
169
|
+
function idcCoveringGroup(item) {
|
|
170
|
+
var chain = idcAncestorClasses(item);
|
|
171
|
+
var i, options, source;
|
|
172
|
+
for (i = 0; i < chain.length; i++) {
|
|
173
|
+
if (chain[i].name !== 'Group') { continue; }
|
|
174
|
+
options = idcExportOptions(chain[i].item);
|
|
175
|
+
if (options === null) { continue; }
|
|
176
|
+
source = idcAccEnumName(IDC_ALT_SOURCES, idcSourceTypeEnum(), options.altTextSourceType);
|
|
177
|
+
if (source !== null && source !== 'structure') {
|
|
178
|
+
try { return Number(chain[i].item.id); } catch (e) { return -1; }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
* XMP-Felder der verknuepften Datei. Titel und Beschreibung sind in der
|
|
186
|
+
* Vorabpruefung belegt (linkXmp.documentTitle, description); die uebrigen
|
|
187
|
+
* Felder ueber linkXmp.getProperty sind eine Annahme – nicht auflösbar heisst
|
|
188
|
+
* null (problem unverified), nicht leer.
|
|
189
|
+
*/
|
|
190
|
+
var IDC_XMP_PROPERTIES = {
|
|
191
|
+
xmp_headline: ['http://ns.adobe.com/photoshop/1.0/', 'Headline'],
|
|
192
|
+
xmp_alt_text: ['http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/', 'AltTextAccessibility[1]'],
|
|
193
|
+
xmp_extended_description: ['http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/', 'ExtDescrAccessibility[1]']
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
function idcFirstGraphic(item) {
|
|
197
|
+
var graphics;
|
|
198
|
+
try { graphics = item.allGraphics; } catch (e) { return null; }
|
|
199
|
+
if (!graphics || graphics.length === 0) { return null; }
|
|
200
|
+
return graphics[0];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function idcLinkXmp(item) {
|
|
204
|
+
try { return idcFirstGraphic(item).itemLink.linkXmp; } catch (e) { return null; }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function idcXmpValue(xmp, kind, options) {
|
|
208
|
+
var pair = null, value;
|
|
209
|
+
if (xmp === null) { return null; }
|
|
210
|
+
try {
|
|
211
|
+
if (kind === 'xmp_title') { return String(xmp.documentTitle); }
|
|
212
|
+
if (kind === 'xmp_description') { return String(xmp.description); }
|
|
213
|
+
} catch (e) { return null; }
|
|
214
|
+
if (kind === 'xmp_other') {
|
|
215
|
+
try { pair = options.altMetadataProperty; } catch (e2) { pair = null; }
|
|
216
|
+
} else if (IDC.hasOwn(IDC_XMP_PROPERTIES, kind)) {
|
|
217
|
+
pair = IDC_XMP_PROPERTIES[kind];
|
|
218
|
+
}
|
|
219
|
+
if (pair === null || pair === undefined || pair.length < 2 || String(pair[0]) === '') { return null; }
|
|
220
|
+
try {
|
|
221
|
+
value = xmp.getProperty(String(pair[0]), String(pair[1]));
|
|
222
|
+
return value === null || value === undefined ? null : String(value);
|
|
223
|
+
} catch (e3) { return null; }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function idcHasXmlElement(item) {
|
|
227
|
+
var element;
|
|
228
|
+
try { element = item.associatedXMLElement; } catch (e) { return false; }
|
|
229
|
+
try { return element !== null && element !== undefined && element.isValid === true; } catch (e2) { return false; }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Beschreibung eines Seitenobjekts fuer list_alt_texts und set_alt_text. */
|
|
233
|
+
function idcDescribeAltText(item) {
|
|
234
|
+
var options = idcExportOptions(item);
|
|
235
|
+
var kind = IDC.itemType(item);
|
|
236
|
+
var image = idcIsGraphicFrame(item);
|
|
237
|
+
var source = null, actualSource = null, tag = null, altText = null, actualText = null;
|
|
238
|
+
var fileName = null, problem = null, coveredBy = null, pair = null;
|
|
239
|
+
|
|
240
|
+
if (options !== null) {
|
|
241
|
+
source = idcAccEnumName(IDC_ALT_SOURCES, idcSourceTypeEnum(), options.altTextSourceType);
|
|
242
|
+
actualSource = idcAccEnumName(IDC_ALT_SOURCES, idcSourceTypeEnum(), options.actualTextSourceType);
|
|
243
|
+
tag = idcAccEnumName(IDC_TAG_TYPES, idcTagTypeEnum(), options.applyTagType);
|
|
244
|
+
if (source === 'custom') { try { altText = String(options.customAltText); } catch (e1) { altText = null; } }
|
|
245
|
+
else if (source === 'decorative') { altText = ''; }
|
|
246
|
+
else if (source === 'structure') { altText = idcHasXmlElement(item) ? null : ''; }
|
|
247
|
+
else if (source !== null) { altText = idcXmpValue(idcLinkXmp(item), source, options); }
|
|
248
|
+
if (actualSource === 'custom') { try { actualText = String(options.customActualText); } catch (e2) { actualText = null; } }
|
|
249
|
+
if (source === 'xmp_other') {
|
|
250
|
+
try { pair = options.altMetadataProperty; } catch (e3) { pair = null; }
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (image) {
|
|
254
|
+
try { fileName = String(idcFirstGraphic(item).itemLink.name); } catch (e4) { fileName = null; }
|
|
255
|
+
coveredBy = idcCoveringGroup(item);
|
|
256
|
+
if (tag === 'artifact') { problem = null; }
|
|
257
|
+
else if (coveredBy !== null) { problem = 'hidden_by_group'; }
|
|
258
|
+
else if (source === 'decorative') { problem = 'decorative_not_artifact'; }
|
|
259
|
+
else if (altText === '') { problem = 'missing'; }
|
|
260
|
+
else if (altText === null) { problem = 'unverified'; }
|
|
261
|
+
} else if (kind === 'group' && source !== null && source !== 'structure' && tag !== 'artifact') {
|
|
262
|
+
problem = 'hides_children';
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
frame_id: Number(item.id),
|
|
266
|
+
kind: image ? 'image' : kind,
|
|
267
|
+
page: idcItemPageName(item),
|
|
268
|
+
on_parent: IDC.isOnParentSpread(item),
|
|
269
|
+
anchored: idcIsAnchored(item),
|
|
270
|
+
in_group: idcIsInGroup(item),
|
|
271
|
+
file_name: fileName,
|
|
272
|
+
alt_source: source,
|
|
273
|
+
alt_text: altText,
|
|
274
|
+
xmp_property: pair === null || pair === undefined || pair.length < 2 || String(pair[0]) === '' ? null : { namespace: String(pair[0]), path: String(pair[1]) },
|
|
275
|
+
actual_text_source: actualSource,
|
|
276
|
+
actual_text: actualText,
|
|
277
|
+
tag: tag,
|
|
278
|
+
covered_by_group_id: coveredBy,
|
|
279
|
+
problem: problem
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Seitennamen eines Bereichs ("1-3, 5"; "+2" absolut) als Menge der Seiten-IDs, null = alle. */
|
|
284
|
+
function idcPageIdsInRange(doc, rangeText) {
|
|
285
|
+
var ids = {};
|
|
286
|
+
var parts, p, part, single, cut, from, to, k, found;
|
|
287
|
+
if (rangeText === undefined || rangeText === null || idcTrim(rangeText) === '' || idcTrim(rangeText).toLowerCase() === 'all') { return null; }
|
|
288
|
+
idcCountPagesInRange(doc, rangeText);
|
|
289
|
+
parts = String(rangeText).split(',');
|
|
290
|
+
for (p = 0; p < parts.length; p++) {
|
|
291
|
+
part = idcTrim(parts[p]);
|
|
292
|
+
if (part === '') { continue; }
|
|
293
|
+
single = idcPageIndexByName(doc, part);
|
|
294
|
+
if (single >= 0) { ids[String(doc.pages.item(single).id)] = true; continue; }
|
|
295
|
+
found = false;
|
|
296
|
+
for (cut = 1; cut < part.length; cut++) {
|
|
297
|
+
if (part.charAt(cut) !== '-') { continue; }
|
|
298
|
+
from = idcPageIndexByName(doc, idcTrim(part.substr(0, cut)));
|
|
299
|
+
to = idcPageIndexByName(doc, idcTrim(part.substr(cut + 1)));
|
|
300
|
+
if (from >= 0 && to >= 0) { found = true; break; }
|
|
301
|
+
}
|
|
302
|
+
if (found) {
|
|
303
|
+
for (k = from; k <= to; k++) { ids[String(doc.pages.item(k).id)] = true; }
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return ids;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function idcItemOnPages(item, pageIds) {
|
|
310
|
+
var page;
|
|
311
|
+
if (pageIds === null) { return true; }
|
|
312
|
+
try { page = item.parentPage; } catch (e) { return false; }
|
|
313
|
+
try { return page !== null && page !== undefined && pageIds[String(page.id)] === true; } catch (e2) { return false; }
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* Alle Seitenobjekte des Dokuments (auch in Gruppen, verankert, auf Musterseiten); Grafiken nicht. */
|
|
317
|
+
function idcAllItemsForAccessibility(doc) {
|
|
318
|
+
var out = [];
|
|
319
|
+
var all, i, item;
|
|
320
|
+
try { all = doc.allPageItems; } catch (e) { all = []; }
|
|
321
|
+
for (i = 0; i < all.length; i++) {
|
|
322
|
+
item = IDC.concrete(all[i]);
|
|
323
|
+
if (IDC.hasOwn(IDC_GRAPHIC_CLASSES, IDC.className(item))) { continue; }
|
|
324
|
+
out.push(item);
|
|
325
|
+
}
|
|
326
|
+
return out;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* --------------------------------------------------------- list_alt_texts */
|
|
330
|
+
|
|
331
|
+
IDC.register('list_alt_texts', { needs_document: true, read_only: true, ruler: 'none' },
|
|
332
|
+
function (ctx) {
|
|
333
|
+
var args = ctx.args;
|
|
334
|
+
var doc = ctx.doc;
|
|
335
|
+
var pageIds = idcPageIdsInRange(doc, args.page_range);
|
|
336
|
+
var maxItems = args.max_items === undefined || args.max_items === null ? 500 : Number(args.max_items);
|
|
337
|
+
var includeParent = args.include_parent_pages === true;
|
|
338
|
+
var items = idcAllItemsForAccessibility(doc);
|
|
339
|
+
var out = [], counts = { images: 0, missing: 0, artifacts: 0, hidden_by_group: 0, decorative_not_artifact: 0, unverified: 0, groups_with_alt_text: 0 };
|
|
340
|
+
var i, item, entry, total = 0;
|
|
341
|
+
|
|
342
|
+
if (isNaN(maxItems) || maxItems < 1 || maxItems > 5000) {
|
|
343
|
+
IDC.fail('invalid_argument', 'max_items must be between 1 and 5000.', { max_items: args.max_items });
|
|
344
|
+
}
|
|
345
|
+
for (i = 0; i < items.length; i++) {
|
|
346
|
+
item = items[i];
|
|
347
|
+
if (!idcIsGraphicFrame(item) && IDC.className(item) !== 'Group') { continue; }
|
|
348
|
+
if (IDC.isOnParentSpread(item) && !includeParent) { continue; }
|
|
349
|
+
if (!IDC.isOnParentSpread(item) && !idcItemOnPages(item, pageIds)) { continue; }
|
|
350
|
+
entry = idcDescribeAltText(item);
|
|
351
|
+
if (entry.kind === 'group' && entry.problem === null) { continue; }
|
|
352
|
+
total += 1;
|
|
353
|
+
if (entry.kind === 'image') { counts.images += 1; }
|
|
354
|
+
if (entry.kind === 'group') { counts.groups_with_alt_text += 1; }
|
|
355
|
+
if (entry.tag === 'artifact') { counts.artifacts += 1; }
|
|
356
|
+
if (entry.problem !== null && IDC.hasOwn(counts, entry.problem)) { counts[entry.problem] += 1; }
|
|
357
|
+
if (out.length < maxItems) { out.push(entry); }
|
|
358
|
+
}
|
|
359
|
+
if (total > out.length) { ctx.warn(total + ' objects, only the first ' + out.length + ' returned (max_items).'); }
|
|
360
|
+
return {
|
|
361
|
+
items: out,
|
|
362
|
+
count: out.length,
|
|
363
|
+
total: total,
|
|
364
|
+
truncated: total > out.length,
|
|
365
|
+
summary: counts,
|
|
366
|
+
page_range_used: pageIds === null ? 'all' : idcTrim(args.page_range)
|
|
367
|
+
};
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
/* ----------------------------------------------------------- set_alt_text */
|
|
371
|
+
|
|
372
|
+
function idcAltTextTargets(doc, args) {
|
|
373
|
+
var ids = [];
|
|
374
|
+
var seen = {};
|
|
375
|
+
var out = [], i, found;
|
|
376
|
+
if (idcGiven(args.frame_id)) { ids.push(args.frame_id); }
|
|
377
|
+
if (idcGiven(args.frame_ids)) {
|
|
378
|
+
if (!IDC.isArray(args.frame_ids)) { IDC.fail('invalid_argument', 'frame_ids must be a list of object ids.', null); }
|
|
379
|
+
for (i = 0; i < args.frame_ids.length; i++) { ids.push(args.frame_ids[i]); }
|
|
380
|
+
}
|
|
381
|
+
if (ids.length === 0) { IDC.fail('invalid_argument', 'frame_id or frame_ids is required.', null); }
|
|
382
|
+
if (ids.length > 500) { IDC.fail('invalid_argument', 'At most 500 objects per call.', { count: ids.length }); }
|
|
383
|
+
for (i = 0; i < ids.length; i++) {
|
|
384
|
+
if (seen[String(ids[i])] === true) {
|
|
385
|
+
IDC.fail('invalid_argument', 'Object ' + ids[i] + ' is named twice.', { code: 'duplicate_frame_id', frame_id: ids[i] });
|
|
386
|
+
}
|
|
387
|
+
seen[String(ids[i])] = true;
|
|
388
|
+
found = idcFrameArg(doc, { frame_id: ids[i] }, 'frame_id');
|
|
389
|
+
out.push(found.item);
|
|
390
|
+
}
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
IDC.register('set_alt_text', { needs_document: true, read_only: false, ruler: 'none' },
|
|
395
|
+
function (ctx) {
|
|
396
|
+
var args = ctx.args;
|
|
397
|
+
var doc = ctx.doc;
|
|
398
|
+
var source = idcGiven(args.source) ? String(args.source) : null;
|
|
399
|
+
var tagName = idcGiven(args.tag) ? String(args.tag) : null;
|
|
400
|
+
var sourceEnum = null, tagEnum = null, pair = null;
|
|
401
|
+
var targets, i, item, options, className, out = [], modified = [];
|
|
402
|
+
var warnedGroupAlt = false, warnedGroupArtifact = false, warnedActualText = false, warnedTextArtifact = false, warnedImageArtifact = false;
|
|
403
|
+
|
|
404
|
+
if (idcGiven(args.text) && source === null) { source = 'custom'; }
|
|
405
|
+
if (source === null && tagName === null && !idcGiven(args.actual_text)) {
|
|
406
|
+
IDC.fail('invalid_argument', 'Pass at least one of source, text, actual_text or tag.', null);
|
|
407
|
+
}
|
|
408
|
+
if (source !== null) { sourceEnum = idcEnumFrom(IDC_ALT_SOURCES, idcSourceTypeEnum(), source, 'source'); }
|
|
409
|
+
if (tagName !== null) { tagEnum = idcEnumFrom(IDC_TAG_TYPES, idcTagTypeEnum(), tagName, 'tag'); }
|
|
410
|
+
if (source === 'custom' && (typeof args.text !== 'string' || idcTrim(args.text) === '')) {
|
|
411
|
+
IDC.fail('invalid_argument', 'source custom needs text (the alternative text). For an image without meaning use source decorative.', { source: source });
|
|
412
|
+
}
|
|
413
|
+
if (idcGiven(args.text) && source !== 'custom') {
|
|
414
|
+
IDC.fail('invalid_argument', 'text belongs to source custom; source "' + source + '" takes the text from elsewhere.', { source: source });
|
|
415
|
+
}
|
|
416
|
+
if (source === 'xmp_other') {
|
|
417
|
+
if (!idcGiven(args.xmp_property) || typeof args.xmp_property.namespace !== 'string' || typeof args.xmp_property.path !== 'string' ||
|
|
418
|
+
args.xmp_property.namespace === '' || args.xmp_property.path === '') {
|
|
419
|
+
IDC.fail('invalid_argument', 'source xmp_other needs xmp_property {namespace, path}.', null);
|
|
420
|
+
}
|
|
421
|
+
pair = [args.xmp_property.namespace, args.xmp_property.path];
|
|
422
|
+
} else if (idcGiven(args.xmp_property)) {
|
|
423
|
+
IDC.fail('invalid_argument', 'xmp_property belongs to source xmp_other.', null);
|
|
424
|
+
}
|
|
425
|
+
if (idcGiven(args.actual_text) && typeof args.actual_text !== 'string') {
|
|
426
|
+
IDC.fail('invalid_argument', 'actual_text must be text ("" removes it).', null);
|
|
427
|
+
}
|
|
428
|
+
/* Entscheidung des Anbieters 11.11.2026 (E-117 Nachtrag 4): dekorativ heisst zugleich Artefakt. */
|
|
429
|
+
if (source === 'decorative' && tagName === null) {
|
|
430
|
+
tagName = 'artifact';
|
|
431
|
+
tagEnum = idcEnumFrom(IDC_TAG_TYPES, idcTagTypeEnum(), tagName, 'tag');
|
|
432
|
+
} else if (source === 'decorative' && tagName !== 'artifact') {
|
|
433
|
+
ctx.warn('source decorative with tag "' + tagName + '": the image is exported as a figure without alternative text (no artifact) – checkers report it as missing alternative text.');
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
targets = idcAltTextTargets(doc, args);
|
|
437
|
+
|
|
438
|
+
for (i = 0; i < targets.length; i++) {
|
|
439
|
+
item = targets[i];
|
|
440
|
+
options = idcExportOptions(item);
|
|
441
|
+
className = IDC.className(item);
|
|
442
|
+
if (options === null) {
|
|
443
|
+
IDC.fail('invalid_state', 'Object ' + item.id + ' (' + className + ') has no object export options.', { frame_id: Number(item.id), found_class: className });
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
if (sourceEnum !== null) {
|
|
447
|
+
options.altTextSourceType = sourceEnum;
|
|
448
|
+
if (source === 'custom') { options.customAltText = String(args.text); }
|
|
449
|
+
if (pair !== null) { options.altMetadataProperty = pair; }
|
|
450
|
+
}
|
|
451
|
+
if (idcGiven(args.actual_text)) {
|
|
452
|
+
if (args.actual_text === '') {
|
|
453
|
+
options.actualTextSourceType = idcSourceTypeEnum().SOURCE_XML_STRUCTURE;
|
|
454
|
+
options.customActualText = '';
|
|
455
|
+
} else {
|
|
456
|
+
options.actualTextSourceType = idcSourceTypeEnum().SOURCE_CUSTOM;
|
|
457
|
+
options.customActualText = String(args.actual_text);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (tagEnum !== null) { options.applyTagType = tagEnum; }
|
|
461
|
+
} catch (e) {
|
|
462
|
+
IDC.fail('script_error', 'InDesign could not set the export options of object ' + item.id + ': ' + String(e.message ? e.message : e), { frame_id: Number(item.id) });
|
|
463
|
+
}
|
|
464
|
+
modified.push(Number(item.id));
|
|
465
|
+
if (className === 'Group' && sourceEnum !== null && source !== 'structure' && tagName !== 'artifact' && !warnedGroupAlt) {
|
|
466
|
+
ctx.warn('Alternative text on a group: the group becomes one figure in the PDF; alternative texts and caption text of its members are lost. Set it on each frame instead unless the group really is one picture.');
|
|
467
|
+
warnedGroupAlt = true;
|
|
468
|
+
}
|
|
469
|
+
if (className === 'Group' && tagName === 'artifact' && !warnedGroupArtifact) {
|
|
470
|
+
ctx.warn('tag artifact on a group has no effect in the PDF (InDesign 21.6) – set it on each member.');
|
|
471
|
+
warnedGroupArtifact = true;
|
|
472
|
+
}
|
|
473
|
+
if (className === 'TextFrame' && idcGiven(args.actual_text) && args.actual_text !== '' && !warnedActualText) {
|
|
474
|
+
ctx.warn('actual_text on a text frame has no effect in the PDF; it works on graphic frames.');
|
|
475
|
+
warnedActualText = true;
|
|
476
|
+
}
|
|
477
|
+
if (className === 'TextFrame' && tagName === 'artifact' && !warnedTextArtifact) {
|
|
478
|
+
ctx.warn('tag artifact on a text frame hides its text from screen readers. Its stroke and fill stay untagged either way; a tagged export marks them as artifacts (unless artifact_untagged_paths is false).');
|
|
479
|
+
warnedTextArtifact = true;
|
|
480
|
+
}
|
|
481
|
+
if (idcIsGraphicFrame(item) && tagName === 'artifact' && source !== 'decorative' && !warnedImageArtifact) {
|
|
482
|
+
ctx.warn('An image marked as artifact is skipped by screen readers; its alternative text is not used.');
|
|
483
|
+
warnedImageArtifact = true;
|
|
484
|
+
}
|
|
485
|
+
out.push(idcDescribeAltText(item));
|
|
486
|
+
}
|
|
487
|
+
ctx.changes = { modified_frame_ids: modified };
|
|
488
|
+
return { items: out, count: out.length };
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
/* ----------------------------------------------------------------- Artikel */
|
|
492
|
+
|
|
493
|
+
function idcStructureOrderIsArticles(doc) {
|
|
494
|
+
try {
|
|
495
|
+
if (typeof TaggedPDFStructureOrderOptions === 'undefined') { return null; }
|
|
496
|
+
return IDC.enumIs(doc.taggedPDFPreferences.structureOrder, TaggedPDFStructureOrderOptions.USE_ARTICLES);
|
|
497
|
+
} catch (e) { return null; }
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function idcSetStructureOrder(doc, useArticles) {
|
|
501
|
+
if (typeof TaggedPDFStructureOrderOptions === 'undefined') {
|
|
502
|
+
IDC.fail('invalid_state', 'This InDesign has no reading order setting for tagged PDF (TaggedPDFStructureOrderOptions).', null);
|
|
503
|
+
}
|
|
504
|
+
doc.taggedPDFPreferences.structureOrder = useArticles
|
|
505
|
+
? TaggedPDFStructureOrderOptions.USE_ARTICLES
|
|
506
|
+
: TaggedPDFStructureOrderOptions.USE_XML_STRUCTURE;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function idcStoryIdOf(item) {
|
|
510
|
+
try {
|
|
511
|
+
if (IDC.className(item) === 'TextFrame') { return Number(item.parentStory.id); }
|
|
512
|
+
} catch (e) { /* keine Story */ }
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function idcDescribeArticle(article, index) {
|
|
517
|
+
var members = [];
|
|
518
|
+
var i, ref, exportStatus = null;
|
|
519
|
+
for (i = 0; i < article.articleMembers.length; i++) {
|
|
520
|
+
try { ref = IDC.concrete(article.articleMembers.item(i).itemRef); } catch (e) { ref = null; }
|
|
521
|
+
if (ref === null || ref === undefined) { continue; }
|
|
522
|
+
members.push({
|
|
523
|
+
frame_id: Number(ref.id),
|
|
524
|
+
kind: IDC.itemType(ref),
|
|
525
|
+
class_name: IDC.className(ref),
|
|
526
|
+
page: idcItemPageName(ref),
|
|
527
|
+
on_parent: IDC.isOnParentSpread(ref),
|
|
528
|
+
story_id: idcStoryIdOf(ref)
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
try { exportStatus = article.articleExportStatus === true; } catch (e2) { exportStatus = null; }
|
|
532
|
+
var out = { name: String(article.name), index: index, members: members, member_count: members.length };
|
|
533
|
+
out['export'] = exportStatus;
|
|
534
|
+
return out;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function idcArticleByName(doc, name) {
|
|
538
|
+
var i, article;
|
|
539
|
+
for (i = 0; i < doc.articles.length; i++) {
|
|
540
|
+
article = doc.articles.item(i);
|
|
541
|
+
if (String(article.name) === String(name)) { return { article: article, index: i }; }
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function idcArticleNames(doc) {
|
|
547
|
+
var names = [];
|
|
548
|
+
var i;
|
|
549
|
+
for (i = 0; i < doc.articles.length; i++) { names.push(String(doc.articles.item(i).name)); }
|
|
550
|
+
return names;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* Welche Objekte und Stories ein Artikel erfasst. */
|
|
554
|
+
function idcArticleCoverage(doc) {
|
|
555
|
+
var ids = {}, stories = {}, where = {};
|
|
556
|
+
var a, m, article, ref, story;
|
|
557
|
+
for (a = 0; a < doc.articles.length; a++) {
|
|
558
|
+
article = doc.articles.item(a);
|
|
559
|
+
for (m = 0; m < article.articleMembers.length; m++) {
|
|
560
|
+
try { ref = IDC.concrete(article.articleMembers.item(m).itemRef); } catch (e) { continue; }
|
|
561
|
+
ids[String(ref.id)] = true;
|
|
562
|
+
where[String(ref.id)] = String(article.name);
|
|
563
|
+
story = idcStoryIdOf(ref);
|
|
564
|
+
if (story !== null) { stories[String(story)] = String(article.name); }
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return { ids: ids, stories: stories, where: where };
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/* Inhaltsobjekte (Textrahmen, Bildrahmen, Gruppen) auf Dokumentseiten ausserhalb jedes Artikels; Artefakte zaehlen nicht. */
|
|
571
|
+
function idcUncoveredItems(doc, coverage) {
|
|
572
|
+
var out = [];
|
|
573
|
+
var reported = {};
|
|
574
|
+
var items = idcAllItemsForAccessibility(doc);
|
|
575
|
+
var i, item, name, chain, j, covered, story, options, skip;
|
|
576
|
+
for (i = 0; i < items.length; i++) {
|
|
577
|
+
item = items[i];
|
|
578
|
+
if (IDC.isOnParentSpread(item)) { continue; }
|
|
579
|
+
name = IDC.className(item);
|
|
580
|
+
if (name !== 'TextFrame' && name !== 'Group' && !idcIsGraphicFrame(item)) { continue; }
|
|
581
|
+
if (idcItemPageName(item) === null) { continue; }
|
|
582
|
+
options = idcExportOptions(item);
|
|
583
|
+
if (options !== null && idcAccEnumName(IDC_TAG_TYPES, idcTagTypeEnum(), options.applyTagType) === 'artifact') { continue; }
|
|
584
|
+
covered = coverage.ids[String(item.id)] === true;
|
|
585
|
+
story = idcStoryIdOf(item);
|
|
586
|
+
if (!covered && story !== null && IDC.hasOwn(coverage.stories, String(story))) { covered = true; }
|
|
587
|
+
skip = false;
|
|
588
|
+
chain = idcAncestorClasses(item);
|
|
589
|
+
for (j = 0; j < chain.length && !covered; j++) {
|
|
590
|
+
if (chain[j].name === 'Character' || chain[j].name === 'InsertionPoint' || chain[j].name === 'Story') { covered = true; break; }
|
|
591
|
+
if (chain[j].name === 'Spread' || chain[j].name === 'Page') { break; }
|
|
592
|
+
try {
|
|
593
|
+
if (coverage.ids[String(chain[j].item.id)] === true) { covered = true; }
|
|
594
|
+
if (reported[String(chain[j].item.id)] === true) { skip = true; }
|
|
595
|
+
} catch (e) { /* weiter */ }
|
|
596
|
+
}
|
|
597
|
+
if (covered || skip) { continue; }
|
|
598
|
+
reported[String(item.id)] = true;
|
|
599
|
+
out.push({ frame_id: Number(item.id), kind: idcIsGraphicFrame(item) ? 'image' : IDC.itemType(item), page: idcItemPageName(item) });
|
|
600
|
+
}
|
|
601
|
+
return out;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
IDC.register('list_articles', { needs_document: true, read_only: true, ruler: 'none' },
|
|
605
|
+
function (ctx) {
|
|
606
|
+
var doc = ctx.doc;
|
|
607
|
+
var out = [];
|
|
608
|
+
var i, result, uncovered;
|
|
609
|
+
for (i = 0; i < doc.articles.length; i++) { out.push(idcDescribeArticle(doc.articles.item(i), i)); }
|
|
610
|
+
result = {
|
|
611
|
+
articles: out,
|
|
612
|
+
count: out.length,
|
|
613
|
+
use_for_tagged_pdf: idcStructureOrderIsArticles(doc)
|
|
614
|
+
};
|
|
615
|
+
if (ctx.args.include_uncovered === true) {
|
|
616
|
+
uncovered = idcUncoveredItems(doc, idcArticleCoverage(doc));
|
|
617
|
+
result.uncovered = uncovered.length > 500 ? uncovered.slice(0, 500) : uncovered;
|
|
618
|
+
result.uncovered_total = uncovered.length;
|
|
619
|
+
}
|
|
620
|
+
if (out.length > 0 && result.use_for_tagged_pdf === false) {
|
|
621
|
+
ctx.warn('The articles are not used for the reading order of a tagged PDF; define_article (use_for_tagged_pdf true) switches it on.');
|
|
622
|
+
}
|
|
623
|
+
return result;
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
IDC.register('define_article', { needs_document: true, read_only: false, ruler: 'none' },
|
|
627
|
+
function (ctx) {
|
|
628
|
+
var args = ctx.args;
|
|
629
|
+
var doc = ctx.doc;
|
|
630
|
+
var name = args.name;
|
|
631
|
+
var position = idcGiven(args.position) ? String(args.position) : null;
|
|
632
|
+
var found, article, isNew = false, items = [], seen = {}, storiesSeen = {}, i, item, cls, story, coverage, ref, orderBefore, orderAfter, index;
|
|
633
|
+
|
|
634
|
+
if (typeof name !== 'string' || idcTrim(name) === '') { IDC.fail('invalid_argument', 'name is required.', null); }
|
|
635
|
+
if (position !== null && position !== 'start' && position !== 'end' && position !== 'before' && position !== 'after') {
|
|
636
|
+
IDC.fail('invalid_argument', 'position must be start, end, before or after.', { position: position });
|
|
637
|
+
}
|
|
638
|
+
if ((position === 'before' || position === 'after') && (typeof args.relative_to !== 'string' || args.relative_to === '')) {
|
|
639
|
+
IDC.fail('invalid_argument', 'position ' + position + ' needs relative_to (the name of another article).', null);
|
|
640
|
+
}
|
|
641
|
+
if (idcGiven(args.relative_to) && position !== 'before' && position !== 'after') {
|
|
642
|
+
IDC.fail('invalid_argument', 'relative_to belongs to position before or after.', null);
|
|
643
|
+
}
|
|
644
|
+
if (idcGiven(args['export']) && typeof args['export'] !== 'boolean') { IDC.fail('invalid_argument', 'export must be true or false.', null); }
|
|
645
|
+
if (idcGiven(args.use_for_tagged_pdf) && typeof args.use_for_tagged_pdf !== 'boolean') { IDC.fail('invalid_argument', 'use_for_tagged_pdf must be true or false.', null); }
|
|
646
|
+
if (idcGiven(args.relative_to)) {
|
|
647
|
+
if (String(args.relative_to) === name) { IDC.fail('invalid_argument', 'relative_to must name another article.', null); }
|
|
648
|
+
if (idcArticleByName(doc, args.relative_to) === null) {
|
|
649
|
+
IDC.fail('not_found', 'No article "' + args.relative_to + '".', { relative_to: args.relative_to, available: idcArticleNames(doc) });
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
found = idcArticleByName(doc, name);
|
|
654
|
+
if (found === null && !idcGiven(args.frame_ids)) {
|
|
655
|
+
IDC.fail('invalid_argument', 'A new article needs frame_ids (its objects in reading order).', { name: name });
|
|
656
|
+
}
|
|
657
|
+
if (idcGiven(args.frame_ids)) {
|
|
658
|
+
if (!IDC.isArray(args.frame_ids) || args.frame_ids.length === 0) { IDC.fail('invalid_argument', 'frame_ids must be a non-empty list of object ids in reading order.', null); }
|
|
659
|
+
coverage = idcArticleCoverage(doc);
|
|
660
|
+
for (i = 0; i < args.frame_ids.length; i++) {
|
|
661
|
+
if (seen[String(args.frame_ids[i])] === true) {
|
|
662
|
+
IDC.fail('invalid_argument', 'Object ' + args.frame_ids[i] + ' is named twice in frame_ids.', { code: 'duplicate_frame_id', frame_id: args.frame_ids[i] });
|
|
663
|
+
}
|
|
664
|
+
seen[String(args.frame_ids[i])] = true;
|
|
665
|
+
item = idcFrameArg(doc, { frame_id: args.frame_ids[i] }, 'frame_id').item;
|
|
666
|
+
cls = IDC.className(item);
|
|
667
|
+
if (idcIsAnchored(item)) {
|
|
668
|
+
IDC.fail('invalid_argument', 'Object ' + args.frame_ids[i] + ' is anchored in text; InDesign does not take it as an article member. It follows its text in the PDF – add the text frame instead.',
|
|
669
|
+
{ code: 'anchored_object', frame_id: args.frame_ids[i] });
|
|
670
|
+
}
|
|
671
|
+
if (IDC.isOnParentSpread(item)) {
|
|
672
|
+
ctx.warn('Object ' + args.frame_ids[i] + ' lies on a parent page (Musterseite); it is read in the article on every page that uses it. Usually such objects should be artifacts.');
|
|
673
|
+
}
|
|
674
|
+
story = idcStoryIdOf(item);
|
|
675
|
+
if (story !== null) {
|
|
676
|
+
if (storiesSeen[String(story)] === true) {
|
|
677
|
+
ctx.warn('Object ' + args.frame_ids[i] + ' continues a story that is already in the list; a text frame brings its whole story (all threaded frames).');
|
|
678
|
+
}
|
|
679
|
+
storiesSeen[String(story)] = true;
|
|
680
|
+
}
|
|
681
|
+
if (coverage.ids[String(item.id)] === true && coverage.where[String(item.id)] !== name) {
|
|
682
|
+
ctx.warn('Object ' + args.frame_ids[i] + ' is also a member of article "' + coverage.where[String(item.id)] + '"; it would be read twice.');
|
|
683
|
+
} else if (story !== null && IDC.hasOwn(coverage.stories, String(story)) && coverage.stories[String(story)] !== name) {
|
|
684
|
+
ctx.warn('The story of object ' + args.frame_ids[i] + ' is also in article "' + coverage.stories[String(story)] + '"; it would be read twice.');
|
|
685
|
+
}
|
|
686
|
+
if (cls === 'Group' || idcIsInGroup(item)) { /* erlaubt (Beleg 08) */ }
|
|
687
|
+
items.push(item);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
orderBefore = idcStructureOrderIsArticles(doc);
|
|
692
|
+
try {
|
|
693
|
+
if (found === null) {
|
|
694
|
+
article = doc.articles.add(name, idcGiven(args['export']) ? args['export'] : true);
|
|
695
|
+
isNew = true;
|
|
696
|
+
} else {
|
|
697
|
+
article = found.article;
|
|
698
|
+
if (idcGiven(args['export'])) { article.articleExportStatus = args['export']; }
|
|
699
|
+
}
|
|
700
|
+
if (items.length > 0) {
|
|
701
|
+
for (i = article.articleMembers.length - 1; i >= 0; i--) { article.articleMembers.item(i).remove(); }
|
|
702
|
+
for (i = 0; i < items.length; i++) {
|
|
703
|
+
try {
|
|
704
|
+
article.articleMembers.add(items[i]);
|
|
705
|
+
} catch (eAdd) {
|
|
706
|
+
IDC.fail('invalid_state', 'InDesign does not take object ' + Number(items[i].id) + ' into the article: ' + String(eAdd.message ? eAdd.message : eAdd),
|
|
707
|
+
{ code: 'member_rejected', frame_id: Number(items[i].id) });
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (position === 'start') { article.move(LocationOptions.AT_BEGINNING); }
|
|
712
|
+
else if (position === 'end') { article.move(LocationOptions.AT_END); }
|
|
713
|
+
else if (position === 'before' || position === 'after') {
|
|
714
|
+
ref = idcArticleByName(doc, args.relative_to).article;
|
|
715
|
+
article.move(position === 'before' ? LocationOptions.BEFORE : LocationOptions.AFTER, ref);
|
|
716
|
+
}
|
|
717
|
+
/* Entscheidung des Anbieters 11.11.2026 (E-117 Nachtrag 3): sonst wirken Artikel im PDF nicht. */
|
|
718
|
+
if (args.use_for_tagged_pdf !== false) { idcSetStructureOrder(doc, true); }
|
|
719
|
+
else { idcSetStructureOrder(doc, false); }
|
|
720
|
+
} catch (err) {
|
|
721
|
+
if (isNew && article) { try { article.remove(); } catch (eRm) { /* bleibt */ } }
|
|
722
|
+
if (err && err.isIdcError) { throw err; }
|
|
723
|
+
IDC.fail('script_error', 'InDesign could not define article "' + name + '": ' + String(err.message ? err.message : err), { name: name });
|
|
724
|
+
}
|
|
725
|
+
orderAfter = idcStructureOrderIsArticles(doc);
|
|
726
|
+
index = idcArticleByName(doc, name).index;
|
|
727
|
+
if (items.length > 0 && article.articleMembers.length < items.length) {
|
|
728
|
+
ctx.warn('InDesign took ' + article.articleMembers.length + ' of ' + items.length + ' objects into the article.');
|
|
729
|
+
}
|
|
730
|
+
ctx.changes = { articles: [name] };
|
|
731
|
+
return {
|
|
732
|
+
article: idcDescribeArticle(article, index),
|
|
733
|
+
created: isNew,
|
|
734
|
+
use_for_tagged_pdf: orderAfter,
|
|
735
|
+
use_for_tagged_pdf_changed: orderBefore !== orderAfter,
|
|
736
|
+
article_names: idcArticleNames(doc)
|
|
737
|
+
};
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
IDC.register('delete_article', { needs_document: true, read_only: false, ruler: 'none' },
|
|
741
|
+
function (ctx) {
|
|
742
|
+
var doc = ctx.doc;
|
|
743
|
+
var name = ctx.args.name;
|
|
744
|
+
var found;
|
|
745
|
+
if (typeof name !== 'string' || name === '') { IDC.fail('invalid_argument', 'name is required.', null); }
|
|
746
|
+
found = idcArticleByName(doc, name);
|
|
747
|
+
if (found === null) {
|
|
748
|
+
IDC.fail('not_found', 'No article "' + name + '".', { name: name, available: idcArticleNames(doc) });
|
|
749
|
+
}
|
|
750
|
+
try { found.article.remove(); } catch (e) {
|
|
751
|
+
IDC.fail('script_error', 'InDesign could not delete article "' + name + '": ' + String(e.message ? e.message : e), { name: name });
|
|
752
|
+
}
|
|
753
|
+
if (doc.articles.length === 0 && idcStructureOrderIsArticles(doc) === true) {
|
|
754
|
+
ctx.warn('No article is left, but the reading order of a tagged PDF is still set to articles; define an article or the PDF has no reading order from articles.');
|
|
755
|
+
}
|
|
756
|
+
ctx.changes = { deleted_articles: [name] };
|
|
757
|
+
return { deleted: name, article_names: idcArticleNames(doc), count: doc.articles.length, use_for_tagged_pdf: idcStructureOrderIsArticles(doc) };
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
/* --------------------------------------------------------------- Sprachen */
|
|
761
|
+
|
|
762
|
+
/*
|
|
763
|
+
* PDF-Sprache (Entscheidung des Anbieters 11.11.2026, E-117 Nachtrag 5): InDesign
|
|
764
|
+
* reicht jeden Text als /Lang durch (Beleg 11_sprachproben: "Quatsch"). Gilt
|
|
765
|
+
* nur der Name einer InDesign-Sprache oder ein BCP-47-Kuerzel (de, de-DE),
|
|
766
|
+
* dessen Sprache (und Region) InDesign kennt.
|
|
767
|
+
*/
|
|
768
|
+
function idcLanguageTable() {
|
|
769
|
+
var names = [], icu = [], list, i, every;
|
|
770
|
+
try {
|
|
771
|
+
list = app.languagesWithVendors;
|
|
772
|
+
every = list.everyItem();
|
|
773
|
+
names = every.name;
|
|
774
|
+
icu = every.icuLocaleName;
|
|
775
|
+
if (!IDC.isArray(names) || !IDC.isArray(icu)) { throw new Error('no arrays'); }
|
|
776
|
+
} catch (e) {
|
|
777
|
+
names = []; icu = [];
|
|
778
|
+
try {
|
|
779
|
+
for (i = 0; i < list.length; i++) { names.push(String(list.item(i).name)); icu.push(String(list.item(i).icuLocaleName)); }
|
|
780
|
+
} catch (e2) { /* leer */ }
|
|
781
|
+
}
|
|
782
|
+
return { names: names, icu: icu };
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function idcPdfLanguageCheck(value) {
|
|
786
|
+
var table, i, text, match, lang, region, locale, parts;
|
|
787
|
+
if (typeof value !== 'string' || idcTrim(value) === '') { return { ok: false, reason: 'empty' }; }
|
|
788
|
+
text = idcTrim(value);
|
|
789
|
+
table = idcLanguageTable();
|
|
790
|
+
for (i = 0; i < table.names.length; i++) {
|
|
791
|
+
if (String(table.names[i]) === text) {
|
|
792
|
+
if (String(table.icu[i]) === 'en_US_POSIX' || text.charAt(0) === '[') { return { ok: false, reason: 'no_language' }; }
|
|
793
|
+
return { ok: true, value: text, name: text, locale: String(table.icu[i]).replace('_', '-') };
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
match = text.match(/^([A-Za-z]{2,3})(?:[-_]([A-Za-z]{2}|[0-9]{3}))?$/);
|
|
797
|
+
if (!match) { return { ok: false, reason: 'unknown' }; }
|
|
798
|
+
lang = match[1].toLowerCase();
|
|
799
|
+
region = match[2] ? match[2].toUpperCase() : null;
|
|
800
|
+
for (i = 0; i < table.icu.length; i++) {
|
|
801
|
+
locale = String(table.icu[i]);
|
|
802
|
+
if (locale === 'en_US_POSIX') { continue; }
|
|
803
|
+
parts = locale.split('_');
|
|
804
|
+
if (parts[0].toLowerCase() !== lang) { continue; }
|
|
805
|
+
if (region !== null && (parts.length < 2 || parts[1].toUpperCase() !== region)) { continue; }
|
|
806
|
+
return { ok: true, value: region === null ? lang : lang + '-' + region, name: String(table.names[i]), locale: region === null ? lang : lang + '-' + region };
|
|
807
|
+
}
|
|
808
|
+
return { ok: false, reason: 'unknown' };
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function idcPdfLanguageArg(value, field) {
|
|
812
|
+
var check = idcPdfLanguageCheck(value);
|
|
813
|
+
if (!check.ok) {
|
|
814
|
+
IDC.fail('invalid_argument', field + ' "' + value + '" is no language InDesign knows. Pass a language code such as de-DE, en-GB or fr, or the name InDesign shows (e.g. "Deutsch: 2006 Rechtschreibreform").',
|
|
815
|
+
{ code: 'unknown_pdf_language', value: value });
|
|
816
|
+
}
|
|
817
|
+
return check;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function idcLanguageIsMissing(language) {
|
|
821
|
+
var name, icu;
|
|
822
|
+
if (!idcPresent(language)) { return true; }
|
|
823
|
+
try { name = String(language.name); } catch (e) { name = ''; }
|
|
824
|
+
try { icu = String(language.icuLocaleName); } catch (e2) { icu = ''; }
|
|
825
|
+
return name === '' || name.charAt(0) === '[' || icu === 'en_US_POSIX';
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/* ------------------------------------------------------------ Prüfwerkzeug */
|
|
829
|
+
|
|
830
|
+
/*
|
|
831
|
+
* Ueberschriftsverdacht nach dem Formatnamen (ohne Hauswerte: allgemeine
|
|
832
|
+
* Woerter, mehrsprachig). Bewusst ohne zusammengesetzten regulaeren
|
|
833
|
+
* Ausdruck: ExtendScript wertete /(^|[^a-z])(h[1-6]|title|...)([^a-z]|$)|.../i
|
|
834
|
+
* fuer "[Einf. Abs.]" als Treffer (Live-Fund Sitzung 82, InDesign 21.6).
|
|
835
|
+
*/
|
|
836
|
+
var IDC_HEADING_WORDS = { h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, head: 1, heading: 1, headline: 1, title: 1, titel: 1, titre: 1, titolo: 1, titulo: 1, kop: 1, kopje: 1 };
|
|
837
|
+
var IDC_HEADING_PARTS = ['heading', 'headline', 'berschrift', 'zwischentitel', 'hauptzeile'];
|
|
838
|
+
|
|
839
|
+
function idcLooksLikeHeading(name) {
|
|
840
|
+
var lower = String(name).toLowerCase();
|
|
841
|
+
var words = [], word = '', i, c, code;
|
|
842
|
+
for (i = 0; i < IDC_HEADING_PARTS.length; i++) {
|
|
843
|
+
if (lower.indexOf(IDC_HEADING_PARTS[i]) >= 0) { return true; }
|
|
844
|
+
}
|
|
845
|
+
for (i = 0; i <= lower.length; i++) {
|
|
846
|
+
c = i < lower.length ? lower.charAt(i) : ' ';
|
|
847
|
+
code = c.charCodeAt(0);
|
|
848
|
+
if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || code >= 192) { word += c; }
|
|
849
|
+
else if (word !== '') { words.push(word); word = ''; }
|
|
850
|
+
}
|
|
851
|
+
for (i = 0; i < words.length; i++) {
|
|
852
|
+
if (IDC.hasOwn(IDC_HEADING_WORDS, words[i]) || words[i] === 't\u00edtulo') { return true; }
|
|
853
|
+
}
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/* Werte von everyItem(): live ein Array (Beleg 14); ein Verweis auf mehrere Objekte liefert sie ueber getElements. */
|
|
858
|
+
function idcEveryValues(value) {
|
|
859
|
+
if (IDC.isArray(value)) { return value; }
|
|
860
|
+
try {
|
|
861
|
+
if (value && value.getElements) { return value.getElements(); }
|
|
862
|
+
} catch (e) { /* einzelner Wert */ }
|
|
863
|
+
return [value];
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
function idcUsedParagraphStyles(doc) {
|
|
867
|
+
var used = {}, list = [], stories, s, styles, j, tables, t, cellStyles, c, style, id;
|
|
868
|
+
try { stories = doc.stories; } catch (e) { return list; }
|
|
869
|
+
for (s = 0; s < stories.length; s++) {
|
|
870
|
+
try { styles = idcEveryValues(stories.item(s).paragraphs.everyItem().appliedParagraphStyle); } catch (e1) { styles = []; }
|
|
871
|
+
for (j = 0; j < styles.length; j++) {
|
|
872
|
+
style = styles[j];
|
|
873
|
+
try { id = String(style.id); } catch (e2) { continue; }
|
|
874
|
+
if (!used[id]) { used[id] = true; list.push(style); }
|
|
875
|
+
}
|
|
876
|
+
try { tables = stories.item(s).tables; } catch (e3) { tables = null; }
|
|
877
|
+
if (tables === null) { continue; }
|
|
878
|
+
for (t = 0; t < tables.length; t++) {
|
|
879
|
+
try { cellStyles = idcEveryValues(tables.item(t).cells.everyItem().paragraphs.everyItem().appliedParagraphStyle); } catch (e4) { cellStyles = []; }
|
|
880
|
+
for (c = 0; c < cellStyles.length; c++) {
|
|
881
|
+
style = cellStyles[c];
|
|
882
|
+
try { id = String(style.id); } catch (e5) { continue; }
|
|
883
|
+
if (!used[id]) { used[id] = true; list.push(style); }
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return list;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function idcVisibleSwatch(value) {
|
|
891
|
+
var name;
|
|
892
|
+
if (!idcPresent(value) || idcIsNothing(value)) { return false; }
|
|
893
|
+
try { name = String(value.name); } catch (e) { return true; }
|
|
894
|
+
return !(name === 'None' || name === '[None]' || name === 'Ohne' || name === '[Ohne]' || name === '');
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/* Textrahmen mit sichtbarer Kontur oder Flaeche: InDesign 21.6 taggt beides nicht (Live-Probe Sitzung 82). */
|
|
898
|
+
function idcFramePaintsPath(item) {
|
|
899
|
+
var stroke = false, fill = false, weight = 0;
|
|
900
|
+
try { weight = Number(item.strokeWeight); } catch (e) { weight = 0; }
|
|
901
|
+
try { stroke = weight > 0 && idcVisibleSwatch(item.strokeColor); } catch (e2) { stroke = false; }
|
|
902
|
+
try { fill = idcVisibleSwatch(item.fillColor); } catch (e3) { fill = false; }
|
|
903
|
+
return stroke || fill;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
function idcTableHasStrokes(table) {
|
|
907
|
+
var every, props = ['topEdgeStrokeWeight', 'bottomEdgeStrokeWeight', 'leftEdgeStrokeWeight', 'rightEdgeStrokeWeight'];
|
|
908
|
+
var p, values, i;
|
|
909
|
+
try { every = table.cells.everyItem(); } catch (e) { return true; }
|
|
910
|
+
for (p = 0; p < props.length; p++) {
|
|
911
|
+
try { values = every[props[p]]; } catch (e2) { return true; }
|
|
912
|
+
if (!IDC.isArray(values)) { values = [values]; }
|
|
913
|
+
for (i = 0; i < values.length; i++) {
|
|
914
|
+
if (Number(values[i]) > 0) { return true; }
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function idcAccTableFrameId(table) {
|
|
921
|
+
var frames;
|
|
922
|
+
try {
|
|
923
|
+
frames = table.parent;
|
|
924
|
+
if (IDC.className(frames) === 'TextFrame') { return Number(frames.id); }
|
|
925
|
+
return Number(frames.textContainers[0].id);
|
|
926
|
+
} catch (e) { return null; }
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function idcAppMajorVersion() {
|
|
930
|
+
try { return parseInt(String(app.version), 10); } catch (e) { return null; }
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/*
|
|
934
|
+
* Befunde zur Barrierefreiheit. opts: pdf_language (geplante Sprache,
|
|
935
|
+
* geprueft oder null), pdf_language_source, artifact_untagged_paths (beim
|
|
936
|
+
* Export geplant). Gemeinsam fuer check_accessibility und die PDF/UA-Kennung
|
|
937
|
+
* in export_pdf/export_interactive_pdf (E-121).
|
|
938
|
+
*/
|
|
939
|
+
function idcAccessibilityFindings(doc, opts) {
|
|
940
|
+
var findings = [];
|
|
941
|
+
var summary = {};
|
|
942
|
+
var items = idcAllItemsForAccessibility(doc);
|
|
943
|
+
var i, item, entry, cls, styles, style, tag, name, basedOnTag, stories, s, tables, t, table, coverage, uncovered, version;
|
|
944
|
+
var images = 0, imagesOk = 0, untaggedPaths = 0, withoutTag = [], titleText = '', language = null;
|
|
945
|
+
|
|
946
|
+
function add(check, severity, hint, extra) {
|
|
947
|
+
var f = { check: check, severity: severity, page: null, object_id: null, hint: hint };
|
|
948
|
+
var k;
|
|
949
|
+
if (extra) { for (k in extra) { if (IDC.hasOwn(extra, k)) { f[k] = extra[k]; } } }
|
|
950
|
+
findings.push(f);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/* Bilder und Gruppen */
|
|
954
|
+
for (i = 0; i < items.length; i++) {
|
|
955
|
+
item = items[i];
|
|
956
|
+
cls = IDC.className(item);
|
|
957
|
+
if (idcIsGraphicFrame(item)) {
|
|
958
|
+
entry = idcDescribeAltText(item);
|
|
959
|
+
if (entry.on_parent) {
|
|
960
|
+
if (entry.tag !== 'artifact' && entry.problem !== null) {
|
|
961
|
+
add('parent_page_image', 'warning', 'Image on a parent page without alternative text: mark it as artifact (set_alt_text tag artifact) or give it a text.', { page: entry.page, object_id: entry.frame_id });
|
|
962
|
+
}
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
images += 1;
|
|
966
|
+
if (entry.problem === null) { imagesOk += 1; continue; }
|
|
967
|
+
if (entry.problem === 'missing') {
|
|
968
|
+
add('alt_text_missing', 'error', 'Image without alternative text: set_alt_text with a text, an XMP source that has a value, or source decorative.', { page: entry.page, object_id: entry.frame_id });
|
|
969
|
+
} else if (entry.problem === 'decorative_not_artifact') {
|
|
970
|
+
add('decorative_not_artifact', 'error', 'Decorative image that is no artifact: it is exported as a figure without alternative text – set_alt_text source decorative (sets the artifact too).', { page: entry.page, object_id: entry.frame_id });
|
|
971
|
+
} else if (entry.problem === 'hidden_by_group') {
|
|
972
|
+
add('alt_text_hidden_by_group', 'warning', 'The image lies in group ' + entry.covered_by_group_id + ', which has its own alternative text; the PDF shows the group as one figure and this image is not read.', { page: entry.page, object_id: entry.frame_id });
|
|
973
|
+
} else if (entry.problem === 'unverified') {
|
|
974
|
+
add('alt_text_unverified', 'warning', 'The alternative text comes from an XMP field of the image file that could not be read here; check it in the PDF.', { page: entry.page, object_id: entry.frame_id });
|
|
975
|
+
}
|
|
976
|
+
} else if (cls === 'Group') {
|
|
977
|
+
entry = idcDescribeAltText(item);
|
|
978
|
+
if (entry.tag === 'artifact') {
|
|
979
|
+
add('group_artifact_no_effect', 'warning', 'Group marked as artifact: InDesign ignores this for groups; mark each member instead.', { page: entry.page, object_id: entry.frame_id });
|
|
980
|
+
}
|
|
981
|
+
} else if (cls === 'TextFrame' && !IDC.isOnParentSpread(item) && idcFramePaintsPath(item)) {
|
|
982
|
+
untaggedPaths += 1;
|
|
983
|
+
if (opts.artifact_untagged_paths !== true) {
|
|
984
|
+
add('path_not_tagged', 'error', 'Text frame with stroke or fill: InDesign writes them as untagged paths (checkers report "path object not tagged"). A tagged export marks them as artifacts unless artifact_untagged_paths is false; otherwise draw the stroke or fill with a separate rectangle.', { page: idcItemPageName(item), object_id: Number(item.id) });
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
summary.images = images;
|
|
989
|
+
summary.images_ok = imagesOk;
|
|
990
|
+
|
|
991
|
+
/* Tabellen */
|
|
992
|
+
version = idcAppMajorVersion();
|
|
993
|
+
try { stories = doc.stories; } catch (eS) { stories = null; }
|
|
994
|
+
summary.tables = 0;
|
|
995
|
+
if (stories !== null) {
|
|
996
|
+
for (s = 0; s < stories.length; s++) {
|
|
997
|
+
try { tables = stories.item(s).tables; } catch (eT) { tables = null; }
|
|
998
|
+
if (tables === null) { continue; }
|
|
999
|
+
for (t = 0; t < tables.length; t++) {
|
|
1000
|
+
table = tables.item(t);
|
|
1001
|
+
summary.tables += 1;
|
|
1002
|
+
if (Number(table.headerRowCount) === 0) {
|
|
1003
|
+
add('table_without_header_row', 'warning', 'Table without header row: its cells are exported as TD without TH – a data table needs a header row (Tabellenkopf).', { object_id: idcAccTableFrameId(table) });
|
|
1004
|
+
} else if (version !== null && version < 21) {
|
|
1005
|
+
add('table_header_scope', 'warning', 'InDesign before 2026 writes header cells without /Scope; add it in Acrobat or export with InDesign 2026.', { object_id: idcAccTableFrameId(table) });
|
|
1006
|
+
}
|
|
1007
|
+
if (idcTableHasStrokes(table)) {
|
|
1008
|
+
untaggedPaths += 1;
|
|
1009
|
+
if (opts.artifact_untagged_paths !== true) {
|
|
1010
|
+
add('path_not_tagged', 'error', 'Table with cell strokes: InDesign writes them as untagged paths. A tagged export marks them as artifacts unless artifact_untagged_paths is false.', { object_id: idcAccTableFrameId(table) });
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
summary.objects_with_untagged_paths = untaggedPaths;
|
|
1017
|
+
|
|
1018
|
+
/* Formate */
|
|
1019
|
+
styles = idcUsedParagraphStyles(doc);
|
|
1020
|
+
for (i = 0; i < styles.length; i++) {
|
|
1021
|
+
style = styles[i];
|
|
1022
|
+
try { name = String(style.name); } catch (eN) { continue; }
|
|
1023
|
+
tag = idcReadPdfTag(style);
|
|
1024
|
+
if (tag !== '' && tag !== null) { continue; }
|
|
1025
|
+
withoutTag.push(name);
|
|
1026
|
+
basedOnTag = null;
|
|
1027
|
+
try { if (style.basedOn && typeof style.basedOn !== 'string') { basedOnTag = idcReadPdfTag(style.basedOn); } } catch (eB) { basedOnTag = null; }
|
|
1028
|
+
if (idcLooksLikeHeading(name) || (basedOnTag !== null && /^H/.test(basedOnTag))) {
|
|
1029
|
+
add('style_without_pdf_tag', 'error', 'Paragraph style "' + name + '" looks like a heading but has no PDF tag; it is exported as P. Set pdf_tag (H1 to H6) with define_paragraph_style – tags are not inherited through based_on.' + (basedOnTag ? ' Its parent style has ' + basedOnTag + '.' : ''), { style: name });
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
summary.paragraph_styles_used = styles.length;
|
|
1033
|
+
summary.styles_without_pdf_tag = withoutTag;
|
|
1034
|
+
|
|
1035
|
+
/* Titel und Sprache */
|
|
1036
|
+
try { titleText = String(doc.metadataPreferences.documentTitle); } catch (eTi) { titleText = ''; }
|
|
1037
|
+
/* Titel, den der Export erst nach bestandener Pruefung schreibt (InDesign nimmt "" nicht an, ein Zurueckstellen geht nicht). */
|
|
1038
|
+
if (typeof opts.planned_title === 'string' && opts.planned_title !== '') { titleText = opts.planned_title; }
|
|
1039
|
+
summary.document_title = titleText;
|
|
1040
|
+
if (idcTrim(titleText) === '') {
|
|
1041
|
+
add('document_title_missing', 'error', 'The document has no title (File > File Info); export with title or set it there.');
|
|
1042
|
+
}
|
|
1043
|
+
try { language = doc.textDefaults.appliedLanguage; } catch (eL) { language = null; }
|
|
1044
|
+
summary.text_language = idcLanguageIsMissing(language) ? null : String(language.name);
|
|
1045
|
+
if (idcLanguageIsMissing(language)) {
|
|
1046
|
+
add('text_language_missing', 'error', 'The text defaults have no language; screen readers and the PDF structure take the language from the text.');
|
|
1047
|
+
}
|
|
1048
|
+
summary.pdf_language = opts.pdf_language;
|
|
1049
|
+
summary.pdf_language_source = opts.pdf_language_source;
|
|
1050
|
+
if (opts.pdf_language === null || opts.pdf_language === '') {
|
|
1051
|
+
add('pdf_language_missing', 'error', 'No PDF language: pass pdf_language when exporting (e.g. de-DE).');
|
|
1052
|
+
} else if (!idcPdfLanguageCheck(opts.pdf_language).ok) {
|
|
1053
|
+
add('pdf_language_invalid', 'error', 'The PDF language "' + opts.pdf_language + '" (' + opts.pdf_language_source + ') is no language InDesign knows; InDesign would write it unchecked.');
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/* Lesereihenfolge */
|
|
1057
|
+
summary.articles = doc.articles.length;
|
|
1058
|
+
summary.use_for_tagged_pdf = idcStructureOrderIsArticles(doc);
|
|
1059
|
+
if (doc.articles.length === 0) {
|
|
1060
|
+
add('no_articles', 'warning', 'No articles: the reading order of the tagged PDF follows the XML structure or the layout. Define it with define_article.');
|
|
1061
|
+
} else {
|
|
1062
|
+
if (summary.use_for_tagged_pdf === false) {
|
|
1063
|
+
add('articles_not_used_for_pdf', 'error', 'Articles exist, but the tagged PDF does not use them for the reading order: call define_article with use_for_tagged_pdf true.');
|
|
1064
|
+
}
|
|
1065
|
+
coverage = idcArticleCoverage(doc);
|
|
1066
|
+
uncovered = idcUncoveredItems(doc, coverage);
|
|
1067
|
+
for (i = 0; i < uncovered.length; i++) {
|
|
1068
|
+
add('item_outside_articles', 'warning', 'This ' + uncovered[i].kind + ' is in no article and no artifact; it is left out of the reading order or placed at the end.', { page: uncovered[i].page, object_id: uncovered[i].frame_id });
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
return { findings: findings, summary: summary };
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/* Exportsprache fuer die Pruefung: Aufruf, sonst Vorgabe bzw. Exporteinstellung. */
|
|
1075
|
+
function idcPlannedPdfLanguage(args, interactive) {
|
|
1076
|
+
var preset = null;
|
|
1077
|
+
if (idcGiven(args.pdf_language)) {
|
|
1078
|
+
return { value: idcPdfLanguageArg(args.pdf_language, 'pdf_language').value, source: 'call' };
|
|
1079
|
+
}
|
|
1080
|
+
if (interactive) {
|
|
1081
|
+
try { return { value: String(app.interactivePDFExportPreferences.defaultDocumentLanguage), source: 'interactive export settings' }; } catch (e) { return { value: null, source: 'interactive export settings' }; }
|
|
1082
|
+
}
|
|
1083
|
+
if (idcGiven(args.preset) && String(args.preset) !== '') {
|
|
1084
|
+
preset = idcPdfPresetByName(String(args.preset));
|
|
1085
|
+
if (preset === null) {
|
|
1086
|
+
IDC.fail('not_found', 'PDF preset "' + args.preset + '" not found.', { preset: args.preset, available: idcPdfPresetNames() });
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (preset === null) { preset = idcHighQualityPreset(); }
|
|
1090
|
+
try {
|
|
1091
|
+
if (preset !== null) { return { value: String(preset.defaultDocumentLanguage), source: 'preset "' + String(preset.name) + '"' }; }
|
|
1092
|
+
return { value: String(app.pdfExportPreferences.defaultDocumentLanguage), source: 'export settings' };
|
|
1093
|
+
} catch (e2) { return { value: null, source: 'preset' }; }
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
var IDC_MANUAL_STEPS = [
|
|
1097
|
+
'Check the exported PDF with a PDF/UA checker and listen to it with a screen reader.',
|
|
1098
|
+
'Captions: InDesign tags them as P; the link between figure and caption (Caption inside Figure) is set in Acrobat.',
|
|
1099
|
+
'Complex tables (merged cells, row headers) need Headers/Scope in Acrobat.',
|
|
1100
|
+
'Contrast and information given by colour alone are design questions this check cannot answer.'
|
|
1101
|
+
];
|
|
1102
|
+
|
|
1103
|
+
IDC.register('check_accessibility', { needs_document: true, read_only: true, ruler: 'none' },
|
|
1104
|
+
function (ctx) {
|
|
1105
|
+
var args = ctx.args;
|
|
1106
|
+
var doc = ctx.doc;
|
|
1107
|
+
var maxFindings = args.max_findings === undefined || args.max_findings === null ? 200 : Number(args.max_findings);
|
|
1108
|
+
var planned = idcPlannedPdfLanguage(args, args.interactive === true);
|
|
1109
|
+
var result = idcAccessibilityFindings(doc, { pdf_language: planned.value, pdf_language_source: planned.source, artifact_untagged_paths: args.artifact_untagged_paths !== false });
|
|
1110
|
+
var errors = 0, warnings = 0, i, steps = IDC_MANUAL_STEPS.slice(0);
|
|
1111
|
+
|
|
1112
|
+
if (isNaN(maxFindings) || maxFindings < 1 || maxFindings > 1000) {
|
|
1113
|
+
IDC.fail('invalid_argument', 'max_findings must be between 1 and 1000.', { max_findings: args.max_findings });
|
|
1114
|
+
}
|
|
1115
|
+
for (i = 0; i < result.findings.length; i++) {
|
|
1116
|
+
if (result.findings[i].severity === 'error') { errors += 1; } else { warnings += 1; }
|
|
1117
|
+
}
|
|
1118
|
+
steps.push(errors === 0
|
|
1119
|
+
? 'PDF/UA identifier: InDesign does not write it. export_pdf or export_interactive_pdf with pdfua_identifier true adds it after this check – it claims conformance that this check cannot fully confirm (see the steps above).'
|
|
1120
|
+
: 'PDF/UA identifier: only after this check reports no errors (pdfua_identifier true in export_pdf or export_interactive_pdf), or in Acrobat.');
|
|
1121
|
+
return {
|
|
1122
|
+
passed: errors === 0,
|
|
1123
|
+
errors: errors,
|
|
1124
|
+
warnings: warnings,
|
|
1125
|
+
findings: result.findings.length > maxFindings ? result.findings.slice(0, maxFindings) : result.findings,
|
|
1126
|
+
findings_total: result.findings.length,
|
|
1127
|
+
findings_truncated: result.findings.length > maxFindings,
|
|
1128
|
+
summary: result.summary,
|
|
1129
|
+
manual_steps: steps
|
|
1130
|
+
};
|
|
1131
|
+
});
|