Skip to content
Shiny.NET

Printing

NuGet package Shiny.AppDeviceBridge.Printers NuGet package Shiny.AppDeviceBridge.Printing

Two bridges, one for each of Shiny’s printing models (Shiny 5.9). Pick by the printer:

You have Bridge What it does
A 58mm or 80mm receipt, label or kitchen printer printers — Shiny.AppDeviceBridge.Printers The page builds a receipt; the device turns it into ESC/POS (or TSPL) and streams it over Bluetooth LE or raw TCP. It can also render the receipt to a PDF.
Any printer the operating system knows — AirPrint, laser, inkjet, a CUPS queue printing — Shiny.AppDeviceBridge.Printing The page hands a PDF, an image or HTML to the OS print dialog or spooler.

They meet in POST /_bridge/printers/render: the same receipt, as a PDF, can go to an office printer through the printing bridge.

A web page can’t do either on its own. window.print() always shows the browser’s dialog and knows nothing of receipt printers, and Web Bluetooth and Web Serial don’t exist in the WebView on iOS or macOS.

Android iOS Mac Catalyst macOS Windows Linux
Receipt printers on the network yes yes yes yes yes yes
Receipt printers over Bluetooth LE yes yes yes yes yes with Shiny.BluetoothLE.Linux
Rendering a receipt to PDF yes yes yes yes yes with SkiaSharp.NativeAssets.Linux
OS printing PrintManager AirPrint AirPrint CUPS spooler CUPS

Anything a platform lacks answers 501.

Terminal window
dotnet add package Shiny.AppDeviceBridge.Printers
dotnet add package Shiny.AppDeviceBridge.Printing
builder.UseAppDeviceBridge(
bridge => bridge
.AddPrintersBridge()
.AddPrintingBridge(),
webApp => { /* … */ }
);

AddPrintersBridge() registers Shiny’s Bluetooth LE service for the platform, mDNS for finding network printers, and the PDF renderer. AddPrintingBridge() registers the platform’s print service. There’s nothing else to call.

On the page, register the clients (AddPrintersBridgeClient(), AddPrintingBridgeClient()), or in TypeScript use PrintersBridge and PrintingBridge from @shinyorg/appdevicebridge.

The printers bridge finds a printer (POST printers/scan with { "transport": "Ble" } or "Network", listening for scanMs: 5 s by default, 30 s at most), connects to one at a time, then prints or renders receipts — every route is under Requests.

@inject IPrintersBridge Printers
var found = await Printers.ScanAsync(new PrinterScanRequest(PrinterTransport.Ble));
var printer = await Printers.ConnectAsync(new PrinterConnectRequest(Id: found[0].Id));
import { PrintersBridge } from "@shinyorg/appdevicebridge";
const printers = new PrintersBridge();
// a fixed install: connect by address
const printer = await printers.connect({ transport: "Network", host: "192.168.1.50", port: 9100, paper: "Paper58mm" });
  • Bluetooth scans match the known printer models in Shiny.Printers, so a scanned printer comes with its GATT service and write characteristic. A printer no profile knows can be connected by peripheralUuid with its serviceUuid and writeCharacteristicUuid. A cheap printer that garbles a long receipt usually needs a lower chunkSize and a chunkDelayMs.
  • Network scans browse mDNS for _pdl-datastream._tcp and _printer._tcp. mDNS never says how wide the paper is, so a network printer is assumed to be 80mm: pass paper when you know better.
  • Sweep for printers that don’t advertise: { transport: "Network", sweep: true } also tries a TCP connection to every host on the device’s IPv4 subnets (narrowed to a /24) on the ports in PrintersBridgeOptions.NetworkPorts. A swept printer has no name, so its name is its address; each network result’s discovery is Mdns or PortSweep.
  • Capabilities: paper picks a preset — Paper58mm is 32 columns and 384 dots, Paper80mm 48 columns and 576 dots with a cutter — and capabilities describes anything else. language is EscPos (the default) or Tspl for label printers, with the stock in label.
  • Lay out against the printer: ConnectAsync and GET printers/status return its capabilities. Use charactersPerLine for columns, never a hard-coded 32 or 48.

