JamesGoslings 62f4532157 fix(pdf): register CJK fallback font so Chinese/Japanese/Korean text renders correctly (#3016)
Closes #2986.

Since v5.1.0 the renderer was migrated from Puppeteer to
@react-pdf/renderer. The new pipeline only registers the user-selected
typography family (e.g. Roboto, IBM Plex Serif), which contains no CJK
glyphs, so any Chinese / Japanese / Korean characters in the resume
fall back to .notdef and render as garbled boxes in both the in-app
preview and the exported PDF.

@react-pdf/renderer's textkit layer already supports per-codepoint
font substitution when a Text node is styled with `fontFamily` as a
string array — but only if every family in the stack has been
registered via Font.register. This change wires that up:

- packages/fonts: new `getPdfCjkFallbackFontFamily(family)` returns
  Noto Sans SC / Noto Serif SC depending on whether the primary font
  is sans-serif or serif, and `null` when no fallback is needed
  (standard PDF font, or primary already is the fallback). Source Han
  Sans/Serif SC covers all CJK-Unified ideographs, so a single font
  transparently handles Simplified/Traditional Chinese, Japanese
  kanji and Korean hanja.

- packages/pdf/hooks/use-register-fonts: after registering the
  primary body/heading fonts as before, additionally register the
  resolved CJK fallback (regular weight only — substitution is
  per-codepoint, not per-weight, so one face is enough). The
  function's return type is widened to a new `PdfTypography` whose
  `body.fontFamily` and `heading.fontFamily` become
  `[primary, cjkFallback]` two-element stacks.

- packages/pdf/document: cast the widened typography back through the
  schema-typed `ResumeData` so the wider runtime value reaches
  templates without changing the public `Typography` schema. All 15
  templates already consume `metadata.typography.body.fontFamily`
  directly, and `StyleSheet.fontFamily` accepts both string and
  string[], so no template edits are required.

Latin-only resumes are unaffected:
- `getPdfCjkFallbackFontFamily` returns `null` for standard PDF fonts
  and existing CJK selections, so the extra Font.register call is
  skipped.
- When no fallback applies, `registerFonts` returns the original
  typography reference unchanged (zero allocation).
- Even when the fallback is registered, textkit only consults it for
  codepoints the primary font cannot render, so Latin glyphs still
  come from the user-selected font with identical metrics.
2026-05-09 18:49:59 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-01-21 23:44:48 +01:00
2026-05-08 13:05:55 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:19:13 +02:00
2026-04-25 11:29:27 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00
2026-05-07 15:12:33 +02:00

Reactive Resume

Reactive Resume

Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.

Get Started · Learn More

Reactive Resume Version GitHub Stars License Docker Pulls Discord Crowdin Sponsors Donations


Reactive Resume makes building resumes straightforward. Pick a template, fill in your details, and export to PDF—no account required for basic use. For those who want more control, the entire application can be self-hosted on your own infrastructure.

Built with privacy as a core principle, Reactive Resume gives you complete ownership of your data. The codebase is fully open-source under the MIT license, with no tracking, no ads, and no hidden costs.

Features

Resume Building

  • Real-time preview as you type
  • Multiple export formats (PDF, JSON)
  • Drag-and-drop section ordering
  • Custom sections for any content type
  • Rich text editor with formatting support

Templates

  • Professionally designed templates
  • A4 and Letter size support
  • Customizable colors, fonts, and spacing
  • Custom CSS for advanced styling

Privacy & Control

  • Self-host on your own infrastructure
  • No tracking or analytics by default
  • Full data export at any time
  • Delete your data permanently with one click

Extras

  • AI integration (OpenAI, Google Gemini, Anthropic Claude)
  • Multi-language support
  • Share resumes via unique links
  • Import from JSON Resume format
  • Dark mode support
  • Passkey and two-factor authentication

Templates

Azurill
Azurill
Bronzor
Bronzor
Chikorita
Chikorita
Ditto
Ditto
Gengar
Gengar
Glalie
Glalie
Kakuna
Kakuna
Lapras
Lapras
Leafish
Leafish
Onyx
Onyx
Pikachu
Pikachu
Rhyhorn
Rhyhorn
Ditgar
Ditgar
Meowth
Meowth

Quick Start

The quickest way to run Reactive Resume locally:

# Clone the repository
git clone https://github.com/amruthpillai/reactive-resume.git
cd reactive-resume

# Start all services
docker compose up -d

# Access the app
open http://localhost:3000

Build with Ona

For detailed setup instructions, environment configuration, and self-hosting guides, see the documentation.

Tech Stack

Category Technology
Framework TanStack Start (React 19, Vite)
Runtime Node.js
Language TypeScript
Database PostgreSQL with Drizzle ORM
API ORPC (Type-safe RPC)
Auth Better Auth
Styling Tailwind CSS
UI Components Radix UI
State Management Zustand + TanStack Query

Documentation

Comprehensive guides are available at docs.rxresu.me:

Guide Description
Getting Started First-time setup and basic usage
Self-Hosting Deploy on your own server
Development Setup Local development environment
Project Architecture Codebase structure and patterns
Exporting Your Resume PDF and JSON export options

Self-Hosting

Reactive Resume can be self-hosted using Docker. The stack includes:

  • PostgreSQL — Database for storing user data and resumes
  • SeaweedFS (optional) — S3-compatible storage for file uploads

From v5.1.0 onwards — PDF generation now runs entirely client-side via @react-pdf/renderer. New deployments no longer require Browserless, Chromium, or any external print service as a dependency. The PRINTER_* and BROWSERLESS_* environment variables are no longer read and can be removed from your .env.

Pull the latest image from Docker Hub or GitHub Container Registry:

# Docker Hub
docker pull amruthpillai/reactive-resume:latest

# GitHub Container Registry
docker pull ghcr.io/amruthpillai/reactive-resume:latest

See the self-hosting guide for complete instructions.

Support

Reactive Resume is and always will be free and open-source. If it has helped you land a job or saved you time, please consider supporting continued development:

GitHub Sponsors Open Collective

Other ways to support:

  • Star this repository
  • Report bugs and suggest features
  • Improve documentation
  • Help with translations

Star History

Star History Chart

Contributing

Contributions make open-source thrive. Whether fixing a typo or adding a feature, all contributions are welcome.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

See the development setup guide for detailed instructions on how to set up the project locally.

License

MIT — do whatever you want with it.

S
Description
A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!
Readme MIT 304 MiB
Languages
TypeScript 99.5%
CSS 0.2%
JavaScript 0.1%