@json-to-office/jto-ops 1.4.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +82 -1
- package/dist/index.js +391 -60
- package/dist/index.js.map +1 -1
- package/package.json +8 -6
package/dist/index.js
CHANGED
|
@@ -892,6 +892,36 @@ function toGenerationWarnings(raw) {
|
|
|
892
892
|
}
|
|
893
893
|
}));
|
|
894
894
|
}
|
|
895
|
+
function preparedThemeLabel(prepared) {
|
|
896
|
+
const label = prepared.metadata?.themeLabel;
|
|
897
|
+
return typeof label === "string" ? label : void 0;
|
|
898
|
+
}
|
|
899
|
+
var PREPARED_SOURCE = Symbol("jto.prepared.source");
|
|
900
|
+
var PREPARED_WARNINGS = Symbol("jto.prepared.warnings");
|
|
901
|
+
function stampPrepared(prepared, source, warnings) {
|
|
902
|
+
return Object.assign(prepared, {
|
|
903
|
+
[PREPARED_SOURCE]: source,
|
|
904
|
+
[PREPARED_WARNINGS]: warnings
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
function preparedFor(prepared, document) {
|
|
908
|
+
if (!prepared) return void 0;
|
|
909
|
+
const source = prepared[PREPARED_SOURCE];
|
|
910
|
+
return source !== void 0 && source === document ? prepared : void 0;
|
|
911
|
+
}
|
|
912
|
+
function warningKey(warning) {
|
|
913
|
+
return `${warning.component}|${warning.context?.code ?? ""}|${warning.message}`;
|
|
914
|
+
}
|
|
915
|
+
function withoutPreparedWarnings(warnings, prepared) {
|
|
916
|
+
const emitted = prepared?.[PREPARED_WARNINGS];
|
|
917
|
+
if (!emitted || emitted.length === 0) return warnings;
|
|
918
|
+
const seen = new Set(emitted.map(warningKey));
|
|
919
|
+
return warnings.filter((warning) => !seen.has(warningKey(warning)));
|
|
920
|
+
}
|
|
921
|
+
function documentRenderer(document) {
|
|
922
|
+
const renderer = document?.renderer;
|
|
923
|
+
return typeof renderer === "string" ? renderer : void 0;
|
|
924
|
+
}
|
|
895
925
|
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
896
926
|
function safeThemeKey(name) {
|
|
897
927
|
return name && !UNSAFE_KEYS.has(name) ? name : "custom";
|
|
@@ -967,12 +997,24 @@ var DocxFormatAdapter = class {
|
|
|
967
997
|
async generateBuffer(json, options) {
|
|
968
998
|
const core = await import("@json-to-office/core-docx");
|
|
969
999
|
const parsed = typeof json === "string" ? JSON.parse(json) : json;
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
parsed
|
|
973
|
-
resolved.requested,
|
|
974
|
-
resolved.customThemes
|
|
1000
|
+
const prepared = preparedFor(
|
|
1001
|
+
options.prepared?.format === "docx" ? options.prepared : void 0,
|
|
1002
|
+
parsed
|
|
975
1003
|
);
|
|
1004
|
+
let docDefinition;
|
|
1005
|
+
let customThemes;
|
|
1006
|
+
if (prepared) {
|
|
1007
|
+
docDefinition = prepared.model.authored;
|
|
1008
|
+
} else {
|
|
1009
|
+
const resolved = await this.resolveThemes(options);
|
|
1010
|
+
const normalized = withRequestedTheme(
|
|
1011
|
+
parsed,
|
|
1012
|
+
resolved.requested,
|
|
1013
|
+
resolved.customThemes
|
|
1014
|
+
);
|
|
1015
|
+
docDefinition = normalized.document;
|
|
1016
|
+
customThemes = normalized.customThemes;
|
|
1017
|
+
}
|
|
976
1018
|
const services = buildDocxServices();
|
|
977
1019
|
const warnings = [];
|
|
978
1020
|
const buffer = await core.generateBufferFromJson(docDefinition, {
|
|
@@ -986,10 +1028,13 @@ var DocxFormatAdapter = class {
|
|
|
986
1028
|
generatedAt: options.generatedAt,
|
|
987
1029
|
baseDir: options.baseDir,
|
|
988
1030
|
renderer: options.renderer,
|
|
1031
|
+
svgRasterFallback: options.svgRasterFallback,
|
|
1032
|
+
prepared,
|
|
989
1033
|
warnings
|
|
990
1034
|
});
|
|
991
|
-
|
|
992
|
-
|
|
1035
|
+
const emitted = withoutPreparedWarnings(warnings, prepared);
|
|
1036
|
+
emitGenerationWarnings(emitted);
|
|
1037
|
+
options.warnings?.push(...toGenerationWarnings(emitted));
|
|
993
1038
|
return buffer;
|
|
994
1039
|
}
|
|
995
1040
|
async createGenerator(plugins, options) {
|
|
@@ -997,32 +1042,46 @@ var DocxFormatAdapter = class {
|
|
|
997
1042
|
const hasPlugins = plugins.length > 0;
|
|
998
1043
|
const pluginNames = plugins.map((p) => p.name);
|
|
999
1044
|
const services = buildDocxServices();
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1045
|
+
const prepared = !hasPlugins && options.prepared?.format === "docx" ? options.prepared : void 0;
|
|
1046
|
+
const resolved = prepared ? void 0 : await this.resolveThemes(options);
|
|
1047
|
+
const requestedTheme = resolved?.requested;
|
|
1048
|
+
const customThemes = resolved?.customThemes;
|
|
1049
|
+
const themeLabel = prepared ? preparedThemeLabel(prepared) : resolved?.label;
|
|
1005
1050
|
if (!hasPlugins) {
|
|
1051
|
+
let fallbackThemes;
|
|
1052
|
+
const themesFor = () => fallbackThemes ??= resolved ? Promise.resolve(resolved) : this.resolveThemes(options);
|
|
1006
1053
|
return {
|
|
1007
1054
|
generateBuffer: async (document) => {
|
|
1008
1055
|
const parsed = typeof document === "string" ? JSON.parse(document) : document;
|
|
1009
|
-
const
|
|
1056
|
+
const usable = preparedFor(prepared, parsed);
|
|
1057
|
+
const themes = usable ? void 0 : await themesFor();
|
|
1058
|
+
const normalized = usable ? { document: usable.model.authored, customThemes: void 0 } : withRequestedTheme(
|
|
1059
|
+
parsed,
|
|
1060
|
+
themes?.requested,
|
|
1061
|
+
themes?.customThemes
|
|
1062
|
+
);
|
|
1010
1063
|
const warnings = [];
|
|
1011
|
-
const buffer = await core.generateBufferFromJson(
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1064
|
+
const buffer = await core.generateBufferFromJson(
|
|
1065
|
+
normalized.document,
|
|
1066
|
+
{
|
|
1067
|
+
customThemes: normalized.customThemes,
|
|
1068
|
+
services,
|
|
1069
|
+
fonts: options.fonts,
|
|
1070
|
+
validation: {
|
|
1071
|
+
allowUnknownFields: options.validation?.allowUnknownFields
|
|
1072
|
+
},
|
|
1073
|
+
deterministic: options.deterministic,
|
|
1074
|
+
generatedAt: options.generatedAt,
|
|
1075
|
+
baseDir: options.baseDir,
|
|
1076
|
+
renderer: options.renderer,
|
|
1077
|
+
svgRasterFallback: options.svgRasterFallback,
|
|
1078
|
+
prepared: usable,
|
|
1079
|
+
warnings
|
|
1080
|
+
}
|
|
1081
|
+
);
|
|
1082
|
+
const emitted = withoutPreparedWarnings(warnings, usable);
|
|
1083
|
+
emitGenerationWarnings(emitted);
|
|
1084
|
+
options.warnings?.push(...toGenerationWarnings(emitted));
|
|
1026
1085
|
return buffer;
|
|
1027
1086
|
},
|
|
1028
1087
|
hasPlugins: false,
|
|
@@ -1045,7 +1104,8 @@ var DocxFormatAdapter = class {
|
|
|
1045
1104
|
deterministic: options.deterministic,
|
|
1046
1105
|
generatedAt: options.generatedAt,
|
|
1047
1106
|
baseDir: options.baseDir,
|
|
1048
|
-
renderer: options.renderer
|
|
1107
|
+
renderer: options.renderer,
|
|
1108
|
+
svgRasterFallback: options.svgRasterFallback
|
|
1049
1109
|
});
|
|
1050
1110
|
for (const plugin of plugins) {
|
|
1051
1111
|
generator = generator.addComponent(plugin);
|
|
@@ -1065,7 +1125,8 @@ var DocxFormatAdapter = class {
|
|
|
1065
1125
|
deterministic: options.deterministic,
|
|
1066
1126
|
generatedAt: options.generatedAt,
|
|
1067
1127
|
baseDir: options.baseDir,
|
|
1068
|
-
renderer: options.renderer
|
|
1128
|
+
renderer: options.renderer,
|
|
1129
|
+
svgRasterFallback: options.svgRasterFallback
|
|
1069
1130
|
});
|
|
1070
1131
|
emitGenerationWarnings(result.warnings ?? []);
|
|
1071
1132
|
options.warnings?.push(...toGenerationWarnings(result.warnings));
|
|
@@ -1104,6 +1165,64 @@ var DocxFormatAdapter = class {
|
|
|
1104
1165
|
...result.errors.length > 0 && { errors: result.errors }
|
|
1105
1166
|
};
|
|
1106
1167
|
}
|
|
1168
|
+
async analyzeQuality(doc, options = {}) {
|
|
1169
|
+
const core = await import("@json-to-office/core-docx");
|
|
1170
|
+
const parsed = typeof doc === "string" ? JSON.parse(doc) : doc;
|
|
1171
|
+
let prepared = preparedFor(
|
|
1172
|
+
options.prepared?.format === "docx" ? options.prepared : void 0,
|
|
1173
|
+
parsed
|
|
1174
|
+
);
|
|
1175
|
+
if (!prepared) {
|
|
1176
|
+
try {
|
|
1177
|
+
prepared = await this.prepareModel(parsed, options, []);
|
|
1178
|
+
} catch {
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return core.analyzeDocxQuality(prepared?.model.authored ?? parsed, {
|
|
1182
|
+
...prepared && { prepared },
|
|
1183
|
+
renderer: options.renderer ?? documentRenderer(parsed),
|
|
1184
|
+
profile: options.quality?.profile,
|
|
1185
|
+
policy: options.quality?.policy
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
async prepareDocument(doc, options = {}) {
|
|
1189
|
+
const warnings = [];
|
|
1190
|
+
const prepared = await this.prepareModel(doc, options, warnings);
|
|
1191
|
+
emitGenerationWarnings(warnings);
|
|
1192
|
+
options.warnings?.push(...toGenerationWarnings(warnings));
|
|
1193
|
+
return prepared;
|
|
1194
|
+
}
|
|
1195
|
+
/** Prepare into a caller-owned sink; `prepareDocument` owns the reporting. */
|
|
1196
|
+
async prepareModel(doc, options, warnings) {
|
|
1197
|
+
const core = await import("@json-to-office/core-docx");
|
|
1198
|
+
const parsed = typeof doc === "string" ? JSON.parse(doc) : doc;
|
|
1199
|
+
const resolved = await this.resolveThemes(options);
|
|
1200
|
+
const normalized = withRequestedTheme(
|
|
1201
|
+
parsed,
|
|
1202
|
+
resolved.requested,
|
|
1203
|
+
resolved.customThemes
|
|
1204
|
+
);
|
|
1205
|
+
const prepared = core.prepareDocxQualityDocument(
|
|
1206
|
+
normalized.document,
|
|
1207
|
+
{
|
|
1208
|
+
customThemes: normalized.customThemes,
|
|
1209
|
+
fonts: options.fonts,
|
|
1210
|
+
renderer: options.renderer ?? documentRenderer(parsed),
|
|
1211
|
+
warnings
|
|
1212
|
+
}
|
|
1213
|
+
);
|
|
1214
|
+
return stampPrepared(
|
|
1215
|
+
{
|
|
1216
|
+
...prepared,
|
|
1217
|
+
metadata: {
|
|
1218
|
+
...prepared.metadata,
|
|
1219
|
+
...resolved.label && { themeLabel: resolved.label }
|
|
1220
|
+
}
|
|
1221
|
+
},
|
|
1222
|
+
parsed,
|
|
1223
|
+
toGenerationWarnings(warnings)
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1107
1226
|
generateSchema(_options) {
|
|
1108
1227
|
return null;
|
|
1109
1228
|
}
|
|
@@ -1115,6 +1234,10 @@ var DocxFormatAdapter = class {
|
|
|
1115
1234
|
return {};
|
|
1116
1235
|
}
|
|
1117
1236
|
}
|
|
1237
|
+
async getBuiltinThemeValues() {
|
|
1238
|
+
const core = await import("@json-to-office/core-docx");
|
|
1239
|
+
return core.themes || {};
|
|
1240
|
+
}
|
|
1118
1241
|
async resolveTheme(options) {
|
|
1119
1242
|
const core = await import("@json-to-office/core-docx");
|
|
1120
1243
|
const { requested } = await this.resolveThemes(options);
|
|
@@ -1227,12 +1350,24 @@ var PptxFormatAdapter = class {
|
|
|
1227
1350
|
async generateBuffer(json, options) {
|
|
1228
1351
|
const core = await import("@json-to-office/core-pptx");
|
|
1229
1352
|
const parsed = typeof json === "string" ? JSON.parse(json) : json;
|
|
1230
|
-
const
|
|
1231
|
-
|
|
1232
|
-
parsed
|
|
1233
|
-
resolved.requested,
|
|
1234
|
-
resolved.customThemes
|
|
1353
|
+
const prepared = preparedFor(
|
|
1354
|
+
options.prepared?.format === "pptx" ? options.prepared : void 0,
|
|
1355
|
+
parsed
|
|
1235
1356
|
);
|
|
1357
|
+
let docDefinition;
|
|
1358
|
+
let customThemes;
|
|
1359
|
+
if (prepared) {
|
|
1360
|
+
docDefinition = prepared.model.authored;
|
|
1361
|
+
} else {
|
|
1362
|
+
const resolved = await this.resolveThemes(options);
|
|
1363
|
+
const normalized = withRequestedTheme(
|
|
1364
|
+
parsed,
|
|
1365
|
+
resolved.requested,
|
|
1366
|
+
resolved.customThemes
|
|
1367
|
+
);
|
|
1368
|
+
docDefinition = normalized.document;
|
|
1369
|
+
customThemes = normalized.customThemes;
|
|
1370
|
+
}
|
|
1236
1371
|
const services = buildServicesFromEnv();
|
|
1237
1372
|
const result = await core.generateBufferWithWarnings(docDefinition, {
|
|
1238
1373
|
customThemes,
|
|
@@ -1244,11 +1379,15 @@ var PptxFormatAdapter = class {
|
|
|
1244
1379
|
deterministic: options.deterministic,
|
|
1245
1380
|
generatedAt: options.generatedAt,
|
|
1246
1381
|
baseDir: options.baseDir,
|
|
1247
|
-
renderer: options.renderer
|
|
1382
|
+
renderer: options.renderer,
|
|
1383
|
+
prepared
|
|
1248
1384
|
});
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1385
|
+
const emitted = withoutPreparedWarnings(
|
|
1386
|
+
toGenerationWarnings(result.warnings),
|
|
1387
|
+
prepared
|
|
1388
|
+
);
|
|
1389
|
+
emitGenerationWarnings(emitted);
|
|
1390
|
+
options.warnings?.push(...emitted);
|
|
1252
1391
|
return result.buffer;
|
|
1253
1392
|
}
|
|
1254
1393
|
async createGenerator(plugins, options) {
|
|
@@ -1256,31 +1395,46 @@ var PptxFormatAdapter = class {
|
|
|
1256
1395
|
const hasPlugins = plugins.length > 0;
|
|
1257
1396
|
const pluginNames = plugins.map((p) => p.name);
|
|
1258
1397
|
const services = buildServicesFromEnv();
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1398
|
+
const prepared = !hasPlugins && options.prepared?.format === "pptx" ? options.prepared : void 0;
|
|
1399
|
+
const resolved = prepared ? void 0 : await this.resolveThemes(options);
|
|
1400
|
+
const requestedTheme = resolved?.requested;
|
|
1401
|
+
const customThemes = resolved?.customThemes;
|
|
1402
|
+
const themeLabel = prepared ? preparedThemeLabel(prepared) : resolved?.label;
|
|
1264
1403
|
if (!hasPlugins) {
|
|
1404
|
+
let fallbackThemes;
|
|
1405
|
+
const themesFor = () => fallbackThemes ??= resolved ? Promise.resolve(resolved) : this.resolveThemes(options);
|
|
1265
1406
|
return {
|
|
1266
1407
|
generateBuffer: async (document) => {
|
|
1267
1408
|
const parsed = typeof document === "string" ? JSON.parse(document) : document;
|
|
1268
|
-
const
|
|
1269
|
-
const
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1409
|
+
const usable = preparedFor(prepared, parsed);
|
|
1410
|
+
const themes = usable ? void 0 : await themesFor();
|
|
1411
|
+
const normalized = usable ? { document: usable.model.authored, customThemes: void 0 } : withRequestedTheme(
|
|
1412
|
+
parsed,
|
|
1413
|
+
themes?.requested,
|
|
1414
|
+
themes?.customThemes
|
|
1415
|
+
);
|
|
1416
|
+
const result = await core.generateBufferWithWarnings(
|
|
1417
|
+
normalized.document,
|
|
1418
|
+
{
|
|
1419
|
+
customThemes: normalized.customThemes,
|
|
1420
|
+
services,
|
|
1421
|
+
fonts: options.fonts,
|
|
1422
|
+
validation: {
|
|
1423
|
+
allowUnknownFields: options.validation?.allowUnknownFields
|
|
1424
|
+
},
|
|
1425
|
+
deterministic: options.deterministic,
|
|
1426
|
+
generatedAt: options.generatedAt,
|
|
1427
|
+
baseDir: options.baseDir,
|
|
1428
|
+
renderer: options.renderer,
|
|
1429
|
+
prepared: usable
|
|
1430
|
+
}
|
|
1431
|
+
);
|
|
1432
|
+
const warnings = withoutPreparedWarnings(
|
|
1433
|
+
toGenerationWarnings(result.warnings),
|
|
1434
|
+
usable
|
|
1435
|
+
);
|
|
1436
|
+
emitGenerationWarnings(warnings);
|
|
1437
|
+
options.warnings?.push(...warnings);
|
|
1284
1438
|
return result.buffer;
|
|
1285
1439
|
},
|
|
1286
1440
|
hasPlugins: false,
|
|
@@ -1345,6 +1499,66 @@ var PptxFormatAdapter = class {
|
|
|
1345
1499
|
...result.errors.length > 0 && { errors: result.errors }
|
|
1346
1500
|
};
|
|
1347
1501
|
}
|
|
1502
|
+
async analyzeQuality(doc, options = {}) {
|
|
1503
|
+
const core = await import("@json-to-office/core-pptx");
|
|
1504
|
+
const parsed = typeof doc === "string" ? JSON.parse(doc) : doc;
|
|
1505
|
+
let prepared = preparedFor(
|
|
1506
|
+
options.prepared?.format === "pptx" ? options.prepared : void 0,
|
|
1507
|
+
parsed
|
|
1508
|
+
);
|
|
1509
|
+
if (!prepared) {
|
|
1510
|
+
try {
|
|
1511
|
+
prepared = await this.prepareModel(parsed, options, []);
|
|
1512
|
+
} catch {
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
return core.analyzePptxQuality(prepared?.model.authored ?? parsed, {
|
|
1516
|
+
...prepared && { prepared },
|
|
1517
|
+
renderer: options.renderer ?? documentRenderer(parsed),
|
|
1518
|
+
profile: options.quality?.profile,
|
|
1519
|
+
policy: options.quality?.policy
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
async prepareDocument(doc, options = {}) {
|
|
1523
|
+
const warnings = [];
|
|
1524
|
+
const prepared = await this.prepareModel(doc, options, warnings);
|
|
1525
|
+
const normalized = toGenerationWarnings(warnings);
|
|
1526
|
+
emitGenerationWarnings(normalized);
|
|
1527
|
+
options.warnings?.push(...normalized);
|
|
1528
|
+
return prepared;
|
|
1529
|
+
}
|
|
1530
|
+
/** Prepare into a caller-owned sink; `prepareDocument` owns the reporting. */
|
|
1531
|
+
async prepareModel(doc, options, warnings) {
|
|
1532
|
+
const core = await import("@json-to-office/core-pptx");
|
|
1533
|
+
const parsed = typeof doc === "string" ? JSON.parse(doc) : doc;
|
|
1534
|
+
const resolved = await this.resolveThemes(options);
|
|
1535
|
+
const normalized = withRequestedTheme(
|
|
1536
|
+
parsed,
|
|
1537
|
+
resolved.requested,
|
|
1538
|
+
resolved.customThemes
|
|
1539
|
+
);
|
|
1540
|
+
const prepared = core.preparePptxQualityDocument(
|
|
1541
|
+
normalized.document,
|
|
1542
|
+
{
|
|
1543
|
+
customThemes: normalized.customThemes,
|
|
1544
|
+
fonts: options.fonts,
|
|
1545
|
+
services: buildServicesFromEnv(),
|
|
1546
|
+
renderer: options.renderer ?? documentRenderer(parsed),
|
|
1547
|
+
warnings
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
return stampPrepared(
|
|
1551
|
+
{
|
|
1552
|
+
...prepared,
|
|
1553
|
+
metadata: {
|
|
1554
|
+
...prepared.metadata,
|
|
1555
|
+
...resolved.label && { themeLabel: resolved.label }
|
|
1556
|
+
}
|
|
1557
|
+
},
|
|
1558
|
+
parsed,
|
|
1559
|
+
toGenerationWarnings(warnings)
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1348
1562
|
generateSchema(_options) {
|
|
1349
1563
|
return null;
|
|
1350
1564
|
}
|
|
@@ -1356,6 +1570,10 @@ var PptxFormatAdapter = class {
|
|
|
1356
1570
|
return {};
|
|
1357
1571
|
}
|
|
1358
1572
|
}
|
|
1573
|
+
async getBuiltinThemeValues() {
|
|
1574
|
+
const core = await import("@json-to-office/core-pptx");
|
|
1575
|
+
return core.pptxThemes || {};
|
|
1576
|
+
}
|
|
1359
1577
|
async resolveTheme(options) {
|
|
1360
1578
|
const core = await import("@json-to-office/core-pptx");
|
|
1361
1579
|
const themes = core.pptxThemes || {};
|
|
@@ -1448,6 +1666,116 @@ function createAdapter(format) {
|
|
|
1448
1666
|
throw new Error(`Unknown format: ${format}`);
|
|
1449
1667
|
}
|
|
1450
1668
|
}
|
|
1669
|
+
|
|
1670
|
+
// src/pdf-text-geometry.ts
|
|
1671
|
+
import { execFile as execFile2 } from "child_process";
|
|
1672
|
+
import * as path6 from "path";
|
|
1673
|
+
var ENTITIES = {
|
|
1674
|
+
"&": "&",
|
|
1675
|
+
"<": "<",
|
|
1676
|
+
">": ">",
|
|
1677
|
+
""": '"',
|
|
1678
|
+
"'": "'",
|
|
1679
|
+
""": '"',
|
|
1680
|
+
"'": "'"
|
|
1681
|
+
};
|
|
1682
|
+
function decodeEntities(value) {
|
|
1683
|
+
return value.replace(
|
|
1684
|
+
/&(?:amp|lt|gt|quot|apos|#34|#39);/g,
|
|
1685
|
+
(entity) => ENTITIES[entity] ?? entity
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
var PAGE_PATTERN = /<page\s+width="([\d.]+)"\s+height="([\d.]+)">([\s\S]*?)<\/page>/g;
|
|
1689
|
+
var WORD_PATTERN = /<word\s+xMin="(-?[\d.]+)"\s+yMin="(-?[\d.]+)"\s+xMax="(-?[\d.]+)"\s+yMax="(-?[\d.]+)">([\s\S]*?)<\/word>/g;
|
|
1690
|
+
function parsePdfTextBbox(bboxXml) {
|
|
1691
|
+
const pages = [];
|
|
1692
|
+
for (const pageMatch of bboxXml.matchAll(PAGE_PATTERN)) {
|
|
1693
|
+
const words = [];
|
|
1694
|
+
for (const wordMatch of pageMatch[3].matchAll(WORD_PATTERN)) {
|
|
1695
|
+
words.push({
|
|
1696
|
+
xMin: Number(wordMatch[1]),
|
|
1697
|
+
yMin: Number(wordMatch[2]),
|
|
1698
|
+
xMax: Number(wordMatch[3]),
|
|
1699
|
+
yMax: Number(wordMatch[4]),
|
|
1700
|
+
text: decodeEntities(wordMatch[5])
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
pages.push({
|
|
1704
|
+
widthPt: Number(pageMatch[1]),
|
|
1705
|
+
heightPt: Number(pageMatch[2]),
|
|
1706
|
+
words
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
return pages;
|
|
1710
|
+
}
|
|
1711
|
+
function pdftotextCandidates() {
|
|
1712
|
+
const candidates = [];
|
|
1713
|
+
const configured = process.env.PDFTOTEXT_PATH?.trim();
|
|
1714
|
+
if (configured) candidates.push(configured);
|
|
1715
|
+
candidates.push("pdftotext");
|
|
1716
|
+
return [...new Set(candidates)];
|
|
1717
|
+
}
|
|
1718
|
+
async function run(binary, args, timeoutMs) {
|
|
1719
|
+
return new Promise((resolve2, reject) => {
|
|
1720
|
+
execFile2(
|
|
1721
|
+
binary,
|
|
1722
|
+
args,
|
|
1723
|
+
{ timeout: timeoutMs, maxBuffer: 64 * 1024 * 1024 },
|
|
1724
|
+
(error, stdout) => {
|
|
1725
|
+
if (error) reject(error);
|
|
1726
|
+
else resolve2(stdout);
|
|
1727
|
+
}
|
|
1728
|
+
);
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
var pdftotextPromise;
|
|
1732
|
+
async function resolvePdftotext() {
|
|
1733
|
+
if (!pdftotextPromise) {
|
|
1734
|
+
pdftotextPromise = (async () => {
|
|
1735
|
+
for (const candidate of pdftotextCandidates()) {
|
|
1736
|
+
if (candidate.includes(path6.sep)) {
|
|
1737
|
+
try {
|
|
1738
|
+
await run(candidate, ["-v"], 1e4);
|
|
1739
|
+
return candidate;
|
|
1740
|
+
} catch {
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
try {
|
|
1745
|
+
await run(candidate, ["-v"], 1e4);
|
|
1746
|
+
return candidate;
|
|
1747
|
+
} catch (error) {
|
|
1748
|
+
const code = error.code;
|
|
1749
|
+
if (code !== "ENOENT" && code !== "EACCES") return candidate;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
throw new Error(
|
|
1753
|
+
`Text geometry extraction needs pdftotext (poppler), which was not found. Install poppler-utils or set PDFTOTEXT_PATH (searched: ${pdftotextCandidates().join(", ")}).`
|
|
1754
|
+
);
|
|
1755
|
+
})().catch((error) => {
|
|
1756
|
+
pdftotextPromise = void 0;
|
|
1757
|
+
throw error;
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
return pdftotextPromise;
|
|
1761
|
+
}
|
|
1762
|
+
async function pdftotextAvailable() {
|
|
1763
|
+
try {
|
|
1764
|
+
await resolvePdftotext();
|
|
1765
|
+
return true;
|
|
1766
|
+
} catch {
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
async function extractPdfTextGeometry(pdfPath, options = {}) {
|
|
1771
|
+
const binary = await resolvePdftotext();
|
|
1772
|
+
const stdout = await run(
|
|
1773
|
+
binary,
|
|
1774
|
+
["-bbox", pdfPath, "-"],
|
|
1775
|
+
options.timeoutMs ?? 6e4
|
|
1776
|
+
);
|
|
1777
|
+
return parsePdfTextBbox(stdout);
|
|
1778
|
+
}
|
|
1451
1779
|
export {
|
|
1452
1780
|
DocxFormatAdapter,
|
|
1453
1781
|
FontconfigStager,
|
|
@@ -1460,8 +1788,11 @@ export {
|
|
|
1460
1788
|
createLibreOfficePptxBatchRasterizer,
|
|
1461
1789
|
createLibreOfficePptxRasterizer,
|
|
1462
1790
|
emitDiagnostic,
|
|
1791
|
+
extractPdfTextGeometry,
|
|
1463
1792
|
getFontStager,
|
|
1464
1793
|
getRasterizerCacheStats,
|
|
1794
|
+
parsePdfTextBbox,
|
|
1795
|
+
pdftotextAvailable,
|
|
1465
1796
|
runWithDiagnosticSink,
|
|
1466
1797
|
stderrDiagnosticSink
|
|
1467
1798
|
};
|