@fctc/usb-printer-drawer-capacitor
v1.3.5
Published
USB Printer and Cash Drawer plugin for Capacitor Android
Readme
🖨️ Capacitor USB Printer Plugin
A Capacitor plugin that allows Android apps to print receipts or images directly to a USB thermal printer using the ESC/POS command set.
Supports both PNG/JPEG and SVG (base64) formats.
📦 Installation
npm i @atomsolution/usb-printer-capacitor
npx cap sync⚙️ Android Setup
This plugin depends on the DantSu ESC/POS Thermal Printer library, distributed via JitPack.
Add this line to your root android/build.gradle file:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}💡 Usually located at
android/build.gradle(not insideapp/).
🚀 Usage
1. Import the Plugin
import { UsbPrinter } from "@atomsolution/usb-printer-capacitor";2. Connect to the USB Printer
async function connectPrinter() {
try {
await UsbPrinter.connect();
console.log('✅ Connected to USB printer');
} catch (err) {
console.error('❌ Failed to connect:', err);
}
}3. Print Text
await UsbPrinter.printText({
text: 'Hello from Capacitor USB Printer ✅\n\n',
});4. Print Base64 Image (PNG / JPG / SVG)
You can print base64-encoded images (including SVG):
await UsbPrinter.printBase64({
data: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...',
});or:
await UsbPrinter.printBase64({
data: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDov...',
});5. Print Labels (die-cut decal) — TSPL/TSPL2
Thermal label printers (Xprinter XP-365B, TSC, Godex…) must not be driven with ESC/POS.
ESC/POS is a continuous-roll protocol with no notion of where a label starts, so on die-cut
decal the content drifts further down with every print and eventually spills onto the next
label. TSPL declares SIZE + GAP, so the printer uses its gap sensor to re-align to the
top edge of each label.
One-time preparation per label roll: calibrate the gap sensor (power off → hold FEED → power on, keep holding until the printer ejects a few labels and stops at an edge). Measure 10 labels in a row and divide by 10 to get accurate label height + gap.
Vietnamese / any non-ASCII text — render the label to an image and send it as a TSPL
bitmap. TSPL's built-in TEXT fonts have no diacritic glyphs:
await UsbPrinter.printLabelTo({
role: 'label',
data: pngBase64, // ảnh render sẵn của con tem
widthMM: 50, // đo trên tem thật
heightMM: 30,
gapMM: 2,
marginMM: 1.5, // lề an toàn
copies: 1,
});The image is scaled (aspect preserved) to fit inside the label box, so it can never bleed onto the next label.
ASCII-only content — build TSPL commands and send them raw:
import { UsbPrinter, TsplLabel, mm } from "@atomsolution/usb-printer-capacitor";
const label = new TsplLabel({ widthMM: 50, heightMM: 30, gapMM: 2 })
.text(mm(2), mm(2), 'SP001', { font: '3' })
.barcode(mm(2), mm(9), '8935001234567', { height: mm(12) });
await UsbPrinter.printRawTo({ role: 'label', text: label.build() });Coordinates are in dots, origin (0,0) = top-left of that label. At 203 DPI, 8 dot = 1 mm,
so a 50×30mm label is 400×240 dot. build() throws if an element would run past the label
edge — the printer silently clips the overflow instead of reporting it, so this is caught
in code rather than by burning through a roll of labels.
Fine-tuning when the position is still off:
| Symptom | Fix |
|---------|-----|
| Off by a constant few mm | offsetMM |
| Content needs to move down | shiftDots, or referenceYMM |
| Off by a random amount each print | Gap sensor not calibrated |
| Labels have a black-printed backing | Use blineMM (black mark) instead of gapMM |
| Output is a negative image | invert: true |
printRawTo() is a plain byte passthrough, so it also carries ZPL or CPCL if the printer
speaks those instead.
💡 Example React + Capacitor App
Here’s a simple example UI you can include in your React app (e.g. src/App.tsx) to test connection and printing:
import React, { useState } from 'react';
import { UsbPrinter } from "@atomsolution/usb-printer-capacitor";
function App() {
const [status, setStatus] = useState('Not connected');
const connect = async () => {
try {
await UsbPrinter.connect();
setStatus('✅ Connected');
} catch (e) {
setStatus('❌ Connection failed');
}
};
const printText = async () => {
try {
await UsbPrinter.printText({ text: 'Hello World!\nFrom Capacitor 🖨️' });
alert('Printed successfully!');
} catch (e) {
alert('Print failed: ' + e);
}
};
const printImage = async () => {
try {
const base64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...'; // your image
await UsbPrinter.printBase64({ data: base64 });
alert('Image printed!');
} catch (e) {
alert('Print failed: ' + e);
}
};
return (
<div style={{ padding: 20, fontFamily: 'sans-serif' }}>
<h2>🖨️ Capacitor USB Printer Demo</h2>
<p>Status: {status}</p>
<button onClick={connect}>Connect Printer</button>
<button onClick={printText}>Print Text</button>
<button onClick={printImage}>Print Image</button>
</div>
);
}
export default App;💡 You can run this with
npm startand test on your connected Android device vianpx cap open android.
🧾 Supported Printers
Tested with:
- XPrinter XP-N160I / XP-58 / XP-80
- Rongta RP80
- Zjiang ZJ-5890 / ZJ-80
- Any printer supporting ESC/POS USB
⚙️ Printer Configuration
| Setting | Default | Description |
|----------|----------|-------------|
| DPI | 203 | Printer resolution |
| Paper Width | 72f mm | Physical paper width |
| Printable Width | auto-calculated | Fitted to printer width |
| Output | Monochrome | Dithered bitmap for better contrast |
🧠 Notes
- Works on Android 5.0 (Lollipop) and above.
- Requires USB Host mode on the device.
- Automatically asks for USB permission.
- Supports PNG, JPEG, and SVG base64 formats.
- Automatically scales and pads width to prevent skew or cutoff.
🧰 Troubleshooting
| Issue | Possible Fix |
|--------|----------------|
| Printer not connected | Ensure printer is powered on and permission granted |
| Failed to decode image data | Verify correct base64 prefix |
| Image shifted or cropped | Adjust paperWidthMM in plugin |
| Faint output | Use higher contrast or adjust printer density |
🧑💻 Development
If you’re building the plugin locally:
# Inside plugin folder
npm run build
# Link with your Capacitor app
npx cap sync androidThen open the Android Studio project:
npx cap open android📜 License
MIT License © 2025
❤️ Credits
- Built with Capacitor Plugin API
- Maintained with ❤️ by LUBAN team
