draftwright 0.1.0__tar.gz → 0.1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: draftwright
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Automated technical-drawing generation for build123d
5
5
  Project-URL: Homepage, https://github.com/pzfreo/draftwright
6
6
  Project-URL: Repository, https://github.com/pzfreo/draftwright
@@ -683,6 +683,8 @@ Requires-Python: >=3.10
683
683
  Requires-Dist: build123d-drafting-helpers>=0.10.0
684
684
  Requires-Dist: build123d>=0.9.0
685
685
  Requires-Dist: kiwisolver<2,>=1.4
686
+ Provides-Extra: pdf
687
+ Requires-Dist: cairosvg>=2.7; extra == 'pdf'
686
688
  Description-Content-Type: text/markdown
687
689
 
688
690
  # draftwright
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "draftwright"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "Automated technical-drawing generation for build123d"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -28,6 +28,9 @@ classifiers = [
28
28
  "Programming Language :: Python :: 3.12",
29
29
  ]
30
30
 
31
+ [project.optional-dependencies]
32
+ pdf = ["cairosvg>=2.7"]
33
+
31
34
  [project.scripts]
32
35
  draftwright = "draftwright.make_drawing:_cli"
33
36
 
@@ -1142,6 +1142,27 @@ class Drawing:
1142
1142
  self.dxf_path = dxf_path
1143
1143
  return svg_path, dxf_path
1144
1144
 
1145
+ def export_pdf(self, out=None) -> str:
1146
+ """Write a PDF rendered from the SVG. Requires ``cairosvg`` (install with
1147
+ ``pip install draftwright[pdf]``). Calls :meth:`export` first if the SVG
1148
+ hasn't been written yet. Returns the PDF path."""
1149
+ try:
1150
+ import cairosvg
1151
+ except ImportError as exc:
1152
+ raise ImportError(
1153
+ "PDF export requires cairosvg. Install it with: pip install draftwright[pdf]"
1154
+ ) from exc
1155
+
1156
+ svg_path: str
1157
+ if not hasattr(self, "svg_path") or self.svg_path is None:
1158
+ svg_path, _ = self.export(out=out)
1159
+ else:
1160
+ svg_path = self.svg_path
1161
+ pdf_path = svg_path[:-4] + ".pdf" if svg_path.endswith(".svg") else svg_path + ".pdf"
1162
+ cairosvg.svg2pdf(url=svg_path, write_to=pdf_path)
1163
+ _log.info("PDF → %s", pdf_path)
1164
+ return pdf_path
1165
+
1145
1166
  def _add_shapes(self, exporter):
1146
1167
  """Add every view layer and annotation to *exporter* with error context."""
1147
1168
  for name, (vis, hid) in self.views.items():
@@ -1233,15 +1254,14 @@ def _auto_annotate(dwg, a):
1233
1254
  return a.PV_Y + (y - a.cy) * a.SCALE
1234
1255
 
1235
1256
  # Tighten right-strip outer_limits to the actual iso view left edge now
1236
- # that the iso has been projected and fitted. Guard: only apply if the
1237
- # limit is above the strip cursor an overflowing iso can produce
1238
- # _iso_x_limit < cursor, which would silence every right-strip allocation.
1257
+ # that the iso has been projected and fitted. Always apply so that any
1258
+ # future allocations are bounded; warn when the cursor has already passed
1259
+ # the limit (dims already placed may overlap the iso view).
1239
1260
  _iso_x0, _, _, _ = _iso_bbox(dwg)
1240
1261
  _iso_x_limit = _iso_x0 - 4
1241
1262
  for _rs in (a.fv_zones.right, a.pv_zones.right, a.sv_zones.right):
1242
- if _iso_x_limit > _rs._cursor:
1243
- _rs.outer_limit = min(_rs.outer_limit, _iso_x_limit)
1244
- else:
1263
+ _rs.outer_limit = min(_rs.outer_limit, _iso_x_limit)
1264
+ if _rs._cursor >= _iso_x_limit:
1245
1265
  _log.warning(
1246
1266
  "right-strip cursor %.1f >= iso_x limit %.1f: right-strip dims"
1247
1267
  " may overlap iso view (iso view overflows into annotation zone)",
@@ -2672,7 +2692,10 @@ def _fit_iso_view(dwg, a):
2672
2692
  if extent > 0
2673
2693
  ]
2674
2694
  needed = min(ratios, default=1.0)
