Service retiredMoFont no longer accepts font generation or training jobs. The free manuscript tool remains available in desktop browsers; guests can export up to 2 pages per day, and exported pages include a watermark.

From zi2zi to Diffusion: What's Happened in Handwritten Chinese Font Generation

May 29, 2026

I taught myself zi2zi, then deepvecfont, then a whole stack of diffusion-model papers you can't understand until you've first read four AAAI papers.

That's not a humblebrag. Before I built MoFont, I naively assumed "turn handwriting into a font" was a solved problem—after all, GitHub is full of repos, and the demo images look gorgeous. It was only after I actually read the literature in chronological order that I understood: between a gorgeous demo image and an .otf file you can install and type all 6,500 common characters with, there's a long tail nobody likes to talk about.

Consider this a tour. I'll walk through what's happened in Chinese font generation over the last few years, in the order I read the papers—and why every generation of methods comes up just short.

The zi2zi era: treat a character as an image, do style transfer

The thing that first got me hooked was Yuchen Tian's (handle: kaonashi-tyc) 2017 zi2zi. Its idea is wonderfully plain—plain enough that I felt a little moved when it finally clicked: treat font generation as an image-to-image translation problem.

It stands directly on the shoulders of pix2pix (Isola et al.'s famous "Image-to-Image Translation with Conditional Adversarial Networks"). pix2pix can translate sketches into photos and satellite imagery into maps; zi2zi repurposes that to "translate the character in a standard Song typeface into the same character in your handwriting." It adds a category embedding so a single model can learn many font styles at once, rather than training one model per font.

For its time, the paradigm was genuinely striking. Feed it a few hundred paired characters and it hallucinates a plausible version of the remaining thousands.

But where does it break? Complex characters. Because it's essentially "painting" characters on a pixel plane, the moment strokes pile up and structure gets dense (think 囊, 鬻, 龘), it starts to smear—strokes fuse, strokes break, ink blobs appear. A GAN doesn't understand "this is a horizontal stroke, that's a falling stroke"; it only understands "this region should be black." Once a character's structure exceeds its grasp, it papers over the gap with a gray smudge. Later community work like DG-Font / CF-Font (CVPR 2023, few-shot generation via content–style disentanglement) was largely about wrestling exactly two demons: complex-character collapse, and too few reference samples.

As an aside, Tian himself hasn't been idle—he later built zi2zi-JiT, swapping in a pixel-space diffusion Transformer. A neat microcosm of the whole GAN-to-diffusion arc.

deepvecfont: stop painting pixels, output vectors directly

I got halfway through zi2zi and snagged on one question: a font file doesn't store images—it stores vector outlines, a pile of Bézier control points. No matter how crisp a bitmap your GAN paints, to turn it into a scalable, renderable font you still have to trace the edges (vectorize), and that step introduces fresh distortion.

So when I read DeepVecFont (Wang & Lian, SIGGRAPH Asia 2021), I had a "yes, this is how it should be done" moment. Its core idea is dual-modality learning: learn from both the "image" and the "vector sequence" representations of a character at once. The image branch handles overall shape; the vector branch generates the actual usable outline curves; the two calibrate each other. The output isn't a picture—it's a set of Bézier curves, which is what a font genuinely needs.

But v1 had a flaw its own authors owned up to: limited capacity for long sequences, jitter and burrs when generating complex outlines, and heavy reliance on an "image-guided outline refinement" post-process to clean up the mess. So two years later came DeepVecFont-v2 (CVPR 2023), swapping RNNs for Transformers and adding a relaxation representation plus a self-refinement module, specifically to tame the jitter and artifacts of "long, complex outlines."

I personally admire the vector route. It looks beautiful on Latin letters and relatively simple glyphs. But scale it to thousands of Chinese characters, each with dozens to hundreds of control points, and the sequence length explodes—stability stays a hard bone to chew. It nailed the right direction ("the output should be vectors") but didn't easily scale to the volume Chinese demands.

Diffusion: a leap on complex structure and few-shot

Then came the diffusion era—the most painful reading, and the most rewarding.

The standout is FontDiffuser (AAAI 2024). It models font generation as a noise-to-denoise process: starting from a blob of noise, under content and style guidance, it gradually "develops" the target character into view. Two designs struck me as especially clever: a Multi-scale Content Aggregation block that preserves the stroke detail of complex characters across scales (precisely to cure zi2zi's old "complex characters smear" disease), and a Style Contrastive module that cleanly disentangles style from the image.

In practice, diffusion really does beat GANs on two fronts: complex structure and few-shot / one-shot. Give it one character's reference style and it'll render unseen characters in that style, with complex characters far less prone to collapse. Reading this, I briefly thought "okay, the problem is actually solved now."

But it has its own ceiling: long-tail rare characters. Common characters it has seen plenty of, it writes convincingly; but hit a character with awkward strokes and vanishingly low frequency, and it still makes things up—conjuring something that looks roughly right but with radicals standing in the wrong place, a plausible-looking gibberish glyph. Confronted with things it has barely seen, the model is fundamentally guessing sensibly—and rare characters are exactly where "guessing sensibly" most easily goes off the rails.

My honest take: no single paper hands you a usable font

After reading this whole stack, my biggest takeaway is, frankly, a buzzkill: none of these methods, on its own, ships a genuinely usable OTF.

A paper's finish line is usually "the characters generated on the test set beat the previous paper on image-quality / style-similarity metrics." But a font you can actually use demands something else entirely:

  • All 6,500 common characters, not one missing—drop one and the user types a tofu box (□);
  • Every glyph's outline must be a clean, closed vector—no self-intersections, no stray fragments, or FontForge won't compile it and it renders fuzzy;
  • Advance widths, baselines, and side bearings must align, or a line of text staggers up and down like it's drunk;
  • For the rare characters even a sensible model can't guess right, there has to be a fallback—composite from structure, or fall back to the reference glyph, but never let it invent nonsense.

That "last mile" is all grunt work: FontForge scripting, outline cleanup, glyph-metric alignment, and a dedicated fallback pipeline babysitting the long tail. Papers don't write about this, because it doesn't get you published. But whether the user can actually install the font and type every single character rides entirely on that one mile.

That's what MoFont is really doing. At the model layer I stand on the shoulders of these papers; at the product layer I spend the bulk of my energy on the last mile. You write about 30–50 characters, I fill in all 6,500 common characters, it runs for roughly three to four hours, and out comes an installable .otf + .ttf. Rare characters get a fallback, outlines are cleaned through FontForge, metrics are aligned. A one-time purchase, no subscription—because the font is your own handwriting, and you shouldn't have to rent yourself back by the month.

What the papers taught me is how the road is built; whether it reaches the user, though, is on me to pave that final mile.

— Iris

Iris Gu

Iris Gu