# java-epic-cli: what changed from 1.16.0 to 1.36.0

From cli-dev to datahandler-dev, 2026-10-13, at Jesse's request. 1.36.0 appended 2026-10-14.

All twenty releases are pushed to master and NOT rolled. The fleet still runs a mix of 1.8.0 and 1.9.0. Every release was regression-checked against the previous jar on 102 fixtures: without the new flags, output is byte-identical except for the four known nondeterministic vectorize PDFs, and every JSON addition is a nullable field that Gson omits when unused. No existing flag, JSON field or exit code changed. `--help=compile` was not touched in any of these releases.

Latest jar: 1.36.0 (commit and md5 in the jar commit on master).

## The one thing that affects you

Your help-parsing commit 72e8d89 must be live before any jar at 1.12.0 or newer rolls. That gate is unchanged. Everything below is additive, but when the roll happens it jumps straight from 1.9.0 to 1.36.0.

## Jesse's directive behind all of this

On 2026-10-12 Jesse set two rules. Every prepress action gets a dedicated flag ("get as many singular things as we can to dedicated features"). And the toolset is a product for hundreds of shops, so every hard-coded shop value becomes a flag with the old value as its default.

## Release by release

### 1.16.0: dedicated prepress flags, batch 3

- `--outline-text <src> [out]`: every text block becomes path geometry in the same colour, overprint, clip and z-order. Type3, vertical and unreadable glyphs stay as text with a warning. Non-embedded fonts get a substitute face.
- `--spot-to-process[=A;B] <src> [out]`: Separation and DeviceN fills and strokes repainted through the file's own tint transform into CMYK, so the plate leaves `--list-spot-colors`. Images and shadings are counted and warned, not re-encoded.
- `--convert=cmyk <src> [out]`: RGB fills and strokes converted to CMYK.
- `--overprint=on|off [--opm=0|1]`: sets overprint on every reachable ExtGState plus a page-level one.
- `--ink-coverage <src.pdf>`: the coverage report from `--separate` without writing any TIFFs.

### 1.17.0: edge band and stroke expansion

- `--edge=spot(...) [--edge-width=PT]`: a band around the solid mass of the combined art, in a spot colour, under or above the art. Page boxes grow by the width. Proven against Jesse's own Illustrator file at IoU 0.99.
- `--expand-strokes <src> [out]`: every stroke operator becomes a filled path, CTM-exact, through forms and patterns.

### 1.18.0: one separation engine

`--separate` was overhauled onto the shop halftone spec. One engine handles spot, CMYK and simulated-process separation from PDF or raster.

- Dot shapes euclidean, round, ellipse, line, square, diamond; per-plate screens via `--screens`, `--angles`, `--lpi`, `--angle`, `--dot`. Solid plates are thresholded, not screened.
- Shop defaults: process C15 M75 Y60 K45 at 85 LPI; other plates 35 LPI at dominance-ranked angles.
- `--profile=naive|icc|FILE.icc`, `--gcr`, `--black-max` on the CMYK split.
- Raster: `--underbase[=TERM][:knockout|full|solved|none]`, `--choke`, `--palette`, perceptually weighted least-squares ink solve.
- Tone shaping `--gain`, `--floor`, `--ceiling`, `--curve` on non-process plates only. The RIP owns the CMYK curve.
- `--proof[=SCALE]` composites the plates for a moiré check.
- Also in this release: `--unit=in|pt|mm|cm` across prepress modes, and join, miter, colour, dash and hairline flags on `--edge`, `--marks`, `--keyline`, `--expand-strokes`.

### 1.19.0 to 1.21.0: hard-code audit

Every remaining fixed shop value became a flag. Defaults unchanged.

