npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jasperreports

v0.5.0

Published

Lightweight, zero-Java JasperReports JRXML to PDF renderer for Cloudflare Workers, Node.js, Deno and browsers

Downloads

384

Readme

jasperreports (lite) (alpha)

npm version CI License: MIT

Lightweight, zero-Java JRXML → PDF renderer for modern JavaScript runtimes.

Runs natively in Cloudflare Workers, Node.js 18+, Deno, and browsers — no JVM, no Puppeteer, no headless Chrome. Pure TypeScript + pdf-lib.

⚠️ This is a pragmatic subset, not a full JasperReports port. It covers the features you need for certificates, confirmations, tickets, labels, invoices, grouped financial reports, multi-column listings and subreports. See the Feature matrix below before adopting it — if your templates need charts, crosstabs, barcodes, Java scriptlets, HTML/RTF markup, or live database queries, you will be disappointed.

Why?

Classic JasperReports requires a Java runtime. That rules it out for edge platforms (Cloudflare Workers, Vercel Edge, Deno Deploy) and makes it awkward for plain Node.js services. jasperreports renders a practical subset of JRXML natively in JavaScript.

| | jasperreports | Java JasperReports | Puppeteer | |---|---|---|---| | Bundle size | ~27 KB + pdf-lib | hundreds of MB + JVM | ~200 MB | | Cloudflare Workers | ✅ | ❌ | ❌ | | Cold start | milliseconds | seconds | seconds | | Dependencies | pdf-lib, fast-xml-parser | JVM | Chromium | | JRXML coverage | subset (see below) | 100% | N/A |

Install

npm install jasperreports

Quick start

import { renderJRXML } from 'jasperreports';
import { writeFile } from 'node:fs/promises';

const jrxml = await (await fetch('https://example.com/certificate.jrxml')).text();

const pdfBytes = await renderJRXML(jrxml, {
  fields: {
    Vorname: 'Max',
    Nachname: 'Mustermann',
    Datum: '15. Januar 2026',
  },
  imageResolver: async (path) => {
    const res = await fetch(path);
    return new Uint8Array(await res.arrayBuffer());
  },
});

await writeFile('certificate.pdf', pdfBytes);

Is my template supported?

Quick sanity check before you adopt this library — grep your template for unsupported elements:

grep -E "<(subreport|group|chart|crosstab|frame|break|componentElement|genericElement)" your-template.jrxml

If any match you are almost certainly outside of this library's supported surface.

Feature matrix

✅ Supported

Report structure

| Feature | Notes | |---|---| | <jasperReport> root | pageWidth, pageHeight, columnWidth, margins, orientation, name | | <field> | name, class (effectively string) | | <parameter> | name, class, defaultValueExpression | | <variable> | Definitions are parsed, but calculations are not executed | | Bands | background, title, pageHeader, columnHeader, detail, columnFooter, pageFooter, lastPageFooter, summary, noData |

Elements

| Element | Support | |---|---| | <staticText> | ✅ Full | | <textField> | ✅ incl. isBlankWhenNull, textAdjust | | <image> | ✅ PNG/JPG via imageResolver callback | | <line> | ✅ incl. direction (TopDown / BottomUp) | | <rectangle> | ✅ fill + border | | <ellipse> | ✅ Full |

Element attributes