A receipt is a list of elements. Each has a type, and reads only the fields that type uses:

type Fields
Text, Line text; a line ends with a line break
Align align: Left, Center, Right
Bold, Underline on (true)
Size width, height: 1–8 times the normal character
ResetStyle
Feed lines (1)
Barcode text, barcode (Code128, Ean13, …), height (80 dots), width (2–6), barcodeText (Below)
QrCode text, moduleSize (1–16, 6), correction (Medium)
Image data: a base64 PNG or JPEG; dithering: Threshold for logos, FloydSteinberg for photos
Cut cut (Partial), lines to feed first (3)
Raw data: base64 bytes sent as they are — a cash drawer kick, a code page

In C#, PrintElements builds the list the way PrintDocument does:

var columns = printer.Capabilities.CharactersPerLine;
var receipt = new PrintElements()
.AlignCenter().Bold().Size(2).Line("SHINY MART").ResetStyle()
.AlignLeft().Line(new string('-', columns))
.Barcode(PrintBarcodeFormat.Code128, "ORDER-100425", height: 70)
.QrCode("https://example.com/r/100425")
.Feed(2)
.Cut();
await Printers.PrintAsync(receipt.ToRequest());
await printers.print({
elements: [
{ type: "Align", align: "Center" },
{ type: "Bold" },
{ type: "Line", text: "SHINY MART" },
{ type: "ResetStyle" },
{ type: "QrCode", text: "https://example.com/r/100425" },
{ type: "Cut" },
],
});
  • An image is decoded on the device and scaled down to the printer’s dotsPerLine if it’s wider.
  • A cut on a printer without a cutter only feeds the paper, so one receipt works on every printer.
  • A barcode, QR code or image the printer can’t print answers 400, naming the element: elements[3]: this printer does not print Ean13 barcodes. So does any element missing what it needs.
  • Prints are serialized, one receipt at a time. Without a printer, print answers 409 not_connected.
  • A network printer that’s been switched off is only noticed when a write fails. That print answers 409 printer_failed, the connection is dropped, and printers.disconnected fires.

A network ESC/POS printer can report its own sensors. When the connection’s supportsStatus is true, GET printers/connection/status (GetPrinterStatusAsync() / getPrinterStatus()) reads them:

const status = await printers.getPrinterStatus();
if (!status.canPrint)
alert(status.isPaperOut ? "Load paper" : status.isCoverOpen ? "Close the cover" : "Check the printer");

It reports isOnline, isCoverOpen, isPaperNearEnd, isPaperOut, isCutterError, hasUnrecoverableError, hasAutoRecoverableError, isDrawerSensorHigh (the cash-drawer pin; whether high means open depends on the drawer) and canPrint. A printer that can’t report status answers 501, one that doesn’t reply answers 504 printer_timeout, and without a printer it’s 409 not_connected. A status read waits for a receipt that’s still printing.

const pdf = await printers.render({ elements, pageWidth: 612, pageHeight: 792 }); // a Blob; Letter, in points

The page is A4 by default, with a 36-point margin and 12-point monospace text. Images fit the connected printer’s width, or paper’s.

The printing bridge hands a PDF, an image or HTML to the OS — as JSON on POST printing/print, or as a raw body on printing/print/pdf and printing/print/image for large files — see Requests.