- 1.19.0, separation: `--process-lpi`, `--spot-lpi`, `--process-angles`, `--fill-angles`, `--sim-angles`, `--angle-rank`, `--min-dot`, `--max-dot`, `--screen-origin`, `--resample`, `--compression`, `--name-template`, `--plate-order` (first printed first; adds `plates[].pass`), `--spread`, `--choke-shape`, `--tac`, `--max-inks`, `--input-dpi=auto` and more.
- 1.20.0, prepress and geometry: `--pages`, `--from-box`, `--fit-mode`, `--marks-reg-pos`, `--step-rotate`, `--keyline-align`, `--edge-tint`, `--outline-missing`, `--overprint-part`, `--render-format`, `--match=exact|ci|glob` on `--remove-color`, `--strip-ai-payload`, and the rest. Also `--bleed --marks` now dispatches to marks.
- 1.21.0, vectorize: `--paper-min-l`, `--paper-max-chroma`, `--dup-delta-e`, `--spot-prefix`, `--backdrop`, `--alpha-threshold`, `--weld-tolerance`, `--dump-map`. Help corrected: `--simplify` is points 0.18, not px 0.75.

### 1.22.0: every halftone dot shape

- New shapes: cross, ellipse with ratio, inverse of any shape, fm (error diffusion), noise (blue-noise mask), hybrid (AM in the midtones, blue-noise outside). All area-linear.
- Manipulations: `--phase`, `--jitter` with `--seed`, `--invert=A;B`, and per-plate `--plate-min-dot`, `--plate-max-dot`, `--plate-phase`, `--plate-gain`, `--plate-range`, `--plate-curve`.
- `--linearity[=STEPS]` screens a wedge through every screen the job uses and reports the max error.

### 1.23.0: stroke alignment

- `--expand-strokes` gains `--expand-join`, `--expand-cap`, `--expand-miter`.
- `--separate --choke-shape=diamond`.

### 1.24.0 and 1.25.0: Illustrator Image Trace parity on `--vectorize`

- 1.24.0: every Image Trace panel control has a flag with the panel's own name. `--mode=color|grayscale|bw`, `--palette=auto|limited|full|FILE` (FILE sniffs Adobe Swatch Exchange, a previous report JSON, or colour grammar), `--paths`, `--corners`, `--noise`, `--method=overlapping`, `--snap-lines`. Report gains `imageTrace{}` only when a panel flag is used. New topic `--help=image-trace`.
- 1.25.0: `--strokes[=MAXPX]` traces thin ink as stroked centrelines (Fills/Strokes with Max Stroke Width), `--no-fills`, `--stroke-join`, `--stroke-cap`, `--stroke-miter`. Objects gain `inks[].strokes[]{d, width_in, closed}`. AI private data is skipped when strokes exist, reported as `imageTrace.aiPrivate=false`.

### 1.26.0: print-order gang check on `--imposition`

A heat transfer prints last-ink-on-top and is applied face down, so the art's z-stack is its print order. Jobs ganged on a sheet share one press rotation, so a job fits only when its order is a subsequence of that rotation.

- Plan keys: `gang_check: off|warn|refuse`, `gang_report`, `gang_check_dpi`, `gang_check_min_overlap_px`, `pdf_documents[].print_order`. Absent means byte-identical behaviour.
- `refuse` plus a conflict is a new exit code 4 with the reason on stderr and no sheet written.
- Standalone `--gang-check a.pdf [b.pdf ..]` with `--rotation`, `--order`, `--print-order`, or `--gang-check=plan.json` as a dry run. Alias `--ink-stack`.

### 1.27.0: proof views and press order

- `--proof-view=garment,sheet`: the plates stacked as the press stacks them. garment is the print as worn. sheet is the transfer off the dryer, last pass on top, mirrored, on the carrier.
- `--press=transfer|direct`: on a transfer the underbase pass goes last, before the adhesive.
- `--carrier`, `--proof-mirror`, `--proof-hide`, `--opacity=Name=A`.

### 1.28.0: duotone

- `--duotone="TERM;TERM[;TERM[;TERM]]"` with aliases `--monotone`, `--tritone`, `--quadtone`. Separates a raster by tone through one to four given inks, each on its own curve via `--duotone-curves`.

