Skip to content

@thermal-print/pdf

PDF generation for thermal printers from PrintNode trees.

The @thermal-print/pdf package provides two modes of PDF generation:

  • Vector PDF (recommended): Native PDF text rendering for crisp, high-quality output
  • Raster PDF (legacy): DOM capture as image for HTML compatibility

Main API

printNodesToPDF()

The recommended method for high-quality PDF output. Uses jsPDF's native text rendering for crisp, vector-based text.

typescript
function printNodesToPDF(
  printNode: PrintNode,
  options?: VectorPDFOptions
): Promise<VectorPDFResult>

Parameters:

ParameterTypeDescription
printNodePrintNodeRoot PrintNode of the tree (from convertToPrintNodes)
optionsVectorPDFOptionsOptional PDF generation options

Returns: Promise<VectorPDFResult> - PDF result with blob, arrayBuffer, and utilities

Example:

typescript
import { convertToPrintNodes } from '@thermal-print/react';
import { printNodesToPDF } from '@thermal-print/pdf';

// Convert React component to PrintNode
const printNode = convertToPrintNodes(<Receipt />);

// Generate vector PDF
const result = await printNodesToPDF(printNode);

// Open in new window for printing
window.open(result.url);

// Or save to file
result.save('receipt.pdf');

// Clean up when done
result.cleanup();

convertToPDF()

Legacy method that captures a DOM element as an image and embeds it in a PDF.

typescript
function convertToPDF(
  elementOrId: string | HTMLElement,
  options?: PDFOptions
): Promise<PDFResult>

Parameters:

ParameterTypeDescription
elementOrIdstring | HTMLElementElement ID or HTMLElement reference
optionsPDFOptionsOptional PDF generation options

Returns: Promise<PDFResult> - PDF result with blob and utilities

Example:

typescript
import { convertToHTML } from '@thermal-print/react';
import { convertToPDF } from '@thermal-print/pdf';

// Step 1: Render React to DOM
const htmlResult = await convertToHTML(<Receipt />, {
  containerId: 'thermal-receipt',
  keepInDOM: true
});

// Step 2: Convert DOM to PDF
const pdfResult = await convertToPDF('thermal-receipt', {
  paperSize: '80mm',
  scale: 2
});

// Step 3: Open print dialog
window.open(pdfResult.url);

// Step 4: Cleanup
htmlResult.cleanup();
pdfResult.cleanup();

Types

VectorPDFOptions

Configuration options for vector PDF generation (printNodesToPDF).

typescript
interface VectorPDFOptions {
  paperWidth?: number;
  paperHeight?: number | 'auto';
  defaultFontSize?: number;
  lineHeight?: number;
  fontFamily?: string;
  pxToMm?: number;
}
OptionTypeDefaultDescription
paperWidthnumberFrom Page or 205ptPaper width in points
paperHeightnumber | 'auto''auto'Paper height in points, or 'auto' for dynamic
defaultFontSizenumber10Default font size in points
lineHeightnumber1.2Line height multiplier
fontFamilystring'Helvetica'Font family (must be available in jsPDF)
pxToMmnumber0.264583Pixel to mm conversion factor

Page Component Override

Paper dimensions are typically read from the Page component's size prop. Options here override those values.


VectorPDFResult

Result returned by printNodesToPDF().

typescript
interface VectorPDFResult {
  blob: Blob;
  arrayBuffer: ArrayBuffer;
  url: string;
  cleanup: () => void;
  save: (filename: string) => void;
}
PropertyTypeDescription
blobBlobGenerated PDF as a Blob
arrayBufferArrayBufferGenerated PDF as ArrayBuffer (for IPC transfer)
urlstringObject URL for the blob (use with window.open)
cleanup() => voidRevokes the object URL (call when done)
save(filename: string) => voidTriggers download in browser

PDFOptions

Configuration options for raster PDF generation (convertToPDF).