@inject IPrintingBridge Printing
var result = await Printing.PrintAsync(new PrintJobRequest(
PrintJobContent.Pdf,
Data: Convert.ToBase64String(pdf),
Options: new PrintJobOptions(JobName: "Invoice 1042")
));
if (result.Status is PrintJobStatus.Cancelled) { /* the dialog was closed */ }
const printing = new PrintingBridge();
const caps = await printing.getCapabilities();
if (caps.silent && caps.listPrinters) {
const target = (await printing.getPrinters()).find(x => x.isDefault);
await printing.print({ content: "Pdf", data: base64, options: { silent: true, printerId: target?.id } });
}
Platform Backend PDF Image HTML HTML to PDF Silent List printers
iOS, Mac Catalyst AirPrint yes yes yes yes to a printer picked before no
Android PrintManager yes yes yes yes no — always the dialog no
Windows GDI+ and the shell yes yes no no yes yes
Linux, macOS CUPS (lp, lpstat) yes yes no no yes yes
  • Large documents: the JSON route holds the whole request, so it’s capped at 16 MB (MaxJsonBytes). Send anything bigger as the body of print/pdf or print/image. These stream it to a temporary file on the device and take up to 512 MB (MaxFileBytes), with the options as query parameters. The format is read from the bytes, and a body that isn’t a PDF, PNG or JPEG answers 400. Past either limit, the answer is 413 too_large.

    const file = await fetch("/invoices/1042.pdf").then(r => r.blob());
    await printing.printPdf(file, { jobName: "Invoice 1042", copies: 2 });
    builder.UseAppDeviceBridge(bridge => bridge.AddPrintingBridge(o => o.MaxFileBytes = 1024L * 1024 * 1024));
    await using var pdf = File.OpenRead(path);
    await Printing.PrintPdfAsync(pdf, jobName: "Invoice 1042");
  • Content: Pdf and Image (PNG or JPEG) carry base64 data; Html carries html; Url carries an http or https url. Content the platform can’t print answers 501: render HTML to a PDF first on Windows and CUPS.

  • The answer comes once the dialog closes or the job is spooled. Completed and Submitted both mean it printed or will, and Cancelled is a result, not an error. A job that fails answers Failed with the error.

  • Options: jobName, copies (1–99), orientation, duplex and color. Android’s dialog lets the person choose these instead. silent is ignored where the platform always shows the dialog.

  • HTML to PDF: html-to-pdf lays a document out with the same engine printing uses and answers application/pdf, for saving, sharing or attaching rather than printing. The page is A4 portrait with a 36-point margin unless pageWidth, pageHeight, orientation or margin say otherwise. It works on iOS, Mac Catalyst and Android, where capabilities.htmlToPdf is true, and answers 501 on Windows, Linux and macOS.

    const pdf = await printing.htmlToPdf({ html: invoiceHtml, pageWidth: 612, pageHeight: 792 }); // a Blob
  • Windows prints a PDF through the registered PDF app’s shell verb, so a machine with no PDF app fails.

Printers/_bridge/printers7 requests · 1 events

Receipt and label printers — ESC/POS or TSPL over Bluetooth LE or the network (raw TCP, port 9100), one at a time: find and connect to a printer, then print receipts built in the page. Printing fails with 409 when none is connected. For an office printer, render the same receipt to a PDF and print that through the printing bridge.

GET/_bridge/printers/status#

The connected printer, and what can be used here.

C#IPrintersBridge.GetStatusAsyncTSPrintersBridge.getStatus
Response
200 PrintersStatus
PrintersStatus · 6 fields
printerConnectedPrinter | nullThe connected printer, if any.
bluetoothSupportedbooleanWhether Bluetooth LE printers can be used here.
bluetoothAccessAccessState | null
networkScanSupportedbooleanWhether network printers can be found by scanning; one can always be connected by address.
renderSupportedbooleanWhether POST render can turn a receipt into a PDF here.
scanningboolean
POST/_bridge/printers/scan#

The printers seen during the scan window: Bluetooth printers matching a known model, or network printers advertising over mDNS (_pdl-datastream._tcp, _printer._tcp) — plus, with sweep, any host on the local subnet that accepts a connection on a printer port.

