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.
Live examples and interactive documentation: https://courthive.github.io/pdf-factory/
pnpm add pdf-factory
# or
npm install pdf-factoryimport { 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');import { generateSchedulePDF, extractScheduleData } from 'pdf-factory';
const scheduleData = extractScheduleData({ tournamentRecord });
const doc = generateSchedulePDF({ scheduleData, tournamentRecord });import { generatePlayerListPDF, extractParticipantData } from 'pdf-factory';
const participants = extractParticipantData({ tournamentRecord, eventId });
const doc = generatePlayerListPDF({ participants, tournamentRecord });import { generateCourtCardPDF, extractCourtCardData } from 'pdf-factory';
const courtCards = extractCourtCardData({ tournamentRecord, eventId });
const doc = generateCourtCardPDF({ courtCards, tournamentRecord });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, ...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,
});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.
| 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 |
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
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
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/Tests are split into two categories:
- Generator tests (always run) — generate PDFs from
mocksEnginetournament 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 viadescribe.skipIf(!hasFixtures).
To run parser tests locally, populate fixtures/reference/ with PDF files (see fixtures/reference/download-draws.sh).
- jsPDF + jspdf-autotable — PDF generation
- tods-competition-factory — tournament data structures and engine
- pdf2json — PDF parsing (parser entry point only)
MIT