All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

0.5.1 - 2026-03-18

Fixed

  • Deflated Explicit VR Little Endian now uses raw RFC1951 deflate/inflate semantics instead of zlib-wrapped payload handling
  • Streaming and eager parsing now fail closed on truncated encapsulated pixel fragments and other malformed binary edge cases that previously leaked partial or ambiguous values
  • Dicom.Value and Dicom.Json now reject partial or malformed DS, IS, numeric VR, PN, and AT values instead of silently truncating, coercing, or exporting invalid JSON
  • DICOM JSON now respects VM=1 text VRs, validates AT tuples on export, decodes multi-valued AT values correctly, and keeps compressed Pixel Data roundtrips writable when transfer syntax context is known
  • Writer validation now rejects malformed encapsulated Pixel Data structure, invalid Basic Offset Tables, and other Pixel Data / transfer syntax mismatches before serialization
  • Dicom.PixelData and Dicom.DataSet.decoded_value/2 now fail safely on malformed numeric metadata instead of raising or leaking undecoded raw bytes

Changed

  • Dicom.Json.from_map/2 now normalizes compressed Pixel Data to {:encapsulated, fragments} only when transfer syntax context is provided, and otherwise preserves raw binary payloads
  • De-identification markers and option handling are now more honest and predictable, including support for direct boolean option flags and LO-safe DeidentificationMethod values
  • Documentation now matches the hardened JSON boundary behavior, transfer syntax context requirements, de-identification option styles, and current release versioning
  • Implementation version name updated to DICOM_0.5.1

0.5.0 - 2026-03-17

Fixed

  • Malformed input paths across the eager parser, streaming parser, DICOM JSON decoder, and pixel-data helpers now return structured errors instead of raising exceptions
  • Top-level sequence de-identification now respects tag action rules instead of always preserving sequence containers
  • File Meta Information validation now rejects empty or malformed required UID values before serialization
  • Dicom.PixelData now handles parser-produced {:encapsulated, fragments} values, computes native frame sizes correctly for bit-packed data, and rejects ambiguous multi-frame encapsulated splitting
  • Dicom.P10.Stream.parse_file/2 now honors the documented :read_ahead option

Changed

  • Dicom.Json.from_map/2 is now strict about malformed typed values and requires an explicit bulk_data_resolver: to materialize BulkDataURI content
  • De-identification private-tag retention is now named and documented honestly via retain_private_tags; retain_safe_private remains as a compatibility alias for retaining all private tags
  • Character set support now explicitly rejects ISO 2022 escape-sequence switching instead of implying code-extension support
  • README and API docs were updated to match the stricter JSON, pixel-data, de-identification, and streaming behavior
  • Implementation version name updated to DICOM_0.5.0

0.4.5 - 2026-03-17

Fixed

  • DICOM JSON multi-value handling now preserves array semantics for PN, AT, string VRs, and numeric VRs
  • DICOM JSON encoder now omits Group Length attributes from output
  • Dicom.write_file/2 now returns writer errors instead of raising MatchError
  • Enumerable slice implementation for Dicom.DataSet updated for Elixir 1.18's 3-arity slice contract
  • Dicom.SOPClass is now the canonical public module name

Changed

  • De-identification profile flags now affect supported tag groups instead of leaving most options inert
  • Release docs corrected to match current registry counts and current behavior
  • README and HexDocs now describe scope more precisely around PS3.10, compressed transfer syntaxes, DICOM JSON, and de-identification limits
  • Hex package and HexDocs extras now include SECURITY.md, and source links default to the release tag
  • Implementation version name updated to DICOM_0.4.5

0.4.2 - 2026-03-17

Removed

  • Dead code in P10.Stream.Parser: unreachable error branches guarded by ensure_bytes (9 branches across read_tag, read_uint32, read_next_data_element, read_item_elements_until_delimiter_eager, read_item_elements_bounded_eager, read_fragments_eager)
  • Dead code in CharacterSet: unreachable iso8859_to_unicode(byte, 1) clause (ISO_IR 100 maps to :latin1, never to {:iso8859, 1})
  • Dead code in PixelData: unreachable error branch in frame/2 (extract_encapsulated_frames always returns {:ok, ...})

