Open Source · Free Forever

KwickPython

Direct printing from web browsers to local printers. Under 50 lines of Python. No popups. No paid agents. Just works.

Download kp.py View Source

What It Does

KwickPython is a tiny Python CGI script that bridges the gap between your web application and local printers. It runs as a local HTTP server on port 9100 and accepts AJAX requests from your webpage — enabling silent, direct printing to any attached printer.

List Printers

Enumerate all local and network printers available on the client machine.

Check Status

Query printer status — online, offline, or paused — before sending print jobs.

Silent Printing

Send RAW data (ESC/POS, PCL, etc.) directly to printers. No dialogs, no previews.

Any Printer

Works with USB, serial, parallel, LAN, and Windows shared printers.

JavaScript API

Get List of Printers

$.ajax({
  url: 'http://127.0.0.1:9100/htbin/kp.py',
  success: function(printers) {
    console.log(printers);  // JSON object of all printers
  }
});

Check Printer Status

$.ajax({
  url: 'http://127.0.0.1:9100/htbin/kp.py',
  data: { p: 'Kitchen_Printer' },
  success: function(status) {
    console.log(status);  // "Kitchen_Printer Ready"
  }
});

Send Print Job

$.ajax({
  url: 'http://127.0.0.1:9100/htbin/kp.py',
  data: {
    p: 'Receipt_Printer',
    d: btoa(rawEscPosData)  // Base64 encoded ESC/POS data
  },
  success: function(bytes) {
    console.log(bytes + ' bytes sent');
  }
});

Setup (5 Minutes)

Requirements: Windows + Python 3 + pywin32

  1. Create directory: C:\KwickPython\htbin
  2. Save kp.py to C:\KwickPython\htbin\kp.py
  3. Start the server:
# Start KwickPython on port 9100
cd C:\KwickPython
python -m http.server --cgi 9100

Your web app can now print to any local printer via AJAX calls to http://127.0.0.1:9100/htbin/kp.py

Port Configuration

Port 9100 is the standard raw printing port. You can use any port — just update both the server command and your AJAX URLs to match:

# Use a custom port
python -m http.server --cgi 1234

Security

By default, KwickPython accepts requests from any origin (Access-Control-Allow-Origin: *). For production, lock it down to your domain:

# In kp.py, change:
print('Access-Control-Allow-Origin: *')
# To:
print('Access-Control-Allow-Origin: https://yourdomain.com')

Full Source Code

The entire script — under 50 lines:

# KwickPython [FREE TO USE FOR ANY PROJECT, NO WARRANTY]
# Link back to https://kwickpos.com if it works for you
import cgi, os, sys, win32print, pywintypes

print("Access-Control-Allow-Origin: *")
print("Content-Type: text/plain\n")

form = cgi.FieldStorage()
p = form.getvalue('p')

if p:
    data = form.getvalue('data')
    if data:
        import base64
        raw_data = base64.b64decode(data)
        if raw_data:
            h = win32print.OpenPrinter(p)
            hJob = win32print.StartDocPrinter(h, 1,
                ("KwickPOS Ticket", None, "RAW"))
            win32print.StartPagePrinter(h)
            b = win32print.WritePrinter(h, raw_data)
            win32print.EndPagePrinter(h)
            win32print.EndDocPrinter(h)
            win32print.ClosePrinter(h)
            print(b)
            sys.exit()
    else:
        try:
            h = win32print.OpenPrinter(p)
            d = win32print.GetPrinter(h, 2)
            win32print.ClosePrinter(h)
            a = d['Attributes']
            hex_val = hex(a)
            if hex_val[-3] == '6':
                print(p + ' Offline')
                sys.exit()
            s = d['Status']
            if s == 1:
                print(p + ' Paused')
                sys.exit()
            print(p + ' Ready')
        except pywintypes.error:
            print(p + ' No Such Printer')
else:
    import json
    p = {}
    lst = win32print.EnumPrinters(2)
    for f, d, n, c in lst:
        p[n] = {'d': d, 'c': c}
    print(json.dumps(p))

Need a Full Restaurant POS System?

KwickPython powers the local printing in KwickPOS — the smart POS system trusted by 5,000+ merchants.

Get a Free Demo →