@ubercode/dcmtk 0.12.1 → 0.13.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/servers.cjs CHANGED
@@ -2312,6 +2312,7 @@ var Dcm2jsonOptionsSchema = zod.z.object({
2312
2312
  signal: zod.z.instanceof(AbortSignal).optional(),
2313
2313
  directOnly: zod.z.boolean().optional(),
2314
2314
  charsetAssume: zod.z.string().min(1).optional(),
2315
+ charsetFallback: zod.z.string().min(1).optional(),
2315
2316
  verbosity: zod.z.enum(["verbose", "debug"]).optional()
2316
2317
  }).strict().optional();
2317
2318
  var VERBOSITY_FLAGS = { verbose: "-v", debug: "-d" };
@@ -2325,27 +2326,34 @@ function buildXmlOpts(options) {
2325
2326
  const result = {};
2326
2327
  if (options?.verbosity !== void 0) result["verbosity"] = options.verbosity;
2327
2328
  if (options?.charsetAssume !== void 0) result["charsetAssume"] = options.charsetAssume;
2329
+ if (options?.charsetFallback !== void 0) result["charsetFallback"] = options.charsetFallback;
2328
2330
  return result;
2329
2331
  }
2332
+ function isCharsetError(stderrOutput) {
2333
+ return stderrOutput.includes("convert character encoding") || stderrOutput.includes("Illegal byte sequence");
2334
+ }
2335
+ async function runXmlAndParse(binary, args, execOpts) {
2336
+ const xmlResult = await execCommand(binary, args, execOpts);
2337
+ if (!xmlResult.ok) return err(xmlResult.error);
2338
+ if (xmlResult.value.exitCode !== 0) {
2339
+ return err(createToolError("dcm2xml", args, xmlResult.value.exitCode, xmlResult.value.stderr));
2340
+ }
2341
+ const jsonResult = xmlToJson(xmlResult.value.stdout);
2342
+ if (!jsonResult.ok) return err(jsonResult.error);
2343
+ return ok({ data: jsonResult.value, source: "xml" });
2344
+ }
2330
2345
  async function tryXmlPath(inputPath, timeoutMs, signal, opts) {
2331
2346
  const xmlBinary = resolveBinary("dcm2xml");
2332
- if (!xmlBinary.ok) {
2333
- return err(xmlBinary.error);
2334
- }
2347
+ if (!xmlBinary.ok) return err(xmlBinary.error);
2335
2348
  const charsetArgs = opts?.charsetAssume !== void 0 ? ["+Ca", opts.charsetAssume] : [];
2336
2349
  const xmlArgs = [...buildVerbosityArgs(opts?.verbosity), ...charsetArgs, "-nat", inputPath];
2337
- const xmlResult = await execCommand(xmlBinary.value, xmlArgs, { timeoutMs, signal });
2338
- if (!xmlResult.ok) {
2339
- return err(xmlResult.error);
2350
+ const execOpts = signal !== void 0 ? { timeoutMs, signal } : { timeoutMs };
2351
+ const result = await runXmlAndParse(xmlBinary.value, xmlArgs, execOpts);
2352
+ if (!result.ok && opts?.charsetFallback !== void 0 && isCharsetError(result.error.message)) {
2353
+ const fallbackArgs = [...buildVerbosityArgs(opts.verbosity), "+Ca", opts.charsetFallback, "-nat", inputPath];
2354
+ return runXmlAndParse(xmlBinary.value, fallbackArgs, execOpts);
2340
2355
  }
2341
- if (xmlResult.value.exitCode !== 0) {
2342
- return err(createToolError("dcm2xml", xmlArgs, xmlResult.value.exitCode, xmlResult.value.stderr));
2343
- }
2344
- const jsonResult = xmlToJson(xmlResult.value.stdout);
2345
- if (!jsonResult.ok) {
2346
- return err(jsonResult.error);
2347
- }
2348
- return ok({ data: jsonResult.value, source: "xml" });
2356
+ return result;
2349
2357
  }
2350
2358
  async function tryDirectPath(inputPath, timeoutMs, signal, verbosity) {
2351
2359
  const jsonBinary = resolveBinary("dcm2json");
@@ -2530,7 +2538,8 @@ var DicomInstance = class _DicomInstance {
2530
2538
  const jsonResult = await dcm2json(path2, {
2531
2539
  timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
2532
2540
  signal: options?.signal,
2533
- charsetAssume: options?.charsetAssume
2541
+ charsetAssume: options?.charsetAssume,
2542
+ charsetFallback: options?.charsetFallback
2534
2543
  });
2535
2544
  if (!jsonResult.ok) return err(jsonResult.error);
2536
2545
  const datasetResult = DicomDataset.fromJson(jsonResult.value.data);
@@ -2848,6 +2857,12 @@ var DicomReceiverOptionsSchema = zod.z.object({
2848
2857
  filenameMode: zod.z.enum(["default", "unique", "short-unique", "system-time"]).optional(),
2849
2858
  filenameExtension: zod.z.string().min(1).optional(),
2850
2859
  storageMode: zod.z.enum(["normal", "bit-preserving", "ignore"]).optional(),
2860
+ instanceOpenOptions: zod.z.object({
2861
+ timeoutMs: zod.z.number().int().positive().optional(),
2862
+ signal: zod.z.instanceof(AbortSignal).optional(),
2863
+ charsetAssume: zod.z.string().min(1).optional(),
2864
+ charsetFallback: zod.z.string().min(1).optional()
2865
+ }).strict().optional(),
2851
2866
  signal: zod.z.instanceof(AbortSignal).optional()
2852
2867
  }).strict().refine((data) => (data.minPoolSize ?? DEFAULT_MIN_POOL_SIZE) <= (data.maxPoolSize ?? DEFAULT_MAX_POOL_SIZE), {
2853
2868
  message: "minPoolSize must be <= maxPoolSize"
@@ -3017,6 +3032,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3017
3032
  __publicField(this, "minPoolSize");
3018
3033
  __publicField(this, "maxPoolSize");
3019
3034
  __publicField(this, "connectionTimeoutMs");
3035
+ __publicField(this, "resolvedInstanceOpenOptions");
3020
3036
  __publicField(this, "workers", /* @__PURE__ */ new Map());
3021
3037
  __publicField(this, "tcpServer");
3022
3038
  __publicField(this, "associationCounter", 0);
@@ -3028,6 +3044,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3028
3044
  this.minPoolSize = options.minPoolSize ?? DEFAULT_MIN_POOL_SIZE;
3029
3045
  this.maxPoolSize = options.maxPoolSize ?? DEFAULT_MAX_POOL_SIZE;
3030
3046
  this.connectionTimeoutMs = options.connectionTimeoutMs ?? DEFAULT_CONNECTION_TIMEOUT_MS;
3047
+ this.resolvedInstanceOpenOptions = { charsetFallback: "Latin1", ...options.instanceOpenOptions };
3031
3048
  }
3032
3049
  // -----------------------------------------------------------------------
3033
3050
  // Public API
@@ -3494,7 +3511,7 @@ var DicomReceiver = class _DicomReceiver extends events.EventEmitter {
3494
3511
  /** Parses a DICOM file and emits INSTANCE_RECEIVED or INSTANCE_ERROR. */
3495
3512
  async parseAndEmitInstance(worker, ctx) {
3496
3513
  try {
3497
- const openResult = await DicomInstance.open(ctx.filePath);
3514
+ const openResult = await DicomInstance.open(ctx.filePath, this.resolvedInstanceOpenOptions);
3498
3515
  if (!openResult.ok) {
3499
3516
  worker.recordInstanceError();
3500
3517
  this.emit("INSTANCE_ERROR", { error: openResult.error, thrown: false, ...ctx });