2675
- factor = next((f for f in _ISO_SHRINK_FACTORS if f <= needed), _ISO_SHRINK_FACTORS[-1])
2695
+ # Apply a 2 % safety margin and floor to 4 decimal places to avoid
2696
+ # floating-point creep past the region boundary. The iso is NTS so
2697
+ # there is no need to constrain to "clean" fractions.
2698
+ factor = math.floor(needed * 0.98 * 10000) / 10000
2676
2699
  _project_iso(dwg, a, a.SCALE * factor)
2677
2700
  bb = _iso_bbox(dwg)
2678
2701
  if not _bbox_within(bb, region):
@@ -2960,7 +2983,6 @@ def generate_script(
2960
2983
 
2961
2984
 
2962
2985
  def _cli():
2963
- logging.basicConfig(level=logging.INFO, format="%(message)s")
2964
2986
  ap = argparse.ArgumentParser(
2965
2987
  description="Zero-AI STEP → technical drawing (SVG + DXF, or editable .py script)",
2966
2988
  formatter_class=argparse.ArgumentDefaultsHelpFormatter,
@@ -2997,13 +3019,29 @@ def _cli():
2997
3019
  "'annotate' — add PMI-derived dimensions to the drawing"
2998
3020
  ),
2999
3021
  )
3022
+ ap.add_argument(
3023
+ "--pdf",
3024
+ action="store_true",
3025
+ help="Also write a PDF (requires draftwright[pdf] / cairosvg)",
3026
+ )
3027
+ ap.add_argument(
3028
+ "-v",
3029
+ "--verbose",
3030
+ action="store_true",
3031
+ help="Show detailed progress (default: warnings and errors only)",
3032
+ )
3000
3033
  args = ap.parse_args()
3001
3034
 
3035
+ logging.basicConfig(
3036
+ level=logging.INFO if args.verbose else logging.WARNING,
3037
+ format="%(message)s",
3038
+ )
3039
+
3002
3040
  if args.script and (args.scale is not None or args.page is not None):
3003
3041
  ap.error("--scale/--page only apply to direct output; edit the generated script instead")
3004
3042
 
3005
3043
  if args.script:
3006
- generate_script(
3044
+ py_path = generate_script(
3007
3045
  step_file=args.step_file,
3008
3046
  out=args.out,
3009
3047
  title=args.title,
@@ -3012,8 +3050,9 @@ def _cli():
3012
3050
  drawn_by=args.drawn_by,
3013
3051
  pmi=args.pmi,
3014
3052
  )
3053
+ print(py_path)
3015
3054
  else:
3016
- make_drawing(
3055
+ dwg = build_drawing(
3017
3056
  step_file=args.step_file,
3018
3057
  out=args.out,
3019
3058
  title=args.title,
@@ -3024,6 +3063,11 @@ def _cli():
3024
3063
  page=args.page,
3025
3064
  pmi=args.pmi,
3026
3065
  )
3066
+ svg_path, dxf_path = dwg.export()
3067
+ print(svg_path)
3068
+ print(dxf_path)
3069
+ if args.pdf:
3070
+ print(dwg.export_pdf())
3027
3071
 
3028
3072
 
3029
3073
  if __name__ == "__main__":
@@ -1167,10 +1167,13 @@ def test_shrunk_iso_keeps_world_to_page_mapping(shrunk_iso_drawing):
1167
1167
  vis, _hid = dwg.views["iso"]
1168
1168
  bb = vis.bounding_box()
1169
1169
  assert bb.min.X < centre[0] < bb.max.X and bb.min.Y < centre[1] < bb.max.Y
1170
- # This fixture shrinks the iso to 1/2 sheet scale (see the NTS test above).
1171
- # World +Z maps to page +Y; the offset must use the shrunk view scale.
1170
+ # World +Z maps to page +Y; the offset must use the shrunk view scale (not
1171
+ # the sheet scale). Derive the actual shrunk scale from _coords so the
1172
+ # test does not depend on a specific discretised shrink factor.
1173
+ shrunk_scale = dwg._coords["iso"]._scale
1174
+ assert shrunk_scale < dwg.scale, "iso should be shrunk below sheet scale"
1172
1175
  raised = dwg.at("iso", cx, cy, cz + 100)
1173
- assert raised[1] - centre[1] == pytest.approx(100 * dwg.scale * 0.5)
1176
+ assert raised[1] - centre[1] == pytest.approx(100 * shrunk_scale)
1174
1177
 
1175
1178
 
1176
1179
  @pytest.mark.timeout(60)
File without changes
File without changes
File without changes
File without changes
File without changes