---
id: S02
parent: M001
milestone: M001
provides:
  - postprocess_svg() function for RDP simplification + island detection + node counting
  - Output generators: generate_dxf(), generate_json(), generate_svg()
  - /engine/simplify endpoint for SVG-to-SVG/DXF/JSON simplification
  - output_format routing on /engine/trace (svg, dxf, json)
  - _format_response() pattern for consistent multi-format responses
requires:
  - slice: S01
    provides: Core pipeline: preprocessing + potrace_trace() + vtracer_trace() producing raw SVG output
affects:
  - S03
key_files:
  - engine/pipeline/postprocess.py
  - engine/output/dxf.py
  - engine/output/json_output.py
  - engine/output/svg.py
  - engine/output/__init__.py
  - engine/api/routes.py
  - engine/tests/test_postprocess.py
  - engine/tests/test_output.py
  - engine/tests/test_api.py
key_decisions:
  - DXF output as raw bytes with application/dxf content-type and metadata in X-Kerf-Metadata header
  - postprocess_svg() replaces regex metadata extraction — full XML path parsing for structured PathInfo objects
  - _format_response() shared helper for consistent response shaping across endpoints
  - Islands placed on separate ISLANDS layer in DXF for downstream CAM tool compatibility
  - Bezier curves linearized during post-processing for RDP simplification and DXF polyline generation
patterns_established:
  - PostProcessResult as the universal intermediate representation consumed by all output generators
  - Output generators are pure functions: PostProcessResult → bytes/string, no side effects
  - _format_response() pattern for consistent multi-format API responses with metadata
observability_surfaces:
  - Metadata in every API response: path_count, node_count_total, open_paths, island_count, warnings, processing_ms
  - X-Kerf-Metadata header on DXF responses carries the same metadata as JSON envelope responses
drill_down_paths:
  - .gsd/milestones/M001/slices/S02/tasks/T01-SUMMARY.md
  - .gsd/milestones/M001/slices/S02/tasks/T02-SUMMARY.md
  - .gsd/milestones/M001/slices/S02/tasks/T03-SUMMARY.md
duration: ""
verification_result: passed
completed_at: 2026-03-26T04:41:57.970Z
blocker_discovered: false
---

# S02: Post-Processing + Output Formats (SVG, DXF, JSON)

**Full post-processing pipeline (RDP simplification, island detection, open path repair) with three output format generators (SVG, DXF, JSON) and /engine/simplify endpoint — 169 tests passing.**

## What Happened

This slice added the complete post-processing layer and output format system to the Kerf Engine pipeline.

**T01 — Post-processing module** (`engine/pipeline/postprocess.py`, 414 lines): Built the core `postprocess_svg()` function that parses raw SVG output from vectorizers into structured `PathInfo` objects. Implements: (1) SVG path d-attribute parsing supporting M, L, C, Q, Z, H, V commands with relative variants; (2) Ramer-Douglas-Peucker simplification with tunable epsilon; (3) Island detection via signed-area calculation (negative area = clockwise winding = interior hole); (4) Open path detection by comparing start/end coordinates; (5) Per-path and aggregate node counting. The `PostProcessResult` dataclass carries all structured data downstream to output generators.

**T02 — Output format generators** (`engine/output/`): Created three format generators consuming `PostProcessResult`. DXF generator (`dxf.py`, 66 lines) uses ezdxf to create AC1015 (AutoCAD R2000) documents with LWPOLYLINE entities — islands go on an "ISLANDS" layer, outer contours on layer "0", and closed paths get the polyline close flag. JSON generator (`json_output.py`, 76 lines) outputs structured path arrays with M/L/Z commands and per-path properties (is_closed, is_island, area, node_count). SVG generator (`svg.py`, 22 lines) returns the simplified SVG string from PostProcessResult.

**T03 — API integration** (`engine/api/routes.py`, 175 lines): Wired post-processing into `/engine/trace` after vectorization. Added `output_format` parameter routing (svg/dxf/json). Created `/engine/simplify` endpoint accepting SVG upload + epsilon + output_format. Built `_format_response()` shared helper for consistent response shaping. DXF returns raw bytes with `application/dxf` content type and metadata in `X-Kerf-Metadata` header. SVG and JSON return JSON envelopes with output + metadata. Wrote 35 integration tests covering all format combinations for both endpoints.

## Verification

All verification runs use `.venv/bin/python -m pytest` per project pattern P002.

- `engine/tests/test_postprocess.py`: Tests for RDP simplification, island detection, open path detection, SVG parsing, node counting — all pass.
- `engine/tests/test_output.py`: Tests for DXF structure (AC1015, LWPOLYLINE entities, closed flags, island layers), JSON structure (path commands, metadata), SVG output, round-trip consistency — all pass.
- `engine/tests/test_api.py`: 35 integration tests covering /engine/trace (SVG, JSON, DXF output for potrace and vtracer modes, validation errors) and /engine/simplify (SVG, JSON, DXF output, validation errors, epsilon defaults) — all pass.
- Full suite: 169 tests pass in 0.92s with only ezdxf pyparsing deprecation warnings (harmless).

## Requirements Advanced

None.

## Requirements Validated

None.

## New Requirements Surfaced

None.

## Requirements Invalidated or Re-scoped

None.

## Deviations

Replaced the old _extract_svg_metadata() regex-based function entirely with postprocess_svg() pipeline. D003 (regex-based metadata extraction) is now superseded — full XML/path parsing is required for DXF/JSON generation. DXF responses use raw binary Response with X-Kerf-Metadata header instead of a JSON envelope, since DXF is binary data.

## Known Limitations

- Bezier curves (C, Q commands) in SVG paths are sampled to linear segments during post-processing. This means DXF output uses polylines only, not splines. Adequate for CNC/laser use cases but loses mathematical curve precision.
- RDP simplification operates on linearized coordinates, not on the original bezier control points. Very small epsilon values may not reconstruct the original curve quality.
- No viewBox/transform matrix handling in SVG path parsing — assumes paths use absolute coordinates in the document space.

## Follow-ups

None.

## Files Created/Modified

- `engine/pipeline/postprocess.py` — New: Post-processing module with RDP simplification, island detection, open path repair, SVG path parsing (414 lines)
- `engine/output/__init__.py` — New: Package init exporting generate_dxf, generate_json, generate_svg
- `engine/output/dxf.py` — New: AC1015 DXF generator using ezdxf — LWPOLYLINE entities with island layer separation (66 lines)
- `engine/output/json_output.py` — New: JSON output generator with path commands and properties (76 lines)
- `engine/output/svg.py` — New: SVG output generator returning simplified SVG from PostProcessResult (22 lines)
- `engine/api/routes.py` — Rewritten: integrated postprocess_svg(), output_format routing, /engine/simplify endpoint, _format_response() helper (175 lines)
- `engine/tests/test_postprocess.py` — New: Tests for RDP, island detection, open paths, SVG parsing, node counting (375 lines)
- `engine/tests/test_output.py` — New: Tests for DXF structure, JSON structure, SVG output, round-trip consistency (274 lines)
- `engine/tests/test_api.py` — Rewritten: 35 integration tests for /engine/trace and /engine/simplify across all format combinations (515 lines)