C#IPrintersBridge.ScanAsyncTSPrintersBridge.scan
Body
application/json · PrinterScanRequest
PrinterScanRequest · 3 fields
transportPrinterTransport
scanMsnumber | nullUp to 30,000 ms; 5 seconds when null.
sweepbooleanNetwork only: also try a TCP connection to every host on this device's IPv4 subnets (a /24 at most) on the ports the app allows — 9100 to 9102 by default — to find printers that do not advertise over mDNS.
Response
200 DiscoveredPrinter[]
DiscoveredPrinter · 9 fields
idstringThe Bluetooth peripheral id, or host:port on the network.
namestring | null
transportPrinterTransport
hoststring | null
portnumber | null
rssinumber | null
profilestring | nullThe known printer model a Bluetooth printer matched.
capabilitiesPrinterCapabilitiesWhat the printer is assumed to be. A network printer never says how wide its paper is, so it is assumed 80mm: pass paper when you know better.
discoveryNetworkDiscoveryMethod | null
POST/_bridge/printers/connection#

Connects to a printer. Fails with 409 while another is connected.

C#IPrintersBridge.ConnectAsyncTSPrintersBridge.connect
Body
application/json · PrinterConnectRequest
PrinterConnectRequest · 14 fields
transportPrinterTransport
idstring | null
peripheralUuidstring | null
serviceUuidstring | null
writeCharacteristicUuidstring | null
hoststring | null
portnumber | null
paperPrinterPaper | nullThe paper width; the scanned printer's, or 80mm on the network and 58mm over Bluetooth, when null.
capabilitiesPrinterCapabilities | nullEverything about the printer, for one that neither preset describes. Wins over paper.
languagePrinterLanguage | nullThe scanned printer's, or ESC/POS, when null.
labelPrinterLabel | nullThe label stock, for TSPL.
timeoutMsnumber | nullUp to 60,000 ms; 15 seconds when null.
chunkSizenumber | nullBytes per Bluetooth write, 20–512; the connection's MTU when null. Lower it when a cheap printer garbles a long receipt.
chunkDelayMsnumber | nullA pause between Bluetooth writes, 0–500 ms, to let a small buffer drain.
Response
200 ConnectedPrinter
ConnectedPrinter · 6 fields
idstringThe Bluetooth peripheral id, or host:port on the network.
namestring | null
transportPrinterTransport
languagePrinterLanguage
capabilitiesPrinterCapabilities
supportsStatusbooleanWhether getPrinterStatus can read this printer's sensors — an ESC/POS printer on the network. Most Bluetooth printers are write-only.
Errors
409Conflict
DELETE/_bridge/printers/connection#

Disconnects the printer.

C#IPrintersBridge.DisconnectAsyncTSPrintersBridge.disconnect
Response
204 No content
GET/_bridge/printers/connection/status#

Reads the connected printer's paper, cover, cutter and drawer sensors — check before a receipt, or poll for a drawer closing. Fails with 409 when no printer is connected, 501 when this printer cannot report its status (supportsStatus), and 504 when it does not answer.

C#IPrintersBridge.GetPrinterStatusAsyncTSPrintersBridge.getPrinterStatus
Response
200 PrinterSensorStatus
PrinterSensorStatus · 9 fields
isOnlinebooleanFalse when the printer has taken itself offline: cover open, paper out, an error, or feeding.
isCoverOpenboolean
isPaperNearEndbooleanThe roll is running low. Not every printer has the sensor.
isPaperOutbooleanThe roll is empty, so printing has stopped.
isCutterErrorbooleanThe auto-cutter jammed or failed.
hasUnrecoverableErrorbooleanAn error the printer cannot clear itself; it needs a power cycle or service.
hasAutoRecoverableErrorbooleanAn error that clears on its own, such as the print head overheating.
isDrawerSensorHighbooleanThe cash-drawer sensor (kick-out connector pin 3) reads high. Whether high means open depends on the drawer.
canPrintbooleanOnline, cover shut, paper loaded and no error.
Errors
409Conflict501Not on this platform504
POST/_bridge/printers/print#

Prints a receipt on the connected printer.

C#IPrintersBridge.PrintAsyncTSPrintersBridge.print
Body
application/json · PrintReceiptRequest
PrintReceiptRequest · 1 field
elementsPrintElement[]
Response
200 PrintReceiptResult
PrintReceiptResult · 1 field
bytesnumberWhat was sent to the printer.
POST/_bridge/printers/render#

