Skip to content

Printing large images over BLE is slow when using ViewShot for non-ASCII text #1404

@irfanqutabbjs

Description

@irfanqutabbjs

Issue / Use Case:

I am trying to print receipts that include Urdu text. The printer does not support Urdu characters natively, so I cannot print plain text.

Workaround:

  1. Design the receipt as a React Native View.
  2. Capture the View as a PNG using react-native-view-shot.
  3. Convert the PNG to ESC/POS raster bytes.
  4. Send the bytes to a BLE thermal printer using BleManager.write.

I would appreciate guidance or suggestions on the following:

  1. Optimizing BLE image printing:
    Are there better ways to stream large ESC/POS raster images over BLE to reduce printing time?
  2. Efficient chunking / buffering strategies:
    Is there a recommended way to split or buffer large byte arrays in react-native-ble-manager for faster printing?
  3. Alternative approaches:
    Any suggestions for reliably printing non-ASCII text (like Urdu) on thermal printers without losing speed or quality?
  4. Best practices for ViewShot-to-ESC/POS workflow:
    If others have experience printing captured views with BLE thermal printers, any performance tips would be very helpful.

Any help, code examples, or advice would be greatly appreciated!

Code Example:

import {Buffer} from 'buffer';
import {PNG} from 'pngjs/browser';
import BleManager from 'react-native-ble-manager';
import ViewShot from 'react-native-view-shot';

// Convert PNG base64 to ESC/POS raster bytes
const convertPngToEscPos = async (base64: string, targetWidth = 576) => {
  const cleanBase64 = base64.replace(/^data:image\/\w+;base64,/, '');
  const buffer = Buffer.from(cleanBase64, 'base64');
  const png = PNG.sync.read(buffer);

  const originalWidth = png.width;
  const originalHeight = png.height;
  const pixels = png.data;

  const targetHeight = Math.floor((originalHeight * targetWidth) / originalWidth);
  const bytesPerLine = Math.ceil(targetWidth / 8);
  const imageBytes: number[] = [];

  for (let y = 0; y < targetHeight; y++) {
    for (let xByte = 0; xByte < bytesPerLine; xByte++) {
      let byte = 0;
      for (let bit = 0; bit < 8; bit++) {
        const x = xByte * 8 + bit;
        if (x >= targetWidth) continue;

        const srcX = Math.floor((x * originalWidth) / targetWidth);
        const srcY = Math.floor((y * originalHeight) / targetHeight);
        const idx = (srcY * originalWidth + srcX) * 4;

        const gray = 0.299 * pixels[idx] + 0.587 * pixels[idx+1] + 0.114 * pixels[idx+2];
        const pixel = gray < 128 ? 1 : 0;
        byte |= pixel << (7 - bit);
      }
      imageBytes.push(byte);
    }
  }

  return {width: bytesPerLine, height: targetHeight, bytes: imageBytes};
};

// Sending to BLE printer
const handlePrint = async (printerId: string, serviceUUID: string, characteristicUUID: string, base64Image: string) => {
  const {bytes, width, height} = await convertPngToEscPos(base64Image);

  const dataToSend = [
    0x1b, 0x40,            // Initialize printer
    0x1b, 0x33, 0x00,      // Set line spacing
    0x1d, 0x76, 0x30, 0x00,
    width & 0xff,
    (width >> 8) & 0xff,
    height & 0xff,
    (height >> 8) & 0xff,
    ...bytes,
    0x1b, 0x64, 0x05,      // Feed 5 lines
    0x1d, 0x56, 0x00       // Cut
  ];

  const chunkSize = 512;
  for (let i = 0; i < dataToSend.length; i += chunkSize) {
    const chunk = dataToSend.slice(i, i + chunkSize);
    await BleManager.write(printerId, serviceUUID, characteristicUUID, chunk);
  }
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions