Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Easy ZPL Viewer

Easy ZPL Viewer — render Zebra ZPL II labels to an Android Bitmap, completely offline

License: MIT minSdk Kotlin

Render ZPL II (Zebra Programming Language) label code to an Android Bitmap completely offline — no Labelary, no network calls, no internet permission. EasyZPLViewer is a pure-Kotlin ZPL viewer, renderer, parser, and preview library for Android that turns raw ZPL/ZPL2 shipping-label code into a crisp on-screen image and sends it to any Android print service — including networked Zebra thermal label printers. This repository is both a reusable library (easyzpl) and an open-source example app showing how to use it.

Keywords: ZPL, ZPL II, ZPL2, Zebra Programming Language, ZPL viewer, ZPL renderer, ZPL parser, ZPL to image, ZPL to PNG, ZPL to bitmap, ZPL preview, Android label printing, Zebra printer, thermal printer, shipping label, barcode generator, Code 128, Code 39, Data Matrix, Kotlin, Jetpack Compose, offline label rendering, Labelary alternative.

Module What it is
easyzpl The Android library: ZplRenderer, a ZPL-to-Canvas renderer, and a Kotlin DSL (k2zpl) for building ZPL. Published via JitPack.
app Example app: loads every ZPL file from assets/ (plain text, Base64, or ZIP), renders each label to a bitmap, and shows them in a swipeable pager — each page with a Print button.

Features

  • 🖨️ Offline ZPL II rendering — convert Zebra ZPL/ZPL2 label code to an Android Bitmap with no network calls and no Labelary dependency.
  • 🔤 Text & fonts — scalable font 0 and bitmap fonts AH, with N/R/I/B rotation and ^FW landscape orientation.
  • 🧱 Graphics — graphic boxes (^GB), ASCII-hex graphic fields (^GF), field reverse (^FR) for logo cut-outs, and field-block word wrap (^FB).
  • 📦 Barcodes — Code 128, Code 39, and a Data Matrix placeholder, plus human-readable interpretation lines.
  • 🧩 Kotlin DSL — build ZPL programmatically with the type-safe k2zpl { } builder instead of hand-writing command strings.
  • 🪶 Tiny & dependency-light — pure Kotlin, minSdk 23, works great with Jetpack Compose or classic Views.
  • 🖼️ Print-ready — hand the rendered bitmap to Android's print framework to reach any Zebra or thermal label printer.

Screenshots

Real ZPL labels rendered on-device, fully offline by the example app's swipeable preview pager:

Text & box label Logo, Code 128 & address Code 39 from a ZIP asset
ZPL shipping invoice label with FROM/TO address blocks rendered offline in the EasyZPLViewer Android app ZPL label with logo, Code 128 barcode and address block rendered to a Bitmap on Android ZPL address label with a Code 39 barcode loaded from a ZIP asset
sample_1 — text, fonts & rules sample_4^FR logo cut-out + barcode sample_7.zip — Code 39 + ZIP loading

Table of contents


Installation

Step 1. Add JitPack to your repositories:

// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://jitpack.io")
    }
}

Step 2. Add the dependency:

// build.gradle.kts (module)
dependencies {
    implementation("com.github.umair13adil:EasyZPLViewer:1.2")
}

Replace v1.0 with the latest release tag shown on the JitPack badge above.


Usage

1. Render a ZPL string to a Bitmap

ZplRenderer.renderZplToBitmap is a suspend function — rendering runs on Dispatchers.Default, so it never blocks the main thread:

import com.github.umair13adil.easyzpl.zpl.ZplRenderer

val renderer = ZplRenderer()

lifecycleScope.launch {
    val bitmap: Bitmap? = renderer.renderZplToBitmap(
        zplCode = """
            ^XA
            ^FO50,50^A0N,40,40^FDHello ZPL^FS
            ^FO50,120^GB400,3,3^FS
            ^BY3,3,90
            ^FO50,160^BCN,90,Y,N,N^FD123456789^FS
            ^XZ
        """.trimIndent(),
        dpmm = 8,        // print density: 8 dpmm = 203 dpi
        widthMm = 102,   // 4 inches
        heightMm = 152   // 6 inches
    )
    imageView.setImageBitmap(bitmap)
}
Parameter Default Meaning
zplCode The ZPL II source (^XA … ^XZ)
dpmm 8 Dots per millimeter (6 = 152 dpi, 8 = 203 dpi, 12 = 300 dpi, 24 = 600 dpi)
widthMm 102 Label width in millimeters
heightMm 51 Label height in millimeters

Returns null if the dimensions are invalid. The output is supersampled up to 4096 px so it stays crisp on high-density screens while remaining inside common GPU texture limits.

Tip: if your ZPL declares ^PW (print width, dots) and ^LL (label length, dots), you can derive the size instead of hard-coding it — see inferLabelSizeMm in the example app.

2. Use it from Jetpack Compose

@Composable
fun ZplLabelImage(zpl: String) {
    val renderer = remember { ZplRenderer() }
    var bitmap by remember(zpl) { mutableStateOf<Bitmap?>(null) }

    LaunchedEffect(zpl) {
        bitmap = renderer.renderZplToBitmap(zpl, dpmm = 8, widthMm = 102, heightMm = 152)
    }

    bitmap?.let {
        Image(
            bitmap = it.asImageBitmap(),
            contentDescription = null,
            modifier = Modifier.fillMaxWidth().background(Color.White),
            contentScale = ContentScale.FillWidth
        )
    }
}

The example app's MainActivity extends this with a HorizontalPager, a loading state, and a print button per page.

3. Build ZPL with the Kotlin DSL

You don't have to hand-write ZPL — the k2zpl { } builder generates it:

import com.github.umair13adil.easyzpl.zpl.k2zpl

val zpl: String = k2zpl {
    startFormat()                       // ^XA
    printWidth(812)                     // ^PW812
    labelLength(1218)                   // ^LL1218

    setDefaultFont(fontHeight = 40, fontWidth = 40)
    field(x = 50, y = 50, data = "Intershipping, Inc.")
    field(x = 50, y = 110, data = "1000 Shipping Lane")

    line(x = 50, y = 170, width = 700, thickness = 3)    // ^GB horizontal rule

    fieldOrigin(x = 550, y = 220)
    graphicBox(width = 200, height = 200, thickness = 4)  // ^GB box at current origin
    fieldSeparator()

    barcode128(
        data = "PKG-778899",
        x = 50, y = 220,
        height = 120,
        interpretationLine = true       // ^BC … with human-readable line
    )
    barcode39(
        data = "ZIP001",
        x = 50, y = 420,
        height = 80,
        interpretationLine = true       // ^B3
    )

    endFormat()                         // ^XZ
}

// Render the built ZPL like any other ZPL string:
val bitmap = ZplRenderer().renderZplToBitmap(zpl)

Available builder functions include startFormat() / endFormat(), field(...), font(...), fieldOrigin(...), fieldData(...), fieldBlock(...), fieldSeparator(), graphicBox(...), graphicField(...), line(...), barcode128(...), barcode39(...), labelHome(...), labelLength(...), printWidth(...), mediaMode(...), mediaType(...), and command("...") for any raw ZPL. Unit helpers Int.mm, Int.cm, Int.inches, Int.dots convert measurements once dpiSetting is set.

4. Load ZPL from assets (plain / Base64 / ZIP)

The example app ships ZplAssetLoader, which you can copy into your project. It scans assets/ and detects each file's format by content, not extension:

  • Plain ZPL text — used as-is (assets/sample_1sample_5)
  • Base64-encoded ZPL — decoded transparently (assets/sample_6_base64.txt)
  • ZIP archives — every entry is extracted; entries may themselves be plain or Base64 ZPL (assets/sample_7.zip)
  • Files containing multiple ^XA … ^XZ blocks produce one label per block
val labels: List<ZplLabel> = withContext(Dispatchers.IO) {
    ZplAssetLoader.loadAll(context)   // List<ZplLabel(name, zpl)>
}

Drop your own ZPL files into app/src/main/assets/ and they appear in the pager automatically — no code changes needed.

5. Print a rendered label

Each pager page in the example app has a Print button backed by the Android print framework:

import androidx.print.PrintHelper

fun printLabel(context: Context, jobName: String, bitmap: Bitmap) {
    PrintHelper(context).apply {
        scaleMode = PrintHelper.SCALE_MODE_FIT
        colorMode = PrintHelper.COLOR_MODE_MONOCHROME
    }.printBitmap(jobName, bitmap)
}

This opens the system print dialog, so the label can go to any print service the device knows — including networked Zebra printers via their print service plugin. (Requires androidx.print:print in your dependencies.)


Supported ZPL commands

Command Description
^XA / ^XZ Label start / end
^FO / ^FT Field origin (top-left) / field typeset (baseline)
^A* Font selection with height, width, and orientation (N/R/I/B)
^CF Change default font
^FW Field orientation default — rotate every following field (landscape labels)
^FD / ^FS Field data / field separator
^FB Field block — word wrap, max lines, line spacing, L/C/R justification
^FR Field reverse — invert the pixels under a field (logo cut-outs)
^FH Field hexadecimal — decode _XX hex escapes in field data
^GB Graphic box (outline or filled, black or white)
^GF Graphic field — ASCII-hex bitmaps with run-length compression (G–Y, g–z, , ! :)
^LH Label home offset
^BY Barcode defaults (module width, ratio, height)
^BC Code 128 (Subset B, with interpretation line)
^B3 Code 39 (with interpretation line)
^BD Data Matrix (rendered as a placeholder symbol)
^FX Comment (ignored)
^CI International character set (parsed, ignored)