Renders a receipt as a PDF. Needs no printer.

C#IPrintersBridge.RenderAsyncTSPrintersBridge.render
Body
application/json · PrintRenderRequest
PrintRenderRequest · 6 fields
elementsPrintElement[]
paperPrinterPaper | nullThe receipt width images are fitted to; the connected printer's, or 80mm, when null.
pageWidthnumber | nullPoints; A4's 595 when null.
pageHeightnumber | nullPoints; A4's 842 when null.
marginnumber | nullPoints; 36 when null.
fontSizenumber | nullPoints; 12 when null.
Response
200 Binary content — Blob in TypeScript, Stream / byte[] in C#
Printing/_bridge/printing6 requests · 0 events

The operating system's own printing — AirPrint on iOS and Mac Catalyst, the print dialog on Android, the spooler on Windows, CUPS on Linux and macOS — for any printer it knows. A receipt printer needs the printers bridge instead.

GET/_bridge/printing/capabilities#

What printing can do here.

C#IPrintingBridge.GetCapabilitiesAsyncTSPrintingBridge.getCapabilities
Response
200 PrintingCapabilities
PrintingCapabilities · 8 fields
supportedboolean
pdfboolean
imageboolean
htmlboolean
systemDialogbooleanPrinting shows the platform's print dialog, where the person picks the printer.
silentbooleanA job with silent skips the dialog: Windows and CUPS (Linux, macOS).
listPrintersbooleanInstalled printers can be listed and named in printerId.
htmlToPdfbooleanHTML can be laid out on pages and returned as a PDF, with no print UI: iOS, Mac Catalyst and Android.
GET/_bridge/printing/printers#

The installed printers; empty where they cannot be listed.

C#IPrintingBridge.GetPrintersAsyncTSPrintingBridge.getPrinters
Response
200 InstalledPrinter[]
InstalledPrinter · 3 fields
idstringPass back as printerId.
displayNamestring
isDefaultboolean
POST/_bridge/printing/print#

Prints a document, through the print dialog unless the job is silent and the platform allows it. Answers once the dialog closes or the job is spooled; a cancelled dialog is a result, not an error. Fails with 501 for content the platform cannot print — HTML on Windows and CUPS, for one: render it to a PDF first.

C#IPrintingBridge.PrintAsyncTSPrintingBridge.print
Body
application/json · PrintJobRequest
PrintJobRequest · 5 fields
contentPrintJobContent
datastring | null
htmlstring | null
urlstring | null
optionsPrintJobOptions | null
Response
200 PrintJobResult
PrintJobResult · 3 fields
statusPrintJobStatus
printerIdstring | null
errorstring | nullWhy, when status is failed.
Errors
501Not on this platform
POST/_bridge/printing/html-to-pdf#

Lays an HTML document out on pages with the engine printing uses and returns it as a PDF, with no print UI — to save, share or attach a document. iOS, Mac Catalyst and Android; 501 elsewhere, where htmlToPdf is false. Capped at the JSON limit (16 MB by default).

C#IPrintingBridge.HtmlToPdfAsyncTSPrintingBridge.htmlToPdf
Body
application/json · HtmlToPdfRequest
HtmlToPdfRequest · 5 fields
htmlstringA complete HTML document.
pageWidthnumber | nullPortrait page width in points; A4's 595 when null. Letter is 612.
pageHeightnumber | nullPortrait page height in points; A4's 842 when null. Letter is 792.
orientationPrintJobOrientationLandscape swaps width and height; anything else is portrait.
marginnumber | nullA uniform margin in points; 36 (half an inch) when null. A CSS @page { margin } in the document wins on Android.
Response
200 Binary content — Blob in TypeScript, Stream / byte[] in C#
Errors
501Not on this platform
POST/_bridge/printing/print/pdf#

Prints a PDF sent as the raw body, streamed to a temporary file on the device rather than held in memory — the route for a document of any size. Options travel in the query. Fails with 400 when the body is not a PDF, and with 413 past the bridge's limit (512 MB by default).

