@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.
@@ -462,6 +462,8 @@ interface FileIOOptions {
462
462
  interface DicomOpenOptions extends FileIOOptions {
463
463
  /** Assume the specified character set when SpecificCharacterSet (0008,0005) is absent. Maps to dcm2xml `+Ca`. */
464
464
  readonly charsetAssume?: string | undefined;
465
+ /** Fallback charset to retry with when UTF-8 conversion fails due to illegal byte sequences. Maps to dcm2xml `+Ca`. When set, a charset conversion failure triggers an automatic retry with this charset assumed. `'Latin1'` is recommended — it maps every byte 0x00-0xFF to a valid character, so conversion never fails. */
466
+ readonly charsetFallback?: string | undefined;
465
467
  }
466
468
 
467
469
  /**
@@ -462,6 +462,8 @@ interface FileIOOptions {
462
462
  interface DicomOpenOptions extends FileIOOptions {
463
463
  /** Assume the specified character set when SpecificCharacterSet (0008,0005) is absent. Maps to dcm2xml `+Ca`. */
464
464
  readonly charsetAssume?: string | undefined;
465
+ /** Fallback charset to retry with when UTF-8 conversion fails due to illegal byte sequences. Maps to dcm2xml `+Ca`. When set, a charset conversion failure triggers an automatic retry with this charset assumed. `'Latin1'` is recommended — it maps every byte 0x00-0xFF to a valid character, so conversion never fails. */
466
+ readonly charsetFallback?: string | undefined;
465
467
  }
466
468
 
467
469
  /**
package/dist/dicom.cjs CHANGED
@@ -30792,6 +30792,7 @@ var Dcm2jsonOptionsSchema = zod.z.object({
30792
30792
  signal: zod.z.instanceof(AbortSignal).optional(),
30793
30793
  directOnly: zod.z.boolean().optional(),
30794
30794
  charsetAssume: zod.z.string().min(1).optional(),
30795
+ charsetFallback: zod.z.string().min(1).optional(),
30795
30796
  verbosity: zod.z.enum(["verbose", "debug"]).optional()
30796
30797
  }).strict().optional();
30797
30798
  var VERBOSITY_FLAGS = { verbose: "-v", debug: "-d" };
@@ -30805,27 +30806,34 @@ function buildXmlOpts(options) {
30805
30806
  const result = {};
30806
30807
  if (options?.verbosity !== void 0) result["verbosity"] = options.verbosity;
30807
30808
  if (options?.charsetAssume !== void 0) result["charsetAssume"] = options.charsetAssume;
30809
+ if (options?.charsetFallback !== void 0) result["charsetFallback"] = options.charsetFallback;
30808
30810
  return result;
30809
30811
  }
30812
+ function isCharsetError(stderrOutput) {
30813
+ return stderrOutput.includes("convert character encoding") || stderrOutput.includes("Illegal byte sequence");
30814
+ }
30815
+ async function runXmlAndParse(binary, args, execOpts) {
30816
+ const xmlResult = await execCommand(binary, args, execOpts);
30817
+ if (!xmlResult.ok) return err(xmlResult.error);
30818
+ if (xmlResult.value.exitCode !== 0) {
30819
+ return err(createToolError("dcm2xml", args, xmlResult.value.exitCode, xmlResult.value.stderr));
30820
+ }
30821
+ const jsonResult = xmlToJson(xmlResult.value.stdout);
30822
+ if (!jsonResult.ok) return err(jsonResult.error);
30823
+ return ok({ data: jsonResult.value, source: "xml" });
30824
+ }
30810
30825
  async function tryXmlPath(inputPath, timeoutMs, signal, opts) {
30811
30826
  const xmlBinary = resolveBinary("dcm2xml");
30812
- if (!xmlBinary.ok) {
30813
- return err(xmlBinary.error);
30814
- }
30827
+ if (!xmlBinary.ok) return err(xmlBinary.error);
30815
30828
  const charsetArgs = opts?.charsetAssume !== void 0 ? ["+Ca", opts.charsetAssume] : [];
30816
30829
  const xmlArgs = [...buildVerbosityArgs(opts?.verbosity), ...charsetArgs, "-nat", inputPath];
30817
- const xmlResult = await execCommand(xmlBinary.value, xmlArgs, { timeoutMs, signal });
30818
- if (!xmlResult.ok) {
30819
- return err(xmlResult.error);
30830
+ const execOpts = signal !== void 0 ? { timeoutMs, signal } : { timeoutMs };
30831
+ const result = await runXmlAndParse(xmlBinary.value, xmlArgs, execOpts);
30832
+ if (!result.ok && opts?.charsetFallback !== void 0 && isCharsetError(result.error.message)) {
30833
+ const fallbackArgs = [...buildVerbosityArgs(opts.verbosity), "+Ca", opts.charsetFallback, "-nat", inputPath];
30834
+ return runXmlAndParse(xmlBinary.value, fallbackArgs, execOpts);
30820
30835
  }
30821
- if (xmlResult.value.exitCode !== 0) {
30822
- return err(createToolError("dcm2xml", xmlArgs, xmlResult.value.exitCode, xmlResult.value.stderr));
30823
- }
30824
- const jsonResult = xmlToJson(xmlResult.value.stdout);
30825
- if (!jsonResult.ok) {
30826
- return err(jsonResult.error);
30827
- }
30828
- return ok({ data: jsonResult.value, source: "xml" });
30836
+ return result;
30829
30837
  }
30830
30838
  async function tryDirectPath(inputPath, timeoutMs, signal, verbosity) {
30831
30839
  const jsonBinary = resolveBinary("dcm2json");
@@ -31010,7 +31018,8 @@ var DicomInstance = class _DicomInstance {
31010
31018
  const jsonResult = await dcm2json(path, {
31011
31019
  timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
31012
31020
  signal: options?.signal,
31013
- charsetAssume: options?.charsetAssume
31021
+ charsetAssume: options?.charsetAssume,
31022
+ charsetFallback: options?.charsetFallback
31014
31023
  });
31015
31024
  if (!jsonResult.ok) return err(jsonResult.error);
31016
31025
  const datasetResult = DicomDataset.fromJson(jsonResult.value.data);