0.4.0 - 2026-03-17

Added

  • VR metadataVR.all/0, VR.description/1, VR.max_length/1, VR.fixed_length?/1 backed by compile-time maps per PS3.5 Table 6.2-1.
  • Tag utilitiesTag.parse/1 for "(GGGG,EEEE)" and "GGGGEEEE" formats, Tag.from_keyword/1 for dictionary keyword lookup, Tag.repeating?/1 for 50XX/60XX/7FXX repeating groups.
  • Date/time conversionValue.to_date/1, Value.to_time/1, Value.to_datetime/1, Value.from_date/1, Value.from_time/1, Value.from_datetime/1 for bidirectional DICOM DA/TM/DT ↔ Elixir Date/Time/DateTime. Supports partial TM, fractional seconds, and timezone offsets.
  • DataSet ergonomicsDataSet.has_tag?/2, DataSet.get/3 (with default), DataSet.fetch/2, DataSet.merge/2, DataSet.from_list/1, DataSet.decoded_value/2 (VR-aware decode).
  • Protocol implementationsInspect for DataElement (shows tag, VR, truncated value; SQ item count; encapsulated fragment count) and DataSet (element count, patient, modality). Enumerable for DataSet (sorted by tag, file_meta merged). Access behaviour for DataSet (ds[tag], get_in, put_in, pop).

Changed

  • Expanded test suite to 1000+ tests (35 doctests, 7 property tests) at 97%+ coverage
  • UID.transfer_syntax?/1 now uses TransferSyntax.known?/1 for authoritative O(1) registry lookup instead of prefix matching (fixes false positives for UIDs like Storage Commitment 1.2.840.10008.1.20.1)
  • Implementation version name updated from DICOM_EX_0.1.1 to DICOM_0.4.0
  • Updated AGENTS.md with current test counts and architecture diagram

0.3.0 - 2026-03-17

Added

  • 62 transfer syntaxes — expanded from 29 to all 49 active + 13 retired DICOM transfer syntaxes. New fields: retired and fragmentable flags. New functions: retired?/1, fragmentable?/1, active/0.
  • Complete PS3.6 tag dictionary — 5,035 entries generated from innolitics attributes.json via mix dicom.gen_dictionary. Keyword reverse lookup with find_by_keyword/1, retired tag detection with retired?/1, and expanded repeating group support (50XX curve, 60XX overlay, 7FXX waveform).
  • DICOM JSON model (Dicom.Json) — encode/decode DataSets to/from the DICOM JSON format (PS3.18 Annex F.2) for DICOMweb. Supports all VR types, Person Name component groups, sequence recursion, InlineBinary (base64), and BulkDataURI callbacks. Zero runtime dependencies — produces plain maps.
  • Pixel data frame extraction (Dicom.PixelData) — extract individual frames from native and encapsulated pixel data (PS3.5 Section A.4). O(1) native frame access via binary_part/3. Encapsulated support with Basic Offset Table, fragment-per-frame convention, and single-frame concatenation. Functions: frames/1, frame/2, frame_count/1, encapsulated?/1.
  • De-identification / anonymization (Dicom.DeIdentification) — Basic Application Level Confidentiality Profile (PS3.15 Table E.1-1) with action codes D, Z, X, K, C, U. Consistent UID replacement across elements. Configurable profile with 10 boolean options. Recursive SQ processing and private tag stripping.
  • ISO 8859-{2..9} full lookup tables — replaced identity mapping with correct Unicode codepoint tables for all 8 ISO 8859 variants.
  • JIS X 0201 character setISO_IR 13 support for Roman + half-width Katakana decoding.
  • Mix task mix dicom.gen_dictionary for regenerating the tag dictionary from the innolitics DICOM standard JSON source.

Changed

  • Expanded test suite to 621 tests (5 doctests, 4 property tests) at 91%+ coverage
  • VR module exposes string_vrs/0, numeric_vrs/0, binary_vrs/0 list accessors
  • Transfer syntax all/0 and active/0 cached at compile time

