HP PCL print jobs
HP PCL 5 jobs for laser printers: pages of text placed in millimetres in the printer's fonts, boxes, lines and shading, pictures as raster graphics, plain listings, paper, tray, duplex and copies, wrapped in PJL, sent to port 9100.
Version 1.0.0 · 5 functions · 3 crates
Functions
What use pcl.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use pcl_pages, pcl_listing from pcl (Quillo 0.6).
pcl_pages(pages_: list, options: any) → stror an errorPages of a PCL job, each a list of items placed in millimetres from the top left: { text, x, y (the baseline; or top: true), size: 11 (points), font: "univers" | "times" | "courier" | "arial" | "times_new_roman" | "letter_gothic", bold, italic, underline }, { box: true, x, y, width, height, thickness, fill: 10 (% grey) }, { rect: true, ..., fill } (filled, black without fill), { line: true, x, y, width, thickness } or { image: "logo.png", x, y, width, dpi: 300 }. options: { paper: "a4" | "letter" | "legal" | "a5" | "a3", landscape, tray, duplex: "long" | "short", copies, pjl: true, name }. Gives the job written for people.
pcl_listing(text: str, options: any) → stror an errorA plain text listing (a report, a log, a picking list) in a fixed font, lines wrapped, pages broken by the printer and at form feeds. options as pcl_pages, and { size: 10, lpi: 6, page_lines: 60, left_margin: 5 (columns), font: "courier" }.
pcl_job(kind: str, content: any, options: any) → stror an errorThe bytes of pages (kind "pages", content a list of pages) or a listing ("listing", content the text), in base64.
pcl_send(kind: str, content: any, options: any, host: str, timeout_ms: int) → intor an errorSends pages or a listing to a printer's raw port (host or host:port, 9100). Gives the bytes sent.
pcl_write(kind: str, content: any, options: any, path: str) → intor an errorWrites pages or a listing to a file (to print later, or to a printer's device). Gives the bytes written.
Get it
$ quillo install pcl # pcl.qwer into this folder$ quillo install pcl --example # and its example, pcl.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/pcl.qwer$ curl -O https://quillolang.org/plugins/files/pcl.qwe$ curl -O https://quillolang.org/plugins/files/pcl_sample.pngThen, in your program, all of it or only what you call:
use pcl.qweruse pcl_pages, pcl_listing from pcl // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
image = { version = "0.25", default-features = false, features = ["png", "jpeg"] }base64 = "0.22"encoding_rs = "0.8"Example
// A warehouse office's laser printer (any HP LaserJet, or a printer that speaks PCL 5): an A4// delivery note with the logo, boxes and shading, printed twice from tray 2 in duplex, and the// day's picking list as a plain listing.//// quillo run pcl.qwe then open http://localhost:8080/note and http://localhost:8080/picking// PRINTER_HOST=192.168.1.70 quillo run pcl.qwe and http://localhost:8080/printuse "pcl.qwer"let NOTE = { no: "DN-55120", date: "29 October 2026", to: ["Brasserie Lumière", "8 Quai de Saône", "69002 Lyon"], lines: [["Espresso beans 1 kg", 12], ["Oat milk 1 l", 24], ["Paper cups 12 oz (1,000)", 2]] }fn note_page(n) = [ { image: "pcl_sample.png", x: 15, y: 12, width: 50, dpi: 300 }, { text: "DELIVERY NOTE", x: 120, y: 22, size: 20, bold: true }, { text: `${n.no} · ${n.date}`, x: 120, y: 30, size: 10 }, { box: true, x: 15, y: 40, width: 85, height: 28, thickness: 0.3 },] + map(range(len(n.to)), (i) => { text: n.to[i], x: 19, y: 48 + i * 6, size: 11, bold: i == 0 }) + [ { rect: true, x: 15, y: 80, width: 180, height: 8, fill: 15 }, { text: "Item", x: 18, y: 85.5, size: 10, bold: true }, { text: "Qty", x: 175, y: 85.5, size: 10, bold: true },] + map(range(len(n.lines)), (i) => [ { text: n.lines[i][0], x: 18, y: 96 + i * 7, size: 10 }, { text: str(n.lines[i][1]), x: 175, y: 96 + i * 7, size: 10 }, { line: true, x: 15, y: 98 + i * 7, width: 180, thickness: 0.1 },]) | flattenlet OPTIONS = { paper: "a4", copies: 2, tray: 4, duplex: "long", pjl: true, name: "DN-55120" }route GET /note = respond(200, pcl_pages([note_page(NOTE)], OPTIONS)?, { "content-type": "text/plain; charset=utf-8" })route GET /picking = respond(200, pcl_listing("PICKING LIST 29/10/2026\n\nA-04-03 Espresso beans 1 kg 12\nB-11-01 Oat milk 1 l 24\nC-02-07 Paper cups 12 oz 2\n", { paper: "letter", size: 12 })?, { "content-type": "text/plain; charset=utf-8" })route GET /print { let host = env("PRINTER_HOST", null) ?? reply 503 "set PRINTER_HOST to the printer's address (host or host:port)" { sent: pcl_send("pages", [note_page(NOTE)], OPTIONS, host, 5000)?, saved: pcl_write("listing", "Hello", {}, "data/hello.pcl")? }}$ quillo run pcl.qwe$ curl 'localhost:8080/print'$ curl 'localhost:8080/note'$ curl 'localhost:8080/picking'It talks to the Redis that REDIS_URL names, else Redis on this machine. The site's test runs it against a stand-in.
Source
//! HP PCL 5 print jobs for laser printers (HP LaserJet and the many that speak PCL): pages of text placed in millimetres in Univers, CG Times, Courier or Arial at any size, bold and italic, rectangles, lines and shading, pictures as raster graphics, plain text listings, paper size, orientation, tray, duplex and copies, wrapped in PJL when asked; sent to port 9100.//! version: 1.0.0//!//! [dependencies]//! image = { version = "0.25", default-features = false, features = ["png", "jpeg"] }//! base64 = "0.22"//! encoding_rs = "0.8"use serde_json::Value;const ESC: u8 = 0x1b;/// PCL units a millimetre: the jobs use 600 units an inch (ESC &u600D).const UPMM: f64 = 600.0 / 25.4;fn cp1252(s: &str) -> Vec<u8> { let mut out = vec![]; for c in s.chars() { let mut buf = [0u8; 4]; let (bytes, _, bad) = encoding_rs::WINDOWS_1252.encode(c.encode_utf8(&mut buf)); if bad || bytes.len() != 1 { out.push(b'?') } else { out.push(bytes[0]) } } out}/// An escape sequence, as PCL writes it: `ESC` then text like `&l26A`.fn esc(o: &mut Out, s: &str) { o.bytes.push(ESC); o.bytes.extend_from_slice(s.as_bytes()); o.note("<ESC>"); o.note(s);}fn typeface(name: &str) -> Result<(u32, bool), String> { // (typeface number, proportional) Ok(match name { "univers" => (4148, true), "times" | "cg_times" => (4101, true), "courier" => (4099, false), "arial" => (16602, true), "times_new_roman" => (16901, true), "letter_gothic" => (4102, false), "line_printer" => (0, false), other => return Err(format!("font {:?}: univers, times, courier, arial, times_new_roman, letter_gothic or line_printer", other)), })}/// Selects a font: symbol set Windows Latin 1 (19U), spacing, size, style, weight, typeface.fn font(o: &mut Out, name: &str, size: f64, bold: bool, italic: bool) -> Result<(), String> { let (tf, prop) = typeface(name)?; esc(o, "(19U"); if prop { esc(o, &format!("(s1p{}v{}s{}b{}T", trim(size), if italic { 1 } else { 0 }, if bold { 3 } else { 0 }, tf)); } else { // Fixed pitch: characters an inch from the size (10 cpi at 12 points). esc(o, &format!("(s0p{}h{}v{}s{}b{}T", trim(120.0 / size), trim(size), if italic { 1 } else { 0 }, if bold { 3 } else { 0 }, tf)); } Ok(())}fn trim(x: f64) -> String { let s = format!("{:.2}", x); s.trim_end_matches('0').trim_end_matches('.').to_string()}fn job_start(o: &mut Out, options: &Value) -> Result<(), String> { if options["pjl"] == true { let name: String = options["name"].as_str().unwrap_or("Quillo").chars().filter(|c| (' '..='~').contains(c) && *c != '"').take(60).collect(); esc(o, "%-12345X"); o.s(&format!("@PJL JOB NAME=\"{}\"\r\n@PJL ENTER LANGUAGE=PCL\r\n", name)); } esc(o, "E"); let paper = match options["paper"].as_str().unwrap_or("a4") { "a4" | "A4" => 26, "letter" => 2, "legal" => 3, "a5" | "A5" => 25, "a3" | "A3" => 27, "executive" => 1, other => return Err(format!("paper {:?}: a4, letter, legal, a5, a3 or executive", other)), }; esc(o, &format!("&l{}A", paper)); esc(o, &format!("&l{}O", if options["landscape"] == true { 1 } else { 0 })); if let Some(t) = options["tray"].as_i64() { esc(o, &format!("&l{}H", t.clamp(0, 99))); } if let Some(d) = options["duplex"].as_str() { esc(o, &format!("&l{}S", match d { "long" => 1, "short" => 2, _ => 0 })); } let copies = options["copies"].as_i64().unwrap_or(1).clamp(1, 999); if copies > 1 { esc(o, &format!("&l{}X", copies)); } esc(o, "&u600D"); // No top margin of the printer's own: y is measured from the top of the logical page. esc(o, "&l0E"); o.note("\n"); Ok(())}fn job_end(o: &mut Out, options: &Value) { esc(o, "E"); if options["pjl"] == true { esc(o, "%-12345X"); o.s("@PJL EOJ\r\n"); esc(o, "%-12345X"); }}fn at(o: &mut Out, x_mm: f64, y_mm: f64) { esc(o, &format!("*p{}x{}Y", (x_mm * UPMM).round() as i64, (y_mm * UPMM).round() as i64));}fn item(o: &mut Out, it: &Value) -> Result<(), String> { let x = it["x"].as_f64().unwrap_or(0.0); let y = it["y"].as_f64().unwrap_or(0.0); if let Some(raw) = it["raw"].as_str() { o.t(raw, &cp1252(raw)); } else if !it["text"].is_null() { let t: String = text_of(&it["text"]).chars().filter(|c| *c != '\r' && *c != '\n' && *c != '\x1b').collect(); let size = it["size"].as_f64().unwrap_or(11.0).clamp(4.0, 999.0); font(o, it["font"].as_str().unwrap_or("univers"), size, it["bold"] == true, it["italic"] == true)?; // y is the text's baseline; with `top: true`, the top of its capitals. let y = if it["top"] == true { y + size * 0.72 * 25.4 / 72.0 } else { y }; at(o, x, y); if it["underline"] == true { esc(o, "&d0D"); } o.t(&t, &cp1252(&t)); if it["underline"] == true { esc(o, "&d@"); } } else if it["box"] == true || it["rect"] == true || it["line"] == true { let t = it["thickness"].as_f64().unwrap_or(0.3).max(0.05); let w = it["width"].as_f64().unwrap_or(10.0); let h = if it["line"] == true && it["height"].is_null() { t } else { it["height"].as_f64().unwrap_or(t) }; let fill = it["fill"].as_f64(); let rect = |o: &mut Out, x: f64, y: f64, w: f64, h: f64, shade: Option<f64>| { at(o, x, y); esc(o, &format!("*c{}a{}B", (w * UPMM).round().max(1.0) as i64, (h * UPMM).round().max(1.0) as i64)); match shade { Some(p) if p < 100.0 => { esc(o, &format!("*c{}G", p.round().clamp(1.0, 100.0) as i64)); esc(o, "*c2P"); } _ => esc(o, "*c0P"), } }; if it["box"] == true { if let Some(p) = fill { rect(o, x, y, w, h, Some(p)); } rect(o, x, y, w, t, None); rect(o, x, y + h - t, w, t, None); rect(o, x, y, t, h, None); rect(o, x + w - t, y, t, h, None); } else { rect(o, x, y, w, h, fill); } } else if let Some(path) = it["image"].as_str() { let dpi = it["dpi"].as_i64().unwrap_or(300); let dpi = if [75, 100, 150, 200, 300, 600].contains(&dpi) { dpi } else { 300 }; let w = it["width"].as_f64().map(|mm| (mm / 25.4 * dpi as f64).round() as u32); let (pw, ph, rows) = bitmap(path, w, it["threshold"].as_u64().unwrap_or(128).clamp(1, 255) as u8, it["dither"].as_bool().unwrap_or(true))?; let (bpr, data) = pack_rows(pw, &rows, true); at(o, x, y); esc(o, &format!("*t{}R", dpi)); esc(o, "*r1A"); let mut sent = 0; for r in 0..ph { let row = &data[r * bpr..(r + 1) * bpr]; let used = row.iter().rposition(|b| *b != 0).map_or(0, |i| i + 1); o.bytes.push(ESC); o.bytes.extend_from_slice(format!("*b{}W", used).as_bytes()); o.bytes.extend_from_slice(&row[..used]); sent += used; } o.note(&format!("<{} raster rows, {} bytes>", ph, sent)); esc(o, "*rB"); } else { return Err(format!("an item is text, box, rect, line, image or raw: {}", it)); } o.note("\n"); Ok(())}fn pages(ps: &[Value], options: &Value) -> Result<Out, String> { if ps.is_empty() || ps.len() > 2000 { return Err("1 to 2,000 pages".into()); } let mut o = Out::new(); job_start(&mut o, options)?; for (k, p) in ps.iter().enumerate() { let items = p.as_array().ok_or("each page is a list of items")?; for it in items { item(&mut o, it)?; } if k + 1 < ps.len() { o.b(&[0x0c]); o.note("\n"); } } job_end(&mut o, options); Ok(o)}fn listing(text: &str, options: &Value) -> Result<Out, String> { let mut o = Out::new(); job_start(&mut o, options)?; let size = options["size"].as_f64().unwrap_or(10.0); let lpi = options["lpi"].as_i64().unwrap_or(6).clamp(1, 48); esc(&mut o, &format!("&l{}D", lpi)); esc(&mut o, &format!("&a{}L", options["left_margin"].as_i64().unwrap_or(5).clamp(0, 100))); // Lines on a page; the printer's own top margin back for a listing. esc(&mut o, "&l3E"); esc(&mut o, &format!("&l{}F", options["page_lines"].as_i64().unwrap_or(60).clamp(1, 200))); font(&mut o, options["font"].as_str().unwrap_or("courier"), size, false, false)?; // Lines wrap at the right margin (ESC &s0C) rather than being cut. esc(&mut o, "&s0C"); o.note("\n"); for line in text.lines() { if line.contains('\x0c') { o.b(&[0x0c]); } let l: String = line.chars().filter(|c| *c != '\x0c' && *c != '\x1b').collect(); o.t(&l, &cp1252(&l)); o.b(&[0x0d, 0x0a]); } job_end(&mut o, options); Ok(o)}/// Pages of a PCL job, each a list of items placed in millimetres from the top left:/// `{ text, x, y (the baseline; or top: true), size: 11 (points), font: "univers" | "times" | "courier" | "arial" | "times_new_roman" | "letter_gothic", bold, italic, underline }`,/// `{ box: true, x, y, width, height, thickness, fill: 10 (% grey) }`, `{ rect: true, ..., fill }` (filled, black without fill), `{ line: true, x, y, width, thickness }` or `{ image: "logo.png", x, y, width, dpi: 300 }`./// `options`: `{ paper: "a4" | "letter" | "legal" | "a5" | "a3", landscape, tray, duplex: "long" | "short", copies, pjl: true, name }`. Gives the job written for people.pub fn pcl_pages(pages_: Vec<serde_json::Value>, options: serde_json::Value) -> Result<String, String> { Ok(pages(&pages_, &options)?.view())}/// A plain text listing (a report, a log, a picking list) in a fixed font, lines wrapped, pages broken by the printer and at form feeds. `options` as `pcl_pages`, and `{ size: 10, lpi: 6, page_lines: 60, left_margin: 5 (columns), font: "courier" }`.pub fn pcl_listing(text: &str, options: serde_json::Value) -> Result<String, String> { Ok(listing(text, &options)?.view())}/// The bytes of pages (`kind` "pages", `content` a list of pages) or a listing ("listing", `content` the text), in base64.pub fn pcl_job(kind: &str, content: serde_json::Value, options: serde_json::Value) -> Result<String, String> { Ok(b64(&make(kind, &content, &options)?.bytes))}fn make(kind: &str, content: &Value, options: &Value) -> Result<Out, String> { match kind { "pages" => pages(content.as_array().ok_or("pages are a list")?, options), "listing" => listing(content.as_str().ok_or("a listing is text")?, options), other => Err(format!("{:?}: pages or listing", other)), }}/// Sends pages or a listing to a printer's raw port (`host` or `host:port`, 9100). Gives the bytes sent.pub fn pcl_send(kind: &str, content: serde_json::Value, options: serde_json::Value, host: &str, timeout_ms: i64) -> Result<i64, String> { send_bytes(&make(kind, &content, &options)?.bytes, host, timeout_ms)}/// Writes pages or a listing to a file (to print later, or to a printer's device). Gives the bytes written.pub fn pcl_write(kind: &str, content: serde_json::Value, options: serde_json::Value, path: &str) -> Result<i64, String> { write_bytes(&make(kind, &content, &options)?.bytes, path)}// ---- Shared by the printer plugins: the job's bytes and a readable copy, pictures as bits, the network./// A job's bytes, and the same job written for people: control bytes as `<ESC>`, graphics as `<N bytes>`.struct Out { bytes: Vec<u8>, text: String,}fn control_name(c: u8) -> String { match c { 0x00 => "<NUL>".into(), 0x02 => "<STX>".into(), 0x03 => "<ETX>".into(), 0x0a => "\n".into(), 0x0c => "<FF>".into(), 0x0d => "<CR>".into(), 0x17 => "<ETB>".into(), 0x18 => "<CAN>".into(), 0x1b => "<ESC>".into(), 0x1e => "<RS>".into(), c if c < 0x20 || c == 0x7f => format!("<{:02X}>", c), c => (c as char).to_string(), }}impl Out { fn new() -> Out { Out { bytes: vec![], text: String::new() } } /// Command bytes, shown as they are with control bytes named. fn b(&mut self, data: &[u8]) { self.bytes.extend_from_slice(data); for c in data { self.text.push_str(&control_name(*c)); } } /// ASCII command text. fn s(&mut self, s: &str) { self.b(s.as_bytes()); } /// Text already encoded for the printer (`enc`), shown as written. fn t(&mut self, shown: &str, enc: &[u8]) { self.bytes.extend_from_slice(enc); for c in shown.chars() { if (c as u32) < 0x20 { self.text.push_str(&control_name(c as u8)); } else { self.text.push(c); } } } /// Text for the readable copy only (a line break between commands), not sent. fn note(&mut self, s: &str) { self.text.push_str(s); } /// The job for people: CR LF line ends shown as plain line breaks. fn view(&self) -> String { self.text.replace("<CR>\n", "\n") } /// Binary data (a picture), shown by its size. fn blob(&mut self, data: &[u8]) { self.bytes.extend_from_slice(data); self.text.push_str(&format!("<{} bytes>", data.len())); }}/// Dots per millimetre for a resolution in dpi.fn dots_per_mm(dpi: i64) -> f64 { dpi.clamp(100, 1200) as f64 / 25.4}/// A length given in `units` ("mm", the default, "in" or "dots") as dots.fn to_dots(v: &Value, default: f64, dpmm: f64, units: &str) -> i64 { let x = v.as_f64().unwrap_or(default); let d = match units { "dots" => x, "in" | "inch" | "inches" => x * 25.4 * dpmm, _ => x * dpmm, }; d.round().max(0.0) as i64}/// A picture as rows of bits, true for black: scaled to `width` dots when given, dithered or cut at a threshold, transparency as white.fn bitmap(path: &str, width: Option<u32>, threshold: u8, dither: bool) -> Result<(usize, usize, Vec<Vec<bool>>), String> { let img = image::open(path).map_err(|e| format!("{}: {}", path, e))?; let img = match width { Some(w) if w > 0 && w != img.width() => { let h = ((img.height() as f64) * (w as f64) / (img.width() as f64)).round().max(1.0) as u32; img.resize_exact(w, h, image::imageops::FilterType::Triangle) } _ => img, }; let rgba = img.to_rgba8(); let (w, h) = (rgba.width() as usize, rgba.height() as usize); if w * h > 8_000_000 { return Err("the picture is over 8 million dots: give a width".into()); } let mut lum: Vec<f32> = rgba .pixels() .map(|p| { let a = p[3] as f32 / 255.0; (0.299 * p[0] as f32 + 0.587 * p[1] as f32 + 0.114 * p[2] as f32) * a + 255.0 * (1.0 - a) }) .collect(); let mut rows = vec![vec![false; w]; h]; for y in 0..h { for x in 0..w { let old = lum[y * w + x]; let black = old < threshold as f32; rows[y][x] = black; if dither { let err = old - if black { 0.0 } else { 255.0 }; for (dx, dy, f) in [(1isize, 0usize, 7.0f32 / 16.0), (-1, 1, 3.0 / 16.0), (0, 1, 5.0 / 16.0), (1, 1, 1.0 / 16.0)] { let nx = x as isize + dx; if nx >= 0 && (nx as usize) < w && y + dy < h { lum[(y + dy) * w + nx as usize] += err * f; } } } } } Ok((w, h, rows))}/// Rows of bits packed 8 dots to a byte, the first dot in the high bit; `black_is_one` false for printers where 0 prints.fn pack_rows(w: usize, rows: &[Vec<bool>], black_is_one: bool) -> (usize, Vec<u8>) { let bpr = (w + 7) / 8; let mut out = Vec::with_capacity(bpr * rows.len()); for r in rows { for b in 0..bpr { let mut byte = 0u8; for bit in 0..8 { let x = b * 8 + bit; let black = x < w && r[x]; if black == black_is_one { byte |= 0x80 >> bit; } } out.push(byte); } } (bpr, out)}/// `host`, `host:port` or `[v6]:port` as an address; `port` when none is written.fn printer_addr(host: &str, port: u16) -> Result<std::net::SocketAddr, String> { use std::net::ToSocketAddrs; let h = host.trim(); let with_port = if h.starts_with('[') { if h.contains("]:") { h.to_string() } else { format!("{}:{}", h, port) } } else if h.matches(':').count() == 1 { h.to_string() } else if h.contains(':') { format!("[{}]:{}", h, port) } else { format!("{}:{}", h, port) }; with_port.to_socket_addrs().map_err(|e| format!("{}: {}", host, e))?.next().ok_or(format!("{} has no address", host))}fn connect(host: &str, timeout_ms: i64) -> Result<std::net::TcpStream, String> { let t = std::time::Duration::from_millis(timeout_ms.clamp(100, 120_000) as u64); let addr = printer_addr(host, 9100)?; let s = std::net::TcpStream::connect_timeout(&addr, t).map_err(|e| format!("cannot reach the printer at {}: {}", addr, e))?; s.set_write_timeout(Some(t)).map_err(|e| e.to_string())?; s.set_read_timeout(Some(t)).map_err(|e| e.to_string())?; Ok(s)}/// Sends bytes to a printer's raw port and closes it.fn send_bytes(bytes: &[u8], host: &str, timeout_ms: i64) -> Result<i64, String> { use std::io::Write; let mut s = connect(host, timeout_ms)?; s.write_all(bytes).map_err(|e| format!("sending to the printer: {}", e))?; s.flush().map_err(|e| e.to_string())?; Ok(bytes.len() as i64)}/// Writes bytes to a file or a device (`/dev/usb/lp0`), folders made for a file.fn write_bytes(bytes: &[u8], path: &str) -> Result<i64, String> { use std::io::Write; let p = std::path::Path::new(path); let device = path.starts_with("/dev/"); if !device { if let Some(dir) = p.parent() { if !dir.as_os_str().is_empty() { std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; } } } let mut f = std::fs::OpenOptions::new().write(true).create(!device).truncate(!device).open(p).map_err(|e| format!("{}: {}", path, e))?; f.write_all(bytes).map_err(|e| format!("{}: {}", path, e))?; Ok(bytes.len() as i64)}fn b64(bytes: &[u8]) -> String { use base64::Engine; base64::engine::general_purpose::STANDARD.encode(bytes)}fn text_of(v: &Value) -> String { match v { Value::String(s) => s.clone(), Value::Null => String::new(), other => other.to_string(), }}Related
escp.qwer · 1.0.0Epson ESC/P and ESC/P2 for dot-matrix printers: pre-printed and multipart forms filled by line and column, reports with bold, double-width and condensed text, pitch, line spacing and page length, in code page 437 with box-drawing lines.7 views · 11 downloadsBrother QL and P-touch ptouch.qwer · 1.0.0Brother QL label printers and P-touch tape printers in raster mode: text made into a label picture, any picture made into the raster job for a DK roll or TZe tape, cut, compressed, sent over the network, and the printer's status read.7 views · 7 downloadsDatamax DPL labels dpl.qwer · 1.0.0Honeywell and Datamax-O'Neil DPL labels from fields placed in millimetres from the top left: resident fonts, Code 128, EAN-13, QR and Data Matrix, lines and boxes, heat, speed and quantity, sent to port 9100.7 views · 9 downloadsEPL2 labels epl.qwer · 1.0.0Zebra and Eltron EPL2 labels from fields in millimetres: text in the resident fonts, Code 128, EAN-13, Code 39 and QR barcodes, lines, boxes and pictures, with label size, gap, speed and copies, sent to port 9100.6 views · 5 downloadsGodex EZPL labels ezpl.qwer · 1.0.0Godex EZPL labels for G500, RT200 and other Godex printers: text in the resident fonts, Code 128, EAN-13, Code 39 and QR codes, lines and boxes, label size, darkness, speed and copies, sent to port 9100.6 views · 8 downloadsIntermec IPL labels ipl.qwer · 1.0.0Intermec IPL labels for EasyCoder, PM4i and PF2i printers: a format saved with its text, Code 128, SSCC, UPC/EAN, QR and Data Matrix fields, lines and boxes, filled with data for each print, sent to port 9100.7 views · 7 downloadsThread 0 posts
On HP PCL print jobs: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.