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

pdf-factory

v0.8.0

Published

Generate tournament PDFs from TODS data — draw sheets, schedules, player lists, court cards

Readme

pdf-factory

Generate and parse tournament PDFs from TODS (Tennis Open Data Standards) data. Draw sheets, schedules, player lists, court cards, sign-in sheets, and match cards — with Grand Slam and tour-level presets.

Part of the CourtHive ecosystem.

Storybook

Live examples and interactive documentation: https://courthive.github.io/pdf-factory/

Install

pnpm add pdf-factory
# or
npm install pdf-factory

Quick start

Generate a draw sheet

import { tournamentEngine } from 'tods-competition-factory';
import { generateDrawPDF } from 'pdf-factory';

// Load a TODS tournament record
tournamentEngine.setState(tournamentRecord);
const { drawIds } = tournamentEngine.getEvent({ eventId });

// Generate a PDF — returns a jsPDF doc
const doc = generateDrawPDF({
  tournamentRecord,
  drawId: drawIds[0],
  preset: 'wimbledon', // or 'usOpen', 'rolandGarros', 'australianOpen', 'itfJunior', ...
});

// Output as bytes
const pdfBytes = doc.output('arraybuffer');

Generate a schedule (Order of Play)

import { generateSchedulePDF, extractScheduleData } from 'pdf-factory';

const scheduleData = extractScheduleData({ tournamentRecord });
const doc = generateSchedulePDF({ scheduleData, tournamentRecord });

Generate a player list

import { generatePlayerListPDF, extractParticipantData } from 'pdf-factory';

const participants = extractParticipantData({ tournamentRecord, eventId });
const doc = generatePlayerListPDF({ participants, tournamentRecord });

Generate court cards

import { generateCourtCardPDF, extractCourtCardData } from 'pdf-factory';

const courtCards = extractCourtCardData({ tournamentRecord, eventId });
const doc = generateCourtCardPDF({ courtCards, tournamentRecord });

Parse an existing PDF back to TODS

The parser entry point is separate (Node-only, depends on pdf2json):

import { parsePdfBuffer, extractDrawFromPage } from 'pdf-factory/parser';

const parsed = await parsePdfBuffer(pdfBuffer);
const draw = extractDrawFromPage(parsed.pages[0]);
// draw.participants, draw.scores, draw.seeds, ...

Presets

Format presets control name display, score formatting, seed placement, round labels, and page layout. Built-in presets mirror real-world tournament PDFs:

| Preset | Style | Round labels | | ---------------- | --------------------- | --------------------- | | wimbledon | LAST, First (GBR) [1] | First Round ... Final | | usOpen | LAST, First (USA) [1] | Round 1 ... Final | | rolandGarros | LAST First (FRA) [1] | 1er TOUR ... FINALE | | australianOpen | LAST, First (AUS) [1] | First Round ... Final | | itfJunior | LAST, First (NAT) [1] | Round of 64 ... Final | | wtaTour | LAST, First (NAT) [1] | First Round ... Final |

Presets are composable — start from a built-in and override individual fields:

import { getPreset, mergePreset } from 'pdf-factory';

const custom = mergePreset('wimbledon', {
  nameFormat: 'First LAST',
  showEntryStatus: true,
});

Composition catalog

Higher-level composition presets bundle header layout, footer layout, draw format, and page config into named units. Categories: Grand Slam, Tour, ITF, National, Collegiate, Club.

import { getCatalogPreset, listCatalogPresets } from 'pdf-factory';

const presets = listCatalogPresets(); // all available compositions
const wimbledon = getCatalogPreset('wimbledon');

See the Header & Footer Catalog for the three-tier footer system derived from real-world tournament PDFs.

Document types

| Generator | Description | | ------------------------------ | ---------------------------------------------------------- | | generateDrawPDF | Auto-selects renderer based on draw type | | generateTraditionalDrawPDF | Single elimination bracket | | generateSplitDrawPDF | Large draws split across pages (halves/quarters + summary) | | generateFeedInDrawPDF | Feed-in consolation structures | | generateConsolationDrawPDF | Consolation bracket | | generateCompassDrawPDF | Compass (8-direction) draws | | generateDoubleEliminationPDF | Winners + losers bracket | | generateLuckyDrawPDF | Lucky loser draw | | generateMirroredDrawPDF | Mirrored (left-right) bracket | | generateBackdrawPDF | Backdraw structures | | generateSchedulePDF | Order of Play (court grid) | | generateScheduleV2PDF | Sequential schedule layout | | generateSequentialOOP | Sequential Order of Play | | generatePlayerListPDF | Participant list with seeds and rankings | | generateCourtCardPDF | Per-court assignment cards | | generateSignInSheetPDF | Player sign-in sheet | | generateMatchCardPDF | Individual match scoring card |

Architecture

TODS tournament record
  → extractors (extractDrawData, extractScheduleData, ...)
    → composition (page config + preset + header/footer)
      → renderers (traditionalDraw, roundRobin, compass, ...)
        → jsPDF document (Uint8Array or blob)

Parser pipeline (reverse direction):

PDF file
  → pdf2json extraction
    → pdfRuler (spatial analysis, column/row detection)
      → TODS structure mapping

Source layout

src/
  config/        Presets (Grand Slam formats, page sizes, composition catalog)
  core/          Data extractors (TODS → renderer-ready data)
  composition/   Page composition, header/footer layouts, print dispatcher
  generators/    High-level PDF generators (one per document type)
  renderers/     Draw-type-specific renderers (bracket, round robin, compass, ...)
  layout/        Page layout engine, header/footer primitives, fonts, tables
  parser/        PDF-to-TODS extraction (Node-only, separate entry point)
  comparison/    Visual regression utilities (pixel-level PDF comparison)
  utils/         Shared formatting primitives
  stories/       Storybook stories for visual development

Development

pnpm install
pnpm storybook       # interactive dev on :6007
pnpm test            # vitest (watch mode)
pnpm test:run        # single run
pnpm test:coverage   # coverage report
pnpm lint            # eslint with fix
pnpm check-types     # tsc --noEmit
pnpm build           # vite library build → dist/

Testing

Tests are split into two categories:

  • Generator tests (always run) — generate PDFs from mocksEngine tournament data and verify output. No external fixtures needed.
  • Parser/fidelity tests (local only) — parse third-party tournament PDFs in fixtures/. These are gitignored and skipped automatically in CI via describe.skipIf(!hasFixtures).

To run parser tests locally, populate fixtures/reference/ with PDF files (see fixtures/reference/download-draws.sh).

Dependencies

License

MIT