Unknown commands are skipped gracefully, so real-world labels render without erroring out.


Running the example app

git clone https://github.com/umair13adil/EasyZPLViewer.git
cd EasyZPLViewer
./gradlew :app:installDebug

On launch, the app renders every ZPL label found in app/src/main/assets/ into a swipeable preview pager. Other useful tasks:

./gradlew :app:assembleDebug          # example app APK
./gradlew :easyzpl:assembleRelease    # library AAR
./gradlew :easyzpl:publishToMavenLocal

Publishing on JitPack

The library is ready to publish from any fork:

  1. Push the repository to GitHub.
  2. Create a release tag, e.g. 1.0.0.
  3. Open https://jitpack.io/#<your-user>/<your-repo> and build the tag.

JitPack uses the included jitpack.yml (JDK 17) and the maven-publish setup in easyzpl/build.gradle.kts, which publishes the release AAR together with a sources jar and a full POM.


Contributing

Contributions are welcome — bug reports, new ZPL command support, rendering fidelity fixes, docs, and sample labels all help. 🎉

Ways to contribute

  • 🐛 Report a bug — open an issue and include the ZPL source, what you expected, what rendered, and ideally a screenshot.
  • Add ZPL command support — implement an unsupported command in the renderer and add it to the Supported ZPL commands table.
  • 🎯 Improve rendering accuracy — fonts, spacing, barcodes, and rotation are the usual suspects; compare against Labelary as a reference.
  • 📖 Improve docs — fix typos, clarify usage, or add examples.

Project layout

Path What lives here
easyzpl/.../zpl/builder/ZplCanvasRenderer.kt The core ZPL-to-Canvas renderer (tokenizer + command dispatch + drawing). Most rendering changes go here.
easyzpl/.../zpl/builder/ZplBuilder.kt The k2zpl { } Kotlin DSL for building ZPL.
easyzpl/.../zpl/command/ One file per ZPL command used by the DSL.
app/src/main/assets/ Sample ZPL labels shown in the example app's pager.

Development workflow

  1. Fork the repository and create a branch: git checkout -b feature/my-change.
  2. Make your change. For renderer work, drop a sample label into app/src/main/assets/ so the behaviour is visible in the example app's pager.
  3. Build and test:
    ./gradlew :easyzpl:assembleDebug   # compile the library
    ./gradlew test                     # run unit tests
    ./gradlew :app:installDebug        # run the example app to eyeball the result
  4. Match the existing style — Kotlin, descriptive names, and a short comment citing the ZPL command/spec behaviour for any new command.
  5. Commit with a clear message and open a pull request describing the change. Include before/after screenshots for any rendering change.

Adding a new ZPL command (quick guide)

  1. Add a tokenizer/dispatch case for the command in ZplCanvasRenderer.kt.
  2. Track any new state on the renderer's State data class, and reset it on ^XA where appropriate.
  3. Implement the drawing in a focused helper function.
  4. Document it in the Supported ZPL commands table.
  5. (Optional) Expose it in the k2zpl DSL by adding a builder function under zpl/command/.

By contributing, you agree that your contributions are licensed under the project's MIT License.


Keywords & related searches

EasyZPLViewer helps you render, view, preview, parse, and print ZPL II labels on Android without an internet connection. If you searched for any of the following, this library is for you:

ZPL viewer Android · ZPL renderer Kotlin · ZPL to bitmap · ZPL to PNG/image · ZPL preview · ZPL parser · Zebra label printing Android · Zebra printer SDK alternative · print ZPL from Android · offline Labelary alternative · ZPL barcode rendering · Code 128 / Code 39 / Data Matrix in ZPL · thermal label printer · shipping label generator · ^XA ^XZ ^FO ^FD ^GB ^BC parser · Jetpack Compose label preview · ZPL II Kotlin DSL (k2zpl).

Tip for maintainers: add these as GitHub repository topics (Settings → Topics) for extra discoverability, e.g. zpl, zpl2, zebra, zebra-printer, label-printing, barcode, thermal-printer, android, kotlin, jetpack-compose, labelary.


Credits & acknowledgements

The k2zpl Kotlin DSL in this project was inspired by the excellent sainsburys-tech/k2zpl — a Kotlin DSL for generating ZPL. Huge thanks to the original authors and contributors for their work, which shaped the builder API here.

EasyZPLViewer extends that idea with an offline ZPL-to-Bitmap renderer and an Android example app. If you only need to generate ZPL on the JVM, do check out the original project.


License

MIT © Umair Adil

About

Offline ZPL II viewer & renderer for Android — convert Zebra label code (ZPL/ZPL2) to a Bitmap with no network calls. Kotlin library + Jetpack Compose example app: render, preview & print shipping labels, Code 128/39 & Data Matrix barcodes. A self-hosted Labelary alternative.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages