pycadkit 0.3.3__tar.gz → 0.3.5__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 (182) hide show
  1. {pycadkit-0.3.3 → pycadkit-0.3.5}/Cargo.lock +10 -10
  2. {pycadkit-0.3.3 → pycadkit-0.3.5}/Cargo.toml +7 -7
  3. {pycadkit-0.3.3 → pycadkit-0.3.5}/PKG-INFO +1 -1
  4. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/native/decode_v8.rs +3 -2
  5. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/mod.rs +15 -23
  6. pycadkit-0.3.5/crates/cadkit-dgn/src/writer/named.rs +203 -0
  7. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/raster_template.rs +36 -12
  8. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tables.rs +201 -44
  9. pycadkit-0.3.5/crates/cadkit-dgn/src/writer/tests/dictionary.rs +604 -0
  10. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tests/raster_template.rs +117 -1
  11. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tests.rs +1 -0
  12. pycadkit-0.3.5/crates/cadkit-dgn/tests/private_dictionary.rs +173 -0
  13. {pycadkit-0.3.3 → pycadkit-0.3.5}/LICENSE-APACHE +0 -0
  14. {pycadkit-0.3.3 → pycadkit-0.3.5}/LICENSE-MIT +0 -0
  15. {pycadkit-0.3.3 → pycadkit-0.3.5}/NOTICE +0 -0
  16. {pycadkit-0.3.3 → pycadkit-0.3.5}/README.md +0 -0
  17. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit/Cargo.toml +0 -0
  18. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit/LICENSE-APACHE +0 -0
  19. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit/LICENSE-MIT +0 -0
  20. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit/NOTICE +0 -0
  21. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit/README.md +0 -0
  22. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit/examples/interchange.rs +0 -0
  23. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit/src/lib.rs +0 -0
  24. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/Cargo.toml +0 -0
  25. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/LICENSE-APACHE +0 -0
  26. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/LICENSE-MIT +0 -0
  27. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/NOTICE +0 -0
  28. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/README.md +0 -0
  29. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/aci.rs +0 -0
  30. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/bytes.rs +0 -0
  31. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/error.rs +0 -0
  32. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/export.rs +0 -0
  33. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/geom.rs +0 -0
  34. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/geom_ops.rs +0 -0
  35. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/lib.rs +0 -0
  36. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/model.rs +0 -0
  37. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/ops.rs +0 -0
  38. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/options.rs +0 -0
  39. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/polygon.rs +0 -0
  40. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/svg.rs +0 -0
  41. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/text.rs +0 -0
  42. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-core/src/units.rs +0 -0
  43. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/Cargo.toml +0 -0
  44. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/LICENSE-APACHE +0 -0
  45. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/LICENSE-MIT +0 -0
  46. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/NOTICE +0 -0
  47. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/README.md +0 -0
  48. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/examples/dgn_summary.rs +0 -0
  49. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/examples/raster_structure.rs +0 -0
  50. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/budget.rs +0 -0
  51. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/cfb.rs +0 -0
  52. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/fuzz_tests.rs +0 -0
  53. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/le.rs +0 -0
  54. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/level_names.rs +0 -0
  55. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/lib.rs +0 -0
  56. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/map/mod.rs +0 -0
  57. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/map/v7.rs +0 -0
  58. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/map/v8.rs +0 -0
  59. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/native/element.rs +0 -0
  60. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/native/linkage.rs +0 -0
  61. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/native/mod.rs +0 -0
  62. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/native/v7.rs +0 -0
  63. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/native/v8.rs +0 -0
  64. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/palette.rs +0 -0
  65. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/text.rs +0 -0
  66. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/container.rs +0 -0
  67. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/encode.rs +0 -0
  68. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/polyline.rs +0 -0
  69. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/raster.rs +0 -0
  70. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/raster_builtin.rs +0 -0
  71. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tests/application.rs +0 -0
  72. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tests/microstation.rs +0 -0
  73. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tests/raster.rs +0 -0
  74. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tests/regressions.rs +0 -0
  75. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/tests/text.rs +0 -0
  76. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/writer/text.rs +0 -0
  77. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/src/zlib.rs +0 -0
  78. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/tests/common/mod.rs +0 -0
  79. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/tests/private_corpus.rs +0 -0
  80. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/tests/robustness.rs +0 -0
  81. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/tests/v7_corpus.rs +0 -0
  82. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/tests/v8_oracle.rs +0 -0
  83. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/tests/v8_public.rs +0 -0
  84. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dgn/tests/v8_range.rs +0 -0
  85. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/Cargo.toml +0 -0
  86. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/LICENSE-APACHE +0 -0
  87. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/LICENSE-MIT +0 -0
  88. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/NOTICE +0 -0
  89. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/README.md +0 -0
  90. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/examples/docsummary.rs +0 -0
  91. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/examples/dwgdump.rs +0 -0
  92. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/bits.rs +0 -0
  93. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/classes.rs +0 -0
  94. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/codepage.rs +0 -0
  95. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/container/lz77_r2004.rs +0 -0
  96. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/container/lz77_r2007.rs +0 -0
  97. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/container/mod.rs +0 -0
  98. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/container/r13.rs +0 -0
  99. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/container/r2004.rs +0 -0
  100. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/container/r2007.rs +0 -0
  101. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/container/reed_solomon.rs +0 -0
  102. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/crc.rs +0 -0
  103. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/handles.rs +0 -0
  104. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/header.rs +0 -0
  105. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/lib.rs +0 -0
  106. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/mapping/entity.rs +0 -0
  107. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/mapping/mod.rs +0 -0
  108. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/native/entities.rs +0 -0
  109. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/native/mod.rs +0 -0
  110. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/annotation.rs +0 -0
  111. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/curves.rs +0 -0
  112. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/entities.rs +0 -0
  113. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/hatch.rs +0 -0
  114. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/misc.rs +0 -0
  115. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/mod.rs +0 -0
  116. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/registry.rs +0 -0
  117. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/object/tables.rs +0 -0
  118. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/reader.rs +0 -0
  119. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/src/version.rs +0 -0
  120. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/tests/corpus.rs +0 -0
  121. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/tests/oracle.rs +0 -0
  122. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dwg/tests/robustness.rs +0 -0
  123. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/Cargo.toml +0 -0
  124. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/LICENSE-APACHE +0 -0
  125. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/LICENSE-MIT +0 -0
  126. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/NOTICE +0 -0
  127. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/README.md +0 -0
  128. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/examples/dump.rs +0 -0
  129. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/lib.rs +0 -0
  130. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/native/mod.rs +0 -0
  131. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/native/text.rs +0 -0
  132. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/ocs.rs +0 -0
  133. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/reader/common.rs +0 -0
  134. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/reader/entities.rs +0 -0
  135. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/reader/hatch.rs +0 -0
  136. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/reader/mod.rs +0 -0
  137. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/reader/records.rs +0 -0
  138. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/reader/tables.rs +0 -0
  139. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/writer/curves.rs +0 -0
  140. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/writer/entities.rs +0 -0
  141. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/writer/mod.rs +0 -0
  142. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/src/writer/out.rs +0 -0
  143. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/ascii_vs_binary.rs +0 -0
  144. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/common/mod.rs +0 -0
  145. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/compare_reader_with_ezdxf.py +0 -0
  146. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/review_fixes.rs +0 -0
  147. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/robustness.rs +0 -0
  148. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/roundtrip.rs +0 -0
  149. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/synthetic_read.rs +0 -0
  150. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/synthetic_write.rs +0 -0
  151. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-dxf/tests/validate_with_ezdxf.py +0 -0
  152. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/Cargo.toml +0 -0
  153. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/LICENSE-APACHE +0 -0
  154. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/LICENSE-MIT +0 -0
  155. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/NOTICE +0 -0
  156. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/README.md +0 -0
  157. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/curves.rs +0 -0
  158. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/export.rs +0 -0
  159. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/geometry.rs +0 -0
  160. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/lib.rs +0 -0
  161. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/mapping.rs +0 -0
  162. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/native.rs +0 -0
  163. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/tkgm.rs +0 -0
  164. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/validate.rs +0 -0
  165. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/src/xml.rs +0 -0
  166. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/tests/roundtrip.rs +0 -0
  167. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/tests/support/export_cases.rs +0 -0
  168. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-gml/tests/support/tkgm_cases.rs +0 -0
  169. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/Cargo.toml +0 -0
  170. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/LICENSE-APACHE +0 -0
  171. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/LICENSE-MIT +0 -0
  172. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/NOTICE +0 -0
  173. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/README.md +0 -0
  174. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/src/lib.rs +0 -0
  175. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/tests/conftest.py +0 -0
  176. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/tests/test_cadkit.py +0 -0
  177. {pycadkit-0.3.3 → pycadkit-0.3.5}/crates/cadkit-py/tests/test_citygml.py +0 -0
  178. {pycadkit-0.3.3 → pycadkit-0.3.5}/pyproject.toml +0 -0
  179. {pycadkit-0.3.3 → pycadkit-0.3.5}/python/cadkit/__init__.py +0 -0
  180. {pycadkit-0.3.3 → pycadkit-0.3.5}/python/cadkit/__init__.pyi +0 -0
  181. {pycadkit-0.3.3 → pycadkit-0.3.5}/python/cadkit/_cadkit.pyi +0 -0
  182. {pycadkit-0.3.3 → pycadkit-0.3.5}/python/cadkit/py.typed +0 -0
@@ -66,7 +66,7 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
66
66
 
67
67
  [[package]]
68
68
  name = "cadkit"
69
- version = "0.3.3"
69
+ version = "0.3.5"
70
70
  dependencies = [
71
71
  "cadkit-core",
72
72
  "cadkit-dgn",
@@ -78,7 +78,7 @@ dependencies = [
78
78
 
79
79
  [[package]]
80
80
  name = "cadkit-capi"
81
- version = "0.3.3"
81
+ version = "0.3.5"
82
82
  dependencies = [
83
83
  "cadkit",
84
84
  "serde",
@@ -87,7 +87,7 @@ dependencies = [
87
87
 
88
88
  [[package]]
89
89
  name = "cadkit-cli"
90
- version = "0.3.3"
90
+ version = "0.3.5"
91
91
  dependencies = [
92
92
  "cadkit",
93
93
  "clap",
@@ -96,7 +96,7 @@ dependencies = [
96
96
 
97
97
  [[package]]
98
98
  name = "cadkit-core"
99
- version = "0.3.3"
99
+ version = "0.3.5"
100
100
  dependencies = [
101
101
  "serde",
102
102
  "serde_json",
@@ -105,7 +105,7 @@ dependencies = [
105
105
 
106
106
  [[package]]
107
107
  name = "cadkit-dgn"
108
- version = "0.3.3"
108
+ version = "0.3.5"
109
109
  dependencies = [
110
110
  "cadkit-core",
111
111
  "cfb",
@@ -116,7 +116,7 @@ dependencies = [
116
116
 
117
117
  [[package]]
118
118
  name = "cadkit-dwg"
119
- version = "0.3.3"
119
+ version = "0.3.5"
120
120
  dependencies = [
121
121
  "cadkit-core",
122
122
  "cadkit-dxf",
@@ -126,7 +126,7 @@ dependencies = [
126
126
 
127
127
  [[package]]
128
128
  name = "cadkit-dxf"
129
- version = "0.3.3"
129
+ version = "0.3.5"
130
130
  dependencies = [
131
131
  "cadkit-core",
132
132
  "encoding_rs",
@@ -135,7 +135,7 @@ dependencies = [
135
135
 
136
136
  [[package]]
137
137
  name = "cadkit-gml"
138
- version = "0.3.3"
138
+ version = "0.3.5"
139
139
  dependencies = [
140
140
  "cadkit-core",
141
141
  "quick-xml",
@@ -145,7 +145,7 @@ dependencies = [
145
145
 
146
146
  [[package]]
147
147
  name = "cadkit-py"
148
- version = "0.3.3"
148
+ version = "0.3.5"
149
149
  dependencies = [
150
150
  "cadkit",
151
151
  "pyo3",
@@ -155,7 +155,7 @@ dependencies = [
155
155
 
156
156
  [[package]]
157
157
  name = "cadkit-wasm"
158
- version = "0.3.3"
158
+ version = "0.3.5"
159
159
  dependencies = [
160
160
  "cadkit",
161
161
  "js-sys",
@@ -4,7 +4,7 @@ members = ["crates/*"]
4
4
  exclude = ["fuzz"]
5
5
 
6
6
  [workspace.package]
7
- version = "0.3.3"
7
+ version = "0.3.5"
8
8
  edition = "2024"
9
9
  rust-version = "1.85"
10
10
  license = "MIT OR Apache-2.0"
@@ -14,12 +14,12 @@ keywords = ["cad", "dwg", "dgn", "dxf", "citygml"]
14
14
  categories = ["parser-implementations", "graphics", "encoding", "science::geo"]
15
15
 
16
16
  [workspace.dependencies]
17
- cadkit-core = { path = "crates/cadkit-core", version = "0.3.3" }
18
- cadkit-dgn = { path = "crates/cadkit-dgn", version = "0.3.3" }
19
- cadkit-dwg = { path = "crates/cadkit-dwg", version = "0.3.3" }
20
- cadkit-dxf = { path = "crates/cadkit-dxf", version = "0.3.3" }
21
- cadkit-gml = { path = "crates/cadkit-gml", version = "0.3.3" }
22
- cadkit = { path = "crates/cadkit", version = "0.3.3" }
17
+ cadkit-core = { path = "crates/cadkit-core", version = "0.3.5" }
18
+ cadkit-dgn = { path = "crates/cadkit-dgn", version = "0.3.5" }
19
+ cadkit-dwg = { path = "crates/cadkit-dwg", version = "0.3.5" }
20
+ cadkit-dxf = { path = "crates/cadkit-dxf", version = "0.3.5" }
21
+ cadkit-gml = { path = "crates/cadkit-gml", version = "0.3.5" }
22
+ cadkit = { path = "crates/cadkit", version = "0.3.5" }
23
23
  serde = { version = "1", features = ["derive"] }
24
24
  serde_json = "1"
25
25
  thiserror = "2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycadkit
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: Science/Research
@@ -136,9 +136,10 @@ pub fn decode(raw: &RawElement, enc: &'static Encoding, limits: &Limits) -> Elem
136
136
  35 => d.cell().map(ElementData::SharedCellInstance),
137
137
  37 => d.tag(&linkages).map(ElementData::Tag),
138
138
  39 => d.tag_set(&linkages).map(ElementData::TagSet),
139
+ // An attachment written with a relative path stores an empty full path.
139
140
  90 => Ok(ElementData::RasterReference {
140
- file_name: linkage::string(&linkages, 3),
141
- full_path: linkage::string(&linkages, 0x1f),
141
+ file_name: linkage::string(&linkages, 3).filter(|s| !s.is_empty()),
142
+ full_path: linkage::string(&linkages, 0x1f).filter(|s| !s.is_empty()),
142
143
  }),
143
144
  94 => le::f64s::<16>(d.b, 0x78)
144
145
  .map(|matrix| ElementData::RasterFrame {
@@ -2,6 +2,7 @@
2
2
 
3
3
  mod container;
4
4
  mod encode;
5
+ mod named;
5
6
  mod polyline;
6
7
  mod raster;
7
8
  mod raster_builtin;
@@ -237,14 +238,7 @@ pub fn write_v8(doc: &Document, seed: &[u8], options: &WriteOptions) -> Result<V
237
238
  }
238
239
  let max_id = max_id.max(le::u64_at(&seed_header.data, 0x128).unwrap_or(0));
239
240
  let rasters = raster::Preserved::prepare(doc, seed, model, &read, options)?;
240
- let mut tables = Tables::new(
241
- native
242
- .named_pages
243
- .iter()
244
- .flat_map(|p| p.elements.iter().cloned()),
245
- max_id,
246
- &read,
247
- )?;
241
+ let mut tables = Tables::new(&native.named_pages, max_id, &read)?;
248
242
  tables.observe_model_fonts(
249
243
  model
250
244
  .graphic_pages
@@ -283,7 +277,7 @@ pub fn write_v8(doc: &Document, seed: &[u8], options: &WriteOptions) -> Result<V
283
277
  let named = state.tables.finish(options.timestamp_ms)?;
284
278
  let max_id = records
285
279
  .iter()
286
- .chain(named.iter())
280
+ .chain(named.iter().map(|r| &r.bytes))
287
281
  .filter_map(|r| le::u64_at(r, 16))
288
282
  .max()
289
283
  .unwrap_or(max_id)
@@ -304,7 +298,7 @@ pub fn write_v8(doc: &Document, seed: &[u8], options: &WriteOptions) -> Result<V
304
298
  .collect();
305
299
  let mut replacements = BTreeMap::new();
306
300
  for stream in &native.streams {
307
- if stream
301
+ let model_stream = stream
308
302
  .path
309
303
  .starts_with(&format!("Dgn-Md/{}/Dgn^G/", model.storage))
310
304
  || stream
@@ -315,9 +309,8 @@ pub fn write_v8(doc: &Document, seed: &[u8], options: &WriteOptions) -> Result<V
315
309
  .starts_with(&format!("Dgn-Md/{}/Dgn^GA/", model.storage))
316
310
  || stream
317
311
  .path
318
- .starts_with(&format!("Dgn-Md/{}/Dgn^CA/", model.storage))
319
- || stream.path.starts_with("Dgn^Nm/")
320
- {
312
+ .starts_with(&format!("Dgn-Md/{}/Dgn^CA/", model.storage));
313
+ if model_stream || stream.path.starts_with("Dgn^Nm/") {
321
314
  // A page the reader could not decode was neither checked for content nor
322
315
  // carried into the named tables; dropping it would lose seed data silently.
323
316
  let page = stream.path.rsplit('/').next().is_some_and(|n| {
@@ -330,7 +323,10 @@ pub fn write_v8(doc: &Document, seed: &[u8], options: &WriteOptions) -> Result<V
330
323
  format!("seed page {} could not be decoded", stream.path),
331
324
  ));
332
325
  }
333
- replacements.insert(stream.path.clone(), None);
326
+ // Dictionary pages are kept and only rewritten where they change (`named`).
327
+ if model_stream {
328
+ replacements.insert(stream.path.clone(), None);
329
+ }
334
330
  }
335
331
  }
336
332
  let version = graphic_page_version(model);
@@ -382,15 +378,11 @@ pub fn write_v8(doc: &Document, seed: &[u8], options: &WriteOptions) -> Result<V
382
378
  // Every control auxiliary page is retained as is; so is its page counter.
383
379
  replacements.remove(&format!("Dgn-Md/{}/Dgn^CA/^AH", model.storage));
384
380
  }
385
- put_pages(
381
+ named::write(
386
382
  &mut replacements,
387
- "Dgn^Nm",
383
+ &native.named_pages,
384
+ &native.named_aux,
388
385
  &named,
389
- native
390
- .named_pages
391
- .first()
392
- .map(|p| p.header.format_version)
393
- .unwrap_or(3),
394
386
  options.limits.max_decompressed_bytes,
395
387
  )?;
396
388
  let mut mh = header.raw.clone();
@@ -456,8 +448,8 @@ pub fn write_v8(doc: &Document, seed: &[u8], options: &WriteOptions) -> Result<V
456
448
  #[cfg(test)]
457
449
  mod tests;
458
450
 
459
- /// Writes `records` to pages `{prefix}/$1`, `$2`, ... of at most 256 KiB and returns the
460
- /// page number of every record.
451
+ /// Writes graphic `records` to pages `{prefix}/$1`, `$2`, ... of at most 256 KiB and returns
452
+ /// the page number of every record. Dictionary pages keep the seed's layout instead (`named`).
461
453
  fn put_pages(
462
454
  replacements: &mut BTreeMap<String, Option<Vec<u8>>>,
463
455
  prefix: &str,
@@ -0,0 +1,203 @@
1
+ //! Dictionary pages of the output: `Dgn^Nm/$n` (tables, tag sets, cell definitions) and
2
+ //! their XAttribute pages `Dgn^NmA/$n`.
3
+ //!
4
+ //! The seed's page layout is kept: every seed page keeps its number, its records stay in
5
+ //! order, and a page whose records did not change keeps its stream byte for byte (empty
6
+ //! pages included). A changed page is rewritten in place; new tables and tag sets share one
7
+ //! new page after the seed's last page. MicroStation keeps a complex element (a table with
8
+ //! its entries, a cell definition with its components) on one page and does not join one
9
+ //! across a page boundary: a level table split over two pages is not loaded as one table.
10
+ //! Its pages hold as many records as that takes (595,424 inflated bytes observed), so only
11
+ //! the caller's decompression limit bounds a page; nothing is split at a fixed size
12
+ //! (`docs/dgn/FORMAT_NOTES.md`, "Dictionary pages").
13
+
14
+ use super::{compressed, encode::u32_at, raster, tables::NamedRecord};
15
+ use crate::native::v8::{AuxPage, AuxRecord, Page, PageHeader, RawElement};
16
+ use cadkit_core::{Error, Result};
17
+ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
18
+
19
+ /// Puts the dictionary pages that differ from the seed's, and the XAttribute pages whose
20
+ /// records moved with their elements, into `replacements`. `records` are in stored order
21
+ /// (as [`super::tables::Tables::finish`] returns them). The inflated records together must
22
+ /// not exceed `max` bytes.
23
+ pub(super) fn write(
24
+ replacements: &mut BTreeMap<String, Option<Vec<u8>>>,
25
+ seed_pages: &[Page],
26
+ seed_aux: &[AuxPage],
27
+ records: &[NamedRecord],
28
+ max: u64,
29
+ ) -> Result<()> {
30
+ let total = records
31
+ .iter()
32
+ .try_fold(0u64, |sum, r| sum.checked_add(r.bytes.len() as u64 + 4))
33
+ .ok_or_else(|| Error::LimitExceeded("DGN dictionary records".into()))?;
34
+ if total > max {
35
+ return Err(Error::LimitExceeded("DGN dictionary records".into()));
36
+ }
37
+ let new_page = seed_pages
38
+ .iter()
39
+ .map(|p| p.number)
40
+ .max()
41
+ .unwrap_or(0)
42
+ .checked_add(1)
43
+ .ok_or_else(|| Error::LimitExceeded("DGN page count".into()))?;
44
+ let mut pages: BTreeMap<u32, Vec<&NamedRecord>> = BTreeMap::new();
45
+ for r in records {
46
+ pages.entry(r.page.unwrap_or(new_page)).or_default().push(r);
47
+ }
48
+ for page in seed_pages {
49
+ let kept = pages.get(&page.number).map(Vec::as_slice).unwrap_or(&[]);
50
+ let same = kept.len() == page.elements.len()
51
+ && kept
52
+ .iter()
53
+ .zip(&page.elements)
54
+ .all(|(r, e)| r.prefix == e.prefix && r.bytes == e.bytes);
55
+ if !same {
56
+ replacements.insert(
57
+ page.path.clone(),
58
+ Some(encode(kept, page.header.format_version)?),
59
+ );
60
+ }
61
+ }
62
+ // As before 0.3.5, new pages take the version of the seed's first page.
63
+ let version = seed_pages
64
+ .first()
65
+ .map(|p| p.header.format_version)
66
+ .unwrap_or(3);
67
+ if let Some(added) = pages.get(&new_page) {
68
+ replacements.insert(
69
+ format!(
70
+ "{}/${new_page}",
71
+ storage(seed_pages.first().map(|p| &p.path), "Dgn^Nm")
72
+ ),
73
+ Some(encode(added, version)?),
74
+ );
75
+ }
76
+ move_xattributes(replacements, seed_pages, seed_aux, &pages)
77
+ }
78
+
79
+ /// The storage of a seed page `path` (`Dgn^Nm` of `Dgn^Nm/$1`), or `default`.
80
+ fn storage<'a>(path: Option<&'a String>, default: &'a str) -> &'a str {
81
+ path.and_then(|p| p.rsplit_once('/'))
82
+ .map(|(storage, _)| storage)
83
+ .unwrap_or(default)
84
+ }
85
+
86
+ /// A dictionary page: `{record count, format version, 0, 0}`, then the zlib-compressed
87
+ /// records, each after its 4-byte prefix. An empty page is the header alone. The page
88
+ /// number and population are 0 as on every dictionary page MicroStation wrote (private
89
+ /// corpus: 3,932 of 3,932 pages); cadkit before 0.3.5 wrote the page number and the
90
+ /// record count there.
91
+ fn encode(records: &[&NamedRecord], version: u32) -> Result<Vec<u8>> {
92
+ let mut out = vec![0; 16];
93
+ u32_at(
94
+ &mut out,
95
+ 0,
96
+ u32::try_from(records.len())
97
+ .map_err(|_| Error::LimitExceeded("DGN page records".into()))?,
98
+ )?;
99
+ u32_at(&mut out, 4, version)?;
100
+ if !records.is_empty() {
101
+ let mut data = vec![];
102
+ for r in records {
103
+ data.extend(r.prefix.to_le_bytes());
104
+ data.extend_from_slice(&r.bytes);
105
+ }
106
+ out.extend(compressed(&data)?);
107
+ }
108
+ Ok(out)
109
+ }
110
+
111
+ /// Keeps every XAttribute on the auxiliary page numbered like the page of its element, as
112
+ /// on every observed file (private corpus: 4,215 of 4,215 `Dgn^NmA` records): records of
113
+ /// moved elements move with them, records of dropped seed elements are dropped, and records
114
+ /// of ids that are not dictionary elements stay where they are. Only pages that change are
115
+ /// rewritten; `Dgn^NmA/^AH` is kept (it never exceeds the highest dictionary page number
116
+ /// in the corpus, and no page is removed).
117
+ fn move_xattributes(
118
+ replacements: &mut BTreeMap<String, Option<Vec<u8>>>,
119
+ seed_pages: &[Page],
120
+ seed_aux: &[AuxPage],
121
+ pages: &BTreeMap<u32, Vec<&NamedRecord>>,
122
+ ) -> Result<()> {
123
+ if seed_aux.is_empty() {
124
+ return Ok(());
125
+ }
126
+ let mut location: HashMap<u64, u32> = HashMap::new();
127
+ for (number, records) in pages {
128
+ for r in records {
129
+ if let Some(id) = crate::le::u64_at(&r.bytes, 16) {
130
+ location.entry(id).or_insert(*number);
131
+ }
132
+ }
133
+ }
134
+ let seed_ids: HashSet<u64> = seed_pages
135
+ .iter()
136
+ .flat_map(|p| &p.elements)
137
+ .filter_map(RawElement::id)
138
+ .collect();
139
+ let mut placed: BTreeMap<u32, Vec<AuxRecord>> = BTreeMap::new();
140
+ let mut changed = BTreeSet::new();
141
+ for page in seed_aux {
142
+ placed.entry(page.number).or_default();
143
+ for record in &page.records {
144
+ let target = match location.get(&record.element_id) {
145
+ Some(number) => *number,
146
+ None if seed_ids.contains(&record.element_id) => {
147
+ changed.insert(page.number);
148
+ continue;
149
+ }
150
+ None => page.number,
151
+ };
152
+ if target != page.number {
153
+ changed.insert(page.number);
154
+ changed.insert(target);
155
+ }
156
+ placed.entry(target).or_default().push(record.clone());
157
+ }
158
+ }
159
+ for number in changed {
160
+ let seed = seed_aux.iter().find(|p| p.number == number);
161
+ if let Some(page) = seed.filter(|p| !p.complete) {
162
+ // Its undecoded tail would be lost.
163
+ return Err(Error::invalid(
164
+ 0,
165
+ format!("seed page {} could not be decoded", page.path),
166
+ ));
167
+ }
168
+ let path = match seed {
169
+ Some(page) => page.path.clone(),
170
+ None => format!(
171
+ "{}/${number}",
172
+ storage(seed_aux.first().map(|p| &p.path), "Dgn^NmA")
173
+ ),
174
+ };
175
+ // A new page takes the version of the seed's first one.
176
+ let version = seed
177
+ .or(seed_aux.first())
178
+ .map(|p| p.header.format_version)
179
+ .unwrap_or(2);
180
+ let records = placed.get(&number).map(Vec::as_slice).unwrap_or(&[]);
181
+ replacements.insert(path, Some(aux_page(records, version)?));
182
+ }
183
+ Ok(())
184
+ }
185
+
186
+ /// An XAttribute page with page number and population 0 (private corpus: 1,188 of 1,188
187
+ /// `Dgn^NmA` pages); an empty page is the header alone.
188
+ fn aux_page(records: &[AuxRecord], version: u32) -> Result<Vec<u8>> {
189
+ let header = PageHeader {
190
+ record_count: 0,
191
+ format_version: version,
192
+ page_number: 0,
193
+ population: 0,
194
+ };
195
+ if records.is_empty() {
196
+ let mut out = vec![0; 16];
197
+ u32_at(&mut out, 4, version)?;
198
+ return Ok(out);
199
+ }
200
+ let mut page = raster::aux_page(records, header)?;
201
+ u32_at(&mut page, 12, 0)?;
202
+ Ok(page)
203
+ }
@@ -168,7 +168,7 @@ impl Template {
168
168
  let (Some(path), Some(size)) = (path, size_px) else {
169
169
  return Err(Error::Unsupported(NEEDS_PATH_AND_SIZE.into()));
170
170
  };
171
- let url = file_url(path)?;
171
+ let url = full_path(path)?;
172
172
  let name = file_name(e, path)?;
173
173
  let p = Placement::new(*position, *u_vector, *v_vector, *size, model)?;
174
174
  let dpi = self.dpi(e)?;
@@ -388,9 +388,11 @@ impl XAttribute {
388
388
  off = start.checked_add(len)?;
389
389
  }
390
390
  let trailer = payload.get(off..)?.to_vec();
391
- // Sub-records 3 and 4 must hold exactly one string, which is what gets replaced.
391
+ // Sub-records 3 and 4 must hold exactly one string, which is what gets replaced; the
392
+ // full path in 4 is empty in attachments written with a relative path.
392
393
  let single = |i: usize| subs.get(i).and_then(|(_, d)| utf16z_text(d));
393
- if trailer.len() > 16 || single(3).is_none() || single(4).is_none() {
394
+ let empty = subs.get(4).is_some_and(|(_, d)| d.as_slice() == [0, 0]);
395
+ if trailer.len() > 16 || single(3).is_none() || (single(4).is_none() && !empty) {
394
396
  return None;
395
397
  }
396
398
  Some(Self {
@@ -466,10 +468,14 @@ fn identity(b: &mut [u8], id: u64, time: f64) -> Result<()> {
466
468
  f64_at(b, 0x18, time)
467
469
  }
468
470
 
469
- /// The stored URL: the path as given when it is already a `file://` URL, else `file://`
470
- /// followed by the path. The path must be absolute (a drive, UNC or rooted path): the
471
- /// attachments carry no relative paths.
472
- fn file_url(path: &str) -> Result<String> {
471
+ /// The stored full path (string linkage `0x1F`, XAttribute sub-record 4).
472
+ ///
473
+ /// An absolute path (a drive, UNC or rooted path) is stored as a `file://` URL, as every
474
+ /// observed attachment stores it; a path that already is one is kept. A relative path
475
+ /// names a file beside the DGN, so the attachment carries no machine-specific location:
476
+ /// a bare file name stores an empty full path, `.\name` (or `./name`) is stored as given.
477
+ /// Relative paths into other folders are refused.
478
+ fn full_path(path: &str) -> Result<String> {
473
479
  let has_scheme = path
474
480
  .get(..7)
475
481
  .is_some_and(|s| s.eq_ignore_ascii_case("file://"));
@@ -478,17 +484,35 @@ fn file_url(path: &str) -> Result<String> {
478
484
  } else {
479
485
  path
480
486
  };
487
+ if local.contains('\0') {
488
+ return Err(Error::invalid(0, "DGN raster path contains NUL"));
489
+ }
481
490
  let bytes = local.as_bytes();
482
491
  let drive = matches!(bytes, [d, b':', b'\\' | b'/', ..] if d.is_ascii_alphabetic());
483
- if local.contains('\0') || !(drive || local.starts_with('\\') || local.starts_with('/')) {
492
+ if drive || local.starts_with('\\') || local.starts_with('/') {
493
+ return Ok(if has_scheme {
494
+ path.to_owned()
495
+ } else {
496
+ format!("file://{path}")
497
+ });
498
+ }
499
+ let name = local
500
+ .strip_prefix(".\\")
501
+ .or_else(|| local.strip_prefix("./"))
502
+ .unwrap_or(local);
503
+ if has_scheme
504
+ || name.is_empty()
505
+ || name.contains(['\\', '/', ':'])
506
+ || matches!(name, "." | "..")
507
+ {
484
508
  return Err(Error::Unsupported(
485
- "DGN raster path must be absolute".into(),
509
+ "DGN raster path must be absolute or name a file beside the DGN".into(),
486
510
  ));
487
511
  }
488
- Ok(if has_scheme {
489
- path.to_owned()
512
+ Ok(if name.len() == local.len() {
513
+ String::new()
490
514
  } else {
491
- format!("file://{path}")
515
+ local.to_owned()
492
516
  })
493
517
  }
494
518