| Attribute | Support | |---|---| | reportElement (x, y, width, height) | ✅ | | forecolor, backcolor | ✅ hex #RRGGBB and shorthand #RGB | | mode (Opaque / Transparent) | ✅ | | textAlignment | ✅ Left, Center, Right | | verticalAlignment | ✅ Top, Middle, Bottom | | fontName | ✅ Helvetica, Times, Courier (standard PDF fonts) | | size, isBold, isItalic, isUnderline | ✅ | | pen (lineWidth, lineColor, lineStyle) | ✅ Solid, Dashed, Dotted | | scaleImage on <image> | ✅ FillFrame, RetainShape, Clip (+ hAlign / vAlign) | | pattern on <textField> | ✅ DecimalFormat / SimpleDateFormat subset | | printWhenExpression | ✅ evaluated via expression engine | | <style> + inheritance | ✅ named styles + parent chain | | <box> borders + padding | ✅ per-side pens + paddings | | rotation on text | ✅ Left, Right, UpsideDown | | markup="styled" | ✅ inline <b>, <i>, <u>, <color> | | <group> + groupHeader / groupFooter | ✅ emitted on value change, reprint on new page | | <variable calculation="…"> | ✅ Sum, Count, Average, Lowest, Highest, First | | Built-in vars PAGE_NUMBER, PAGE_COUNT, REPORT_COUNT | ✅ two-pass render resolves PAGE_COUNT | | Multi-page layout | ✅ auto page break + repeat pageHeader / columnHeader | | <lastPageFooter> | ✅ replaces pageFooter on final page | | textAdjust="StretchHeight" | ✅ element grows to fit wrapped lines | | Custom font embedding | ✅ via fonts: { fontkit, families } render option | | Iterable data source | ✅ via dataSource: Row[] render option | | Resource bundles $R{key} | ✅ via resources render option | | <frame> nested layout | ✅ with background, box, and child offsets | | <break type="Page"/> / <break type="Column"/> | ✅ page break / column break | | Multi-column detail (columnCount, columnWidth, columnSpacing) | ✅ auto column flow | | <subreport> | ✅ via subreportResolver render option | | hyperlinkType="Reference" (URL link) | ✅ native PDF link annotations | | hyperlinkType="LocalAnchor" + <anchorNameExpression> | ✅ internal GoTo links | | bookmarkLevel on anchors | ✅ flat PDF outline / bookmarks |

Expressions

| Pattern | Example | |---|---| | Field / param / var | $F{fieldName} / $P{paramName} / $V{varName} | | Resource | $R{bundleKey} | | String concatenation | $F{first} + " " + $F{last} | | Arithmetic | $F{a} + $F{b} * 2 | | Comparison + logic | $F{n} > 0 && $F{n} < 100 | | Ternary | $F{n} > 0 ? "pos" : "neg" | | String methods | $F{s}.toUpperCase(), .substring(0, 3), .replace("a", "b") | | Number methods | $F{x}.toFixed(2), .intValue() | | SimpleDateFormat | new SimpleDateFormat("yyyy-MM-dd").format($F{date}) |

❌ Not supported

Report-level features

| Feature | Why not | |---|---| | <template> / external stylesheets | No external resource loading | | <queryString> | No database access | | <scriptlet> | No Java execution | | <sortField> | No data sorting |

Elements

| Element | Status | |---|---| | <componentElement> | ❌ barcodes, lists, maps | | <genericElement> | ❌ custom extensions | | <chart> / <barChart> / <pieChart> / … | ❌ no chart engine | | <crosstab> | ❌ pivot tables |

Attributes

| Attribute | Status | Workaround | |---|---|---| | markup (html, rtf) | ❌ | Use markup="styled" | | markup="styled" | ✅ | <b>, <i>, <u>, <color rgb="#..."> | | rotation (text rotation) | ✅ | Left, Right, UpsideDown | | hyperlinkType / anchors / bookmarks | ✅ | Reference, LocalAnchor, bookmarkLevel | | box / border / padding | ✅ | per-side pens + paddings | | Line spacing / paragraph indent | ❌ | — | | Custom fonts via <fontName> | ✅ | via fonts render option |

Expressions

| Feature | Example that won't work | |---|---| | Java method calls | $F{name}.toUpperCase() | | Date formatting | new SimpleDateFormat(...) | | Arithmetic | $F{price} * $F{quantity} | | Conditional expressions | $F{x} != null ? "yes" : "no" | | String manipulation | $F{name}.substring(0, 3) |

Layout

| Feature | Status | |---|---| | Multi-page output (automatic page break) | ✅ | | Multi-column layout | ✅ columnCount / columnWidth / columnSpacing | | Dynamic band height (stretchType / textAdjust="StretchHeight") | ✅ StretchHeight on <textField> |

Rough coverage estimate

