ezmi2d 0.1.0__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 (52) hide show
  1. ezmi2d-0.1.0/Cargo.lock +229 -0
  2. ezmi2d-0.1.0/Cargo.toml +16 -0
  3. ezmi2d-0.1.0/LICENSE +22 -0
  4. ezmi2d-0.1.0/PKG-INFO +233 -0
  5. ezmi2d-0.1.0/README.md +197 -0
  6. ezmi2d-0.1.0/benchmarks/README.md +32 -0
  7. ezmi2d-0.1.0/benchmarks/benchmark_pipeline.py +291 -0
  8. ezmi2d-0.1.0/benchmarks/results/phase6-baseline.json +1697 -0
  9. ezmi2d-0.1.0/benchmarks/results/phase6-baseline.md +51 -0
  10. ezmi2d-0.1.0/crates/ezmi2d-core/Cargo.toml +17 -0
  11. ezmi2d-0.1.0/crates/ezmi2d-core/src/compression.rs +220 -0
  12. ezmi2d-0.1.0/crates/ezmi2d-core/src/encoding.rs +163 -0
  13. ezmi2d-0.1.0/crates/ezmi2d-core/src/error.rs +41 -0
  14. ezmi2d-0.1.0/crates/ezmi2d-core/src/lib.rs +36 -0
  15. ezmi2d-0.1.0/crates/ezmi2d-core/src/model.rs +449 -0
  16. ezmi2d-0.1.0/crates/ezmi2d-core/src/options.rs +51 -0
  17. ezmi2d-0.1.0/crates/ezmi2d-core/src/raw.rs +242 -0
  18. ezmi2d-0.1.0/crates/ezmi2d-core/src/scanner.rs +611 -0
  19. ezmi2d-0.1.0/crates/ezmi2d-core/src/semantic.rs +1970 -0
  20. ezmi2d-0.1.0/crates/ezmi2d-core/tests/hardening.rs +86 -0
  21. ezmi2d-0.1.0/crates/ezmi2d-core/tests/raw_scan.rs +67 -0
  22. ezmi2d-0.1.0/crates/ezmi2d-core/tests/semantic_read.rs +159 -0
  23. ezmi2d-0.1.0/crates/ezmi2d-python/Cargo.toml +19 -0
  24. ezmi2d-0.1.0/crates/ezmi2d-python/src/lib.rs +632 -0
  25. ezmi2d-0.1.0/docs/api.md +186 -0
  26. ezmi2d-0.1.0/docs/mi-format-research.md +371 -0
  27. ezmi2d-0.1.0/docs/releasing.md +77 -0
  28. ezmi2d-0.1.0/examples/plot_mi.py +101 -0
  29. ezmi2d-0.1.0/fuzz/Cargo.lock +204 -0
  30. ezmi2d-0.1.0/fuzz/Cargo.toml +29 -0
  31. ezmi2d-0.1.0/fuzz/fuzz_targets/raw_scan.rs +20 -0
  32. ezmi2d-0.1.0/fuzz/fuzz_targets/semantic_read.rs +19 -0
  33. ezmi2d-0.1.0/pyproject.toml +86 -0
  34. ezmi2d-0.1.0/samples/README.md +48 -0
  35. ezmi2d-0.1.0/samples/manifest.toml +111 -0
  36. ezmi2d-0.1.0/scripts/check_release_version.py +72 -0
  37. ezmi2d-0.1.0/scripts/fetch_external_samples.sh +105 -0
  38. ezmi2d-0.1.0/scripts/smoke_installed_package.py +99 -0
  39. ezmi2d-0.1.0/scripts/verify_release_artifacts.py +183 -0
  40. ezmi2d-0.1.0/src/ezmi2d/__init__.py +157 -0
  41. ezmi2d-0.1.0/src/ezmi2d/__main__.py +173 -0
  42. ezmi2d-0.1.0/src/ezmi2d/_core.pyi +22 -0
  43. ezmi2d-0.1.0/src/ezmi2d/diagnostics.py +33 -0
  44. ezmi2d-0.1.0/src/ezmi2d/document.py +716 -0
  45. ezmi2d-0.1.0/src/ezmi2d/entities.py +384 -0
  46. ezmi2d-0.1.0/src/ezmi2d/plotting.py +266 -0
  47. ezmi2d-0.1.0/src/ezmi2d/py.typed +1 -0
  48. ezmi2d-0.1.0/src/ezmi2d/raw.py +463 -0
  49. ezmi2d-0.1.0/tests/data/geometry.mi +156 -0
  50. ezmi2d-0.1.0/tests/data/minimal.mi +51 -0
  51. ezmi2d-0.1.0/tests/data/phase5.mi +441 -0
  52. ezmi2d-0.1.0/tests/data/text-utf8.mi +121 -0
