QRForge4J is a multi-module JVM QR generation library written in Kotlin. It ships a core QR engine, SVG/PNG/JPEG/PDF output, a unified render pipeline, optional ZXing verification, a Kotlin DSL, batch helpers, server integrations for Ktor, Spring Boot, and Micronaut, a CLI, and a standalone Gradle plugin build.
- Current release: 2.0.0
- Main multi-module build:
./gradlew build - Standalone Gradle plugin build:
./gradlew -p qrgen-gradle-plugin build - Java baseline: 17
- Source builds require a locally available JDK 17 toolchain
- Gradle wrapper: 9.4.0
- Main repo Kotlin plugin: 2.3.10
Project status details are in documents/STATE_OF_REPO.md.
Installation details are in documents/MAVEN_INSTALL.md.
| Module | Purpose |
|---|---|
qrgen-core |
QR matrix generation, QrStyleConfig, templates/profiles, cache helpers |
qrgen-svg |
SVG renderer with advanced styling |
qrgen-png |
PNG and JPEG raster rendering (Batik-backed) |
qrgen-pdf |
PDF output (raster QR embedded in page) |
qrgen-render |
QrRenderPipeline and QrOutputFormat — single entry point for all output formats |
qrgen-verify |
QrDecodeVerifier — ZXing decode/scannability checks on rendered images |
qrgen-dsl |
Kotlin builder and fluent API |
qrgen-cli |
Command-line interface |
qrgen-batch |
Batch generation helpers |
qrgen-test |
Integration tests, goldens, and benchmark entry points (not published for app use) |
qrgen-ktor |
Ktor routes and helpers |
qrgen-spring-boot-starter |
Spring Boot auto-configuration |
qrgen-micronaut |
Micronaut controllers and services |
qrgen-gradle-plugin |
Standalone Gradle plugin (nested build) |
Typical dependency choices:
- Styled SVG only:
qrgen-core+qrgen-svg - PNG/JPEG in apps: add
qrgen-pngor depend onqrgen-render(pulls format modules transitively) - HTTP integrations:
qrgen-ktor,qrgen-spring-boot-starter, orqrgen-micronaut - Post-render scan checks:
qrgen-verify(lightweight; no Batik/PDF required)
- Module styles: circle, square, classy, rounded, extra-rounded, classy-rounded (
DotType) - Neighbor-aware rounded modules and
roundSize-style dynamic sizing - Corner locator styling with separate outer/inner shapes, per-corner overrides, and optional corner logos
- Custom alignment pattern styling: square, circle, diamond, star
- Background colors, rounded backgrounds, gradients, gradient masking, and background patterns
- Quiet zone accents, module outlines, drop shadows, and micro typography
- SVG animation presets: fade, pulse, draw-in
- Center logo support with hole carving
- JSON/YAML templates and named profile loading
- Jackson
JsonNodedeep merge for template/profile overrides - SVG, PNG, JPEG, and PDF output through one pipeline
With mise:
mise install
mise run build
mise run verify_integrations
mise run docs
mise run benchmarks
mise run build_plugin
mise run publish_localWith the Gradle wrapper:
./gradlew build
./gradlew -p qrgen-gradle-plugin buildmise manages the toolchain. If you build with the Gradle wrapper directly, make sure JDK 17 is installed and discoverable locally. The repo does not auto-download JDK toolchains from settings.gradle.kts.
Build version is taken from the current git tag when present (v2.0.0 → artifact version 2.0.0). Untagged working trees publish as 1.0.0-SNAPSHOT.
Published modules use group io.github.willmortimer and artifact ids that match the module names.
Detailed install guidance for GitHub Packages, mavenLocal(), and Ktor apps is in documents/MAVEN_INSTALL.md.
GitHub Packages repository:
repositories {
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/willmortimer/QRForge4J")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}Kotlin app — core + styled PNG:
dependencies {
implementation("io.github.willmortimer:qrgen-render:2.0.0") // core, svg, png, pdf
implementation("io.github.willmortimer:qrgen-verify:2.0.0") // optional decode checks
}Kotlin app — DSL + Ktor:
dependencies {
implementation("io.github.willmortimer:qrgen-dsl:2.0.0")
implementation("io.github.willmortimer:qrgen-ktor:2.0.0")
}QrRenderPipeline is the canonical way to generate any output format. Integrations, batch, CLI, and the Gradle plugin all delegate to it.
import io.github.qrgen.core.QrStyleConfig
import io.github.qrgen.render.QrOutputFormat
import io.github.qrgen.render.QrRenderPipeline
val pipeline = QrRenderPipeline()
val pngBytes = pipeline.render("https://example.com", QrStyleConfig(), QrOutputFormat.PNG)
val output = pipeline.renderOutput("https://example.com", QrStyleConfig(), QrOutputFormat.SVG)
// output.bytes, output.contentType, output.filenameSupported formats: SVG, PNG, JPEG, PDF. Parse user input with QrOutputFormat.parse("png").
Use qrgen-verify when you need to assert a PNG (or BufferedImage) still scans after styling:
import io.github.qrgen.verify.QrDecodeVerifier
val result = QrDecodeVerifier().verifyRenderedPng(pngBytes, "https://example.com")
check(result.isSuccessful && result.contentMatches)import io.github.qrgen.dsl.QRCode
import io.github.qrgen.core.LocatorFrameShape
val svg = QRCode.ofRoundedSquares()
.size(640)
.withColor("#111827")
.backgroundCorners(28.0)
.cornerLocators {
rounded()
logo("https://example.com/corner-logo.png")
topRight {
outer(LocatorFrameShape.DIAMOND)
}
}
.alignmentPattern {
star()
color = "#0f766e"
}
.dotStyle {
dynamicSize(0.92)
}
.animation {
pulse()
durationSeconds = 2.0
}
.buildSvg("https://example.com")Lower-level access when you want separate generate and render steps:
import io.github.qrgen.core.DefaultQrGenerator
import io.github.qrgen.core.QrStyleConfig
import io.github.qrgen.svg.DefaultSvgRenderer
val qr = DefaultQrGenerator().generateFromText("Hello QRForge4J", QrStyleConfig())
val svg = DefaultSvgRenderer().render(qr)import io.github.qrgen.ktor.qrGenRoutes
import io.ktor.server.application.Application
import io.ktor.server.routing.routing
fun Application.module() {
routing {
qrGenRoutes("/qr")
}
}Routes:
GET /qr/generate?data=...&format=PNGPOST /qr/generate(JSON body withdata, optionalformat, style fields)GET /qr/health
Format names match QrOutputFormat (SVG, PNG, JPEG, PDF).
Auto-configuration registers QrRenderPipeline, QrGenService, and QrBatchProcessor. Inject QrGenService and call generateQr(data, format = QrOutputFormat.PNG).
echo "https://example.com" | ./gradlew qrgen-cli:run --args="--format pdf --dots rounded --round-size --corner-style classy --alignment-shape star --output qr.pdf"profile: kiosk
config:
layout:
width: 640
height: 640
margin: 24
modules:
type: ROUNDED
alignmentPatterns:
enabled: true
shape: STARLoad with QrConfigIO.readTemplate(...) and merge through QrProfileRegistry.merge(...).
Merge rules (2.0):
- Key absent in template
config→ keep base profile value - Key present as JSON/YAML
null→ clear nullable field on base - Object values → recursive merge
- Scalars and arrays → replace base value
Register named profiles with full configs or JsonNode patches:
registry.register("kiosk", patchJsonNode)
registry.register("minimal", QrStyleConfig())2.0 is a breaking release. Main changes:
| Removed / changed | Migration |
|---|---|
QrStyleConfigPatch, QrStyleConfigMerger, OptionalField |
Use QrConfigMerger.merge(base, jsonPatch) or QrProfileRegistry.register(name, JsonNode) |
Per-module format enums (QrFormat, QrKtorFormat, etc.) |
Use io.github.qrgen.render.QrOutputFormat |
ModuleOptions.rounded, extraRounded, classyRounded |
Set modules.type / DSL DotType instead (booleans were unused in library render path) |
Decode helpers only in qrgen-test |
Use qrgen-verify artifact: QrDecodeVerifier |
Java2DPngRenderer |
Use BatikPngRenderer via QrRenderPipeline; Java2D class is deprecated and throws |
Integration modules now take QrOutputFormat in service methods. Batch output format strings still accept svg, png, jpeg, pdf (case-insensitive).
Published modules are configured for GitHub Packages. The release workflow lives at .github/workflows/release.yml and runs on GitHub Release published events.
Tag releases as v2.0.0 (with v prefix). Gradle derives the Maven version from that tag.
Required environment variables for GitHub Packages:
GITHUB_ACTORGITHUB_TOKEN
Optional Maven Central release secrets for GitHub Actions:
CENTRAL_PORTAL_USERNAMECENTRAL_PORTAL_PASSWORDSIGNING_KEYSIGNING_PASSWORD
Local publishing:
./gradlew publishAllToMavenLocal
./gradlew -p qrgen-gradle-plugin publishToMavenLocalMaven Central publishing:
./gradlew releaseToCentralPortal
./gradlew -p qrgen-gradle-plugin releaseToCentralPortalAPI docs:
./gradlew verifyDocsapp/is legacy sample code and is not part of the published library.ported-library/qr-code-styling-master/is a vendored JS reference, not a Kotlin module.- PDF output embeds a rendered QR image in the page. It is suitable for distribution and scanning tests, but it is not a vector PDF renderer.
BatikPngRendereris the supported production raster path.Java2DPngRendereris deprecated and throws until a real lightweight rasterizer lands in a future release.- Template/profile merging uses Jackson
JsonNodedeep merge onQrStyleConfig; explicit JSON/YAMLnullclears nullable fields.
MIT License. See LICENSE.