Ticketsmith

What you see is where the dots land.

Ticketsmith is a ticket designer for Boca Systems printers. Put text, barcodes and the printer's own ticket counter on the stock, set a quantity, print. What leaves the app is plain FGL, Boca's Friendly Ghost Language, and you can read every byte before it goes.

Free and open source (MIT) · macOS 12+ · Apple silicon and Intel

Ticketsmith
TicketsmithBoca FGLwidth2.0inlength5.5in200 dpi 2 x 5.5
MIDNIGHT ELECTRICSAT OCT 18 2026 8:00 PMTHE ORPHEUM - LOS ANGELESSEC 12 ROW H SEAT 24GENERAL ADMISSION$45.00NO REFUNDS - NO EXCHANGES - ALL SALES FINALSCAN AT GATETKT-100240000001

FGL for one job — 482 bytes

<RC20,20><LT2><BX360,1060><NR><F6><HW1,1><RC34,44>MIDNIGHT ELECTRIC<NR><F3><HW1,1><RC100,44>SAT OCT 18 2026   8:00 PM<NR><F2><HW1,1><RC140,44>THE ORPHEUM - LOS ANGELES<RC176,44><LT2><HX620><NR><F3><HW1,1><RC200,44>SEC 12   ROW H   SEAT 24<NR><F2><HW1,1><RC246,44>GENERAL ADMISSION<NR><F6><HW1,1><RC280,44>$45.00<NR><F1><HW1,1><RC352,44>NO REFUNDS - NO EXCHANGES - ALL SALES FINAL<NR><F1><HW1,1><RC120,748>SCAN AT GATE<NR><RC140,748><X2><BI><oP6>^TKT-10024^<NR><F3><RC240,748><PC><p>

Elements

Inspector

Text

Print

482 bytes per job

12 elements on a 2.00" x 5.50" ticket.v0.1.0

That window is drawn, not screenshotted, and it works. The ticket is the app's starter ticket, element for element; the FGL under it comes from a port of the app's emitter and matches it byte for byte. Drag anything, nudge it with the arrow keys, rename the band — and Save really writes ticket.fgl.

FGL is the file format.

No driver, no PDF, no raster in the middle. Ticketsmith writes the commands you would write by hand — <RC> to place, <F3> to pick a font, <p> to print and cut — and the printer does the rest at full speed.

Measured the way the printer measures.

Positions are printer dots. Text advances one font box per character, using the resident fonts' metrics from Boca's own supplement, so the preview's letters land where the head will put them.

Free, with the source.

MIT licensed and on GitHub — a native Mac app in Rust, signed and notarized, that keeps itself up to date. Fork it for your box office if it does not do the thing you need.

Coordinates

FGL's coordinates trip up everybody exactly once.

<RC row,column> counts rows down across the print head and columns along the paper feed. So a 2" × 5.5" ticket is 400 rows by 1,100 columns, and the long side is column — which reads backwards until you hold the ticket the way the printer does.

Ticketsmith draws the ticket in exactly that orientation, the 5.5" feed axis running left to right, so the numbers in the inspector and the picture on the screen never disagree. The status bar shows the dot grid, because that is the unit everything is in.

col 0column → along the feed · 5.5"1100
row 0row↓2"400
<RC34,44> — the event namelast printable dot
<RC383,1049>
The guide quotes the bottom-right printable corner of this stock as row 383, column 1049. The dashed line is the 16 dot margin the app draws as a guide.
200 dpi head1,100 × 400
300 dpi head1,650 × 600
600 dpi head3,300 × 1,200

The same stock at each head density. Both dimensions are free in the app; the glyphs are not — a font's dots are the same at every density, so on a 600 dpi head the same text prints a third the size.

Fonts

Fourteen resident fonts, measured in dots.

Every Boca carries its fonts in firmware, from a 5 × 7 dot fine-print face to Orator at 46 × 91. Each has a glyph size and a larger box it advances by, and Ticketsmith lays text out by the box, the way the printer does. Below, every one at the same scale: the outline is the box, the fill is the glyph. Fonts 14 and 15 are marked “for Miltope users only” in the guide, and are left out.

1459271638131011612
F1tiny5×7 in 7×8
F4OCR-A5×9 in 7×11
F5—5×11 in 7×12
F9OCR-B13×20 in 13×22
F2small8×16 in 10×18
F7OCR-A15×29 in 20×31
F16Cyrillic18×31 in 20×33
F3OCR-B17×31 in 20×33
F8Courier20×40 in 20×33
F13Courier intl20×40 in 20×42
F10Prestige25×41 in 28×41
F11Script25×49 in 26×49
F6OCR-B large30×52 in 34×56
F12Orator46×91 in 47×91

Barcodes

The printer draws the bars. The app gets the payload right.

Boca firmware encodes every symbol itself — Code 128's code-set switching, the check characters, the lot. What it needs from software is a payload in the right delimiters, data the symbology can actually hold, and a size. So the data is checked per symbology as you type, UPC and EAN check digits are worked out for you, and the preview reserves exactly the space the printed symbol will take.

