Print Daemon 0.1.0

Print job messages

Each queue message is one complete print job: the printer, every byte to print, and the options. The daemon needs nothing else to print it. node dist/cli.js schema prints the JSON Schema of the message, and the admin UI's API tab shows the format with producer samples in Java, TypeScript, Python and C#.

Schema v1#

A ZPL job with two labels, printed as one print job:

json
{
  "schemaVersion": 1,
  "jobId": "ORD-100045-labels",
  "type": "zpl",
  "printer": { "id": "dock-1-zebra" },
  "copies": 1,
  "documents": [
    { "name": "carton-1.zpl", "data": "^XA^FO50,50^A0N,50,50^FDCarton 1 of 2^FS^FO50,120^BCN,100,Y,N,N^FD1Z999AA10123456784^FS^XZ" },
    { "name": "carton-2.zpl", "data": "^XA^FO50,50^A0N,50,50^FDCarton 2 of 2^FS^FO50,120^BCN,100,Y,N,N^FD1Z999AA10123456793^FS^XZ" }
  ],
  "metadata": { "orderId": "100045", "warehouse": "WH1" }
}
Field Notes
schemaVersion Always 1.
jobId Unique per job (1–200 characters). Used for dedupe: a redelivered message with the same id is acked, not reprinted.
type zpl, raw (any printer language, e.g. EPL/PCL), pdf, image (png/jpg/gif/tiff/bmp, OS queues only), office (doc/docx/xls/xlsx/ppt/pptx/odt/ods/odp/rtf), text
printer Exactly one of:
• id: a printer registered in this daemon
• name: an OS queue name (exact), or the display name of a manually added printer. A name that matches several printers is rejected.
• host (+ port, default 9100): raw socket
name and host work without registering the printer while printing.allowAdHocPrinters is true. An unregistered host must use a port in printing.adHocTcpPorts.
documents[] At least one. data plus encoding: utf8 (default) or base64, and an optional name. Office documents need format or a name with the extension.
copies 1–999, default 1. zpl/raw: the whole document set is repeated inside the same single job. Documents: passed to the driver.
options Document printers only: duplex (one-sided, long-edge, short-edge), orientation (portrait, landscape), paperSize (e.g. A4, Letter; letters, digits, ., _ and - only), color, fitToPage. Missing values come from the printer's defaults.
metadata Optional string values (order id, warehouse, ...). Logged, never interpreted.
createdAt Optional string.

Encoding. JSON can't carry raw bytes, so PDF, Office and image files must be base64 ("encoding": "base64"); ZPL and text can be sent as plain utf8. Base64 adds about 33% to the size; large jobs use the queue's claim-check mechanism (Producing jobs).

More examples#

A PDF to an OS print queue, two copies, duplex:

json
{
  "schemaVersion": 1,
  "jobId": "ORD-100045-packslip",
  "type": "pdf",
  "printer": { "name": "Brother_HL_L2360D_series" },
  "copies": 2,
  "options": { "duplex": "long-edge", "paperSize": "Letter" },
  "documents": [{ "name": "packing-slip.pdf", "encoding": "base64", "data": "<base64 PDF bytes>" }]
}

A Word document (converted to PDF with LibreOffice) to a registered printer:

json
{
  "schemaVersion": 1,
  "jobId": "BOL-55102",
  "type": "office",
  "printer": { "id": "office-laser" },
  "documents": [{ "name": "bill-of-lading.docx", "format": "docx", "encoding": "base64", "data": "<base64 DOCX bytes>" }]
}

ZPL straight to a printer's address, without registering it (needs printing.allowAdHocPrinters):

json
{
  "schemaVersion": 1,
  "jobId": "SHIP-7781",
  "type": "zpl",
  "printer": { "host": "10.4.1.107", "port": 9100 },
  "documents": [{ "encoding": "base64", "data": "XlhBXkZPNTAsNTBeQTBOLDUwLDUwXkZESGVsbG9eRlNeWFo=" }]
}

Rules#

Routing#

The daemon prints every job it receives. Give each site or daemon its own queue or subscription. A job for a printer this daemon can't resolve is dead-lettered.

Producing jobs#

Any system that can send a message to the queue can produce jobs. Credentials for the daemon itself come from each SDK's default chain (Install › Configuration file).

AWS SQS (Java, with the extended client)#

Messages over 256 KB go through S3 with the Amazon SQS Extended Client Library; the daemon reads both its 1.x and 2.x pointer formats:

java
AmazonS3 s3 = AmazonS3ClientBuilder.defaultClient();
ExtendedClientConfiguration cfg = new ExtendedClientConfiguration()
        .withPayloadSupportEnabled(s3, "print-job-payloads")
        .withAlwaysThroughS3(false);                          // only offload messages > 256 KB
AmazonSQS sqs = new AmazonSQSExtendedClient(AmazonSQSClientBuilder.defaultClient(), cfg);
sqs.sendMessage(queueUrl, printJobJson);

Any language can produce the same result by hand. Upload the JSON to S3, then send this message body with a Number attribute ExtendedPayloadSize set to the payload's byte size:

json
["software.amazon.payloadoffloading.PayloadS3Pointer",{"s3BucketName":"print-job-payloads","s3Key":"<uuid>"}]

IAM permissions the daemon needs:

Azure Service Bus#

Use the Premium tier and raise the entity's Max message size (up to 100 MB). Send the job JSON as the message body (string, or JSON with contentType: application/json).

Google Cloud Pub/Sub#

Publish the job JSON as message data if it fits in 10 MB. For larger jobs, upload the JSON to GCS and publish a message with the attribute payloadGcsUri=gs://bucket/path/job.json. The message data is then ignored, and the object is deleted after the job is acked. The daemon needs roles/pubsub.subscriber on the subscription and storage.objects.get and storage.objects.delete on the bucket. Use a Pull subscription (Queues › Network and firewall).

The queue paths above (S3 pointers, Service Bus large messages, GCS claim checks) are covered by the daemon's tests against simulated services; test your producer against your own queue before you go live.