Roughly ~90 % of common certificate / invoice / receipt templates should work out of the box.

  • ✅ Works well: certificates, diplomas, confirmations, tickets, labels, invoices (including multi-page, grouped, multi-column, subreports, hyperlinks), financial reports with sums / averages / counts, reports with nested frames.
  • ❌ Will not work: charts, crosstabs, barcodes, reports that rely on Java scriptlets or live database queries, HTML/RTF markup.

API

renderJRXML(jrxml, options?) => Promise<Uint8Array>

Renders a JRXML template string to a PDF byte array.

interface JRXMLRenderOptions {
  /** Values for `$F{fieldName}` expressions. */
  fields?: Record<string, unknown>;
  /** Values for `$P{paramName}` expressions. */
  parameters?: Record<string, unknown>;
  /** Resolve image paths to PNG/JPG bytes. */
  imageResolver?: (path: string) => Promise<Uint8Array | null>;
  /** Enable verbose console logging. */
  debug?: boolean;
}

parseJRXML(jrxml, debug?) => ParsedReport

Parses a JRXML template without rendering. Useful for introspection, validation, or building custom renderers.

import { parseJRXML } from 'jasperreports';

const report = parseJRXML(jrxml);
console.log('Fields:', [...report.fields.keys()]);
console.log('Page:', report.config.pageWidth, 'x', report.config.pageHeight);

Low-level classes

For advanced use cases the underlying classes are also exported: JRXMLParser, JRXMLRenderer, ExpressionEvaluator.

Cloudflare Workers example

import { renderJRXML } from 'jasperreports';

export default {
  async fetch(request: Request): Promise<Response> {
    const { jrxml, fields, images } = await request.json();

    const pdfBytes = await renderJRXML(jrxml, {
      fields,
      imageResolver: async (path) => {
        const url = images?.[path];
        if (!url) return null;
        const res = await fetch(url);
        return new Uint8Array(await res.arrayBuffer());
      },
    });

    return new Response(pdfBytes, {
      headers: {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'attachment; filename="document.pdf"',
      },
    });
  },
};

A full Worker example lives in examples/cloudflare-worker.ts.

Design notes

  • No DOMParser. Cloudflare Workers do not ship DOMParser, so XML parsing is handled by fast-xml-parser via a tiny adapter in src/xml-parser.ts that preserves the existing XMLElement shape used by the rest of the parser.
  • pdf-lib over Puppeteer. ~200 KB versus ~200 MB, and it actually runs on the edge.
  • Pluggable image resolution. Images are not embedded in the JRXML; you pass an async resolver that can pull bytes from URLs, KV, R2, the filesystem, base64, etc.

Roadmap

See ROADMAP.md for the full prioritized list. Highlights of what's still on the list:

  • [ ] Automatic multi-page layout
  • [ ] Group headers / footers
  • [ ] Custom font embedding helpers
  • [ ] Richer expression engine (method calls, SimpleDateFormat, arithmetic, conditionals)
  • [x] Subreport support (simple cases)
  • [ ] <style> / style inheritance
  • [ ] <box> borders and padding
  • [ ] Barcode elements via plug-in

Development

npm install
npm test
npm run build

Publishing

Releases are triggered by bumping the version field in package.json on main. No tags, no extra commands.

  1. One-time setup: add an NPM_TOKEN secret to the repo (Settings → Secrets and variables → Actions → New repository secret) — a granular npm token with Read and write access to the jasperreports package.

  2. Edit package.json, change "version", commit, push:

    # bump to whatever you want
    git commit -am "release: v0.1.1"
    git push

The .github/workflows/release.yml workflow compares the local version to what's on npm. If they differ, it runs typecheck + tests + build, publishes with provenance, and creates a matching GitHub Release (e.g. v0.1.1) with auto-generated notes. If the versions already match, it does nothing.

Prefer the command line? npm version patch still works — it edits package.json for you and creates a git tag. The workflow will pick up the version change on push.

Manual publish from your workstation

npm login
npm publish    # prepublishOnly runs clean + typecheck + test + build

License

MIT © Brand Boosting GmbH