typescript
interface PDFOptions {
  paperSize?: PaperSize;
  orientation?: Orientation;
  scale?: number;
  margin?: number;
  filename?: string;
  imageQuality?: number;
  backgroundColor?: string;
  waitTime?: number;
}
OptionTypeDefaultDescription
paperSizePaperSize'A4'Paper size (see PaperSize type)
orientationOrientation'portrait'Page orientation
scalenumber2html2canvas rendering scale (higher = better quality)
marginnumber10PDF margins in mm
filenamestring-If provided, auto-downloads the PDF
imageQualitynumber0.95JPEG quality (0-1)
backgroundColorstring'#ffffff'Background color for transparent areas
waitTimenumber0Wait time (ms) before capturing element

PDFResult

Result returned by convertToPDF().

typescript
interface PDFResult {
  blob: Blob;
  url: string;
  cleanup: () => void;
}
PropertyTypeDescription
blobBlobGenerated PDF as a Blob
urlstringObject URL for the blob
cleanup() => voidRevokes the object URL

PaperSize

Paper size options for PDF generation.

typescript
type PaperSize =
  | 'A4'
  | 'Letter'
  | '80mm'
  | '58mm'
  | { width: number; height: number }; // Custom size in mm

Predefined Sizes:

SizeDimensionsUsage
'A4'210mm × 297mmStandard documents
'Letter'215.9mm × 279.4mmUS Letter
'80mm'80mm × 297mmStandard thermal receipts
'58mm'58mm × 297mmCompact thermal receipts

Custom Size:

typescript
const result = await convertToPDF(element, {
  paperSize: { width: 100, height: 200 } // mm
});

Orientation

Page orientation for PDF.

typescript
type Orientation = 'portrait' | 'landscape';

PDFGenerator

Low-level PDF generation class using jsPDF's native text APIs. Use this for fine-grained control over PDF generation.

Constructor

typescript
constructor(options?: PDFGeneratorOptions)
typescript
interface PDFGeneratorOptions {
  paperWidth?: number;      // Default: 80pt
  paperHeight?: number | 'auto'; // Default: 'auto'
  defaultFontSize?: number; // Default: 10pt
  lineHeight?: number;      // Default: 1.2
  fontFamily?: string;      // Default: 'Helvetica'
  pxToMm?: number;          // Default: 0.264583
}

Methods

Initialization

MethodSignatureDescription
initialize()voidReset generator to initial state
setPageMargins(left, right, top)(number, number, number): voidSet page margins in points

Text & Formatting

MethodSignatureDescription
addText(text)text: stringAdd text at current position
addWrappedText(text)text: stringAdd text with automatic word wrapping
addTextAtPosition(text, position)text: string, position: 'left' | 'center' | 'right'Add text at specific alignment without advancing Y
addTextAtX(text, xOffset)text: string, xOffset: numberAdd text at specific X offset
setAlign(align)align: 'left' | 'center' | 'right'Set text alignment
setFontSize(size)size: numberSet font size in points
setBold(bold)bold: booleanEnable/disable bold
resetFormatting()voidReset to default formatting

Example:

typescript
import { PDFGenerator } from '@thermal-print/pdf';

const generator = new PDFGenerator({ paperWidth: 227 }); // 80mm
generator.initialize();

generator.setAlign('center');
generator.setBold(true);
generator.setFontSize(16);
generator.addText('RECEIPT');
generator.setBold(false);
generator.addNewline(2);

generator.setAlign('left');
generator.setFontSize(10);
generator.addText('Item 1');
generator.addTextAtPosition('$10.00', 'right');
generator.addNewline();

Layout & Spacing

MethodSignatureDescription
addNewline(count?)count?: numberAdd newline(s) (default: 1)
addSpacing(pt)pt: numberAdd vertical spacing in points
addPadding(left, top)(number, number): voidAdd padding to current position
pushHorizontalPadding(left, right)(number, number): voidPush horizontal padding (reduces content width)
popHorizontalPadding(left, right)(number, number): voidRestore previous horizontal padding
addDivider(style?)style?: 'solid' | 'dashed'Add horizontal divider line

Graphics

MethodSignatureDescription
addImage(source, width?, height?)source: string, width?: number, height?: numberAdd image from data URI

Measurement