C#IPrintingBridge.PrintPdfAsyncTSPrintingBridge.printPdf
ParameterInType
jobNamequerystring | nulloptional
silentquerybooleanoptional
printerIdquerystring | nulloptional
copiesquerynumber | nulloptional
orientationqueryPrintJobOrientationoptional
duplexqueryPrintJobDuplexoptional
colorqueryPrintJobColoroptional
Body
application/pdf — raw content, not JSON
Response
200 PrintJobResult
PrintJobResult · 3 fields
statusPrintJobStatus
printerIdstring | null
errorstring | nullWhy, when status is failed.
Errors
400Bad request413Too large512
POST/_bridge/printing/print/image#

Prints a PNG or JPEG sent as the raw body, streamed to a temporary file like printPdf. The format is read from the bytes.

C#IPrintingBridge.PrintImageAsyncTSPrintingBridge.printImage
ParameterInType
jobNamequerystring | nulloptional
silentquerybooleanoptional
printerIdquerystring | nulloptional
copiesquerynumber | nulloptional
orientationqueryPrintJobOrientationoptional
duplexqueryPrintJobDuplexoptional
colorqueryPrintJobColoroptional
Body
application/octet-stream — raw content, not JSON
Response
200 PrintJobResult
PrintJobResult · 3 fields
statusPrintJobStatus
printerIdstring | null
errorstring | nullWhy, when status is failed.
Printers1 event

JSON property names are camelCase and enums travel as their names. A ? marks a property the payload may omit;| null one that may be null.

PrintersStatusobject#
printerConnectedPrinter | nullThe connected printer, if any.
bluetoothSupportedbooleanWhether Bluetooth LE printers can be used here.
bluetoothAccessAccessState | null
networkScanSupportedbooleanWhether network printers can be found by scanning; one can always be connected by address.
renderSupportedbooleanWhether POST render can turn a receipt into a PDF here.
scanningboolean
ConnectedPrinterobject#
idstringThe Bluetooth peripheral id, or host:port on the network.
namestring | null
transportPrinterTransport
languagePrinterLanguage
capabilitiesPrinterCapabilities
supportsStatusbooleanWhether getPrinterStatus can read this printer's sensors — an ESC/POS printer on the network. Most Bluetooth printers are write-only.
PrinterTransportenum#
"Ble""Network"
PrinterLanguageenum#

The command language the printer speaks: ESC/POS for receipt printers, TSPL for label printers.

"EscPos""Tspl"
PrinterCapabilitiesobject#

What a printer can do. Lay a receipt out against these, never against a fixed width: charactersPerLine columns of text, and images up to dotsPerLine wide.

charactersPerLinenumber
charactersPerLineFontBnumberColumns in the smaller font.
dpinumber
paperWidthMmnumberThe printable width.
dotsPerLinenumber
supportsImagesboolean
supportsCutboolean
supportsQrCodesboolean
supportedBarcodesPrintBarcodeFormat[]
PrintBarcodeFormatenum#
"UpcA""UpcE""Ean13""Ean8""Code39""Itf""Codabar""Code93""Code128"
AccessStateenum#

Where a permission stands. Reported by every bridge that needs one; the names match Shiny's own.

"Unknown""NotSupported""NotSetup""Disabled""Restricted""Denied""Available"
PrinterScanRequestobject#
transport?PrinterTransport
scanMs?number | nullUp to 30,000 ms; 5 seconds when null.
sweep?booleanNetwork only: also try a TCP connection to every host on this device's IPv4 subnets (a /24 at most) on the ports the app allows — 9100 to 9102 by default — to find printers that do not advertise over mDNS.
DiscoveredPrinterobject#

A printer a scan found. Connect with its id.