### 1.29.0: film output

- `--film` frames every plate film-ready: label strip, margin with register targets in the same place on every plate, mirrored with `--film-mirror=on`. The plate file is the film. `--film-margin`, `--film-target`, `--film-marks`, `--film-text`, `--film-label=TEMPLATE`.
- `--gang-films=WxH` packs framed plates onto film sheets of that size.

### 1.30.0: shop profile and ink library

- `--shop[=FILE]`: a flat INI of flags without dashes, expanded in front of the command line so what is typed wins. `--shop` alone reads `$EPIC_SHOP`, else `./shop.ini`. Sections per mode.
- `--library=FILE[;FILE]` (aliases `--inks`, `--garments`): `Name = TERM` lines under `[inks]` and `[garments]`. A bare name then resolves anywhere a colour TERM is accepted. Black, White, Cyan, Magenta, Yellow are built in.

### 1.31.0: screen check

- `--screen-check` (alias `--moire`): every pair of conventional screens measured as two lattices, with the beat period in cells. Reports `screen_check{risk, plates[], pairs[]}` with risk none, low or high, and warns on anything above none.
- `--mesh=N` or `--mesh=Name=N` and `--mesh-angle=N` add the shop's mesh rules: mesh to LPI ratio, printable dot range, angle distance from thread and diagonal.

### 1.32.0: index separation

- `--index`: every painted pixel becomes exactly one solid ink, nearest in Lab with error diffusion. No halftone, no moiré. `--index-dpi=N`, `--index-dither=diffusion|none`. Reports `index{dpi, dither, width_px, height_px, inks[]}`.

### 1.33.0: dry run and image prep

- `--dry-run` (alias `--plan`): the whole job computed and reported with the paths it would write, nothing written, no directory created. Result gains `dry_run: true`.
- `--prep-levels=black,white[,gamma]`, `--prep-saturation=F`, `--prep-sharpen=amount[,radius]` prepare a raster before separation. Result gains `prep{}`.

### 1.34.0: simulated process

- `--sim-process[=N]` (default 3): a raster through N pantones beside Black and White. White is one screen serving as underbase and highlight. Black anchors shadow and outline, `--sim-black=off` drops it. Pantones are picked greedily by colour error from the picture's chroma clusters or from `--palette`. Inks given in `--colors="TERM;TERM"` are kept first. Picks are named by hue with a spec to swap for the matched PANTONE. A pick worth under 0.5 dE warns.
- Result gains `sim_process{pantones, black, ground, candidates, inks[{name, rgb, source, fit_de, gain_de}]}`.
- Measured on four full-colour photos: 95th-percentile dE fell from 7.6 to 24.7 with `--colors=4` down to 4.2 to 5.6 with three pantones.

### 1.35.0: plates PDF

- `--separate --pdf` writes `<base>_plates.pdf` beside the TIFs: every plate as a real `/Separation` ink (Acrobat Output Preview and Illustrator list them by name). Page 1 the plates stacked in press order over the paper, page 2 the original picture or source page, page 3 the swatches, then one page per plate. Result gains `pdf` and `pages[].pdf`.
- Renderer fix: a luminosity soft mask inside a plate render now draws all of its content. Illustrator re-saves of separated art (each ink image in a soft-masked form) read as empty plates before this; `--ink-coverage` and `--separate` on such a file reported 0% for every masked ink.

### 1.36.0: native Illustrator warps, full range

- `ai_private` now authors a live Illustrator Warp (Deform envelope, editable text inside, same style and bend in the Warp Options dialog) for every warp style the designer offers: `arc`, `arc-lower`, `arc-upper`, `bulge`, `flag`, at any bend from −100 to 100. Before this only a positive `arc` was authored; anything else was a hard error with `ai_private: true` and a soft-degrade without it.
- `mesh` warps are Envelope objects, not Illustrator Warps, and still soft-degrade (hard error when explicit). That wording is unchanged.
- Kerning fix in the cached warp letters: a face with only a legacy `kern` table (no GPOS) was shaped unkerned inside the private data, so the letters Illustrator shows before the first edit sat a few points right of the printed ink. Both now use the same kern precedence.
- No request-schema, flag, JSON or exit-code change. A request that compiled before compiles the same; the only behaviour change is in the private data, and in requests that previously failed.
- `--help=compile` changed in two places (the ai_private line and the mesh soft-degrade line). Nothing in the phrases you parse moved, but read the diff before the roll.

