dcmspec 0.4.0__tar.gz → 0.4.2__tar.gz

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.
Files changed (49) hide show
  1. {dcmspec-0.4.0 → dcmspec-0.4.2}/CHANGELOG.md +190 -113
  2. {dcmspec-0.4.0 → dcmspec-0.4.2}/LICENSE +201 -201
  3. {dcmspec-0.4.0 → dcmspec-0.4.2}/PKG-INFO +2 -2
  4. {dcmspec-0.4.0 → dcmspec-0.4.2}/README.md +98 -98
  5. {dcmspec-0.4.0 → dcmspec-0.4.2}/pyproject.toml +90 -90
  6. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/__init__.py +1 -1
  7. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/__init__.py +1 -1
  8. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/__init__.py +1 -1
  9. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/dataelements.py +93 -89
  10. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/iodattributes.py +156 -148
  11. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/iodmodules.py +110 -92
  12. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/modattributes.py +293 -289
  13. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/tdwiicontent.py +368 -364
  14. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/uidvalues.py +88 -84
  15. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/upsdimseattributes.py +113 -109
  16. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/cli/upsioddimseattributes.py +334 -330
  17. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/README.md +33 -33
  18. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/__init__.py +9 -9
  19. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/config/README.md +171 -171
  20. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config.json +4 -4
  21. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_debug.json +4 -4
  22. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_example.json +4 -4
  23. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/config/iod_explorer_config_minimal_logging.json +4 -4
  24. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/apps/ui/iod_explorer/iod_explorer.py +989 -989
  25. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/config.py +90 -90
  26. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/csv_table_spec_parser.py +94 -94
  27. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/doc_handler.py +284 -284
  28. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/dom_section_spec_parser.py +223 -187
  29. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/dom_table_spec_parser.py +901 -892
  30. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/dom_utils.py +150 -150
  31. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/iod_spec_builder.py +471 -471
  32. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/iod_spec_printer.py +255 -253
  33. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/json_spec_store.py +112 -110
  34. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/module_registry.py +50 -50
  35. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/module_spec_builder.py +237 -211
  36. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/pdf_doc_handler.py +674 -668
  37. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/progress.py +231 -231
  38. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/section_image_resolver.py +64 -59
  39. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/section_registry.py +53 -53
  40. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/service_attribute_defaults.py +124 -124
  41. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/service_attribute_model.py +230 -230
  42. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/spec_factory.py +526 -473
  43. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/spec_merger.py +536 -536
  44. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/spec_model.py +466 -466
  45. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/spec_parser.py +86 -75
  46. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/spec_printer.py +430 -414
  47. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/spec_store.py +56 -50
  48. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/ups_xhtml_doc_handler.py +117 -117
  49. {dcmspec-0.4.0 → dcmspec-0.4.2}/src/dcmspec/xhtml_doc_handler.py +181 -181