Fixed

  • De-identification :D action now computes correct byte_size for dummy values
  • ISO 8859-4 table: removed erroneous 0xE3 mapping
  • Character set decoding DRYed with shared decode_bytewise/3

0.2.0 - 2026-03-17

Added

  • Streaming DICOM P10 parser via Dicom.P10.Stream with lazy, event-based parsing
  • BEAM DICOM library comparison table in the README covering licensing, features, test coverage, and CI status
  • Comprehensive PS3.6 data dictionary — expanded from ~95 hand-written entries to 5032 entries generated from the DICOM standard. Overlay repeating group (60XX) support included. Previously-unknown tags (especially SQ) now resolve correctly in Implicit VR parsing.
  • Specific Character Set support (Dicom.CharacterSet) — decodes text values according to (0008,0005). Supports default repertoire, ISO_IR 100 (Latin-1), ISO 8859-2 through 9, and ISO_IR 192 (UTF-8). Returns explicit errors for unsupported charsets instead of silently producing incorrect text.
  • Expanded transfer syntax registry — from 9 to 29 entries. Added JPEG-LS, JPEG 2000 Part 2, MPEG-2/4, HEVC/H.265, HTJ2K, and JPIP transfer syntaxes.
  • Interoperability test suite exercising unknown TS rejection, expanded dictionary in implicit VR parsing, rich multi-element roundtrips across all four uncompressed transfer syntaxes, encapsulated pixel data with compressed TSes, and character set integration through the parse pipeline.

Fixed

  • Reject serialization when required File Meta Information is missing
  • Encode numeric values according to their VR width instead of forcing 32-bit little-endian output
  • Respect transfer syntax endianness for numeric value encoding and decoding
  • Preserve leading spaces for padded text VRs such as LT and UT
  • Return {:error, :unexpected_end} for truncated defined-length sequence payloads instead of crashing the parser
  • Tighten UID validation for invalid root arcs

Changed

  • Strict transfer syntax policyTransferSyntax.encoding/2 now returns {:error, :unknown_transfer_syntax} for unrecognized UIDs instead of silently falling back to Explicit VR Little Endian. Use encoding(uid, lenient: true) to opt in to the old fallback behavior. Reader and writer now propagate this error.
  • Expanded test coverage to 448 tests (streaming, big-endian numeric value decode/encode, eager path, edge cases, property-based equivalence) at 91%+ coverage

0.1.0 - 2026-03-17

Added

  • P10 file parsing with Dicom.parse/1 and Dicom.parse_file/1
  • P10 file writing with Dicom.write/1 and Dicom.write_file/2
  • Data set creation and manipulation via Dicom.DataSet
  • DICOM data dictionary (PS3.6) with tag lookup via Dicom.Dictionary.Registry
  • Tag constants for common clinical attributes via Dicom.Tag
  • UID constants for SOP Classes and Transfer Syntaxes via Dicom.UID
  • UID generation and validation
  • VR-aware value encoding and decoding via Dicom.Value
  • Transfer syntax support:
    • Implicit VR Little Endian
    • Explicit VR Little Endian
    • Explicit VR Big Endian (retired)
    • Deflated Explicit VR Little Endian
  • Sequence support (SQ) with defined and undefined length items
  • Encapsulated pixel data with fragment parsing
  • File Meta Information validation per PS3.10 Section 7.1
  • Preamble validation and sanitization (PS3.10 Section 7.5)
  • Data Set Trailing Padding support (FFFC,FFFC)

Changed

  • Expanded PS3.10 support with compliance tests, deflated transfer syntax support, sequence handling, additional VR coverage, and Explicit VR Big Endian support
  • Hardened parsing and validation around malformed input, edge cases, and file meta handling
  • Optimized hot paths in the reader, writer, transfer syntax registry, and VR utilities
  • Prepared the project for public release with CI, licensing, contribution policy, security policy, and open-source documentation

Performance

  • Performance benchmarks (parse, write, roundtrip, VR lookup)
  • 100% test coverage across all 12 modules (259 tests)
  • Property-based tests with StreamData for encode/decode roundtrips