### 1.37.0: warps pinned against native Illustrator saves

- Thirty native Illustrator 30.2.1 warp saves (five styles, bends ±25/60/100, Aachen Bold) were diffed against the jar's private data. Every mesh row now matches to within 0.02 pt. Warped layers are authored as point text inside the Deform group, exactly as Illustrator writes them (frame record, composed-layout cache, warp dictionary, DeviceCMYK mesh rows, edit-mode flag), instead of an area frame inside a donor template.
- **Arc page ink changed, with or without `ai_private`:** the Arc renderer was a polar fit calibrated at one bend; it now uses the Illustrator envelope law measured from the natives. The post-warp ink box of an `arc` layer grows with the bend the way Illustrator's does (27.5 % Aachen 60 pt "ST. PETE BEACH": 571.5 × 109.8 pt where 1.36.0 gave 550.2 × 105.6). `x`/`y` placement is still the top-left of that box. Straight text and the other four styles are byte-identical to 1.36.0.
- No request-schema, flag, JSON, exit-code or `--help=compile` change.

### 1.38.0: Illustrator private data follows page transforms

- Through 1.37.0, every transform dropped the Illustrator private data, so a live-font master came back as plain PDF. That covered `--resize`, `--mirror`, `--flip`, `--rotate90/180/270`, `--fit`, `--scale`, `--align`, `--offset` and `--rotate-art`. The legacy modes had done this since 2024; the art-moving modes did it on purpose and warned.
- Now the private data is moved through the same matrix as the page art, so the output still opens in Illustrator as the live document. This covers paths, stroke widths and dashes, rasters, live-text frames, Warp envelope meshes, the artboard and the bounds.
- Payloads that cannot be moved exactly are still removed: live effects, non-Warp plugin objects, several artboards and pre-CC payloads. The warning names the reason.
- Verified exactly on 72 Illustrator files, 31 native warp saves and 30 `--compile` warp outputs. coca-cola.pdf (a live effect) is the one refusal. Plain PDFs give byte-identical output and JSON to 1.37.0, apart from the per-run /ID and dates.
- **JSON (art-moving modes, Illustrator sources only):** `ai_private_data_removed` is now `false` where it used to be `true`. The new field `ai_private_data_transformed` is true or false, and is absent when the source has no Illustrator data.
- **Legacy modes:** there is no JSON. Their output PDF now carries the moved private data where it used to carry none.
- **Fix:** `--compile` outputs write no `/NumBlock`, and the payload reader used to treat them as having no private data. `--artboard` and `--bleed` now move their artboard too (`ai_artboard_patched:true`, where it used to be false). `--backers` used to silently drop their private data. It now grafts the PRODUCTION_ONLY layer into it, as it does for native saves. The `--backers` JSON is unchanged.
- `--help=transforms` text updated. `--help=compile` untouched.

## New exit code

Exit 4 is `GangConflictException` from 1.26.0, only reachable with `gang_check: refuse` or `--gang-check`. Codes 1, 2 and 3 are unchanged.

## Help topics added or changed

`--help=prepress`, `--help=separate`, `--help=image-trace`, `--help=transforms`, `--help=remove-color`, `--help=backers`, and the index. `--help=compile` was untouched through 1.35.0; 1.36.0 edits two lines of it (see above).

## Where to read more

Every feature has its full description in the commit message on master, and `--help=<topic>` on the 1.36.0 jar is current. Ask me on storm for anything that needs expanding before the roll.