@@ -0,0 +1,229 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "adler2"
7
+ version = "2.0.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
10
+
11
+ [[package]]
12
+ name = "cfg-if"
13
+ version = "1.0.4"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
16
+
17
+ [[package]]
18
+ name = "crc32fast"
19
+ version = "1.5.0"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
22
+ dependencies = [
23
+ "cfg-if",
24
+ ]
25
+
26
+ [[package]]
27
+ name = "encoding_rs"
28
+ version = "0.8.35"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
31
+ dependencies = [
32
+ "cfg-if",
33
+ ]
34
+
35
+ [[package]]
36
+ name = "ezmi2d-core"
37
+ version = "0.1.0"
38
+ dependencies = [
39
+ "encoding_rs",
40
+ "flate2",
41
+ "thiserror",
42
+ ]
43
+
44
+ [[package]]
45
+ name = "ezmi2d-python"
46
+ version = "0.1.0"
47
+ dependencies = [
48
+ "ezmi2d-core",
49
+ "pyo3",
50
+ ]
51
+
52
+ [[package]]
53
+ name = "flate2"
54
+ version = "1.1.9"
55
+ source = "registry+https://github.com/rust-lang/crates.io-index"
56
+ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
57
+ dependencies = [
58
+ "crc32fast",
59
+ "miniz_oxide",
60
+ ]
61
+
62
+ [[package]]
63
+ name = "heck"
64
+ version = "0.5.0"
65
+ source = "registry+https://github.com/rust-lang/crates.io-index"
66
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
67
+
68
+ [[package]]
69
+ name = "libc"
70
+ version = "0.2.189"
71
+ source = "registry+https://github.com/rust-lang/crates.io-index"
72
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
73
+
74
+ [[package]]
75
+ name = "miniz_oxide"
76
+ version = "0.8.9"
77
+ source = "registry+https://github.com/rust-lang/crates.io-index"
78
+ checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
79
+ dependencies = [
80
+ "adler2",
81
+ "simd-adler32",
82
+ ]
83
+
84
+ [[package]]
85
+ name = "once_cell"
86
+ version = "1.21.4"
87
+ source = "registry+https://github.com/rust-lang/crates.io-index"
88
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
89
+
90
+ [[package]]
91
+ name = "portable-atomic"
92
+ version = "1.15.0"
93
+ source = "registry+https://github.com/rust-lang/crates.io-index"
94
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
95
+
96
+ [[package]]
97
+ name = "proc-macro2"
98
+ version = "1.0.107"
99
+ source = "registry+https://github.com/rust-lang/crates.io-index"
100
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
101
+ dependencies = [
102
+ "unicode-ident",
103
+ ]
104
+
105
+ [[package]]
106
+ name = "pyo3"
107
+ version = "0.29.2"
108
+ source = "registry+https://github.com/rust-lang/crates.io-index"
109
+ checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
110
+ dependencies = [
111
+ "libc",
112
+ "once_cell",
113
+ "portable-atomic",
114
+ "pyo3-build-config",
115
+ "pyo3-ffi",
116
+ "pyo3-macros",
117
+ ]
118
+
119
+ [[package]]
120
+ name = "pyo3-build-config"
121
+ version = "0.29.2"
122
+ source = "registry+https://github.com/rust-lang/crates.io-index"
123
+ checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
124
+ dependencies = [
125
+ "target-lexicon",
126
+ ]
127
+
128
+ [[package]]
129
+ name = "pyo3-ffi"
130
+ version = "0.29.2"
131
+ source = "registry+https://github.com/rust-lang/crates.io-index"
132
+ checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
133
+ dependencies = [
134
+ "libc",
135
+ "pyo3-build-config",
136
+ ]
137
+
138
+ [[package]]
139
+ name = "pyo3-macros"
140
+ version = "0.29.2"
141
+ source = "registry+https://github.com/rust-lang/crates.io-index"
142
+ checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
143
+ dependencies = [
144
+ "proc-macro2",
145
+ "pyo3-macros-backend",
146
+ "quote",
147
+ "syn 2.0.119",
148
+ ]
149
+
150
+ [[package]]
151
+ name = "pyo3-macros-backend"
152
+ version = "0.29.2"
153
+ source = "registry+https://github.com/rust-lang/crates.io-index"
154
+ checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
155
+ dependencies = [
156
+ "heck",
157
+ "proc-macro2",
158
+ "quote",
159
+ "syn 2.0.119",
160
+ ]
161
+
162
+ [[package]]
163
+ name = "quote"
164
+ version = "1.0.47"
165
+ source = "registry+https://github.com/rust-lang/crates.io-index"
166
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
167
+ dependencies = [
168
+ "proc-macro2",
169
+ ]
170
+
171
+ [[package]]
172
+ name = "simd-adler32"
173
+ version = "0.3.10"
174
+ source = "registry+https://github.com/rust-lang/crates.io-index"
175
+ checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
176
+
177
+ [[package]]
178
+ name = "syn"
179
+ version = "2.0.119"
180
+ source = "registry+https://github.com/rust-lang/crates.io-index"
181
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
182
+ dependencies = [
183
+ "proc-macro2",
184
+ "quote",
185
+ "unicode-ident",
186
+ ]
187
+
188
+ [[package]]
189
+ name = "syn"
190
+ version = "3.0.3"
191
+ source = "registry+https://github.com/rust-lang/crates.io-index"
192
+ checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
193
+ dependencies = [
194
+ "proc-macro2",
195
+ "quote",
196
+ "unicode-ident",
197
+ ]
198
+
199
+ [[package]]
200
+ name = "target-lexicon"
201
+ version = "0.13.5"
202
+ source = "registry+https://github.com/rust-lang/crates.io-index"
203
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
204
+
205
+ [[package]]
206
+ name = "thiserror"
207
+ version = "2.0.20"
208
+ source = "registry+https://github.com/rust-lang/crates.io-index"
209
+ checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
210
+ dependencies = [
211
+ "thiserror-impl",
212
+ ]
213
+
214
+ [[package]]
215
+ name = "thiserror-impl"
216
+ version = "2.0.20"
217
+ source = "registry+https://github.com/rust-lang/crates.io-index"
218
+ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
219
+ dependencies = [
220
+ "proc-macro2",
221
+ "quote",
222
+ "syn 3.0.3",
223
+ ]
224
+
225
+ [[package]]
226
+ name = "unicode-ident"
227
+ version = "1.0.24"
228
+ source = "registry+https://github.com/rust-lang/crates.io-index"
229
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
@@ -0,0 +1,16 @@
1
+ [workspace]
2
+ members = ["crates/ezmi2d-core", "crates/ezmi2d-python"]
3
+ resolver = "2"
4
+
5
+ [workspace.package]
6
+ version = "0.1.0"
7
+ edition = "2021"
8
+ rust-version = "1.83"
9
+ authors = ["neka-nat <nekanat.stock@gmail.com>"]
10
+ description = "Lossless reader for PTC Creo Elements/Direct Drafting MI files"
11
+ repository = "https://github.com/monozukuri-ai/ezmi2d"
12
+ license = "MIT"
13
+
14
+ [workspace.dependencies]
15
+ pyo3 = { version = "0.29.0", features = ["abi3-py310"] }
16
+
ezmi2d-0.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ezmi2d contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
ezmi2d-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: ezmi2d
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 2 - Pre-Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3 :: Only
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Programming Language :: Rust
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Requires-Dist: ezdxf>=1.4,<2.0 ; extra == 'corpus'
17
+ Requires-Dist: matplotlib>=3.8,<4.0 ; extra == 'dev'
18
+ Requires-Dist: maturin>=1.13,<2.0 ; extra == 'dev'
19
+ Requires-Dist: pytest>=8.0 ; extra == 'dev'
20
+ Requires-Dist: ruff>=0.12 ; extra == 'dev'
21
+ Requires-Dist: matplotlib>=3.8,<4.0 ; extra == 'plot'
22
+ Provides-Extra: corpus
23
+ Provides-Extra: dev
24
+ Provides-Extra: plot
25
+ License-File: LICENSE
26
+ Summary: Lossless reader for PTC Creo Elements/Direct Drafting MI files powered by Rust
27
+ Keywords: mi,cad,me10,creo,parser,rust
28
+ Author-email: neka-nat <nekanat.stock@gmail.com>
29
+ Requires-Python: >=3.10
30
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
31
+ Project-URL: Documentation, https://github.com/monozukuri-ai/ezmi2d/blob/main/docs/api.md
32
+ Project-URL: Homepage, https://github.com/monozukuri-ai/ezmi2d
33
+ Project-URL: Issues, https://github.com/monozukuri-ai/ezmi2d/issues
34
+ Project-URL: Repository, https://github.com/monozukuri-ai/ezmi2d
35
+
36
+ # ezmi2d
37
+
38
+ `ezmi2d` is an experimental reader for the line-oriented MI drawing format used
39
+ by HP ME10 and PTC Creo Elements/Direct Drafting. Its parser and reference
40
+ resolver are written in Rust and exposed to Python through PyO3.
41
+
42
+ The public API provides typed geometry, annotations, and part structure while retaining the
43
+ complete, byte-preserving raw scan:
44
+
45
+ ```python
46
+ import ezmi2d
47
+
48
+ drawing = ezmi2d.read("drawing.mi")
49
+ modelspace = drawing.modelspace()
50
+
51
+ print(drawing.version, drawing.units, drawing.extents)
52
+ print(drawing.encoding, drawing.encoding_source)
53
+
54
+ for entity in modelspace.query("LIN ARC CIR"):
55
+ print(entity.id, entity.mi_type, entity.property_id)
56
+
57
+ line = drawing.entitydb[10]
58
+ if isinstance(line, ezmi2d.Line):
59
+ print(line.start, line.end)
60
+
61
+ for text in modelspace.query("TEXT"):
62
+ print(text.text, text.origin, text.height, text.font_name)
63
+
64
+ for spline in drawing.query("FIL BSPL"):
65
+ print(spline.id, spline.mi_type)
66
+
67
+ for sheet in drawing.sheets:
68
+ print(sheet.name, sheet.child_part_indices)
69
+ ```
70
+
71
+ ## Matplotlib preview
72
+
73
+ Install the optional plotting dependency and render a decoded fixture to PNG:
74
+
75
+ ```console
76
+ pip install "ezmi2d[plot]"
77
+ python examples/plot_mi.py tests/data/geometry.mi -o geometry.png
78
+ ```
79
+
80
+ The plotting helper can also be embedded directly:
81
+
82
+ ```python
83
+ import matplotlib.pyplot as plt
84
+ import ezmi2d
85
+
86
+ drawing = ezmi2d.read("drawing.mi")
87
+ fig, ax = plt.subplots()
88
+ ezmi2d.draw(drawing, ax=ax)
89
+ ax.legend()
90
+ fig.savefig("drawing.png", dpi=160)
91
+ ```
92
+
93
+ `ezmi2d.draw(Document)` renders every decoded part definition once; use
94
+ `ezmi2d.draw(drawing.parts[index])` to inspect one part. This is a semantic
95
+ diagnostic preview, not a style-faithful MI renderer: unknown display fields,
96
+ typed-but-opaque annotations, and assembly instance transforms are not applied.
97
+ The current sample corpus verifies `orientation=0` arcs as counter-clockwise;
98
+ other orientation values are skipped with a warning rather than guessed.
99
+
100
+ `read()` accepts a path or bytes-like object, including the verified
101
+ gzip-compressed MI envelope. Point and property pointers are
102
+ resolved after all records have been decoded, so forward references work and
103
+ dangling or wrong-type references produce source-located diagnostics rather
104
+ than aborting the whole drawing.
105
+
106
+ Text is decoded strictly. The reader checks an explicit `encoding=` override,
107
+ a UTF-8 BOM, MI 3.20-or-newer version metadata, and a `#~1` `ENCODING:`
108
+ declaration before conservatively inspecting known text fields in older files.
109
+ Supported canonical encodings are `utf-8`, `shift_jis` (including `cp932` and
110
+ `windows-31j` aliases), and `hp-roman8`:
111
+
112
+ ```python
113
+ drawing = ezmi2d.read("legacy-japanese.mi", encoding="cp932")
114
+ text = drawing.query("TEX")[0]
115
+ print(text.text, text.text_bytes, text.content_value.encoding)
116
+ ```
117
+
118
+ Invalid or undecidable text is never decoded with replacement characters.
119
+ `TextValue.text` remains `None`, `TextValue.raw_bytes` retains the source, and a
120
+ source-located diagnostic identifies the bad byte.
121
+
122
+ The lower-level lossless scanner remains available when inspecting unsupported
123
+ records or developing new entity decoders:
124
+
125
+ ```python
126
+ scan = ezmi2d.scan("drawing.mi")
127
+ for record in scan.records:
128
+ print(record.section_number, record.record_type, record.raw_bytes)
129
+
130
+ packed = ezmi2d.scan("drawing.bi")
131
+ print(packed.format.compression, packed.container_size, packed.source_size)
132
+ assert packed.container_bytes != packed.source_bytes
133
+ ```
134
+
135
+ For compressed inputs, every source span addresses the decompressed logical MI
136
+ stream exposed as `source_bytes`. The exact caller input remains available as
137
+ `container_bytes`. Streaming decompression is bounded independently by
138
+ `ScanLimits.max_file_size`, `max_decompressed_size`, and
139
+ `max_compression_ratio`.
140
+
141
+ The CLI emits a raw structural summary or JSON suitable for corpus inspection:
142
+
143
+ ```console
144
+ ezmi2d inspect drawing.mi
145
+ ezmi2d inspect drawing.mi --json --records
146
+ ```
147
+
148
+ ## Current scope
149
+
150
+ - legacy MI 2.10 global metadata, drawing extents, units, and serialized transform values
151
+ - `#~6` part ownership and an ezdxf-style `modelspace()`, `query()`, and `entitydb`
152
+ - typed `P`, `LIN`, `ARC`, `FIL`, `BSPL`, `CIR`, and `TEX` entities; point references are resolved
153
+ - De Boor evaluation for non-rational `BSPL`, including modern variable-prefix records
154
+ - typed dimension (`DANG`, `DCHMF`, `DDIA`, `DRAD`, `DSGL`), `DTV`, `LED`, `HAT`, and `SYML`
155
+ - nested/shared `ASSE` instances, serialized 3x3 transforms, root parts, and `DOCU_SHEET` links
156
+ - strict UTF-8, Shift_JIS/CP932, and HP Roman-8 text decoding with explicit override support
157
+ - minimally decoded `PSTAT`, `ASSP`, dimension/hatch properties, and `ASSE` records
158
+ - stable diagnostics for duplicate IDs, bad records, dangling pointers, wrong pointer types,
159
+ table-of-contents mismatch, and Phase 1 structural problems
160
+ - `UnsupportedEntity` fallback with its original `RawRecord`; no addressable record is silently
161
+ discarded merely because its semantic decoder is unavailable
162
+ - bounded line, section, and `|~` scanning with read-only access to every logical MI byte range
163
+ - streaming gzip decompression with container-size, expanded-size, ratio, truncation, checksum,
164
+ trailing-data, and concatenated-member guards
165
+
166
+ The four common graphic display fields are exposed conservatively as
167
+ `display_values`, and the ARC direction field is retained as `orientation`:
168
+ their formal names and complete semantics have not yet been established.
169
+ Modern variable-prefix B-splines expose `display_values=None` and retain that
170
+ prefix as `prefix_values` instead of guessing a style layout.
171
+ For `TEX`, only fields validated across the available MI/DXF pairs are named:
172
+ the serialized 3x3 transform, its translation as `origin`, font name, two text
173
+ size values, height, and content. Every post-ID field remains available through
174
+ `values`. Annotation fields, B-spline prefix values, and assembly relationship fields whose
175
+ formal names are not verified are likewise exposed as lossless serialized values. Writers are
176
+ not implemented. gzip-wrapped product-generated compressed MI is supported
177
+ by content signature. zlib-wrapper, ZIP, and historical UNIX-compress variants
178
+ remain unsupported. The available genuine sample is a compressed 2D MI member
179
+ inside a Creo bundle, not a separately exported standalone `.bi`; compatibility
180
+ with every Drafting/ME10 `.bi` generation is therefore not claimed.
181
+
182
+ See the [Python API reference](docs/api.md),
183
+ [MI format research](docs/mi-format-research.md), and
184
+ [sample corpus guide](samples/README.md). Third-party drawings are not distributed in Git,
185
+ sdists, or wheels.
186
+
187
+ The opt-in corpus suite currently exercises 19 MI 2.10 drawings containing
188
+ 10,166 points, 4,030 lines, 1,059 arcs, 353 fillets, 1,196 circles, 6 B-splines,
189
+ and 57 text entities. The
190
+ paired DXF tests compare line endpoints, arc center/start/end points, circle
191
+ centers/radii, drawing extents, and text content/insertion/height to seven
192
+ decimal places. Fillets exactly match the additional DXF arcs. Stored B-spline
193
+ interpolation points and every vertex of the corresponding DXF polylines are
194
+ evaluated against the decoded curves.
195
+
196
+ It also verifies a product-generated MI 3.40 / UTF-8 compressed member from a
197
+ public Creo bundle: 87,506 compressed bytes expand to 393,805 logical bytes.
198
+ Its 88 B-splines, 88 typed annotations, 25-part hierarchy, and sheet association
199
+ produce identical semantic models and diagnostics from compressed and expanded input.
200
+
201
+ ## Distribution
202
+
203
+ Release automation builds `cp310-abi3` wheels for Linux x86-64 and ARM64, macOS universal2, and
204
+ Windows x64. Artifact-only smoke tests cover Python 3.10 and 3.14, and the sdist is rebuilt and
205
+ installed in a clean job. See [docs/releasing.md](docs/releasing.md) for the complete release gates
206
+ and Trusted Publishing setup.
207
+
208
+ ## Development
209
+
210
+ ```console
211
+ uv sync --locked --extra dev
212
+ uv run maturin develop
213
+ uv run pytest
214
+ cargo test --workspace
215
+ uv run python benchmarks/benchmark_pipeline.py tests/data --warmup 0 --repeat 1 \
216
+ --json-output /tmp/ezmi2d-benchmark.json --markdown-output /tmp/ezmi2d-benchmark.md
217
+ ```
218
+
219
+ Parser fuzzing uses nightly Rust and `cargo-fuzz`:
220
+
221
+ ```console
222
+ cargo +nightly fuzz run raw_scan
223
+ cargo +nightly fuzz run semantic_read
224
+ ```
225
+
226
+ To fetch and run the separately licensed corpus comparison:
227
+
228
+ ```console
229
+ ./scripts/fetch_external_samples.sh
230
+ uv sync --locked --extra dev --extra corpus
231
+ uv run --extra corpus pytest tests/python/test_external_corpus.py
232
+ ```
233
+