# Generating Valid HPGL for the XY4160 Pen Plotter

Your XY4160 is a great machine — it's essentially HP 7470A-compatible, uses RS-232 at **4800 bps**, and understands a **subset of HPGL**. The key issue with Inkscape is that it generates bloated/modern HPGL with commands the XY4160's simple Z80-based interpreter doesn't understand.

Here are your best options, from easiest to most powerful:

---

## ✅ Option 1: `vpype` — Best Tool for the Job

[`vpype`](https://github.com/abey79/vpype) is a modern command-line tool specifically built for pen plotters. It reads SVG and outputs **clean, minimal HPGL** perfectly suited for vintage plotters.

### Install

```bash
pip install vpype
```

### Basic SVG → HPGL conversion

```bash
vpype read input.svg write --device hp7470a output.hpgl
```

The XY4160 is HP 7470A-compatible, so use `hp7470a` as the device. The output will be clean `IN; SP1; PU; PD; PA;` style HPGL — exactly what your plotter expects.

### Optimized pipeline (recommended)

```bash
vpype read input.svg \
  linesimplify \
  linemerge \
  linesort \
  write --device hp7470a --page-size a4 output.hpgl
```

| Command | What it does |
|---|---|
| `linesimplify` | Reduces excess nodes/segments |
| `linemerge` | Merges connected lines to reduce pen lifts |
| `linesort` | Minimizes pen-up travel (faster plotting) |

### Custom config for XY4160 (optional, for exact dimensions)

The XY4160 has **7400 × 10400 HPGL units** over **186 × 258 mm** (A4). Create a `~/.vpype.toml`:

```toml
[device.xy4160]
name = "XY4160 Czechoslovak Plotter"
plotter_unit_length = "0.025mm"
pen_count = 1

[[device.xy4160.paper]]
name = "a4"
paper_size = ["258mm", "186mm"]
x_range = [0, 10400]
y_range = [0, 7400]
y_axis_up = true
origin_location = ["0mm", "186mm"]
final_pu_params = "0,0"
```

Then use:

```bash
vpype read input.svg linesimplify linemerge linesort write --device xy4160 output.hpgl
```

---

## ✅ Option 2: Write HPGL Directly in Python

Since the XY4160 only needs **basic HPGL commands**, you can write a simple Python script to generate it directly. This is the most reliable approach — no conversion artifacts.

The XY4160 understands these commands:

| Command | Meaning |
|---|---|
| `IN;` | Initialize plotter |
| `SP1;` | Select pen 1 |
| `PU x,y;` | Pen up, move to x,y |
| `PD x,y;` | Pen down, draw to x,y |
| `PA x,y;` | Plot absolute (line if pen down) |
| `CI r;` | Draw circle of radius r |
| `SP0;` | Return pen to holder |

### Example Python script

```python
def hpgl_box(x1, y1, x2, y2):
    """Draw a rectangle from (x1,y1) to (x2,y2)"""
    return [
        f"PU{x1},{y1};",
        f"PD{x2},{y1};",
        f"PA{x2},{y2};",
        f"PA{x1},{y2};",
        f"PA{x1},{y1};",
        "PU;",
    ]

def hpgl_circle(cx, cy, r):
    """Draw a circle centered at (cx,cy) with radius r"""
    return [
        f"PU{cx},{cy};",
        f"CI{r};",
        "PU;",
    ]

# Build the HPGL file
commands = []
commands.append("IN;")       # Initialize
commands.append("SP1;")      # Select pen 1

# Draw a box (XY4160 units: 40 units = 1mm)
# This draws a 100mm x 80mm box starting at 20mm,20mm
commands += hpgl_box(800, 800, 4800, 4000)

# Draw a circle at center of box, radius 30mm
commands += hpgl_circle(2800, 2400, 1200)

commands.append("SP0;")      # Return pen

# Write to file
with open("output.hpgl", "w") as f:
    f.write("\n".join(commands))

print("Done! output.hpgl generated.")
```

> 💡 **Unit reminder:** The XY4160 uses **40 HPGL units = 1 mm**, so multiply your mm dimensions by 40.

---

## ✅ Option 3: Fix Inkscape Output (Quick Workaround)

If you want to keep using Inkscape, post-process its output with a simple Python filter to strip unsupported commands:

```python
# Strip unsupported HPGL commands for XY4160
ALLOWED_PREFIXES = ("IN", "SP", "PU", "PD", "PA", "PR", "CI", "VS", "LT")

with open("inkscape_output.hpgl") as f:
    lines = f.read().split(";")

cleaned = []
for cmd in lines:
    cmd = cmd.strip()
    if any(cmd.startswith(p) for p in ALLOWED_PREFIXES):
        cleaned.append(cmd + ";")

with open("cleaned_output.hpgl", "w") as f:
    f.write("\n".join(cleaned))

print("Cleaned HPGL written.")
```

---

## 📡 Sending HPGL to the XY4160 via Serial

Once you have a valid `.hpgl` file, send it via RS-232:

```bash
# Linux/macOS — set port parameters first
stty -F /dev/ttyUSB0 4800 cs8 -cstopb -parenb crtscts

# Then send the file
cat output.hpgl > /dev/ttyUSB0
```

Or use Python:

```python
import serial
import time

with serial.Serial('/dev/ttyUSB0', baudrate=4800,
                   bytesize=8, parity='N', stopbits=1,
                   rtscts=True) as ser:  # Hardware handshaking!
    with open("output.hpgl", "rb") as f:
        data = f.read()
    # Send in small chunks — XY4160 only has 512 bytes buffer!
    chunk_size = 64
    for i in range(0, len(data), chunk_size):
        ser.write(data[i:i+chunk_size])
        time.sleep(0.1)  # Give the plotter time to process
```

> ⚠️ **Critical:** The XY4160 has only **512 bytes of buffer**. Always use **hardware handshaking (RTS/CTS)** or add delays, otherwise data will be lost!

---

## 🏆 Summary Recommendation

| Goal | Best Tool |
|---|---|
| Convert existing SVG files | **`vpype`** |
| Full control / custom drawings | **Python script** |
| Fix existing Inkscape files | **Python filter script** |

**`vpype` is the overall winner** — it's actively maintained, understands vintage plotters, and produces the cleanest minimal HPGL output your XY4160 will love.