idstringThe Bluetooth peripheral id, or host:port on the network.
namestring | null
transportPrinterTransport
hoststring | null
portnumber | null
rssinumber | null
profilestring | nullThe known printer model a Bluetooth printer matched.
capabilitiesPrinterCapabilitiesWhat the printer is assumed to be. A network printer never says how wide its paper is, so it is assumed 80mm: pass paper when you know better.
discoveryNetworkDiscoveryMethod | null
NetworkDiscoveryMethodenum#

How a network printer was found.

"Mdns""PortSweep"
PrinterConnectRequestobject#

The printer to connect to: the id of a printer the last scan found, or one named outright — a Bluetooth peripheralUuid with its serviceUuid and writeCharacteristicUuid, or a network host (an IP address on the local network) and port (9100).

transport?PrinterTransport
id?string | null
peripheralUuid?string | null
serviceUuid?string | null
writeCharacteristicUuid?string | null
host?string | null
port?number | null
paper?PrinterPaper | nullThe paper width; the scanned printer's, or 80mm on the network and 58mm over Bluetooth, when null.
capabilities?PrinterCapabilities | nullEverything about the printer, for one that neither preset describes. Wins over paper.
language?PrinterLanguage | nullThe scanned printer's, or ESC/POS, when null.
label?PrinterLabel | nullThe label stock, for TSPL.
timeoutMs?number | nullUp to 60,000 ms; 15 seconds when null.
chunkSize?number | nullBytes per Bluetooth write, 20–512; the connection's MTU when null. Lower it when a cheap printer garbles a long receipt.
chunkDelayMs?number | nullA pause between Bluetooth writes, 0–500 ms, to let a small buffer drain.
PrinterPaperenum#

The common receipt widths: 58mm is 32 columns and 384 dots, 80mm is 48 columns and 576 dots with a cutter.

"Paper58mm""Paper80mm"
PrinterLabelobject#

A label printer's stock, for Tspl.

widthMm?number | null50 when null.
heightMm?number | null30 when null.
gapMm?number | nullThe gap between labels; 3 when null.
PrinterSensorStatusobject#

A point-in-time read of the connected printer's own sensors.

isOnlinebooleanFalse when the printer has taken itself offline: cover open, paper out, an error, or feeding.
isCoverOpenboolean
isPaperNearEndbooleanThe roll is running low. Not every printer has the sensor.
isPaperOutbooleanThe roll is empty, so printing has stopped.
isCutterErrorbooleanThe auto-cutter jammed or failed.
hasUnrecoverableErrorbooleanAn error the printer cannot clear itself; it needs a power cycle or service.
hasAutoRecoverableErrorbooleanAn error that clears on its own, such as the print head overheating.
isDrawerSensorHighbooleanThe cash-drawer sensor (kick-out connector pin 3) reads high. Whether high means open depends on the drawer.
canPrintbooleanOnline, cover shut, paper loaded and no error.
PrintReceiptRequestobject#

A receipt for the connected printer.

elementsPrintElement[]
PrintElementTypeenum#

What a PrintElement does. Each reads only the fields its description names.

"Text""Line""Align""Bold""Underline""Size""ResetStyle""Feed""Barcode""QrCode""Image""Cut""Raw"
PrintAlignenum#
"Left""Center""Right"
PrintBarcodeTextenum#

Where the barcode's human-readable text goes.

"None""Above""Below""Both"
PrintQrCorrectionenum#

How much of a QR code can be damaged and still read: about 7, 15, 25 or 30%.

"Low""Medium""Quartile""High"
PrintCutModeenum#
"Full""Partial"
PrintDitheringenum#

How an image becomes black dots: Threshold for logos and line art, FloydSteinberg for photos.

"Threshold""FloydSteinberg"
PrintReceiptResultobject#
bytesnumberWhat was sent to the printer.
PrintRenderRequestobject#

A receipt to render as a PDF, for a printer that speaks no ESC/POS — an office printer through the printing bridge, or a download — on pages of pageWidth by pageHeight points.