@@ -1,113 +1,190 @@
1
- # Release Notes
2
-
3
- These release notes summarize key changes, improvements, and breaking updates for each version of **dcmspec**.
4
-
5
- ## [0.4.0] - 2026-09-11
6
-
7
- ### Added
8
-
9
- - `DOMSectionSpecParser` parses a DICOM standard section, given its anchor id.
10
- - `ModuleSpecBuilder` builds a module attribute model and resolves the sections it
11
- directly references, caching each one separately and downloading their images.
12
- - `IODSpecBuilder` accepts an optional `module_builder` so each module built as part
13
- of a full IOD has its referenced sections resolved.
14
-
15
- ### Changed
16
-
17
- - **Breaking change:** `XHTMLDocHandler.download`/`PDFDocHandler.download` renamed to
18
- `download_to_cache`. Callers using `load_document` are unaffected.
19
-
20
- ### Fixed
21
-
22
- - `DOMTableSpecParser.get_version` no longer raises `UnboundLocalError` when the document has no
23
- `<div class="titlepage">`; it will return `""` with a warning.
24
- - `SpecModel.exclude_titles` no longer raises `AttributeError` when a model's metadata has no
25
- `column_to_attr` such as a section model.
26
-
27
- ## [0.3.1] - 2026-08-27
28
-
29
- ### Added
30
-
31
- - Reorganized `tests/` into `tests/unit`, `tests/integration`, and `tests/e2e` tiers, and added an opt-in `tests/e2e`
32
- canary suite that downloads and parses real, current DICOM standard tables (Part 3 IOD+modules, Part 6 data
33
- dictionary, Part 4 UPS DIMSE attributes, and a Part 3/Part 6 merge) to catch upstream markup changes between
34
- standard releases. Excluded from the default test run; also runs on a monthly schedule via
35
- `.github/workflows/e2e.yml`.
36
-
37
- ### Fixed
38
-
39
- - `PDFDocHandler.select_tables` now raises `ValueError` when a header cell contains a DICOM tag pattern (e.g. `(300A,0230)`), indicating that a data row was absorbed into the header — typically a `table_header_rowspan` over-count. This fails loudly instead of silently dropping an attribute row. Opt out with `strict_header_check=False` to restore the prior warn-and-continue behavior.
40
- - `PDFDocHandler.concat_tables` now reconstructs page-straddling description cells. When an attribute's description wraps across a PDF page boundary, pdfplumber emits the continuation as an untagged row (empty name and tag, description only) atop the next page's table; this is now merged into the preceding tagged row's description instead of becoming a floating node. Recovers enum values and conditional requirements that were silently lost at page breaks (e.g. HDSS Contour Geometric Type `(3006,0042)` losing its `POINT` / `CLOSED_PLANAR` / `CLOSEDPLANAR_XOR` enumeration, where `CLOSEDPLANAR_XOR` reflects the original DICOM spelling).
41
- - `PDFDocHandler.load_document` / `extract_tables_pdfplumber` now accept a `snap_tolerance_overrides` dict (keyed by 1-indexed page number) to lower pdfplumber's `snap_tolerance` for individual pages. The default (8) snaps two near-coincident header rules together on a few pages, fusing a repeated continuation sub-header (e.g. `Presence | Specific Rules`) into the first data row of the next page; that row then trips the tag-in-header guard (rowspan over-count) and the page cannot be extracted. Lowering the tolerance for just that page (e.g. `{34: 6}`) keeps the two rules distinct so the sub-header is recognized as a header row, recovering the first attribute of the continuation table (e.g. TPPC-Brachy HDR/PDR Source `(0008,1040)`) without disturbing extraction of any other page. This also un-fuses line-snapped "frankenrows" at the source on the affected page; the `concat_tables` split below remains as a downstream safety net for pages that still fuse at the default tolerance.
42
- - `PDFDocHandler.concat_tables` now splits fused "frankenrow" rows back into their constituent attribute rows. pdfplumber's line-snapping can merge two adjacent table rows whose separating rule falls within `snap_tolerance` into one row, newline-joining every column (e.g. tag `(300A,0214)\n(300A,0216)`, type `1\n3`). A legitimate attribute row carries exactly one DICOM tag, so a tag cell holding N≥2 DICOM-tag patterns is unambiguously a fusion; the row is split into N rows **only** when every structured column yields exactly N aligned newline-parts (the description must be blank or also N parts), otherwise it is left intact and logged. Mirrors the continuation-merge discriminator and recovers attributes (e.g. TPPC-Brachy `(300A,0214)` Source Type / `(300A,0216)` Source Manufacturer) that were fused by line-snapping into a single malformed node.
43
- - `IODSpecBuilder.build_from_url` no longer raises a spurious `DeprecationWarning` when callers use only `progress_observer`. An internal call was incorrectly re-passing it as the legacy `progress_callback` argument as well.
44
-
45
- ## [0.3.0] - 2025-11-27
46
-
47
- ### Added
48
-
49
- - CSV output mode in `SpecPrinter` via `print_csv` method
50
- - Excel OOXML output mode in `SpecPrinter` via `print_xlsx` method
51
- - Optional `output` parameter in `SpecPrinter` for writing to files
52
- - Optional `column_width` parameter in `print_table` and `print_xlsx`
53
- - `--print-mode` option in `modattributes` CLI for CSV and OOXML output
54
- - `--output` option in `modattributes` CLI for directing output to files
55
-
56
- ### Changed
57
-
58
- - Switch PyPI version badge in `README` from `badge.fury.io` to `shields.io` for faster updates
59
- - Update modattributes CLI example for CSV and XLSX output
60
- - Improved nesting level color schemes for consistency and better contrast and readability.
61
-
62
- ### Fixed
63
-
64
- - Correct handling of `include_table` names in `DOMTableSpecParser`
65
- - Correct handling of empty node attributes in `SpecPrinter`
66
-
67
- ## [0.2.3] - 2025-09-29
68
-
69
- ### Fixed
70
-
71
- - Hotfix: Force UTF-8 decoding for DICOM standard XHTML downloads to prevent mojibake when server omits charset ([#85](https://github.com/dwikler/dcmspec/issues/85)).
72
- - Hotfix: Add missing `progress_observer` argument to `CSVTableSpecParser.parse` for interface compatibility and to prevent `TypeError` when used with `SpecFactory` ([#86](https://github.com/dwikler/dcmspec/issues/86)).
73
-
74
- ## [0.2.2] - 2025-09-25
75
-
76
- ### Fixed
77
-
78
- - Fix CONTRIBUTING.md link in README for PyPI compatibility
79
- - Remove focus border and misleading text cursor in iod-explorer details panel
80
-
81
- ### Changed
82
-
83
- - Update README: add PyPI and Python version badges
84
- - Replace Unicode ▶ with ASCII > in status bar for compatibility
85
- - Improve DICOM Modules usage condition parsing using regex for robustness to missing spaces
86
- - Add PR template to remind contributors to check the target branch and check tests and docs were updated
87
- - Move detailed table parsing logs to DEBUG level for less verbose INFO output
88
-
89
- ## [0.2.1] - 2025-09-19
90
-
91
- ### Fixed
92
-
93
- - Sanitize node and attribute names to remove "/" in DOMTableSpecParser ([#56](https://github.com/dwikler/dcmspec/issues/56))
94
-
95
- ### Changed
96
-
97
- - Major project restructure: move CLI and UI apps to new `apps/cli` and `apps/ui` folders
98
- - Improve installation instructions and documentation
99
- - Prepare and publish the package to [PyPI](https://pypi.org/project/dcmspec/)
100
-
101
- ## [0.2.0] - 2025-09-13
102
-
103
- ### Changed
104
-
105
- - **Breaking change:** `IODSpecBuilder.build_from_url` now returns a tuple `(iod_model, module_models)` instead of just the IOD model. All callers must be updated to unpack the tuple
106
- - Update CLI and UI applications to support new return value
107
- - Add registry mode to `IODSpecBuilder` for efficient module model sharing
108
-
109
- ## [0.1.0] - 2025-05-25
110
-
111
- ### Added
112
-
113
- - Initial release
1
+ # Release Notes
2
+
3
+ These release notes summarize key changes, improvements, and breaking updates for each version of **dcmspec**.
4
+
5
+ ## [0.4.2] - 2026-11-14
6
+
7
+ ### Fixed
8
+
9
+ - `PDFDocHandler.load_document` now accepts `strict_header_check`, so the check for a DICOM tag in the header
10
+ can be turned off through the `handler_kwargs` of `SpecFactory`
11
+ ([#117](https://github.com/dwikler/dcmspec/issues/117)).
12
+ - `SpecPrinter` and `IODSpecPrinter` no longer write ANSI escape sequences or wrap rows at 80
13
+ characters when stdout is redirected, and `--no-color` also removes the magenta table header
14
+ ([#122](https://github.com/dwikler/dcmspec/issues/122)).
15
+ - `SpecPrinter` and `IODSpecPrinter` no longer wrap rows at 80 characters when writing to a file
16
+ with `--output`
17
+ ([#112](https://github.com/dwikler/dcmspec/issues/112)).
18
+ - `ServiceAttributeModel.select_dimse` now keeps indices of `metadata.column_to_attr`
19
+ aligned with indices of `metadata.header` when filtering DIMSE columns
20
+ ([#26](https://github.com/dwikler/dcmspec/issues/26)).
21
+ - `SectionImageResolver` now stores `image_paths` with forward slashes on every OS, instead of
22
+ the OS-specific separator. Section models cached on Windows by an earlier version keep their
23
+ backslash paths: delete them from the cache to rebuild them
24
+ ([#156](https://github.com/dwikler/dcmspec/issues/156)).
25
+ - `DOMTableSpecParser` no longer puts the title of a skipped column in `metadata.header` and
26
+ drops the last one when `skip_columns` is used. Models cached by an earlier version keep the
27
+ wrong header: delete them from the cache to rebuild them
28
+ ([#154](https://github.com/dwikler/dcmspec/issues/154)).
29
+ - The IOD Explorer install instructions now use the correct syntax: `poetry install --extras gui`.
30
+ - The `iodmodules` and `iodattributes` apps now handle Normalized IOD tables correctly. Models
31
+ cached by an earlier version keep the old values: delete them from the cache to rebuild them
32
+ ([#51](https://github.com/dwikler/dcmspec/issues/51)).
33
+ - The CLI apps no longer fail with a `UnicodeEncodeError` on Windows when their output is
34
+ redirected with `>` ([#166](https://github.com/dwikler/dcmspec/issues/166)).
35
+
36
+ ## [0.4.1] - 2026-09-27
37
+
38
+ ### Added
39
+
40
+ - `ModuleSpecBuilder.resolve_section` resolves a single section by id, for callers that
41
+ built a module model without resolving its referenced attribute descriptions
42
+ ([#131](https://github.com/dwikler/dcmspec/issues/131)).
43
+ - The [API Overview](https://dwikler.github.io/dcmspec/api/) page documents the
44
+ architecture of the library and the SpecFactory pipeline; the API Reference nav is
45
+ reorganized into consistent functional categories
46
+ ([#139](https://github.com/dwikler/dcmspec/issues/139)).
47
+
48
+ ### Changed
49
+
50
+ - **Breaking change:** `SpecFactory.try_load_cache`'s `ref_columns` parameter is replaced
51
+ by a generic `parser_kwargs` dict ([#124](https://github.com/dwikler/dcmspec/issues/124)).
52
+ - **Breaking change:** `SpecStore` gains a new abstract `file_extension` property, reporting
53
+ a store's cache file extension; custom subclasses must implement it
54
+ ([#137](https://github.com/dwikler/dcmspec/issues/137)).
55
+
56
+ ### Fixed
57
+
58
+ - `ModuleSpecBuilder` no longer resolves a "See Section X" reference that isn't an
59
+ attribute description of a module or macro; such references are now skipped
60
+ ([#132](https://github.com/dwikler/dcmspec/issues/132)).
61
+ - A resolved section's cached model is now updated with its resolved image paths ([#131](https://github.com/dwikler/dcmspec/issues/131)).
62
+ - `SpecFactory.build_model`'s docstring now notes `doc_object` is ignored when a cached
63
+ model is already found, and `try_load_cache` documents how to check for one without a
64
+ parsed document ([#133](https://github.com/dwikler/dcmspec/issues/133)).
65
+ - `SpecFactory` now checks all `parser_kwargs` (e.g. `skip_columns`, `unformatted`) against
66
+ a cache hit, not just `ref_columns`; a cache built with different values is now rebuilt
67
+ instead of silently reused ([#124](https://github.com/dwikler/dcmspec/issues/124)).
68
+ - `SpecFactory.create_model` and `ModuleSpecBuilder.build_from_url` no longer rely on
69
+ `input_handler.cache_file_name` being set for cache lookup, fixing a mix-up
70
+ risk when reusing one factory/builder for multiple calls with different
71
+ `cache_file_name` values ([#138](https://github.com/dwikler/dcmspec/issues/138)).
72
+ - `SpecFactory` and `ModuleSpecBuilder` no longer hardcode `.json` when deriving a default
73
+ cache filename; they now use the configured `model_store`'s file extension
74
+ ([#137](https://github.com/dwikler/dcmspec/issues/137)).
75
+ - `SpecFactory` no longer reparses when a `parser_kwargs` option was omitted and the default
76
+ value is used ([#142](https://github.com/dwikler/dcmspec/issues/142)).
77
+ - `SpecFactory` now checks `column_to_attr` and `name_attr` against a cache hit, along with
78
+ the existing `include_depth` and `parser_kwargs` checks; a cache built with different
79
+ values is rebuilt instead of silently reused
80
+ ([#146](https://github.com/dwikler/dcmspec/issues/146)).
81
+
82
+ ## [0.4.0] - 2026-09-11
83
+
84
+ ### Added
85
+
86
+ - `DOMSectionSpecParser` parses a DICOM standard section, given its anchor id.
87
+ - `ModuleSpecBuilder` builds a module attribute model and resolves the sections it
88
+ directly references, caching each one separately and downloading their images.
89
+ - `IODSpecBuilder` accepts an optional `module_builder` so each module built as part
90
+ of a full IOD has its referenced sections resolved.
91
+
92
+ ### Changed
93
+
94
+ - **Breaking change:** `XHTMLDocHandler.download`/`PDFDocHandler.download` renamed to
95
+ `download_to_cache`. Callers using `load_document` are unaffected.
96
+
97
+ ### Fixed
98
+
99
+ - `DOMTableSpecParser.get_version` no longer raises `UnboundLocalError` when the document has no
100
+ `<div class="titlepage">`; it will return `""` with a warning.
101
+ - `SpecModel.exclude_titles` no longer raises `AttributeError` when a model's metadata has no
102
+ `column_to_attr` such as a section model.
103
+
104
+ ## [0.3.1] - 2026-08-27
105
+
106
+ ### Added
107
+
108
+ - Reorganized `tests/` into `tests/unit`, `tests/integration`, and `tests/e2e` tiers, and added an opt-in `tests/e2e`
109
+ canary suite that downloads and parses real, current DICOM standard tables (Part 3 IOD+modules, Part 6 data
110
+ dictionary, Part 4 UPS DIMSE attributes, and a Part 3/Part 6 merge) to catch upstream markup changes between
111
+ standard releases. Excluded from the default test run; also runs on a monthly schedule via
112
+ `.github/workflows/e2e.yml`.
113
+
114
+ ### Fixed
115
+
116
+ - `PDFDocHandler.select_tables` now raises `ValueError` when a header cell contains a DICOM tag pattern (e.g. `(300A,0230)`), indicating that a data row was absorbed into the header — typically a `table_header_rowspan` over-count. This fails loudly instead of silently dropping an attribute row. Opt out with `strict_header_check=False` to restore the prior warn-and-continue behavior.
117
+ - `PDFDocHandler.concat_tables` now reconstructs page-straddling description cells. When an attribute's description wraps across a PDF page boundary, pdfplumber emits the continuation as an untagged row (empty name and tag, description only) atop the next page's table; this is now merged into the preceding tagged row's description instead of becoming a floating node. Recovers enum values and conditional requirements that were silently lost at page breaks (e.g. HDSS Contour Geometric Type `(3006,0042)` losing its `POINT` / `CLOSED_PLANAR` / `CLOSEDPLANAR_XOR` enumeration, where `CLOSEDPLANAR_XOR` reflects the original DICOM spelling).
118
+ - `PDFDocHandler.load_document` / `extract_tables_pdfplumber` now accept a `snap_tolerance_overrides` dict (keyed by 1-indexed page number) to lower pdfplumber's `snap_tolerance` for individual pages. The default (8) snaps two near-coincident header rules together on a few pages, fusing a repeated continuation sub-header (e.g. `Presence | Specific Rules`) into the first data row of the next page; that row then trips the tag-in-header guard (rowspan over-count) and the page cannot be extracted. Lowering the tolerance for just that page (e.g. `{34: 6}`) keeps the two rules distinct so the sub-header is recognized as a header row, recovering the first attribute of the continuation table (e.g. TPPC-Brachy HDR/PDR Source `(0008,1040)`) without disturbing extraction of any other page. This also un-fuses line-snapped "frankenrows" at the source on the affected page; the `concat_tables` split below remains as a downstream safety net for pages that still fuse at the default tolerance.
119
+ - `PDFDocHandler.concat_tables` now splits fused "frankenrow" rows back into their constituent attribute rows. pdfplumber's line-snapping can merge two adjacent table rows whose separating rule falls within `snap_tolerance` into one row, newline-joining every column (e.g. tag `(300A,0214)\n(300A,0216)`, type `1\n3`). A legitimate attribute row carries exactly one DICOM tag, so a tag cell holding N≥2 DICOM-tag patterns is unambiguously a fusion; the row is split into N rows **only** when every structured column yields exactly N aligned newline-parts (the description must be blank or also N parts), otherwise it is left intact and logged. Mirrors the continuation-merge discriminator and recovers attributes (e.g. TPPC-Brachy `(300A,0214)` Source Type / `(300A,0216)` Source Manufacturer) that were fused by line-snapping into a single malformed node.
120
+ - `IODSpecBuilder.build_from_url` no longer raises a spurious `DeprecationWarning` when callers use only `progress_observer`. An internal call was incorrectly re-passing it as the legacy `progress_callback` argument as well.
121
+
122
+ ## [0.3.0] - 2025-11-27
123
+
124
+ ### Added
125
+
126
+ - CSV output mode in `SpecPrinter` via `print_csv` method
127
+ - Excel OOXML output mode in `SpecPrinter` via `print_xlsx` method
128
+ - Optional `output` parameter in `SpecPrinter` for writing to files
129
+ - Optional `column_width` parameter in `print_table` and `print_xlsx`
130
+ - `--print-mode` option in `modattributes` CLI for CSV and OOXML output
131
+ - `--output` option in `modattributes` CLI for directing output to files
132
+
133
+ ### Changed
134
+
135
+ - Switch PyPI version badge in `README` from `badge.fury.io` to `shields.io` for faster updates
136
+ - Update modattributes CLI example for CSV and XLSX output
137
+ - Improved nesting level color schemes for consistency and better contrast and readability.
138
+
139
+ ### Fixed
140
+
141
+ - Correct handling of `include_table` names in `DOMTableSpecParser`
142
+ - Correct handling of empty node attributes in `SpecPrinter`
143
+
144
+ ## [0.2.3] - 2025-09-29
145
+
146
+ ### Fixed
147
+
148
+ - Hotfix: Force UTF-8 decoding for DICOM standard XHTML downloads to prevent mojibake when server omits charset ([#85](https://github.com/dwikler/dcmspec/issues/85)).
149
+ - Hotfix: Add missing `progress_observer` argument to `CSVTableSpecParser.parse` for interface compatibility and to prevent `TypeError` when used with `SpecFactory` ([#86](https://github.com/dwikler/dcmspec/issues/86)).
150
+
151
+ ## [0.2.2] - 2025-09-25
152
+
153
+ ### Fixed
154
+
155
+ - Fix CONTRIBUTING.md link in README for PyPI compatibility
156
+ - Remove focus border and misleading text cursor in iod-explorer details panel
157
+
158
+ ### Changed
159
+
160
+ - Update README: add PyPI and Python version badges
161
+ - Replace Unicode ▶ with ASCII > in status bar for compatibility
162
+ - Improve DICOM Modules usage condition parsing using regex for robustness to missing spaces
163
+ - Add PR template to remind contributors to check the target branch and check tests and docs were updated
164
+ - Move detailed table parsing logs to DEBUG level for less verbose INFO output
165
+
166
+ ## [0.2.1] - 2025-09-19
167
+
168
+ ### Fixed
169
+
170
+ - Sanitize node and attribute names to remove "/" in DOMTableSpecParser ([#56](https://github.com/dwikler/dcmspec/issues/56))
171
+
172
+ ### Changed
173
+
174
+ - Major project restructure: move CLI and UI apps to new `apps/cli` and `apps/ui` folders
175
+ - Improve installation instructions and documentation
176
+ - Prepare and publish the package to [PyPI](https://pypi.org/project/dcmspec/)
177
+
178
+ ## [0.2.0] - 2025-09-13
179
+
180
+ ### Changed
181
+
182
+ - **Breaking change:** `IODSpecBuilder.build_from_url` now returns a tuple `(iod_model, module_models)` instead of just the IOD model. All callers must be updated to unpack the tuple
183
+ - Update CLI and UI applications to support new return value
184
+ - Add registry mode to `IODSpecBuilder` for efficient module model sharing
185
+
186
+ ## [0.1.0] - 2025-05-25
187
+
188
+ ### Added
189
+
190
+ - Initial release