A Java 21 library that models Minecraft's chat text system - colours, formatting codes, segment trees, click/hover event payloads - and renders it to raw pixel buffers via the vanilla OTF font family.
Important
This project depends on font files derived from copyrighted bitmap assets owned by Mojang AB (a Microsoft subsidiary). The OTF files are never committed to this repository. They are generated on demand by the bundled fonts Gradle task, which invokes minecraft-library/font-generator against the official Minecraft client JAR. You are responsible for ensuring your use of the generated font files complies with the Minecraft EULA and Minecraft Usage Guidelines.
- Chat color model -
ChatColor.Legacyfor vanilla 1.8.9-style codes (0-9,a-f) andChatColor.Customfor arbitrary modern RGB ("color": "#FF00FF") with automatic shadow derivation via(rgb & 0xFCFCFC) >> 2 - Formatting codes -
ChatFormatenum coveringOBFUSCATED,BOLD,STRIKETHROUGH,UNDERLINE,ITALIC,RESET, plus theSECTION_SYMBOLconstant and legacy/alternate-code translation helpers - Segment primitives -
ColorSegment(styled text run),TextSegment(styled run plus click/hover events), andLineSegment(list of styled runs) with builders and legacy-string parsing - Event payloads -
ClickEventandHoverEventwith GsonJsonObject(de)serialization - Native OTF rendering -
MinecraftFontloads the official Minecraft fonts (Regular, Bold, Italic, BoldItalic, Galactic, Illageralt) and rasterizes glyphs intoPixelBuffers with zero AWT overhead after initialization - Runtime font bootstrap - downstream consumers don't need to ship OTFs:
MinecraftFontinvokes the generator on first use when the classpath has nofonts/resources - Gson-backed JSON - segment, color, and event types round-trip through
JsonObject
| Requirement | Version | Notes |
|---|---|---|
| JDK | 21+ | Required - the build toolchain targets Java 21 |
| Git | 2.x+ | Required for cloning and for the fonts task |
| Python | 3.10+ | Required only when running the fonts task or the runtime bootstrap |
Note
Python is not required to consume this library as a dependency if the upstream artifact already bundles the generated OTFs on its classpath. It is required only when this repository has to produce fresh font files.
Add the Gradle coordinates via JitPack:
repositories {
mavenCentral()
maven(url = "https://jitpack.io")
}
dependencies {
implementation("com.github.minecraft-library:minecraft-text:<commit-or-tag>")
}Or clone the repository and build locally:
git clone https://github.com/minecraft-library/minecraft-text.git
cd minecraft-text
./gradlew buildBuild a single styled run and serialize it to JSON:
import lib.minecraft.text.ChatColor;
import lib.minecraft.text.TextSegment;
TextSegment hello = new TextSegment.Builder()
.withText("Hello, world!")
.withColor(ChatColor.Legacy.GOLD)
.isBold()
.build();
String json = hello.toJson().toString();Compose multiple styled runs into a single line:
import lib.minecraft.text.ColorSegment;
import lib.minecraft.text.LineSegment;
LineSegment line = LineSegment.builder()
.withSegments(
ColorSegment.builder()
.withText("Hello, ")
.withColor(ChatColor.Legacy.GOLD)
.build(),
ColorSegment.builder()
.withText("world!")
.withColor(ChatColor.of(0xFF00FF))
.isBold()
.build()
)
.build();Or parse a legacy section-symbol string directly:
// Accepts § natively and '&' as a substitute (second overload lets you pick another substitute)
LineSegment parsed = ColorSegment.fromLegacy("&6Hello, &lworld!");Render a line to a PixelBuffer:
import dev.simplified.image.pixel.PixelBuffer;
import lib.minecraft.text.font.MinecraftFont;
import lib.minecraft.text.font.MinecraftGraphics;
// Output buffer is sized in native render pixels (mcPixels * MC_PIXEL_SCALE).
PixelBuffer buffer = PixelBuffer.create(256, 32);
MinecraftGraphics gfx = new MinecraftGraphics(buffer);
// drawString takes mcPixel coordinates; the baseline convention matches AWT.
int cursorXMcPx = 0;
int baselineYMcPx = MinecraftFont.REGULAR.getFontMetrics().getAscentMcPixels();
for (ColorSegment segment : line.getSegments()) {
segment.getColor().ifPresent(c -> gfx.setColor(c.color()));
gfx.setFont(MinecraftFont.of(segment.fontStyle()));
gfx.drawString(segment.getText(), cursorXMcPx, baselineYMcPx);
// Font metrics return native-pixel advances; divide back into mcPixel space.
cursorXMcPx += gfx.getFontMetrics().stringWidth(segment.getText()) / MinecraftFont.MC_PIXEL_SCALE;
}Tip
Call MinecraftFont.REGULAR.glyph(codepoint) directly if you need per-glyph bitmaps (e.g. for a custom layout engine). The glyph cache is thread-safe and lazily populated via ConcurrentMap#computeIfAbsent.
On first use, MinecraftFont resolves each OTF in priority order:
- Classpath -
fonts/Minecraft-*.otfviaClassLoader.getResourceAsStream - Module cache -
cache/fonts/Minecraft-*.otf - On-demand generation - invokes
ToolingFontsto clone the generator and produce the OTFs intocache/fonts/(version driven byMinecraftFont.DEFAULT_VERSION)
This means downstream consumers can depend on minecraft-text without shipping any font assets - the first MinecraftFont.REGULAR.glyph(...) call transparently produces them.
Important
The runtime bootstrap requires git and Python 3.10+ on the host's PATH. In sealed environments (e.g. production Docker images) you should run the build-time generation during image build and ship the OTFs on the classpath instead.
Run the fonts Gradle task to populate cache/fonts/:
./gradlew fonts # defaults to Minecraft 26.1
./gradlew fonts -PfontVersion=26.2 # pin a different versionThe task clones minecraft-library/font-generator into cache/font-generator/, creates a Python virtual environment, installs the generator, and emits .otf files to cache/fonts/. Subsequent runs reuse the clone and venv so only the generator itself re-executes.
The standard processResources task then copies cache/fonts/ onto the runtime classpath under fonts/:
./gradlew buildNote
cache/ and src/main/resources/fonts/ are both gitignored. The fonts task writes exclusively into cache/fonts/; the source tree is never modified.
Build order for a fresh checkout
./gradlew fonts # emit cache/fonts/Minecraft-*.otf (run once per version bump)
./gradlew build # compile, test, and package the jarYou can skip the first step if you are only working on code paths that do not exercise MinecraftFont. Unit tests under MinecraftFontTest will fail without OTFs present.
| Package | Purpose |
|---|---|
lib.minecraft.text |
Top-level model - ChatColor, ChatFormat, TextSegment, LineSegment, ColorSegment |
lib.minecraft.text.event |
ClickEvent, HoverEvent payloads with Gson (de)serialization |
lib.minecraft.text.font |
MinecraftFont, MinecraftFontMetrics, MinecraftGraphics - OTF loading and glyph rendering |
lib.minecraft.text.tooling |
ToolingFonts - Gradle/runtime-invoked wrapper around the Python font generator |
A Minecraft chat line is modelled as a list of styled text runs rather than a recursive tree. Three classes cover the full surface area:
ColorSegment # styled text run
├── text (String)
├── color (Optional<ChatColor>)
└── bold / italic / underlined / obfuscated / strikethrough (boolean)
TextSegment extends ColorSegment # run + interaction events
├── clickEvent (Optional<ClickEvent>)
└── hoverEvent (Optional<HoverEvent>)
LineSegment # an ordered list of runs
└── segments (ConcurrentList<ColorSegment>)
ColorSegment.fromLegacy(String) and LineSegment.fromLegacy(String, char) parse legacy section-symbol strings (§6...§l... or a user-chosen substitute) into the model. TextSegment#toJson emits the wire-format JsonObject; TextSegment.fromJson round-trips it back. MinecraftGraphics draws the resulting runs one at a time, picking a font variant via ColorSegment#fontStyle.
Each MinecraftFont enum value wraps a pre-loaded AWT Font and a lazy glyph atlas:
- Enum init - the OTF resolves via the runtime bootstrap, AWT loads it at
FONT_POINT_SIZE = 16f, font-level metrics (ascent, descent, height) are captured, and printable ASCII (U+0020 - U+007E) is eagerly rasterized so the first render has zero AWT overhead - Lazy rasterization - non-ASCII codepoints are rasterized on first access via
ConcurrentMap#computeIfAbsentand cached for subsequent renders - Draw-time tint - glyphs are stored as white-on-transparent
PixelBuffers; callers multiply each pixel's alpha against a target colour at draw time, so noGraphics2Dis needed after initialization
The OTFs use unitsPerEm = 1024 with 128 units = 1 mcPixel, and MC_PIXEL_SCALE = 2 maps vanilla Minecraft pixels to output pixels at the standard load size.
minecraft-text/
├── src/
│ ├── main/java/lib/minecraft/text/
│ │ ├── ChatColor.java # Legacy enum + Custom record
│ │ ├── ChatFormat.java # Bold/italic/etc. enum
│ │ ├── TextSegment.java # Recursive segment model
│ │ ├── LineSegment.java # Flattened same-style runs
│ │ ├── ColorSegment.java # Draw-ready segment
│ │ ├── event/
│ │ │ ├── ClickEvent.java
│ │ │ └── HoverEvent.java
│ │ ├── font/
│ │ │ ├── MinecraftFont.java # OTF-backed glyph atlas enum
│ │ │ ├── MinecraftFontMetrics.java # Ascent/descent/advance capture
│ │ │ └── MinecraftGraphics.java # Line layout + PixelBuffer output
│ │ └── tooling/
│ │ └── ToolingFonts.java # Gradle + runtime generator wrapper
│ └── test/java/lib/minecraft/renderer/text/
│ ├── ChatColorTest.java
│ └── font/MinecraftFontTest.java
├── build.gradle.kts
├── settings.gradle.kts
├── gradle/libs.versions.toml
├── LICENSE.md
├── COPYRIGHT.md
├── CONTRIBUTING.md
└── README.md
Created during execution and excluded from version control:
| Directory | Contents |
|---|---|
cache/font-generator/ |
Clone of the Python font-generator repo and its virtual environment |
cache/fonts/ |
Generated Minecraft-*.otf files - copied onto the classpath by processResources |
build/ |
Gradle outputs (compiled classes, resources, jar) |
See CONTRIBUTING.md for development setup, code style conventions, and how to submit a pull request.
This project is licensed under the Apache License 2.0 - see LICENSE.md for the full text.
See COPYRIGHT.md for third-party attribution notices, including information about Mojang AB's copyrighted assets and the upstream font-generator tool.