Code 128any printable ASCII^TKT-10024^
Code 390-9 A-Z space - . $ / + %*TKT-10024*
Interleaved 2 of 5digits, even count:10024000:
Codabardigits and - $ : / . +A10024B
UPC-A11 digits, check digit addedJ401234K567893L
EAN-1312 digits, check digit added4J006381K333931L
QRany printable ASCII<QR6,0,0,0>{…}
UPC-A

Type eleven digits; the twelfth is arithmetic.

The guide works through 40123456789 and gets a check digit of 3. So does the app, and then splits the twelve into the guarded halves FGL wants:

<NR><RC0,10><X2><uP5>J401234K567893L

Orientation is picket fence or ladder, with bar height in 8 dot units, the narrow-bar width, the 3:1 wide ratio where the symbology allows it, and the human-readable line underneath. QR takes a version, a point size and an error-correction level — and FGL numbers those levels M, L, H, Q, which is the kind of thing an app should remember so you do not have to.

A run

Fifty numbered tickets are one job.

Set a quantity and the layout goes out once, with an <RE> repeat asking for the rest — which is how a Boca runs at full speed, instead of waiting on a computer to send fifty copies of the same thing.

Turn on numbering and the job preloads the printer's own seven-digit counter. A Number element on the ticket stamps it, and the printer increments it per ticket, so the run comes out sequential without the layout being sent again.

Deliberately absent is <CB>, the clear-buffer command. The guide is explicit that it costs throughput, and the printer clears itself between tickets anyway.

The ticket above, fifty times, numbered from 1001 — 499 bytes
  1. <TC0001001>Preload the counter at 1001
  2. <RC20,20><LT2><BX360,1060>…<PC>The layout, 479 bytes, sent once
  3. <RE49>Forty-nine more — the quantity less the one
  4. <p>Print and cut. <q> leaves it uncut; <z> ejects
One ticket is 482 bytes. Fifty are 17 more.

Getting it to the printer

Four ways out, covering both ports a Boca ships with.

Sending runs on a background executor, so a printer that is switched off waits out its connect timeout without freezing the window — and the status line says what happened in words, because “the printer is unplugged” is a normal Tuesday.

01

Ethernet

Raw TCP to port 9100

Every Boca ethernet and WiFi interface listens there, and nothing is wrapped around the FGL. Type the printer's address and press print.

02

USB device

A device node

USB when the printer's port is in serial mode (<usbs>): /dev/cu.usbmodem* on macOS. The bytes are written straight at it.

03

Print queue

lp -d QUEUE -o raw

USB when the printer is left in its default printer mode (<usbp>) and installed as a CUPS queue. Raw, so no driver gets to reinterpret it.

04

Save .fgl

Write the job to disk

Test the whole pipeline without burning stock — and keep the file, because it is the exact bytes a printer would get.

Updates

A release is what every installed copy will run.

The status bar shows the version. When a newer release is out, a link beside it downloads the update and checks it, and nothing is replaced until you click again — a second click because restarting clears the ticket on screen. Then the new app is swapped in for the old in a single step and relaunched.

Its signature is intact

codesign --verify --deep --strict on the app inside the mounted disk image, the first of the three checks.

It is signed by this team

A valid Developer ID only proves that somebody signed it, so the leaf certificate has to carry the team the app was built by, and that team is compiled in rather than read from the bundle.

Apple has notarized it

spctl --assess, so an update clears the same bar a first download does when you open it.

The network is /usr/bin/curl and the rest is Apple's own tools, so the updater adds no HTTP or TLS stack to the app. The swap is renamex_np with RENAME_SWAP: there is no moment where the old app is gone and the new one is not there yet. A copy that cannot replace itself — run from the disk image, or from a folder you cannot write to — offers the release page instead.

Underneath

Rust on GPUI, and the core knows nothing about a window.

The window is GPUI, the GPU-drawn framework Zed is built with, plus a component library on top of it — because GPUI has no text input, dropdown or checkbox of its own, and this app is mostly a form. Under that, the FGL, the barcodes and the document are plain Rust with no UI dependency at all: 52 of the 98 tests run against them without a window.

src/fgl.rs

The resident fonts and their dot metrics, text, rules, boxes, the counter, print and repeat.

src/barcode.rs

Validation per symbology, FGL emission, GTIN check digits, and bars for the preview.

src/ticket.rs

The document: stock, elements, their geometry, hit testing, and the whole-job emitter.

src/transport.rs

The four ways out: TCP, a device node, lp, and a file.

src/update.rs

Asking GitHub for a newer release, and verifying and installing it.

src/preview.rs

Projection from printer dots to window pixels, and all the painting.

src/app.rs

The window: toolbar, preview interaction, inspector, print panel.

$ git clone https://github.com/wes/ticketsmith.git
$ cd ticketsmith && cargo run

The first build takes a few minutes because it compiles GPUI. After that it is seconds.

Free, and the source comes with it.

Open the disk image and drag Ticketsmith to Applications — it is signed and notarized, so it opens like anything else. No account, no licence key, no trial. Point it at a printer, or save a .fgl first and read what it would have sent.

1 downloadof v0.1.0 since 6 October 2026

macOS 12+ · Apple silicon and IntelRelease notesMIT licenceReport an issue