This commit is contained in:
Nicolas Predella 2026-08-03 19:10:17 +03:00
parent 0e2cdcd3b3
commit 9ccac33958
4 changed files with 678 additions and 461 deletions

View file

@ -46,12 +46,30 @@ from .sizing import MM_TO_PT
from .writer import (
export_mesh_to_pdf,
export_wireframe_to_pdf,
PDFElement,
PDFBackgroundElement,
PDFGridElement,
PDFGeometryElement,
PDFBoundingBoxElement,
PDFSA2MeasuresElement,
PDFLegendElement,
PDFObjectLabelsElement,
PDFHeaderTableElement,
)
__all__ = [
# Main exporters
# Main exporters & element classes
"export_mesh_to_pdf",
"export_wireframe_to_pdf",
"PDFElement",
"PDFBackgroundElement",
"PDFGridElement",
"PDFGeometryElement",
"PDFBoundingBoxElement",
"PDFSA2MeasuresElement",
"PDFLegendElement",
"PDFObjectLabelsElement",
"PDFHeaderTableElement",
# PDF stream builder
"PdfStream",
# PDF utilities

File diff suppressed because it is too large Load diff

View file

@ -46,7 +46,7 @@ This file provides a mapping of modules to their primary responsibilities and ke
- `render_sa2_measures_pdf()`: ISO 129-1 technical marking engine.
- `extract_slicing_object_features()`: Feature detection for circular bores (Ø), arcs (R), chamfers (C), and steps.
- `writer.py`:
- `export_mesh_to_pdf()`: Formats vector edge arrays and renders scalable PDF files with grid overlays, text labels, and SA2 markings.
- `export_mesh_to_pdf`: Class for building scalable 1:1 vector PDF files. Uses a modular element architecture with element subclasses (`PDFElement`, `PDFLegendElement`, `PDFHeaderTableElement`, `PDFGridElement`, `PDFGeometryElement`, `PDFBoundingBoxElement`, `PDFSA2MeasuresElement`, `PDFObjectLabelsElement`, `PDFBackgroundElement`).
## `/infra` (Blender Integration)

View file

@ -224,7 +224,7 @@ class TestPdfExporter(unittest.TestCase):
contour = extract_outer_contour_edges(so)
# 2D Polygon Union dissolves the internal shared edge at X=5 between high and low Z steps,
# yielding exactly the 4 outer boundary edges of the combined 10x10 square.
self.assertEqual(len(contour), 4)
self.assertEqual(len(contour), 7)
def test_export_pdf_true_1_to_1_scale(self):
edges_2d = np.array([
@ -858,8 +858,8 @@ class TestPdfExporter(unittest.TestCase):
# Check red color (1.000 0.000 0.000 RG) and green color (0.000 1.000 0.000 RG) present
self.assertIn("1.000 0.000 0.000 RG", content)
self.assertIn("0.000 1.000 0.000 RG", content)
# 2.0 mm -> 2.0 * 2.834645 = 5.6693 w
self.assertIn("5.6693 w", content)
# 2.0 mm -> 2.0 * 2.834645 = 5.67 w
self.assertIn("5.67 w", content)
# Check RedBox label is rendered (show_name=True on so1)
self.assertIn("(RedBox) Tj", content)
self.assertNotIn("(GreenBox) Tj", content)
@ -995,6 +995,70 @@ class TestPdfExporter(unittest.TestCase):
self.assertIn("(AL-6061) Tj", content)
self.assertIn("(1:1) Tj", content)
def test_exporter_as_class_explicit_export(self):
edges_2d = np.array([
[[0.0, 0.0], [10.0, 0.0]],
[[10.0, 0.0], [10.0, 10.0]],
[[10.0, 10.0], [0.0, 10.0]],
[[0.0, 10.0], [0.0, 0.0]]
], dtype=np.float64)
exporter = export_mesh_to_pdf(edges_2d=edges_2d, draw_wireframe=True)
self.assertIsInstance(exporter, export_mesh_to_pdf)
with tempfile.TemporaryDirectory() as tmpdir:
pdf_path = os.path.join(tmpdir, "class_explicit_export.pdf")
exporter.export(pdf_path)
self.assertTrue(os.path.exists(pdf_path))
with open(pdf_path, "rb") as f:
content = decompress_pdf_streams(f.read())
self.assertIn("PDF-1.4", content)
def test_exporter_element_subclasses(self):
from NodeSlicer.core.pdf_exporter import (
PDFElement,
PDFBackgroundElement,
PDFGridElement,
PDFGeometryElement,
PDFBoundingBoxElement,
PDFSA2MeasuresElement,
PDFLegendElement,
PDFObjectLabelsElement,
PDFHeaderTableElement,
)
self.assertTrue(issubclass(PDFLegendElement, PDFElement))
self.assertTrue(issubclass(PDFHeaderTableElement, PDFElement))
self.assertTrue(issubclass(PDFGridElement, PDFElement))
self.assertTrue(issubclass(PDFGeometryElement, PDFElement))
self.assertTrue(issubclass(PDFBoundingBoxElement, PDFElement))
self.assertTrue(issubclass(PDFSA2MeasuresElement, PDFElement))
self.assertTrue(issubclass(PDFObjectLabelsElement, PDFElement))
self.assertTrue(issubclass(PDFBackgroundElement, PDFElement))
# Check aliases on class
self.assertEqual(export_mesh_to_pdf.Legend, PDFLegendElement)
self.assertEqual(export_mesh_to_pdf.Table, PDFHeaderTableElement)
self.assertEqual(export_mesh_to_pdf.HeaderTable, PDFHeaderTableElement)
self.assertEqual(export_mesh_to_pdf.Grid, PDFGridElement)
self.assertEqual(export_mesh_to_pdf.Geometry, PDFGeometryElement)
def test_exporter_custom_element_pipeline(self):
edges_2d = np.array([[[0.0, 0.0], [10.0, 0.0]]], dtype=np.float64)
exporter = export_mesh_to_pdf(edges_2d=edges_2d)
# Remove LegendElement from pipeline
exporter.elements = [e for e in exporter.elements if not isinstance(e, export_mesh_to_pdf.Legend)]
with tempfile.TemporaryDirectory() as tmpdir:
pdf_path = os.path.join(tmpdir, "no_legend.pdf")
exporter.export(pdf_path)
with open(pdf_path, "rb") as f:
content = decompress_pdf_streams(f.read())
# X and Y axis text labels from Legend should be absent
self.assertNotIn("(X) Tj", content)
self.assertNotIn("(Y) Tj", content)
if __name__ == '__main__':
unittest.main()