RollCast/User Guide/Website integration

Website integration

Print text, HTML or ESC/POS from a web page with one tap.

A page in Chrome on Android can print without a dialog by opening a rollcast://print link from a user gesture (a click). RollCast decodes the payload and prints on the chosen or default printer.

typeWhat data contains
textUTF-8 text. Printed with the printer's Text charset.
htmlA complete UTF-8 HTML document. Laid out at the roll width and printed as an image.
rawESC/POS bytes, sent unchanged. This is the default when type is missing.

Note

The user must turn on "Allow web prints" in RollCast. It is off by default, and links are rejected while it is off.

Print text

JavaScript
// UTF-8 -> base64, in chunks so long strings don't overflow the call stack.
function toBase64Utf8(str) {
  const bytes = new TextEncoder().encode(str);
  let binary = '';
  for (let i = 0; i < bytes.length; i += 0x8000) {
    binary += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
  }
  return btoa(binary);
}

// Chrome intent link: goes to the Play Store if RollCast is not installed.
function rollcastLink(type, base64, printerId) {
  let url = 'intent://print?type=' + type + '&data=' + encodeURIComponent(base64);
  if (printerId) url += '&printer=' + encodeURIComponent(printerId);
  return url + '#Intent;scheme=rollcast;package=com.rollcast.app;end';
}

// Call from a click handler.
function printText(text, printerId) {
  location.href = rollcastLink('text', toBase64Utf8(text), printerId);
}

// <button onclick="printText('Order #42\nTea x2      40.00\n\n')">Print</button>

One-tap HTML print

This helper copies one element and the page's CSS into a standalone document, encodes it and hands it to RollCast. Outside Android it falls back to window.print().

JavaScript
/**
 * paperMm: 48 for 58 mm rolls, 72 for 80 mm rolls.
 * fallbackUrl: page to open when RollCast is not installed (optional).
 */
function rollcastPrint(element, { paperMm = 48, printerId = '', fallbackUrl = '' } = {}) {
  if (!/Android/i.test(navigator.userAgent)) {
    window.print();
    return;
  }

  // 1. Collect readable CSS (inline <style> and same-origin sheets).
  let css = '';
  for (const sheet of document.styleSheets) {
    try {
      for (const rule of sheet.cssRules) css += rule.cssText + '\n';
    } catch (e) {
      // Cross-origin sheet: rules are not readable. Skip it.
    }
  }

  // 2. Clone the element and make image URLs absolute.
  const clone = element.cloneNode(true);
  const originals = element.querySelectorAll('img');
  clone.querySelectorAll('img').forEach((img, i) => {
    img.setAttribute('src', originals[i].currentSrc || originals[i].src);
    img.removeAttribute('srcset');
    img.removeAttribute('loading'); // lazy images may never load offscreen
  });

  // 3. Build a complete document sized for the roll.
  const html = `<!doctype html>
<html><head><meta charset="utf-8">
<style>
${css}
@page { size: ${paperMm}mm auto; margin: 0 }
html, body { margin: 0; padding: 0; background: #fff; color: #000; }
</style></head>
<body>${clone.outerHTML}</body></html>`;

  // 4. UTF-8 -> base64 in chunks.
  const bytes = new TextEncoder().encode(html);
  let binary = '';
  for (let i = 0; i < bytes.length; i += 0x8000) {
    binary += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
  }
  const b64 = btoa(binary);

  // 5. Hand it to RollCast.
  let url = 'intent://print?type=html&data=' + encodeURIComponent(b64);
  if (printerId) url += '&printer=' + encodeURIComponent(printerId);
  url += '#Intent;scheme=rollcast;package=com.rollcast.app;';
  if (fallbackUrl) url += 'S.browser_fallback_url=' + encodeURIComponent(fallbackUrl) + ';';
  url += 'end';
  location.href = url;
}

// <button onclick="rollcastPrint(document.getElementById('receipt'))">Print receipt</button>

Tip

The HTML is rendered with JavaScript off. Send finished markup. Canvas content and typed form values are not copied.

Print raw ESC/POS

JavaScript
// Uses rollcastLink() from the text example. Keep raw text ASCII:
// the printer reads it with its own code page, not UTF-8.
function printRawReceipt(printerId) {
  const ascii = (s) => Array.from(s, (c) => c.charCodeAt(0) & 0xFF);
  const bytes = [
    0x1B, 0x40,          // ESC @    reset
    0x1B, 0x45, 0x01,    // ESC E 1  bold on
    ...ascii('MY SHOP\n'),
    0x1B, 0x45, 0x00,    // ESC E 0  bold off
    ...ascii('Tea x2          40.00\n'),
    ...ascii('TOTAL           40.00\n'),
    0x1B, 0x64, 0x03,    // ESC d 3  feed 3 lines
    0x1D, 0x56, 0x01,    // GS V 1   cut
  ];
  const b64 = btoa(String.fromCharCode(...bytes));
  location.href = rollcastLink('raw', b64, printerId);
}

Receipt print CSS

HTML
<style>
  @page { size: 48mm auto; margin: 0 }   /* 72mm for 80 mm rolls */
  #receipt {
    width: 100%;
    color: #000;                 /* black only: grey is dithered and looks fuzzy */
    font: 500 10pt/1.3 sans-serif; /* weight 500 or more, 9pt or larger */
  }
  #receipt .total { font-weight: 700; }
  #receipt img { width: 60%; }   /* size in %, mm or pt, not px */
  #receipt hr { border: 0; border-top: 1px dashed #000; }
  @media print { .no-print { display: none } }
</style>

<div id="receipt">
  <!-- Absolute https:// URLs only; relative and http:// URLs are blocked. -->
  <img src="https://shop.example.com/logo-bw.png" alt="">
  <p>Order #42</p>
  <hr>
  <p class="total">TOTAL 40.00</p>
</div>

Limits

LimitValue
Link size2 MB of query string. Keep payloads to a few hundred KB; Android may drop bigger links.
Rate1 link per second. Extra links are dropped ("Too many print requests").
User switch"Allow web prints" must be on.
GestureOpen the link from a click. Chrome blocks app links a page opens by itself.
HTML resourceshttps:// and data: only. Loading stops after 15 seconds. No cookies are sent.