elementsPrintElement[]
paper?PrinterPaper | nullThe receipt width images are fitted to; the connected printer's, or 80mm, when null.
pageWidth?number | nullPoints; A4's 595 when null.
pageHeight?number | nullPoints; A4's 842 when null.
margin?number | nullPoints; 36 when null.
fontSize?number | nullPoints; 12 when null.
PrintingCapabilitiesobject#

What printing can do here. Feature-detect with these rather than printing to find out.

supportedboolean
pdfboolean
imageboolean
htmlboolean
systemDialogbooleanPrinting shows the platform's print dialog, where the person picks the printer.
silentbooleanA job with silent skips the dialog: Windows and CUPS (Linux, macOS).
listPrintersbooleanInstalled printers can be listed and named in printerId.
htmlToPdfbooleanHTML can be laid out on pages and returned as a PDF, with no print UI: iOS, Mac Catalyst and Android.
InstalledPrinterobject#
idstringPass back as printerId.
displayNamestring
isDefaultboolean
PrintJobRequestobject#

One document: data as base64 for a PDF or a PNG/JPEG image, html for markup, or url for an http or https page.

contentPrintJobContent
data?string | null
html?string | null
url?string | null
options?PrintJobOptions | null
PrintJobContentenum#
"Pdf""Image""Html""Url"
PrintJobOptionsobject#
jobName?string | nullWhat the print queue shows.
silent?booleanPrint without the dialog where silent; the dialog shows anyway elsewhere.
printerId?string | nullThe printer for a silent job; the default printer when null.
copies?number | null1–99; one when null.
orientation?PrintJobOrientation
duplex?PrintJobDuplex
color?PrintJobColor
PrintJobOrientationenum#
"Auto""Portrait""Landscape"
PrintJobDuplexenum#
"Default""OneSided""TwoSidedLongEdge""TwoSidedShortEdge"
PrintJobColorenum#
"Default""Color""Monochrome"
PrintJobResultobject#
statusPrintJobStatus
printerIdstring | null
errorstring | nullWhy, when status is failed.
PrintJobStatusenum#

Completed and Submitted both mean it printed, or will: submitted is a job handed to a spooler whose end the platform does not report.

"Completed""Submitted""Cancelled""Failed"
HtmlToPdfRequestobject#

An HTML document to lay out on pages with the platform's own engine — the one printing uses — and return as a PDF.

htmlstringA complete HTML document.
pageWidth?number | nullPortrait page width in points; A4's 595 when null. Letter is 612.
pageHeight?number | nullPortrait page height in points; A4's 842 when null. Letter is 792.
orientation?PrintJobOrientationLandscape swaps width and height; anything else is portrait.
margin?number | nullA uniform margin in points; 36 (half an inch) when null. A CSS @page { margin } in the document wins on Android.

Both bridges sit behind the bridge policy like every other. On top of that, the printers bridge limits where it connects, because a receipt can carry raw bytes:

  • A printer named by address must be an IP address on the local network (loopback, private or link-local), never a host name that could resolve anywhere.
  • It must be on a raw printer port: 9100, 9101 or 9102. Add others with AddPrintersBridge(o => o.NetworkPorts.Add(…)). A printer a scan found is connected where it advertised.
  • The printing bridge prints only http and https URLs, not file: or anything else on the device.
Platform Details
iOS and Mac Catalyst NSBluetoothAlwaysUsageDescription, NSLocalNetworkUsageDescription, and _pdl-datastream._tcp and _printer._tcp in NSBonjourServices. Without the Bonjour entries a network scan finds nothing, and without the local network description iOS blocks the socket to the printer too.
macOS (sandboxed) the same Info.plist keys, plus the com.apple.security.network.client, com.apple.security.device.bluetooth and com.apple.security.print entitlements.
Android BLUETOOTH_SCAN and BLUETOOTH_CONNECT. OS printing shows its dialog from the foreground activity, so print while the app is visible.
Linux Bluetooth printers need Shiny.BluetoothLE.Linux registered with services.AddBluetoothLE(). Rendering needs SkiaSharp.NativeAssets.Linux in the app, or render answers 501. CUPS printing needs lp and lpstat (cups-client).