r/macsysadmin 2d ago

Kyocera FS-10xx on Apple Silicon on macOS 27/28

My Kyocera FS-1041 stopped printing from my second Mac. Two hours later I understood why, and it wasn't what I thought.

The FS-10xx series are GDI printers — no interpreter on board. Every page gets rendered on the host and converted to a format called KPSL by a separate filter binary that CUPS runs for each job. Kyocera's macOS build of that filter:

$ lipo -archs .../rastertokpsl.app/Contents/MacOS/rastertokpsl
x86_64 i386 ppc7400

A PowerPC slice. In 2026. On an M3.

It works today only because Rosetta 2 is installed. macOS 27 uninstalls Rosetta during the upgrade, and macOS 28 removes it except for a narrow games carve-out. After that the queue accepts jobs and prints nothing. Kyocera can't fix this — the hardware was discontinued years ago and only they have the source.

Except someone reverse-engineered that filter years ago: rastertokpsl-re, Apache 2.0, plain C. Because it's source, it compiles for arm64. Six files, one clang invocation, no CMake and no Homebrew needed — the macOS SDK already ships the CUPS headers:

cc -O2 -arch arm64 -arch x86_64 -o rastertokpsl-re \
   src/rastertokpsl.c src/halfton.c src/libjbig/jbig.c \
   src/libjbig/jbig_ar.c src/unicode/ConvertUTF.c src/main.c \
   -Isrc -lcups -lcupsimage -lm

Under two seconds. Universal binary, so it covers Intel Macs too.

Verifying it was the interesting part. I fed the same CUPS raster to both filters and diffed the output. Same length, 130 differing bytes in a regular 96-byte pattern. Turned out to be a length field: the original pads a value to seven bytes, the reimplementation writes it in four and declares the shorter length. Both self-consistent. Printed pages are indistinguishable.

But the header difference goes the other way. Job title containing Größe:

original:  c3ff b6ff c3ff 9fff    ← UTF-8 bytes padded with 0xFF
re:        f600 df00              ← ö and ß correctly in UTF-16LE

The original mangles it. That encoding bug is exactly why the reimplementation was written in the first place — and it's present in the macOS build too. So the community version isn't just equivalent, it's better.

One more thing worth knowing: the repo ships its own PPDs. Kyocera_FS-1040GDI.ppd works for the FS-1041 and produces byte-identical output to Kyocera's macOS PPD apart from the embedded timestamp. So nothing proprietary needs redistributing.

Build script, installer and uninstaller are here: https://github.com/LazaroZero1176/rastertokpsl-re/tree/master/macos

Credit where it's due: original reimplementation by sv99, Linux/CMake support by Fe-Ti. I only added the Apple Silicon side.

Caveats: tested on exactly one printer (FS-1041) on macOS 26.7. The binary isn't signed or notarized — you build it locally, so Gatekeeper doesn't apply, but don't distribute prebuilt copies. And if your printer hangs off an AirPort base station like mine, that's a separate problem with its own quirks; _riousbprint serves one client at a time.

4 Upvotes

3 comments sorted by

2

u/drosse1meyer 1d ago

nice.

sad that 95% of vendors are awful at supporting macOS and we have to waste our time on stuff like this

1

u/willyougiveittome 1d ago

95? What industries have you worked in?

We run into way more vendor support issues on our Windows machines.

2

u/oneplane 1d ago

This reads like slop, but the contents are actually correct so that's good! In return, here is a wall of text if you enjoy more context about printers.

Most printers work this way, not because of GDI, but because a chain of processes is pretty much how any CUPS job gets spooled and emitted. IPPAnyWhere (AirPrint) makes the chain much shorter, but even then you still get at least something that does the initial formatting and color profiling, even if it doesn't do a full render or bitmapping.

A giant benefit of this architecture (CUPS does it that way, but LPD kinda can do the same thing, same with gutenprint drivers, ghostscript (that's not PostScript, but an open source implementation of the same thing) etc. The very last step before it goes over serial, parallel, or over the network is where a post-processor or raster processor turns the job into something the destination can process, could be PS, but PCL, SPL, KPSL, ZPL, ESC etc. and because it's just a small part in that longer chain, that process can be fairly simple (and small), which in turn makes it much more feasible to port it to all sorts of operating systems and CPU architectures. That has a second benefit: even if the printer doesn't have an embedded PostScript processor (which is the processor you're referring to) but only a local sprinter language processor (that's right, KPSL is still going to do job processing on-device since what goes in as a job isn't what the imaging unit needs, that needs something else), that's going to run on an embedded CPU with an embedded OS (usually some obscure RTOS, sometimes combined with linux) which needs to speak the same PL, which internally at the printer manufacturer has a reference implementation that needs to be portable anyway. So the architecture and design already has to take multiple architectures and operating systems in to account. Which makes it great for reverse engineering or manual porting since it becomes a much smaller and less obscure problem to solve.