MethodSignatureDescription
getTextWidth(text)text: stringnumberGet text width in points using font metrics
getContentWidth()numberGet current content width in points
getPaperWidth()numberGet paper width in points
getCharsPerLine()numberGet approximate characters per line
getCurrentY()numberGet current Y position
getCurrentX()numberGet current X position (left margin)
getCurrentFontSize()numberGet current font size in points

Conversion

MethodSignatureDescription
pxToPt(px)px: numbernumberConvert CSS pixels to points
pxToPoints(px)px: numbernumberConvert CSS pixels to points (alias)

Output

MethodSignatureDescription
getBlob()BlobGet generated PDF as Blob
getArrayBuffer()ArrayBufferGet generated PDF as ArrayBuffer
getDataUrl()stringGet generated PDF as data URL
save(filename)filename: stringDownload the PDF
finalizePageHeight(margin?)margin?: numberFinalize page height for dynamic mode

PDFTraverser

Walks through the PrintNode tree and generates vector PDF using PDFGenerator.

typescript
class PDFTraverser {
  constructor(generator: PDFGenerator)
  traverse(node: PrintNode | null): Promise<void>
}

Handles element types:

TypeDescription
documentRoot document container
pagePage element with margins
viewContainer with CSS box model layout
textText element with styling
textnodeRaw text node
imageImage element

Example:

typescript
import { PDFGenerator, PDFTraverser } from '@thermal-print/pdf';

const generator = new PDFGenerator({ paperWidth: 227 });
const traverser = new PDFTraverser(generator);

await traverser.traverse(printNode);
generator.finalizePageHeight();

const blob = generator.getBlob();

Style Support

TextStyle Properties

PropertyTypeSupportDescription
fontSizenumberYesFont size in points
fontWeightstring | numberYesBold detection ('bold' or ≥700)
fontFamilystringPartialOnly bold detection from name
textAlign'left' | 'center' | 'right'YesText alignment

ViewStyle Properties

Layout Properties:

PropertyTypeSupportDescription
flexDirection'row' | 'column'YesLayout direction
justifyContentstringPartial'space-between', 'center' supported
alignItemsstringPartialText/image alignment fallback
widthstring | numberYesPercentage widths for columns
heightstring | numberYesExplicit height for empty views

Spacing Properties (in points):

PropertyTypeSupportDescription
paddingnumberYesAll sides padding
paddingTopnumberYesTop padding
paddingBottomnumberYesBottom padding
paddingLeftnumberYesLeft padding
paddingRightnumberYesRight padding
marginnumberYesAll sides margin
marginTopnumberYesTop margin
marginBottomnumberYesBottom margin

Border Properties:

PropertyTypeSupportDescription
borderTopstringYesTop divider line (solid/dashed)
borderBottomstringYesBottom divider line

Dynamic Height

For receipts that grow with content, use wrap={true} on the Page component:

tsx
<Document>
  <Page wrap={true}>
    <Text>Content flows naturally without page breaks</Text>
  </Page>
</Document>

When wrap={true}:

  • Initial height is set to 5000pt
  • Content flows without page breaks
  • Height is finalized after content is rendered

Known Limitation

jsPDF stores absolute Y coordinates. The page cannot be resized after content is rendered, so there may be blank space at the bottom of dynamic-height PDFs.


Units

The PDF package uses points (pt) as the primary unit, matching @react-pdf/renderer:

ConversionFormula
CSS px → ptpx * 0.75 (at 96dpi)
mm → ptmm * 2.83465
pt → mmpt * 0.352778

Paper Size Reference (points):

SizePointsMillimeters
58mm165pt58mm
80mm227pt80mm
A4595pt × 842pt210mm × 297mm

CSS Box Model

Views follow the standard CSS box model order:

  1. Margin top - Space before element's border box
  2. Border top - Top divider line
  3. Padding top - Inside element, before content
  4. Content - Children rendered here
  5. Padding bottom - Inside element, after content
  6. Border bottom - Bottom divider line
  7. Margin bottom - Space after element's border box
tsx
<View style={{
  marginTop: 10,
  borderTop: '1px solid black',
  paddingTop: 5,
  paddingBottom: 5,
  borderBottom: '1px solid black',
  marginBottom: 10
}}>
  <Text>Content here</Text>
</View>

Released under the MIT License.