diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 1d9842a..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.containerignore b/.containerignore new file mode 100644 index 0000000..29dfea3 --- /dev/null +++ b/.containerignore @@ -0,0 +1,6 @@ +# Keep version-control metadata and macOS junk out of the build context. +.jj +.git +.opencode +.DS_Store +**/.DS_Store diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd3d04f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# macOS metadata +.DS_Store diff --git a/Containerfile b/Containerfile index ed39d83..d5f19b6 100644 --- a/Containerfile +++ b/Containerfile @@ -7,28 +7,32 @@ # printer is described by environment variables: # # podman run --rm --network host \ -# -e PRINTER_IP=192.168.1.50 \ +# -e PRINTER_IP=192.168.100.7 \ # -e PRINTER_NAME=HL-4150CDN \ # --name printserver printserver:latest # # PRINTER_URI can be used instead of PRINTER_IP for full control: # -# -e PRINTER_URI=lpd://192.168.1.50/binary_p1 +# -e PRINTER_URI=lpd://192.168.100.7/binary_p1 # +# The HL-4150CDN emulates BR-Script3 (PostScript 3). Jobs are converted to +# PostScript inside CUPS and forwarded to the printer unchanged; the official +# BR-Script3 PPD turns the queue's options (tray, duplex, colour, ...) into +# PostScript "setpagedevice" instructions. No Brother vendor binaries or 32-bit +# libraries are involved. FROM debian:bookworm-slim ENV DEBIAN_FRONTEND=noninteractive -# --- Task 2.1 / 2.2: base system, CUPS, Avahi, DBus and i386 runtime ------- -# The Brother filter binaries are 32-bit i386 ELF executables, so the i386 -# architecture has to be enabled and the matching runtime libraries installed. -RUN dpkg --add-architecture i386 \ - && apt-get update \ +# --- Task 3.1: base system, CUPS, Avahi, DBus and conversion filters ------- +# cups-filters provides the PDF/PWG/URF -> PostScript filters used for +# driverless clients (AirPrint/IPP Everywhere). Ghostscript and Poppler supply +# the underlying converters. +RUN apt-get update \ && apt-get install -y --no-install-recommends \ cups \ cups-client \ cups-filters \ - cups-ppdc \ avahi-daemon \ avahi-utils \ libnss-mdns \ @@ -36,43 +40,23 @@ RUN dpkg --add-architecture i386 \ procps \ curl \ ghostscript \ - ca-certificates \ poppler-utils \ - libc6:i386 \ - libstdc++6:i386 \ - libgcc-s1:i386 \ + ca-certificates \ && rm -rf /var/lib/apt/lists/* -# Make sure the 32-bit dynamic loader the Brother binaries ask for exists, -# even if the i386 libc places it only in the multiarch directory. -RUN if [ ! -e /lib/ld-linux.so.2 ] && [ -e /lib/i386-linux-gnu/ld-linux.so.2 ]; then \ - ln -s /lib/i386-linux-gnu/ld-linux.so.2 /lib/ld-linux.so.2; \ - fi \ - && test -e /lib/ld-linux.so.2 - -# --- Task 2.3: install the Brother HL-4150CDN driver packages -------------- -# Copy the local driver packages from the repository into the build context and -# install them. The LPR package must be installed before the CUPS wrapper -# package, which depends on it. `--force-all` is used because the packages are -# built for i386 and have no dependency information Debian can satisfy here. +# --- Task 3.2: install the BR-Script3 PostScript PPD ----------------------- +# The PPD encodes every device option (paper source, duplex, colour/mono, +# media type, toner save, resolution, secure print) as PostScript +# "setpagedevice". CUPS's pstops filter injects the selected option code and +# the PPD's JCL entries wrap the job in PJL for the printer. # The whole assets tree is copied because the directory name contains a space, # which some COPY implementations cannot quote. COPY assets /tmp/assets - -RUN mkdir -p /var/spool/lpd \ - && dpkg -i --force-all "/tmp/assets/Brother HL-4150CDN/lpr-driver/hl4150cdnlpr-1.1.1-5.i386.deb" \ - && dpkg -i --force-all "/tmp/assets/Brother HL-4150CDN/cupswrapper/hl4150cdncupswrapper-1.1.1-5.i386.deb" \ - && dpkg -s hl4150cdnlpr:i386 hl4150cdncupswrapper:i386 >/dev/null \ - && rm -rf /tmp/assets - -# --- Task 2.4: configure CUPS and verify the driver files are in place ----- -# The CUPS wrapper postinst copies the PPD into the CUPS model directory and -# creates the brlpdwrapper filter. Fail the build if anything is missing. -RUN test -f /usr/share/cups/model/brhl4150cdn.ppd \ - && test -x /usr/lib/cups/filter/brlpdwrapperhl4150cdn \ - && test -x /usr/local/Brother/Printer/hl4150cdn/lpd/brhl4150cdnfilter \ - && mkdir -p /usr/share/ppd \ - && ln -sf /usr/share/cups/model/brhl4150cdn.ppd /usr/share/ppd/brhl4150cdn.ppd \ +RUN install -m 0644 \ + "/tmp/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD" \ + /usr/share/cups/model/BR4150_2.PPD \ + && test -s /usr/share/cups/model/BR4150_2.PPD \ + && rm -rf /tmp/assets \ && rm -f /etc/cups/printers.conf /etc/cups/printers.conf.O COPY cupsd.conf /etc/cups/cupsd.conf @@ -80,11 +64,11 @@ COPY cupsd.conf /etc/cups/cupsd.conf # CUPS needs the runtime directories to exist in the image. RUN mkdir -p /run/cups /run/dbus /run/avahi-daemon /var/spool/cups /var/log/cups -# --- Task 2.5: startup script ----------------------------------------------- +# --- Task 3.5: startup script ---------------------------------------------- COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod 0755 /usr/local/bin/entrypoint.sh -# --- Task 2.6: exposed ports ------------------------------------------------ +# --- Task 3.6: exposed ports ----------------------------------------------- # 631/tcp CUPS administration and IPP # 5353/udp mDNS / DNS-SD for AirPrint discovery EXPOSE 631/tcp diff --git a/README.md b/README.md index 95b3d45..05cd009 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,50 @@ # Printserver A containerized CUPS print server that shares a **Brother HL-4150CDN** network -printer over IPP and advertises it via AirPrint (mDNS/DNS-SD), so client -devices can print without installing the Brother driver themselves. +printer over IPP and advertises it via AirPrint (mDNS/DNS-SD). It prints by +sending **PostScript (BR-Script3)** straight to the printer, so no vendor driver +and no 32-bit libraries run in the container. The container bundles: -- CUPS (IPP/IPP Everywhere + web admin on port 631) +- CUPS (IPP + web admin on port 631) with cups-filters for format conversion - Avahi / DNS-SD for AirPrint discovery (UDP 5353) -- The Brother HL-4150CDN LPR + CUPS wrapper drivers and 32-bit runtime libs +- Brother's official HL-4150CDN **BR-Script3 PPD** for device options + +## How it works + +The HL-4150CDN emulates PCL6 and BR-Script3 (PostScript 3), and accepts +PostScript over raw AppSocket (port 9100). CUPS converts whatever a client +sends into PostScript and the BR-Script3 PPD turns the selected queue options +into PostScript `setpagedevice` instructions: + +``` + macOS / iOS / Windows / Linux container printer + ┌─────────────────┐ IPP/AirPrint ┌──────────────────────────┐ raw ┌────────────┐ + │ AirPrint (URF) │ ───────────────▶ │ CUPS │ ──────▶ │ HL-4150CDN │ + │ PDF / PostScript│ │ URF/PWG/PDF → PostScript│ :9100 │ BR-Script3 │ + └─────────────────┘ │ pstops → setpagedevice │ └────────────┘ + └──────────────────────────┘ +``` + +For example, `Duplex=DuplexNoTumble BRPrintQuality=Color InputSlot=Tray2` +becomes: + +```postscript +<>setpagedevice +<>setpagedevice +<> setpagedevice +``` ## Layout | Path | Purpose | | --- | --- | -| `Containerfile` | Image definition (Debian base, i386 multiarch, Brother driver install) | +| `Containerfile` | Image definition (Debian base, CUPS + cups-filters + Avahi, BR-Script3 PPD) | | `cupsd.conf` | Container CUPS configuration (listen on all interfaces, share printers) | | `entrypoint.sh` | Starts D-Bus, Avahi and CUPS, then registers the printer queue | -| `assets/Brother HL-4150CDN/` | Brother driver `.deb` packages used at build time | +| `assets/Brother HL-4150CDN/ppd/` | Official BR-Script3 PPD used for the queue | +| `assets/Brother HL-4150CDN/Windows/` | Vendor Windows driver the PPD was extracted from | ## Prerequisites @@ -32,15 +59,8 @@ Run from the repository root: podman build -t printserver:latest . ``` -On an arm64 host, build for x86_64 explicitly (the Brother binaries are i386 -and need an x86_64 target): - -```sh -podman build --platform linux/amd64 -t printserver:latest . -``` - -The build installs the Brother LPR and CUPS wrapper packages from -`assets/Brother HL-4150CDN/` and verifies that the PPD and filter are in place. +The image is architecture-independent (no i386 or other 32-bit binaries), so it +builds natively on x86_64 and arm64 hosts alike. ## Run @@ -50,7 +70,7 @@ networking**: ```sh podman run -d --name printserver \ --network host \ - -e PRINTER_IP=192.168.1.50 \ + -e PRINTER_IP=192.168.100.7 \ -e PRINTER_NAME=HL-4150CDN \ printserver:latest ``` @@ -61,34 +81,34 @@ podman run -d --name printserver \ | --- | --- | --- | | `PRINTER_IP` | *(empty)* | IP address / host name of the network printer. A device URI is derived from it (see `PRINTER_PROTOCOL`). | | `PRINTER_URI` | *(empty)* | Full device URI. Overrides `PRINTER_IP` when set. | -| `PRINTER_PROTOCOL` | `lpd` | Protocol used with `PRINTER_IP`: `lpd`, `socket` or `ipp`. | +| `PRINTER_PROTOCOL` | `socket` | Protocol used with `PRINTER_IP`: `socket`, `lpd` or `ipp`. | | `PRINTER_NAME` | `HL4150CDN` | CUPS queue name. Also used as the name advertised via AirPrint/DNS-SD. | | `PRINTER_DESCRIPTION` | *(empty)* | CUPS printer description (`lpadmin -D`), shown in the CUPS web UI. | -| `PPD_FILE` | `/usr/share/cups/model/brhl4150cdn.ppd` | PPD used for the queue. | +| `PPD_FILE` | `/usr/share/cups/model/BR4150_2.PPD` | PPD used for the queue. | No queue is created when neither `PRINTER_IP` nor `PRINTER_URI` is set. ### Choosing the connection -The HL-4150CDN is a network printer. Set `PRINTER_IP` and let the entrypoint -build the URI: +The HL-4150CDN accepts PostScript directly, so the default is raw AppSocket. +Set `PRINTER_IP` and let the entrypoint build the URI: | `PRINTER_PROTOCOL` | Resulting URI | | --- | --- | -| `lpd` (default) | `lpd:///binary_p1` — LPR/LPD, the URI Brother documents | -| `socket` | `socket://:9100` — raw AppSocket/JetDirect | +| `socket` (default) | `socket://:9100` — raw AppSocket/JetDirect | +| `lpd` | `lpd:///binary_p1` — LPR/LPD | | `ipp` | `ipp:///ipp/print` | -For example, a printer at `192.168.1.50` using LPD: +For example, a printer at `192.168.100.7`: ```sh --e PRINTER_IP=192.168.1.50 +-e PRINTER_IP=192.168.100.7 ``` Or specify the URI explicitly for full control: ```sh --e PRINTER_URI=lpd://192.168.1.50/binary_p1 +-e PRINTER_URI=socket://192.168.100.7:9100 ``` ### Advertised name @@ -101,6 +121,39 @@ set `PRINTER_DESCRIPTION` (this does not change the advertised name): -e PRINTER_NAME=HL4150CDN -e PRINTER_DESCRIPTION="Brother HL-4150CDN (Office)" ``` +## Printing options + +The BR-Script3 PPD exposes the HL-4150CDN's hardware options. Common ones: + +| Option | PPD keyword | Values | +| --- | --- | --- | +| Paper source | `InputSlot` | `AutoSelect`, `Tray1`, `Tray2`, `MPTray` | +| Two-sided printing | `Duplex` | `None`, `DuplexNoTumble` (long edge), `DuplexTumble` (short edge) | +| Colour / mono | `BRPrintQuality` | `Auto`, `Color`, `Black` | +| Colour mode | `BRColorMode` | `Normal`, `Vivid`, `None` | +| Media type | `BRMediaType` | `Plain`, `Thin`, `Thick`, `Recycled`, `Glossy`, `Label`, envelopes, … | +| Print quality | `CAPT` | `Fine` (600×600), `SuperFine` (2400 dpi class) | +| Toner save | `TonerSaveMode` | `True`, `False` | +| Secure print | `BRJobHold` | `None`, `Private` (+ `BRJobPIN`) | + +They can be selected per job, e.g.: + +```sh +podman exec printserver lp -d HL4150CDN \ + -o Duplex=DuplexNoTumble -o BRPrintQuality=Color -o InputSlot=Tray1 report.pdf +``` + +## Client support + +| Platform | Path | Notes | +| --- | --- | --- | +| macOS | AirPrint / IPP Everywhere | Discovered via mDNS; no driver needed | +| iOS / iPadOS | AirPrint | Discovered via mDNS; no driver needed | +| Linux | IPP / IPP Everywhere | Native or with the shared PPD | +| Windows | IPP or raw 9100 | Install Brother's **BR-Script3** Windows driver, pointed at the printer or the shared queue | + +Windows uses a client-side Brother driver; the server does not share via SMB. + ## Verify Browse the CUPS web interface: @@ -113,9 +166,10 @@ open http://localhost:631/admin # administration From the host, or inside the container: ```sh -podman exec printserver lpstat -t # queues and status -podman exec printserver lpstat -v HL4150CDN # device URI -podman exec printserver avahi-browse -rt _ipp._tcp # mDNS advertisement +podman exec printserver lpstat -t # queues and status +podman exec printserver lpstat -v HL4150CDN # device URI +podman exec printserver lpoptions -p HL4150CDN -l # available options +podman exec printserver avahi-browse -rt _ipp._tcp # mDNS advertisement ``` A print job can be submitted with: @@ -128,10 +182,6 @@ On macOS/iOS the shared queue should appear in the printer list via AirPrint. ## Notes and limitations -- **Run on an x86_64 host.** The Brother driver binaries are 32-bit i386 ELF - executables. They run natively on an x86_64 kernel, but on arm64 hosts the - image can be built and the drivers installed while the filter cannot execute - without i386 emulation (for example qemu-user / binfmt). - **Host networking is required** for Avahi to send and receive mDNS traffic. `-p 631:631` alone is enough for direct IPP access but not for AirPrint discovery. @@ -142,9 +192,8 @@ On macOS/iOS the shared queue should appear in the printer list via AirPrint. ```sh -v printserver-cups:/etc/cups ``` -- The Brother filter is a PostScript-based CUPS filter. AirPrint clients that - rely on driverless (URF) printing may not be able to use the queue; macOS - clients that can use the shared CUPS queue's PPD will work. -- The Brother driver packages are 32-bit (i386); the image enables Debian's - i386 multiarch to provide `libc6:i386` and the `ld-linux.so.2` loader they - need. +- Colour handling is done by the printer's own PostScript RIP. Output may differ + slightly from Brother's host-side (rasterising) driver. +- The queue advertises AirPrint capabilities (`image/urf`, `image/pwg-raster`, + `sides-supported`, `color-supported`). macOS and iOS printing is converted by + cups-filters; verify end to end on your network. diff --git a/assets/.DS_Store b/assets/.DS_Store deleted file mode 100644 index cdbd280..0000000 Binary files a/assets/.DS_Store and /dev/null differ diff --git a/assets/Brother HL-4150CDN/.DS_Store b/assets/Brother HL-4150CDN/.DS_Store deleted file mode 100644 index b119284..0000000 Binary files a/assets/Brother HL-4150CDN/.DS_Store and /dev/null differ diff --git a/assets/Brother HL-4150CDN/Windows/Y10B_C1-ps-win8-64-103-en.EXE b/assets/Brother HL-4150CDN/Windows/Y10B_C1-ps-win8-64-103-en.EXE new file mode 100644 index 0000000..02ffe92 Binary files /dev/null and b/assets/Brother HL-4150CDN/Windows/Y10B_C1-ps-win8-64-103-en.EXE differ diff --git a/assets/Brother HL-4150CDN/cupswrapper/README.md b/assets/Brother HL-4150CDN/cupswrapper/README.md deleted file mode 100644 index 60bd930..0000000 --- a/assets/Brother HL-4150CDN/cupswrapper/README.md +++ /dev/null @@ -1,38 +0,0 @@ -1. Login as a superuser ( or use "sudo" option if it is required ) - -2. Install LPR driver and cupswrapper driver - - Turn on the printer and connect the USB cable. - Open the terminal and go to the directory where the drivers are. - Install LPR driver.The install process may take some time. Please wait until it is complete. - Command (for dpkg) : dpkg -i --force-all (lpr-drivername) - - Install cupswrapper driver.The install process may take some time. Please wait until it is complete. - Command (for dpkg) : dpkg -i --force-all (cupswrapper-drivername) - - Check if the LPR driver and cupswrapper driver are installed - Command (for dpkg) : dpkg -l | grep Brother - - -3. Depending on the connection type you are using (USB or Network), follow one of the steps below. - -(for USB Connection) - - Open a web browser and go to "http://localhost:631/printers". - Check if the Device URI of your printer is "usb://Brother/(your printer's model name)" - - If the device URI is different from the example above, please go to "Modify Printer" of your printer to select proper device and driver. - If your printer is not listed on "http://localhost:631/printers", please go to "http://localhost:631/admin" and click "Add printer" and select proper device and driver. - - -(for Network Connection) - - Open a web browser and go to "http://localhost:631/printers". - Click "Modify Printer" and set following parameters. - - "LPD/LPR Host or Printer" or "AppSocket/HP JetDirect" for Device - - lpd://(Your printer's IP address)/binary_p1 for Device URI - - Brother for Make/Manufacturer Selection - - Your printer's name for Model/Driver Selection - -4. Try a test print -Open a text editor, write something and select "print" from the menu. diff --git a/assets/Brother HL-4150CDN/cupswrapper/hl4150cdncupswrapper-1.1.1-5.i386.deb b/assets/Brother HL-4150CDN/cupswrapper/hl4150cdncupswrapper-1.1.1-5.i386.deb deleted file mode 100644 index 2034dd6..0000000 Binary files a/assets/Brother HL-4150CDN/cupswrapper/hl4150cdncupswrapper-1.1.1-5.i386.deb and /dev/null differ diff --git a/assets/Brother HL-4150CDN/installer/.DS_Store b/assets/Brother HL-4150CDN/installer/.DS_Store deleted file mode 100644 index 8bc8643..0000000 Binary files a/assets/Brother HL-4150CDN/installer/.DS_Store and /dev/null differ diff --git a/assets/Brother HL-4150CDN/installer/README.md b/assets/Brother HL-4150CDN/installer/README.md deleted file mode 100644 index 4cafc5e..0000000 --- a/assets/Brother HL-4150CDN/installer/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Step 1. Enter this command to extract the downloaded file: - -Command: `gunzip linux-brprinter-installer-*.*.*-*.gz` -e.g. `gunzip linux-brprinter-installer-2.2.6-0.gz` - -# Step 2. Get superuser authorization with the "su" command or "sudo su" command. - -# Step 3. Run the tool: - -Command: `bash linux-brprinter-installer-*.*.*-* Brother machine name` -e.g. `bash linux-brprinter-installer-2.1.1-1 HL-4150CDN` - -# Step 4. The driver installation will start. Follow the installation screen directions. - - When you see the message "Will you specify the DeviceURI ?", - - For USB Users: Choose N(No) - For Network Users: Choose Y(Yes) and DeviceURI number. - -The install process may take some time. Please wait until it is complete. diff --git a/assets/Brother HL-4150CDN/installer/linux-brprinter-installer-2.2.6-0.gz b/assets/Brother HL-4150CDN/installer/linux-brprinter-installer-2.2.6-0.gz deleted file mode 100644 index ea5588a..0000000 Binary files a/assets/Brother HL-4150CDN/installer/linux-brprinter-installer-2.2.6-0.gz and /dev/null differ diff --git a/assets/Brother HL-4150CDN/lpr-driver/README.md b/assets/Brother HL-4150CDN/lpr-driver/README.md deleted file mode 100644 index 61409d1..0000000 --- a/assets/Brother HL-4150CDN/lpr-driver/README.md +++ /dev/null @@ -1,38 +0,0 @@ -1. Login as a superuser ( or use "sudo" option if required). - -2. Install the driver. - - Turn on the printer and connect the usb, network or parallel cable. - Go to the directory where the driver is. - Install LPR driver.The install process may take some time. Please wait until it is complete. - Command : dpkg -i --force-all (lpr-drivername) - Check if the LPR driver is installed. - Command : dpkg -l | grep Brother - - -3. Confirm/Configure a file according to your connection. - - - - Check the configuration filename for your distribution. - Example: - openSUSE, Ubuntu, Debian : /etc/printcap - Redhat, fedora, Mandriva : /etc/printcap.local - - Edit the file according to your connection. - For USB Connection (Default) - Check if the parameter of ":lp" is ":lp=/dev/usb/lp0" - For Network Connection - replace ":lp" line to the following 2 lines - :rm=(ip address of your printer)\ - :rp=lp\ - For Parallel Connection - replace ":lp" line to the following line - :lp=/dev/lp0\ - - Restart the print system. - Command (for lpr): /etc/init.d/lpr restart - Command (for lprng) : /etc/init.d/lprng restart - - -4. Try a test print. diff --git a/assets/Brother HL-4150CDN/lpr-driver/hl4150cdnlpr-1.1.1-5.i386.deb b/assets/Brother HL-4150CDN/lpr-driver/hl4150cdnlpr-1.1.1-5.i386.deb deleted file mode 100644 index e570e53..0000000 Binary files a/assets/Brother HL-4150CDN/lpr-driver/hl4150cdnlpr-1.1.1-5.i386.deb and /dev/null differ diff --git a/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD b/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD new file mode 100644 index 0000000..6e84215 --- /dev/null +++ b/assets/Brother HL-4150CDN/ppd/BR4150_2.PPD @@ -0,0 +1,680 @@ +*PPD-Adobe: "4.3" +*%================================================ +*% Copyright(C) 2010 Brother Industries, Ltd. +*% "Brother HL-4150CDN BR-Script3" +*%================================================ + +*%==== General Information Keywords ======================== +*FormatVersion: "4.3" +*FileVersion: "1.01" +*LanguageEncoding: ISOLatin1 +*LanguageVersion: English +*Manufacturer: "Brother" +*PCFileName: "BR4150_2.PPD" +*Product: "(Brother HL-4150CDN)" +*PSVersion: "(3010.106) 3" +*ShortNickName: "Brother HL-4150CDN BR-Script3" +*ModelName: "Brother HL-4150CDN BR-Script3" +*NickName: "Brother HL-4150CDN BR-Script3" + +*%==== Basic Device Capabilities ============= +*LanguageLevel: "3" +*TTRasterizer: Type42 +*ColorDevice: True +*DefaultColorSpace: CMYK +*FileSystem: True +*?FileSystem:" +save + /devname (%disk0%) def + /ret false def + 0 1 7{ + devname exch 48 add 5 exch put + devname devstatus { + 0 ne {/ret true def}if + pop pop pop pop pop pop pop + }if + }for + ret {(True)}{(False)} ifelse = flush +restore +" +*End + +*Throughput: "20" +*FreeVM: "2700000" + +*%==== Emulations and Protocols ========== +*Protocols: PJL TBCP + +*SuggestedJobTimeout: "0" +*SuggestedWaitTimeout: "300" +*PrintPSErrors: True + +*%==== PostScript Patches ========== +*%*JobPatchFile 1: "statusdict/setusbbinary known{statusdict begin true setusbbinary end}if" + +*%==== JCL Features ========================== +*JCLBegin: "<1B>%-12345X@PJL JOB<0A>" +*JCLToPSInterpreter: "@PJL ENTER LANGUAGE = POSTSCRIPT <0A>" +*JCLEnd: "<1B>%-12345X@PJL EOJ <0A><1B>%-12345X" + +*%==== Installable Options =================== + +*OpenGroup: InstallableOptions/Options Installed + +*OpenUI *OptionTrays/Number of Input Trays: PickOne +*DefaultOptionTrays: 2Trays +*OptionTrays 1Trays/ 1: "" +*OptionTrays 2Trays/ 2: "" +*?OptionTrays:" +save + <>setpagedevice currentpagedevice/BRFeeder get + 1 eq{(2Trays)}{(1Trays)}ifelse + = flush +restore +" +*End +*CloseUI: *OptionTrays + +*CloseGroup: InstallableOptions + +*UIConstraints: *OptionTrays 1Trays *InputSlot Tray2 +*UIConstraints: *InputSlot Tray2 *OptionTrays 1Trays + +*%==== Media Selection ====================== + +*OpenUI *PageSize: PickOne +*OrderDependency: 30 AnySetup *PageSize +*DefaultPageSize: A4 +*PageSize Letter/Letter: "<< /PageSize [612 792] /ImagingBBox null >> setpagedevice" +*PageSize Legal/Legal: "<< /PageSize [612 1008] /ImagingBBox null >> setpagedevice" +*PageSize Executive/Executive: "<< /PageSize [522 756] /ImagingBBox null >> setpagedevice" +*PageSize A4/A4: "<< /PageSize [595 842] /ImagingBBox null >> setpagedevice" +*PageSize A5/A5: "<< /PageSize [420 595] /ImagingBBox null >> setpagedevice" +*PageSize A5L/A5 Long Edge: "<< /PageSize [595 419] /ImagingBBox null >> setpagedevice" +*PageSize A6/A6: "<< /PageSize [297 420] /ImagingBBox null >> setpagedevice" +*PageSize Env10/Com-10: "<< /PageSize [297 684] /ImagingBBox null >> setpagedevice" +*PageSize EnvMonarch/Monarch: "<> setpagedevice" +*PageSize EnvDL/DL: "<< /PageSize [312 624] /ImagingBBox null >> setpagedevice" +*PageSize EnvDLRotated/DL Long Edge: "<< /PageSize [624 312] /ImagingBBox null >> setpagedevice" +*PageSize EnvC5/C5: "<> setpagedevice" +*PageSize EnvISOB5/B5 : "<< /PageSize [499 709] /ImagingBBox null >> setpagedevice" +*PageSize EnvISOB6/B6 : "<< /PageSize [354 499] /ImagingBBox null >> setpagedevice" +*PageSize B5/JIS B5: "<< /PageSize [515.0 727.0] /ImagingBBox null >> setpagedevice" +*PageSize B6/JIS B6: "<< /PageSize [363.0 515.0] /ImagingBBox null >> setpagedevice" +*PageSize 3x5/3x5: "<< /PageSize [216 360] /ImagingBBox null >> setpagedevice" +*PageSize FanFoldGermanLegal/Folio: "<< /PageSize [612 936] /ImagingBBox null >> setpagedevice" +*CloseUI: *PageSize + +*OpenUI *PageRegion: PickOne +*OrderDependency: 40 AnySetup *PageRegion +*DefaultPageRegion: A4 +*PageRegion Letter/Letter: "<< /PageSize [612 792] /ImagingBBox null >> setpagedevice" +*PageRegion Legal/Legal: "<< /PageSize [612 1008] /ImagingBBox null >> setpagedevice" +*PageRegion Executive/Executive: "<< /PageSize [522 756] /ImagingBBox null >> setpagedevice" +*PageRegion A4/A4: "<< /PageSize [595 842] /ImagingBBox null >> setpagedevice" +*PageRegion A5/A5: "<< /PageSize [420 595] /ImagingBBox null >> setpagedevice" +*PageRegion A5L/A5 Long Edge: "<< /PageSize [595 419] /ImagingBBox null >> setpagedevice" +*PageRegion A6/A6: "<< /PageSize [297 420] /ImagingBBox null >> setpagedevice" +*PageRegion Env10/Com-10: "<< /PageSize [297 684] /ImagingBBox null >> setpagedevice" +*PageRegion EnvMonarch/Monarch: "<> setpagedevice" +*PageRegion EnvDL/DL: "<< /PageSize [312 624] /ImagingBBox null >> setpagedevice" +*PageRegion EnvDLRotated/DL Long Edge: "<< /PageSize [624 312] /ImagingBBox null >> setpagedevice" +*PageRegion EnvC5/C5: "<> setpagedevice" +*PageRegion EnvISOB5/B5 : "<< /PageSize [499 709] /ImagingBBox null >> setpagedevice" +*PageRegion EnvISOB6/B6 : "<< /PageSize [354 499] /ImagingBBox null >> setpagedevice" +*PageRegion B5/JIS B5: "<< /PageSize [515.0 727.0] /ImagingBBox null >> setpagedevice" +*PageRegion B6/JIS B6: "<< /PageSize [363.0 515.0] /ImagingBBox null >> setpagedevice" +*PageRegion 3x5/3x5: "<< /PageSize [216 360] /ImagingBBox null >> setpagedevice" +*PageRegion FanFoldGermanLegal/Folio: "<< /PageSize [612 936] /ImagingBBox null >> setpagedevice" +*CloseUI: *PageRegion + +*DefaultImageableArea: A4 +*ImageableArea Letter/Letter: "12.0 12.12 599.88 780.0" +*ImageableArea Legal/Legal: "12.0 12.12 599.88 996.0" +*ImageableArea Executive/Executive: "11.99 12.12 510.0 744.0" +*ImageableArea A4/A4: "12.0 12.24 583.08 829.92" +*ImageableArea A5/A5: "12.0 11.88 407.4 582.96" +*ImageableArea A5L/A5 Long Edge: "12.0 11.88 407.4 582.96" +*ImageableArea A6/A6: "12.0 12.6 285.48 408.0" +*ImageableArea Env10/Com-10: "12.0 12.12 284.88 672.0" +*ImageableArea EnvMonarch/Monarch: "12.0 12.12 266.76 528.0" +*ImageableArea EnvDL/DL: "12.0 12.24 299.88 612.0" +*ImageableArea EnvDLRotated/DL Long Edge: "17.76 12.12 606.24 300.0" +*ImageableArea EnvC5/C5: "12.0 12.24 446.76 636.96" +*ImageableArea EnvISOB5/B5 : "12.0 12.48 486.72 696.96" +*ImageableArea EnvISOB6/B6 : "12.0 12.24 342.12 486.96" +*ImageableArea B5/JIS B5: "12.0 12.48 502.92 714.96" +*ImageableArea B6/JIS B6: "12.0 12.00 350.88 503.00" +*ImageableArea 3x5/3x5: "12.0 12.12 204.00 348.00" +*ImageableArea FanFoldGermanLegal/Folio: "12.0 12.12 599.88 924.0" + +*%==== Information About Media Sizes ======== + +*DefaultPaperDimension: A4 +*PaperDimension Letter/Letter: "612 792" +*PaperDimension Legal/Legal: "612 1008" +*PaperDimension Executive/Executive: "522 756" +*PaperDimension A4/A4: "595 842" +*PaperDimension A5/A5: "420 595" +*PaperDimension A5L/A5 Long Edge: "420 595" +*PaperDimension A6/A6: "297 420" +*PaperDimension Env10/Com-10: "297 684" +*PaperDimension EnvMonarch/Monarch: "279 540" +*PaperDimension EnvDL/DL: "312 624" +*PaperDimension EnvDLRotated/DL Long Edge: "624 312" +*PaperDimension EnvC5/C5: "459 649" +*PaperDimension EnvISOB5/B5 : "499 709" +*PaperDimension EnvISOB6/B6 : "354 499" +*PaperDimension B5/JIS B5: "516 729" +*PaperDimension B6/JIS B6: "363 516" +*PaperDimension 3x5/3x5: "216 360" +*PaperDimension FanFoldGermanLegal/Folio: "612 936" + +*%==== Custom Page Sizes ==================== +*NonUIOrderDependency: 40 AnySetup *CustomPageSize +*VariablePaperSize: True +*LeadingEdge Short: "" +*LeadingEdge Long: "" +*DefaultLeadingEdge: Short +*MaxMediaWidth: "612" +*MaxMediaHeight: "1152" +*HWMargins: 13 13 13 13 +*CustomPageSize True: " + <> setpagedevice + exch pop exch pop 0 eq{exch}if + << /PageSize [ 5 -2 roll ] /ImagingBBox null >> + setpagedevice +" +*End +*ParamCustomPageSize Width: 1 points 198 612 +*ParamCustomPageSize Height: 2 points 328 1152 +*ParamCustomPageSize WidthOffset: 3 points 0 0 +*ParamCustomPageSize HeightOffset: 4 points 0 0 +*ParamCustomPageSize Orientation: 5 int 0 1 + +*%==== 5.12 Finishing Features ================================= +*%%%%% Resolution and Appearance Control %%%%% +*DefaultResolution: 600dpi + +*OpenUI *CAPT/Print Quality:PickOne +*OrderDependency: 10 AnySetup *CAPT +*DefaultCAPT: Fine +*CAPT Fine/Normal (600 x 600 dpi): "<> setpagedevice" +*CAPT SuperFine/Fine (2400 dpi class): "<> setpagedevice" +*?CAPT: " + currentpagedevice /BRApt get 2 eq + {(SuperFine)} + { + currentpagedevice/HWResolution get 0 get 600 eq{(Fine)}{(Draft)}ifelse + }ifelse = flush +" +*End +*CloseUI: *CAPT + +*%==== 5.13 Media Handling Features ============================ +*OpenUI *BRMediaType/Media Type: PickOne +*OrderDependency: 28 AnySetup *BRMediaType +*DefaultBRMediaType:Plain +*BRMediaType Plain/Plain Paper: "<>setpagedevice" +*BRMediaType Thin/Thin Paper: "<>setpagedevice" +*BRMediaType Thick/Thick Paper: "<>setpagedevice" +*BRMediaType ThickPaper2/Thicker Paper: "<>setpagedevice" +*BRMediaType BOND/Bond Paper: "<>setpagedevice" +*BRMediaType Env/Envelopes: "<>setpagedevice" +*BRMediaType EnvThick/Env. Thick: "<>setpagedevice" +*BRMediaType EnvThin/Env. Thin: "<>setpagedevice" +*BRMediaType Recycled/Recycled Paper: "<>setpagedevice" +*BRMediaType Label/Label: "<>setpagedevice" +*BRMediaType Glossy/Glossy Paper: "<>setpagedevice" +*CloseUI: *BRMediaType + +*OpenUI *InputSlot: PickOne +*OrderDependency: 29 AnySetup *InputSlot +*DefaultInputSlot: AutoSelect +*InputSlot AutoSelect/Auto Select: "<> setpagedevice" +*InputSlot Tray1/Tray1: "<> setpagedevice" +*InputSlot Tray2/Tray2: "<> setpagedevice" +*InputSlot MPTray/MP Tray: "<> setpagedevice" +*CloseUI: *InputSlot + +*RequiresPageRegion All:True + +*OpenUI *ManualFeed/Manual Feed: Boolean +*OrderDependency: 19 AnySetup *ManualFeed +*DefaultManualFeed: False +*ManualFeed True: "<> setpagedevice" +*ManualFeed False: "<> setpagedevice" +*CloseUI: *ManualFeed + +*%=== Duplex ================================ +*OpenUI *Duplex: PickOne +*OrderDependency: 25 AnySetup *Duplex +*DefaultDuplex: None +*Duplex DuplexTumble: "<>setpagedevice" +*Duplex DuplexNoTumble: "<>setpagedevice" +*Duplex None: "<>setpagedevice" +*CloseUI: *Duplex + +*%=== Output Bin ============================= +*DefaultOutputBin: OnlyOne + +*% === Collate ========== + +*%=== JobHold ================================ +*OpenUI *BRJobHold/Secure Print: PickOne +*OrderDependency: 21 AnySetup *BRJobHold +*DefaultBRJobHold: None +*BRJobHold None/Off: "<>setpagedevice" +*BRJobHold Private/On: "<>setpagedevice" +*CloseUI: *BRJobHold + +*OpenUI *BRJobPIN/Password: PickOne +*OrderDependency: 22 AnySetup *BRJobPIN +*DefaultBRJobPIN: HoldKey0 +*BRJobPIN HoldKey0/0000: "<> setpagedevice" +*BRJobPIN HoldKey1/1002: "<> setpagedevice" +*BRJobPIN HoldKey2/2833: "<> setpagedevice" +*BRJobPIN HoldKey3/3410: "<> setpagedevice" +*BRJobPIN HoldKey4/4791: "<> setpagedevice" +*BRJobPIN HoldKey5/0052: "<> setpagedevice" +*BRJobPIN HoldKey6/9612: "<> setpagedevice" +*BRJobPIN HoldKey7/0438: "<> setpagedevice" +*BRJobPIN HoldKey8/7328: "<> setpagedevice" +*BRJobPIN HoldKey9/0001: "<> setpagedevice" +*CloseUI: *BRJobPIN + +*OpenUI *BRUser/User Name: PickOne +*OrderDependency: 91 AnySetup *BRUser +*DefaultBRUser: UserSystem +*BRUser UserSystem/System Name: " +" +*End +*CloseUI: *BRUser + +*OpenUI *BRJobName/Job Name: PickOne +*OrderDependency: 92 AnySetup *BRJobName +*DefaultBRJobName: JobNameSystem +*BRJobName JobNameSystem/System Name: "" +*BRJobName JobName1/1: "%%BRTitle: 1 +" +*End +*BRJobName JobName2/2: "%%BRTitle: 2 +" +*End +*BRJobName JobName3/3: "%%BRTitle: 3 +" +*End +*BRJobName JobName4/4: "%%BRTitle: 4 +" +*End +*BRJobName JobName5/5: "%%BRTitle: 5 +" +*End +*BRJobName JobName6/6: "%%BRTitle: 6 +" +*End +*BRJobName JobName7/7: "%%BRTitle: 7 +" +*End +*BRJobName JobName8/8: "%%BRTitle: 8 +" +*End +*BRJobName JobName9/9: "%%BRTitle: 9 +" +*End +*CloseUI: *BRJobName + +*OpenUI *TonerSaveMode/Toner Save: Boolean +*DefaultTonerSaveMode: False +*OrderDependency: 10 AnySetup *TonerSaveMode +*TonerSaveMode False/Off: " + statusdict/tonersave known{statusdict begin false tonersave end}if +" +*End +*TonerSaveMode True/On: " + statusdict/tonersave known{statusdict begin true tonersave end}if +" +*End +*CloseUI: *TonerSaveMode + +*OpenUI *Sleep/Sleep Time [Min.]: PickOne +*DefaultSleep: PrinterDefault +*OrderDependency: 10 AnySetup *Sleep +*Sleep PrinterDefault/Printer Default: "" +*Sleep 2minutes/2: "statusdict/powersavetime known{statusdict begin 2 powersavetime end}if" +*Sleep 10minutes/10: "statusdict/powersavetime known{statusdict begin 10 powersavetime end}if" +*Sleep 30minutes/30: "statusdict/powersavetime known{statusdict begin 30 powersavetime end}if" +*CloseUI: *Sleep + +*%==== BR-Script Color Original UI ================================== + +*OpenUI *BRPrintQuality/Colour/Mono: PickOne +*OrderDependency: 15 AnySetup *BRPrintQuality +*DefaultBRPrintQuality: Auto +*BRPrintQuality Auto/Auto: " + <>setpagedevice + <>setpagedevice + <>setpagedevice" +*BRPrintQuality Color/Colour: " + <>setpagedevice + <>setpagedevice + <>setpagedevice" +*BRPrintQuality Black/Mono: " + <>setpagedevice + <>setpagedevice + <>setpagedevice" +*?BRPrintQuality: " + save + currentpagedevice /BRProcessColor get 1 eq + {(Black)}{(Color)}ifelse + = flush + restore +" +*End +*CloseUI: *BRPrintQuality + +*% Color Mode Information =============== +*OpenUI *BRColorMode/Colour Mode: PickOne +*OrderDependency: 131.0 AnySetup *BRColorMode +*DefaultBRColorMode: Normal +*BRColorMode Normal/Normal: " + <>setpagedevice + <>setpagedevice +" +*End +*BRColorMode Vivid/Vivid: " + <>setpagedevice + <>setpagedevice +" +*End +*BRColorMode None/None: " + <>setpagedevice +" +*End +*CloseUI: *BRColorMode + +*% Improve Gray Color Information =============== +*OpenUI *BRImprovedGray/Improve Grey Colour: PickOne +*OrderDependency: 132.0 AnySetup *BRImprovedGray +*DefaultBRImprovedGray: True +*BRImprovedGray True/On: " + <>setpagedevice +" +*End +*BRImprovedGray False/Off: " + <>setpagedevice +" +*End +*CloseUI: *BRImprovedGray + +*% UCRGCR Information =============== +*OpenUI *UCRGCRForImage/Enhance Black Printing: Boolean +*OrderDependency: 133 AnySetup *ScreenLock +*DefaultUCRGCRForImage: False +*UCRGCRForImage False/Off: " + statusdict begin false ucrgcrforimage end +" +*End +*UCRGCRForImage True/On: " + statusdict begin true ucrgcrforimage end +" +*End +*CloseUI: *UCRGCRForImage + +*% Halftone Screen Lock Information =============== +*OpenUI *ScreenLock/Halftone Screen Lock: Boolean +*OrderDependency: 90 AnySetup *ScreenLock +*DefaultScreenLock: True +*ScreenLock True/On: " + <>setuserparams +" +*End +*ScreenLock False/Off: " + <>setuserparams +" +*End +*CloseUI: *ScreenLock + +*% Reduced Image =============== +*OpenUI *BRReducedImage/High Quality Image Printing: Boolean +*OrderDependency: 131.0 AnySetup *BRReducedImage +*DefaultBRReducedImage: False +*BRReducedImage False/Off: " + <>setpagedevice +" +*End +*BRReducedImage True/On: " + <>setpagedevice +" +*End +*CloseUI: *BRReducedImage + +*% Improve Print Output =============== +*OpenUI *ImprovePrintOutput/Improve Print Output: PickOne +*OrderDependency: 135.0 AnySetup *ImprovePrintOutput +*DefaultImprovePrintOutput: None +*ImprovePrintOutput None/Off: " + <>setpagedevice +" +*End +*ImprovePrintOutput ReducePaperCurl/Reduce Paper Curl: " + <>setpagedevice +" +*End +*ImprovePrintOutput ImproveTonerFixing/Improve Toner Fixing: " + <>setpagedevice +" +*End +*CloseUI: *ImprovePrintOutput + +*%==== 5.17 Gray Levels and Halftoninig ======================== +*AccurateScreenSupport: False +*ScreenFreq: "60.0" +*ScreenAngle: "0.0" +*DefaultScreenProc: Dot +*ScreenProc Dot: "{1.0 add 180 mul 1 add sin 0.001 add exch 1.0 add 180 mul 2 add sin mul}" + +*DefaultTransfer: Null +*Transfer Null: "{}" +*Transfer Null.Inverse: "{1 exch sub}" + +*%==== 5.18 Color Issues ======================================= +*% BlackSubstitution -> Original UI + +*%==== 5.19 Color Separation Keywords ========================== +*DefaultColorSep: ProcessBlack +*ColorSepScreenFreq ProcessBlack.71lpi.600dpi/71 lpi / 600 dpi: "70.7107" +*ColorSepScreenFreq CustomColor.71lpi.600dpi/71 lpi / 600 dpi: "70.7107" +*ColorSepScreenFreq ProcessCyan.71lpi.600dpi/71 lpi / 600 dpi: "63.2456" +*ColorSepScreenFreq ProcessMagenta.71lpi.600dpi/71 lpi / 600 dpi: "63.2456" +*ColorSepScreenFreq ProcessYellow.71lpi.600dpi/71 lpi / 600 dpi: "66.6667" +*ColorSepScreenFreq ProcessBlack.60lpi.300dpi/60 lpi / 300 dpi: "60" +*ColorSepScreenFreq CustomColor.60lpi.300dpi/60 lpi / 300 dpi: "60" +*ColorSepScreenFreq ProcessCyan.60lpi.300dpi/60 lpi / 300 dpi: "60" +*ColorSepScreenFreq ProcessMagenta.60lpi.300dpi/60 lpi / 300 dpi: "60" +*ColorSepScreenFreq ProcessYellow.60lpi.300dpi/60 lpi / 300 dpi: "60" +*ColorSepScreenFreq ProcessBlack.53lpi.300dpi/53 lpi / 300 dpi: "53.033" +*ColorSepScreenFreq CustomColor.53lpi.300dpi/53 lpi / 300 dpi: "53.033" +*ColorSepScreenFreq ProcessCyan.53lpi.300dpi/53 lpi / 300 dpi: "47.4342" +*ColorSepScreenFreq ProcessMagenta.53lpi.300dpi/53 lpi / 300 dpi:"47.4342" +*ColorSepScreenFreq ProcessYellow.53lpi.300dpi/53 lpi / 300 dpi: "50.0" +*ColorSepScreenAngle ProcessBlack.71lpi.600dpi/71 lpi / 600 dpi: "45.0" +*ColorSepScreenAngle CustomColor.71lpi.600dpi/71 lpi / 600 dpi: "45.0" +*ColorSepScreenAngle ProcessCyan.71lpi.600dpi/71 lpi / 600 dpi: "71.5651" +*ColorSepScreenAngle ProcessMagenta.71lpi.600dpi/71 lpi / 600 dpi: "18.4349" +*ColorSepScreenAngle ProcessYellow.71lpi.600dpi/71 lpi / 600 dpi: "0.0" +*ColorSepScreenAngle ProcessBlack.60lpi.300dpi/60 lpi / 300 dpi: "45" +*ColorSepScreenAngle CustomColor.60lpi.300dpi/60 lpi / 300 dpi: "45" +*ColorSepScreenAngle ProcessCyan.60lpi.300dpi/60 lpi / 300 dpi: "15" +*ColorSepScreenAngle ProcessMagenta.60lpi.300dpi/60 lpi / 300 dpi: "75" +*ColorSepScreenAngle ProcessYellow.60lpi.300dpi/60 lpi / 300 dpi: "0" +*ColorSepScreenAngle ProcessBlack.53lpi.300dpi/53 lpi / 300 dpi: "45.0" +*ColorSepScreenAngle CustomColor.53lpi.300dpi/53 lpi / 300 dpi: "45.0" +*ColorSepScreenAngle ProcessCyan.53lpi.300dpi/53 lpi / 300 dpi: "71.5651" +*ColorSepScreenAngle ProcessMagenta.53lpi.300dpi/53 lpi / 300 dpi: "18.4349" +*ColorSepScreenAngle ProcessYellow.53lpi.300dpi/53 lpi / 300 dpi: "0.0" +*End + +*InkName: ProcessBlack/Process Black +*InkName: CustomColor/Custom Color +*InkName: ProcessCyan/Process Cyan +*InkName: ProcessMagenta/Process Magenta +*InkName: ProcessYellow/Process Yellow + +*%==== 5.20 Font Related Keywords ============================== +*DefaultFont: Courier +*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM +*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM +*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM +*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM +*Font Bookman-Demi: Standard "(001.004S)" Standard ROM +*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM +*Font Bookman-Light: Standard "(001.004S)" Standard ROM +*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM +*Font Courier: Standard "(002.004S)" Standard ROM +*Font Courier-Bold: Standard "(002.004S)" Standard ROM +*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM +*Font Courier-Oblique: Standard "(002.004S)" Standard ROM +*Font Helvetica: Standard "(001.006S)" Standard ROM +*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM +*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM +*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM +*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM +*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM +*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM +*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM +*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM +*Font Palatino-Bold: Standard "(001.005S)" Standard ROM +*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM +*Font Palatino-Italic: Standard "(001.005S)" Standard ROM +*Font Palatino-Roman: Standard "(001.005S)" Standard ROM +*Font Times-Bold: Standard "(001.007S)" Standard ROM +*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM +*Font Times-Italic: Standard "(001.007S)" Standard ROM +*Font Times-Roman: Standard "(001.007S)" Standard ROM +*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM +*Font ZapfDingbats: Special "(001.004S)" Special ROM +*Font Symbol: Special "(001.007S)" Special ROM +*Font Alaska: Standard "(001.005)" Standard ROM +*Font AlaskaExtrabold: Standard "(001.005)" Standard ROM +*Font AntiqueOakland: Standard "(001.005)" Standard ROM +*Font AntiqueOakland-Bold: Standard "(001.005)" Standard ROM +*Font AntiqueOakland-Oblique: Standard "(001.005)" Standard ROM +*Font ClevelandCondensed: Standard "(001.005)" Standard ROM +*Font Connecticut: Standard "(001.005)" Standard ROM +*Font Guatemala-Antique: Standard "(001.005)" Standard ROM +*Font Guatemala-Bold: Standard "(001.005)" Standard ROM +*Font Guatemala-Italic: Standard "(001.005)" Standard ROM +*Font Guatemala-BoldItalic: Standard "(001.005)" Standard ROM +*Font LetterGothic: Standard "(001.005)" Standard ROM +*Font LetterGothic-Bold: Standard "(001.005)" Standard ROM +*Font LetterGothic-Oblique: Standard "(001.005)" Standard ROM +*Font Maryland: Standard "(001.005)" Standard ROM +*Font Oklahoma: Standard "(001.005)" Standard ROM +*Font Oklahoma-Bold: Standard "(001.005)" Standard ROM +*Font Oklahoma-Oblique: Standard "(001.005)" Standard ROM +*Font Oklahoma-BoldOblique: Standard "(001.005)" Standard ROM +*Font Utah: Standard "(001.005)" Standard ROM +*Font Utah-Bold: Standard "(001.005)" Standard ROM +*Font Utah-Oblique: Standard "(001.005)" Standard ROM +*Font Utah-BoldOblique: Standard "(001.005)" Standard ROM +*Font UtahCondensed: Standard "(001.005)" Standard ROM +*Font UtahCondensed-Bold: Standard "(001.005)" Standard ROM +*Font UtahCondensed-Oblique: Standard "(001.004)" Standard ROM +*Font UtahCondensed-BoldOblique: Standard "(001.005)" Standard ROM +*Font BermudaScript: Standard "(001.005)" Standard ROM +*Font Germany: Standard "(001.005)" Standard ROM +*Font SanDiego: Standard "(001.005)" Standard ROM +*Font US-Roman: Standard "(001.005)" Standard ROM +*?FontQuery: " +save +count 1 gt + {exch dup dup + =string cvs (/) print print (:) print + FontDirectory exch known + {pop(Yes)} + {(fonts/)AppendName exch pop mark exch + {}=string filenameforall counttomark + 0 gt + {cleartomark(Yes)} + {cleartomark(No)}ifelse + }ifelse + = + }if + (*) = flush +restore +" +*End +*?FontList: " +save + FontDirectory{pop ==}forall + (fonts/*) + {dup length 6 sub 6 exch getinterval cvn == + }=string filenameforall + (*) = flush +restore +" +*End + + +*%==== 5.21 Printer Messages =================================== +*% +*% Printer Message +*% +*PrinterError: "service call" +*PrinterError: "cover open" +*PrinterError: "end of ink" +*PrinterError: "out of maintenance paper" +*PrinterError: "out of paper" +*PrinterError: "paper jam" +*PrinterError: "manual feed time out" +*PrinterError: "load paper letter size" +*PrinterError: "load paper legal size" +*PrinterError: "load paper B5 size" +*PrinterError: "load paper A4 size" +*PrinterError: "offline" + +*Status: "idle" +*Status: "busy" +*Status: "waiting" +*Status: "printing" +*Status: "warming up" +*Status: "PrinterError: service call" +*Status: "PrinterError: cover open" +*Status: "PrinterError: end of ink" +*Status: "PrinterError: out of paper" +*Status: "PrinterError: paper jam" +*Status: "PrinterError: manual feed time out" +*Status: "PrinterError: load paper letter size" +*Status: "PrinterError: load paper legal size" +*Status: "PrinterError: load paper B5 size" +*Status: "PrinterError: load paper A4 size" +*Status: "PrinterError: offline" + +*% Input Sources (format: %%[ status: ; source: ]%% ) +*Source: "Serial" +*Source: "Parallel" +*Source: "EtherTalk" +*Source: "LPR" +*Source: "PrintServer" +*Source: "Internal" + +*Message: "%%[ exitserver: permanent state may be changed ]%%" +*Message: "%%[ Flushing: rest of job (to end-of-file) will be ignored ]%%" +*Message: "\FontName\ not found, using BR-03B." +*%==== 5.22 System Management ================================== +*Password: "0" +*ExitServer: " + serverdict begin 0 exitserver +" +*End +*Reset: " + clear cleardictstack + serverdict begin 0 exitserver + systemdict /quit get exec + " +*End + diff --git a/assets/Brother HL-4150CDN/ppd/README.md b/assets/Brother HL-4150CDN/ppd/README.md new file mode 100644 index 0000000..8a829b8 --- /dev/null +++ b/assets/Brother HL-4150CDN/ppd/README.md @@ -0,0 +1,41 @@ +# BR-Script3 PPD — Brother HL-4150CDN + +`BR4150_2.PPD` is Brother's official **BR-Script3** (PostScript 3) PPD for the +HL-4150CDN. + +## Provenance + +Extracted from the vendor Windows driver in `../Windows/`: + +``` +Y10B_C1-ps-win8-64-103-en.EXE (ZIP self-extracting archive) +└── eng/BR4150_2.PPD +``` + +```sh +unzip -j "Y10B_C1-ps-win8-64-103-en.EXE" "eng/BR4150_2.PPD" +``` + +The archive also contains PPDs for the related BR4140 and BR4570 models; only +the HL-4150CDN PPD is used here. Line endings were normalised from CRLF to LF. + +## Why this file + +The HL-4150CDN emulates PCL6 and BR-Script3 (PostScript 3). This PPD encodes +every device setting as PostScript `setpagedevice`, for example: + +| PPD option | Emitted PostScript | +| --- | --- | +| `InputSlot Tray2` | `<> setpagedevice` | +| `InputSlot MPTray` | `<> setpagedevice` | +| `Duplex DuplexNoTumble` | `<>setpagedevice` | +| `BRPrintQuality Black` | `<>… <>setpagedevice` | +| `BRColorMode Normal` | `<>… <>setpagedevice` | +| `BRMediaType Recycled` | `<>setpagedevice` | + +CUPS's `pstops` filter injects the selected option code, and the PPD's +`*JCLBegin` / `*JCLToPSInterpreter` / `*JCLEnd` entries wrap the job in PJL. + +This lets the HL-4150CDN be driven with a plain PostScript queue, so the +container does **not** need Brother's 32-bit Linux CUPS wrapper/filter +(`brlpdwrapperhl4150cdn`) or its Ghostscript rasterisation step. diff --git a/entrypoint.sh b/entrypoint.sh index 8054724..3fe2187 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,10 +5,15 @@ # the Brother HL-4150CDN queue if it does not exist yet. The CUPS daemon is # kept in the foreground so the container lives as long as the print server. # +# The queue uses Brother's BR-Script3 (PostScript 3) PPD and, by default, +# sends jobs to the printer over raw AppSocket (socket://:9100). CUPS +# converts client documents to PostScript and the PPD turns device options +# (tray, duplex, colour, ...) into PostScript "setpagedevice" instructions. +# # Configuration (all optional, via environment): # PRINTER_IP IP address / host name of the network printer. # PRINTER_URI Full device URI; overrides PRINTER_IP when set. -# PRINTER_PROTOCOL lpd (default), socket or ipp - used with PRINTER_IP. +# PRINTER_PROTOCOL socket (default), lpd or ipp - used with PRINTER_IP. # PRINTER_NAME CUPS queue name and advertised (DNS-SD) name. # PRINTER_DESCRIPTION CUPS Description (lpadmin -D) shown in the web UI. # PPD_FILE PPD used for the queue. @@ -17,28 +22,30 @@ set -eu PRINTER_NAME="${PRINTER_NAME:-HL4150CDN}" PRINTER_IP="${PRINTER_IP:-}" PRINTER_URI="${PRINTER_URI:-}" -PRINTER_PROTOCOL="${PRINTER_PROTOCOL:-lpd}" +PRINTER_PROTOCOL="${PRINTER_PROTOCOL:-socket}" PRINTER_DESCRIPTION="${PRINTER_DESCRIPTION:-}" -PPD_FILE="${PPD_FILE:-/usr/share/cups/model/brhl4150cdn.ppd}" +PPD_FILE="${PPD_FILE:-/usr/share/cups/model/BR4150_2.PPD}" LOG_PREFIX="[entrypoint]" log() { echo "${LOG_PREFIX} $*"; } # Build a device URI from PRINTER_IP unless an explicit PRINTER_URI is given. +# Raw AppSocket is the default: the HL-4150CDN accepts PostScript directly on +# port 9100 (its BR-Script3 interpreter auto-detects the job language). if [ -z "${PRINTER_URI}" ] && [ -n "${PRINTER_IP}" ]; then case "${PRINTER_PROTOCOL}" in - lpd) - PRINTER_URI="lpd://${PRINTER_IP}/binary_p1" - ;; socket) PRINTER_URI="socket://${PRINTER_IP}:9100" ;; + lpd) + PRINTER_URI="lpd://${PRINTER_IP}/binary_p1" + ;; ipp) PRINTER_URI="ipp://${PRINTER_IP}/ipp/print" ;; *) - log "unknown PRINTER_PROTOCOL '${PRINTER_PROTOCOL}', defaulting to lpd" - PRINTER_URI="lpd://${PRINTER_IP}/binary_p1" + log "unknown PRINTER_PROTOCOL '${PRINTER_PROTOCOL}', defaulting to socket" + PRINTER_URI="socket://${PRINTER_IP}:9100" ;; esac fi diff --git a/openspec/changes/add-cups-printserver-container/design.md b/openspec/changes/add-cups-printserver-container/design.md index 6ce41f0..6828508 100644 --- a/openspec/changes/add-cups-printserver-container/design.md +++ b/openspec/changes/add-cups-printserver-container/design.md @@ -2,61 +2,104 @@ ## Context -See proposal.md for motivation - we need to create a Podman container that runs a CUPS print server for Brother HL-4150CDN. The repo contains the Brother driver packages in `assets/Brother HL-4150CDN/`: -- `lpr-driver/hl4150cdnlpr-1.1.1-5.i386.deb` -- `cupswrapper/hl4150cdncupswrapper-1.1.1-5.i386.deb` -- `installer/linux-brprinter-installer-2.2.6-0.gz` (reference only; we will NOT run it in the build - it is interactive and downloads from the network) +See proposal.md. We need a Podman container that runs CUPS for the Brother +HL-4150CDN network printer and shares it over IPP/AirPrint. -Key observations: -- Brother drivers are i386 binaries (32-bit ELF). Container needs 32-bit runtime libraries. -- CUPS wrapper + LPR packages provide PPD, filters, and helper binaries. -- AirPrint requires CUPS + Avahi/mDNS (DNS-SD). -- Brother's cups wrapper installs files to /usr/local/Brother/ and may need symlinks into CUPS directories. +Facts established while exploring the device at `192.168.100.7`: + +- The printer is a Brother HL-4150CDN (`Ver.1.22`) with PCL6 and **BR-Script3 + (PostScript 3)** emulation — `@PJL INFO CONFIG` lists `POSTSCRIPT`. +- Open services: `:9100` (raw/AppSocket), `:515` (LPR/LPD), `:631` (IPP/IPPS) + and `:80` (Brother web UI). It has one paper cassette plus the MP tray. +- A raw PostScript job sent to `:9100` is interpreted by the printer, and a full + CUPS job with `Duplex=DuplexNoTumble BRPrintQuality=Color` printed correctly + (duplex, colour). + +An earlier iteration used Brother's Linux packages. That vendor CUPS chain is +broken in `debian:bookworm-slim`: `filterhl4150cdn` needs `file` (and `a2ps` as +a fallback), which are absent, so the filter exits 1 and emits zero bytes. That +chain also rasterises locally (Ghostscript → `ppmraw` → `brhl4150cdnfilter`) and +stores per-queue options in a single global file (`brhl4150cdnrc`), which is +heavy and racy. The packages have since been removed from the repository. + +Crucially, the Brother **vendor** PPD (`brhl4150cdn.ppd`) cannot be used for +direct PostScript because its option values are blank (`" "`); all +option handling lives in the vendor filter. Brother's **BR-Script3 PPD**, in +contrast, encodes every option as PostScript `setpagedevice`. ## Goals / Non-Goals **Goals:** -- Create a Containerfile that builds a working image for Podman -- Install and configure Brother HL-4150CDN drivers -- Share printer via IPP with AirPrint discovery -- Expose ports 631 (CUPS/IPP) and 5353/udp (mDNS) -- Make it easy to run with podman +- Build a working CUPS image for Podman with no 32-bit dependencies. +- Drive the printer with its native PostScript (BR-Script3) interpreter. +- Preserve the hardware options the user cares about: paper tray, duplex and + colour/mono (plus the other options the PPD exposes). +- Share the queue over IPP and advertise it via AirPrint. +- Keep the printer address/name runtime-configurable. **Non-Goals:** -- Managing multiple printers -- Custom SSL/TLS configuration for CUPS (use defaults or documented) -- Persistent CUPS spool/state beyond basic setup +- Managing multiple printers. +- Sharing via SMB/Samba (Windows uses a client-side Brother driver). +- Persisting CUPS spool/state beyond the container lifecycle. +- TLS for the CUPS web interface. ## Decisions -### Base image -- Use Debian/Ubuntu (or similar) as base to handle .deb packages and 32-bit libs easily. Alternatively Alpine, but Alpine needs musl + 32-bit support can be more complex for prebuilt i386 ELF binaries from Brother. Debian stable is practical. +### Base image and packages +Use `debian:bookworm-slim` with `cups`, `cups-client`, `cups-filters`, +`avahi-daemon`, `avahi-utils`, `libnss-mdns`, `dbus`, `ghostscript` and +`poppler-utils`. No `dpkg --add-architecture i386` and no Brother `.deb` +packages: the job is converted to PostScript in CUPS and interpreted by the +printer, so no vendor binary is needed. -### Driver installation -- Use the local `.deb` packages from `assets/Brother HL-4150CDN/` (copied into the build context) and install with `dpkg -i --force-all`. No network download at build time for the driver itself. -- Do not run the Brother installer script (`linux-brprinter-installer-2.2.6-0.gz`) in the build - it is interactive and downloads packages from download.brother.com. -- Also need to install brcupsconfpt1 helper if needed - comes with cups wrapper. -- Run the cups wrapper install logic as per Brother installer (or manually create symlinks). +_Alternative considered:_ keep Brother's Linux CUPS wrapper + LPR filter. It +provides the same options but requires 32-bit libraries, Ghostscript +rasterisation and the missing `file`/`a2ps` tools, and it writes options to a +single global config file. Rejected. -### CUPS + Avahi + DBus -- Install cups, avahi-daemon, dbus. Run dbus and avahi-daemon; start cupsd. -- Configure CUPS to allow remote access/sharing. For containerized setup, typically need to listen on all interfaces and configure sharing. +### Print path: direct PostScript via the BR-Script3 PPD +Install Brother's official BR-Script3 PPD as +`/usr/share/cups/model/BR4150_2.PPD` (committed at +`assets/Brother HL-4150CDN/ppd/BR4150_2.PPD`, extracted from the vendor Windows +driver) and create the queue with it. CUPS's `pstops` filter reads the PPD and +injects the selected option code (e.g. `/BRFeeder`, `/Duplex`, +`/BRProcessColor`, `/MediaType`) into the job; the PPD's `*JCLBegin`, +`*JCLToPSInterpreter` and `*JCLEnd` entries wrap it in PJL. The result is +forwarded unchanged to the printer. -### Printer configuration -- Add printer via lpadmin using the Brother PPD. Point to network URI (e.g., ipp://PRINTER_IP/ipp or socket://PRINTER_IP:9100 - depends on printer; HL-4150CDN is network printer). -- Enable printer sharing and default options. +A stock PostScript PPD (like CUPS's own `generic.ppd`) has no `*cupsFilter` +either, and CUPS accepts such PPDs — the printer's native format is +`application/vnd.cups-postscript`. + +### Device URI +Use raw AppSocket as the default: `socket://:9100`. `lpd:///binary_p1` +and `ipp://…` remain available through `PRINTER_PROTOCOL`/`PRINTER_URI`. + +### Client support +- macOS / iOS / iPadOS: AirPrint → CUPS. The queue advertises `image/urf`, + `image/pwg-raster`, `application/pdf` and `application/postscript`, with + `urf-supported = V1.4,CP1,W8,PQ4,SRGB24,RS600,DM1,FN3`, `sides-supported` + (one-sided/two-sided long/short edge) and `color-supported = true`. +- Linux: IPP / IPP Everywhere, or plain IPP with PDF/PostScript. +- Windows: client-side Brother BR-Script3 Windows driver, pointed at the + printer directly or at the shared queue. ### Runtime configuration -- The printer endpoint and advertised name are supplied through environment variables rather than baked into the image, so the same image can be pointed at any HL-4150CDN: - - `PRINTER_IP` + `PRINTER_PROTOCOL` (lpd/socket/ipp) derive the device URI. - - `PRINTER_URI` overrides the derived URI for full control. - - `PRINTER_NAME` sets the CUPS queue name and the DNS-SD/AirPrint advertised name. - - `PRINTER_DESCRIPTION` sets the CUPS Description (Info) shown in the web UI. -- The entrypoint builds the URI and runs lpadmin at startup, then shares the queue. +The printer endpoint and advertised name come from environment variables +(`PRINTER_IP`, `PRINTER_PROTOCOL`, `PRINTER_URI`, `PRINTER_NAME`, +`PRINTER_DESCRIPTION`, `PPD_FILE`) rather than being baked into the image. The +entrypoint derives the URI and runs `lpadmin` on startup, then shares the queue. ## Risks / Trade-offs -- [Risk] Brother binaries are 32-bit only → require multiarch libs (libc6:i386, etc.). Increases image size slightly. -- [Risk] Using .deb packages assumes glibc-based base. If we choose different base, need to ensure compatibility. -- [Risk] Avahi in container needs --net=host or proper mDNS setup. AirPrint discovery typically works better with host network mode; document this. -- [Risk] CUPS state (printers.conf, etc.) should be persisted if we want config to survive restarts - may need volumes. +- [Risk] The AirPrint path (URF → PDF → PostScript) relies on cups-filters' + `rastertopdf`; it was not exercised with a real URF job during exploration. + macOS/iOS printing should be verified end to end. +- [Risk] Colour handling now relies on the printer's PostScript RIP rather than + Brother's host-side colour tables; output may differ slightly from the vendor + raster driver. +- [Risk] The committed PPD is a Windows PPD and contains no CUPS-specific keys. + CUPS accepts it as a PostScript PPD (verified for this CUPS version), but this + should be re-checked on CUPS upgrades. +- [Risk] Avahi needs host networking (or equivalent) to advertise mDNS. +- [Risk] CUPS state is not persisted; the queue is recreated on every start. diff --git a/openspec/changes/add-cups-printserver-container/proposal.md b/openspec/changes/add-cups-printserver-container/proposal.md index 8a98f59..293c37d 100644 --- a/openspec/changes/add-cups-printserver-container/proposal.md +++ b/openspec/changes/add-cups-printserver-container/proposal.md @@ -2,28 +2,56 @@ ## Why -Provide a Podman container that runs CUPS and shares the Brother HL-4150CDN network printer as an AirPrint and IPP printer. Clients should be able to print without installing local printer drivers. +Run a Podman container that shares the Brother HL-4150CDN network printer over +IPP/AirPrint, so clients on macOS, iOS/iPadOS, Linux and Windows can print. + +The HL-4150CDN already emulates BR-Script3 (PostScript 3) and accepts PostScript +on its AppSocket/LPD ports, so the container can forward PostScript directly to +the printer instead of shipping Brother's 32-bit Linux CUPS wrapper plus +Ghostscript rasterisation. This removes the i386 dependency, the multiarch +runtime and the fragile vendor filter chain. + +The previous approach (Brother Linux `.deb` packages) turned out not to work in +`debian:bookworm-slim`: the vendor filter `filterhl4150cdn` needs `file` (with +`a2ps` as a fallback), which are not installed, so it exits with an error and +produces no output. The official BR-Script3 PPD avoids that entire code path. ## What Changes -- Create a Containerfile for Podman (based on a Linux distro that supports CUPS and 32-bit binaries) -- Extract and install Brother driver files (from `assets/Brother HL-4150CDN/` - local .deb packages: `hl4150cdnlpr` and `hl4150cdncupswrapper`) needed for HL-4150CDN -- Configure CUPS to run in the container and share the network printer -- Enable mDNS/Avahi for AirPrint discovery -- Provide minimal runtime configuration (ports 631, 5353/udp) and documentation for running with podman -- Handle 32-bit dependencies required by Brother i386 binaries +- Create a Containerfile for Podman based on Debian stable running CUPS, Avahi + and D-Bus (no i386 multiarch). +- Install Brother's official **BR-Script3 PPD** (committed under + `assets/Brother HL-4150CDN/ppd/`) so CUPS emits device options — paper source + (tray), duplex and colour/mono, plus media type, print quality, toner save and + secure print — as PostScript `setpagedevice`. +- Point the queue at the printer with a raw AppSocket URI (`socket://:9100`) + by default. +- Convert incoming client formats (URF/Apple Raster, PWG Raster, PDF, PostScript) + to PostScript inside CUPS and pass them through to the printer's PostScript + interpreter. +- Share the queue over IPP and advertise it via mDNS/DNS-SD (Avahi/AirPrint). +- Keep runtime configuration (printer address, protocol, queue name) in + environment variables. +- Document the supported client matrix. ## Capabilities ### New Capabilities -- `printserver/cups`: Containerized CUPS print server with AirPrint/IPP sharing and Brother HL-4150CDN driver support +- `printserver/cups`: Containerised CUPS print server which shares the Brother + HL-4150CDN over IPP/AirPrint and prints by sending PostScript (BR-Script3) + directly to the printer. ### Modified Capabilities None ## Impact -- New Containerfile (and possibly supporting files) in repository root -- Uses Brother driver artifacts in `assets/Brother HL-4150CDN/` -- Runtime ports: CUPS (631), mDNS (5353/udp) for AirPrint -- Depends on Avahi/DBus, CUPS, and 32-bit compatibility libs for Brother binaries +- Containerfile plus supporting `cupsd.conf` and `entrypoint.sh` in the + repository root. +- Uses `assets/Brother HL-4150CDN/ppd/BR4150_2.PPD`. +- Runtime ports: CUPS/IPP (631/tcp), mDNS/DNS-SD (5353/udp). +- No 32-bit runtime libraries; the image no longer depends on the host CPU + architecture. +- Windows clients install Brother's own BR-Script3 Windows driver client-side. +- The unused Brother Linux `.deb` packages previously under + `assets/Brother HL-4150CDN/` have been removed. diff --git a/openspec/changes/add-cups-printserver-container/specs/printserver/cups/spec.md b/openspec/changes/add-cups-printserver-container/specs/printserver/cups/spec.md index 4be9d97..06c61b2 100644 --- a/openspec/changes/add-cups-printserver-container/specs/printserver/cups/spec.md +++ b/openspec/changes/add-cups-printserver-container/specs/printserver/cups/spec.md @@ -2,34 +2,69 @@ ## Purpose -Provide a containerized CUPS print server that shares the Brother HL-4150CDN network printer as an AirPrint and IPP printer so clients can print without installing local printer drivers. +Provide a containerized CUPS print server that shares the Brother HL-4150CDN +network printer as an AirPrint/IPP printer and prints by sending PostScript +directly to the printer, so clients can print without a server-side vendor +driver. ## ADDED Requirements ### Requirement: Container image builds with required dependencies -The container SHALL include CUPS, Avahi/mDNS, DBus, and required 32-bit runtime libraries to run Brother HL-4150CDN i386 binaries. +The container SHALL include CUPS, cups-filters, Avahi/mDNS, D-Bus and the +conversion tools needed to turn client documents into PostScript, and SHALL NOT +require any 32-bit runtime libraries. #### Scenario: Image contains CUPS - **WHEN** the image is built - **THEN** CUPS is installed and available at /usr/sbin/cupsd -#### Scenario: Image supports 32-bit Brother binaries +#### Scenario: No 32-bit runtime required - **WHEN** the image is built -- **THEN** required 32-bit libraries are present for the Brother i386 filters +- **THEN** the image contains no i386 multiarch libraries and no Brother vendor binaries -### Requirement: Brother HL-4150CDN driver is installed in container -The container SHALL install the Brother HL-4150CDN CUPS wrapper, PPD, LPR/filter components from the provided driver artifacts. +### Requirement: Brother BR-Script3 PPD is installed +The container SHALL install Brother's official HL-4150CDN BR-Script3 PPD from +the committed asset so CUPS can emit device options as PostScript. -#### Scenario: Driver files present -- **WHEN** the container image is built -- **THEN** the HL-4150CDN PPD and Brother filters are installed in standard system locations +#### Scenario: PPD present +- **WHEN** the image is built +- **THEN** the BR-Script3 PPD is present in the CUPS model directory -#### Scenario: CUPS recognizes HL-4150CDN -- **WHEN** CUPS starts with the driver installed -- **THEN** CUPS is able to use the HL-4150CDN PPD +#### Scenario: CUPS recognises the model +- **WHEN** the queue is created with the PPD +- **THEN** CUPS reports the printer make and model as "Brother HL-4150CDN BR-Script3" + +### Requirement: Hardware options are sent to the printer +The system SHALL translate the PPD's device options into PostScript +`setpagedevice` instructions so the printer's own hardware options are used. + +#### Scenario: Paper source +- **WHEN** a job selects an input slot (auto select, a tray or the MP tray) +- **THEN** the emitted PostScript selects the corresponding paper source on the printer + +#### Scenario: Duplex +- **WHEN** a job selects long-edge or short-edge two-sided printing +- **THEN** the emitted PostScript sets `/Duplex` and `/Tumble` accordingly + +#### Scenario: Colour or mono +- **WHEN** a job selects colour or mono +- **THEN** the emitted PostScript sets the corresponding colour/mono device parameters + +### Requirement: Jobs are printed via direct PostScript +The container SHALL forward the converted job as PostScript to the printer +without a server-side vendor filter or host-side rasterisation. + +#### Scenario: Default device URI +- **WHEN** the container is started with a printer IP and no explicit URI +- **THEN** the queue is created with a raw AppSocket URI (`socket://:9100`) + +#### Scenario: A job is printed +- **WHEN** a print job is submitted to the queue +- **THEN** the job is delivered to the printer as PostScript and printed by the printer's PostScript interpreter ### Requirement: Printer is shared and discoverable -The system SHALL share the Brother HL-4150CDN printer over IPP and make it discoverable via AirPrint (mDNS/DNS-SD) on the container network. +The system SHALL share the queue over IPP and advertise it via AirPrint +(mDNS/DNS-SD). #### Scenario: IPP printer is shared - **WHEN** CUPS runs in the container @@ -37,7 +72,22 @@ The system SHALL share the Brother HL-4150CDN printer over IPP and make it disco #### Scenario: AirPrint discovery - **WHEN** Avahi runs alongside CUPS -- **THEN** the printer is advertised via AirPrint so macOS/iOS clients can discover it without driver installation +- **THEN** the printer is advertised via AirPrint with URF, sides and colour capabilities so macOS and iOS/iPadOS clients can discover it without a driver + +### Requirement: Supported client platforms +The shared printer SHALL be usable from macOS, iOS/iPadOS, Linux and Windows. + +#### Scenario: macOS and iOS/iPadOS +- **WHEN** a macOS or iOS/iPadOS client prints via AirPrint +- **THEN** the job is converted to PostScript and printed + +#### Scenario: Linux +- **WHEN** a Linux client prints over IPP +- **THEN** the job is converted to PostScript and printed + +#### Scenario: Windows +- **WHEN** a Windows client prints +- **THEN** it uses a client-side BR-Script3 driver against the printer or the shared queue ### Requirement: Runtime configuration is exposed The container SHALL expose CUPS on port 631 and mDNS on UDP port 5353. @@ -50,13 +100,6 @@ The container SHALL expose CUPS on port 631 and mDNS on UDP port 5353. - **WHEN** the container runs - **THEN** UDP port 5353 is available for AirPrint discovery -### Requirement: No client drivers required -The shared printer SHALL be exposed in a way that client devices can print without installing vendor printer drivers. - -#### Scenario: AirPrint compatibility -- **WHEN** a client discovers the printer via AirPrint -- **THEN** the client uses native IPP/AirPrint drivers - ### Requirement: Printer endpoint and advertised name are configurable at runtime The container SHALL accept the network printer address and the advertised printer name through environment variables so the same image can target a different printer or name without rebuilding. @@ -66,7 +109,7 @@ The container SHALL accept the network printer address and the advertised printe #### Scenario: Printer protocol is selected - **WHEN** the container is started with `PRINTER_IP` and `PRINTER_PROTOCOL` -- **THEN** the device URI uses that protocol (lpd, socket or ipp) +- **THEN** the device URI uses that protocol (socket, lpd or ipp), defaulting to socket #### Scenario: Full URI override - **WHEN** `PRINTER_URI` is set diff --git a/openspec/changes/add-cups-printserver-container/tasks.md b/openspec/changes/add-cups-printserver-container/tasks.md index a7314f4..147e23d 100644 --- a/openspec/changes/add-cups-printserver-container/tasks.md +++ b/openspec/changes/add-cups-printserver-container/tasks.md @@ -3,21 +3,31 @@ ## 1. Repository initialization - [x] 1.1 Initialize jujutsu repository with `jj git init` in repo root and verify `.jj` directory exists -## 2. Container build setup -- [x] 2.1 Create Containerfile in repository root based on Debian/Ubuntu base image with multiarch support for i386 -- [x] 2.2 Install required system packages: cups, avahi-daemon, dbus, wget/curl, and 32-bit runtime libraries (libc6:i386, libstdc++6:i386, etc.) -- [x] 2.3 Copy the local Brother driver packages (`assets/Brother HL-4150CDN/lpr-driver/hl4150cdnlpr-1.1.1-5.i386.deb` and `assets/Brother HL-4150CDN/cupswrapper/hl4150cdncupswrapper-1.1.1-5.i386.deb`) into the build context and install them in the container with `dpkg -i --force-all` (verify install reports success) -- [x] 2.4 Configure CUPS and Brother drivers (symlink PPD/filter files as needed, set up cupsd.conf for container use) and verify files are in correct locations -- [x] 2.5 Set up startup script to run dbus, avahi-daemon, and cupsd in the container -- [x] 2.6 Expose ports 631 (CUPS/IPP) and 5353/udp (mDNS) in Containerfile and verify ports are declared +## 2. BR-Script3 PostScript PPD asset +- [x] 2.1 Extract Brother's official HL-4150CDN BR-Script3 PPD (`eng/BR4150_2.PPD`) from the vendor Windows driver archive and commit it under `assets/Brother HL-4150CDN/ppd/BR4150_2.PPD` +- [x] 2.2 Document the provenance and the option→`setpagedevice` mapping in `assets/Brother HL-4150CDN/ppd/README.md` -## 3. Printer configuration -- [x] 3.1 Add logic/config to create and share the Brother HL-4150CDN printer (using lpadmin) with appropriate network URI and PPD; ensure printer is shared for network clients -- [x] 3.2 Configure Avahi/CUPS for AirPrint advertisement and verify printer will be discoverable -- [x] 3.3 Add environment-based configuration for the printer address and advertised name (`PRINTER_IP`, `PRINTER_PROTOCOL`, `PRINTER_NAME`, `PRINTER_DESCRIPTION`; `PRINTER_URI` overrides) +## 3. Container build +- [x] 3.1 Create Containerfile based on Debian stable with CUPS, cups-filters, Avahi, D-Bus, Ghostscript and Poppler (no i386 multiarch) +- [x] 3.2 Install the BR-Script3 PPD into the CUPS model directory in the Containerfile +- [x] 3.3 Remove the Brother vendor `.deb` installation, i386 multiarch and the 32-bit loader setup from the Containerfile +- [x] 3.4 Keep cupsd.conf (listen on all interfaces, share printers, DNS-SD) and the required runtime directories +- [x] 3.5 Set up the startup script to run dbus, avahi-daemon and cupsd, and create the queue +- [x] 3.6 Expose ports 631/tcp and 5353/udp in the Containerfile -## 4. Documentation and testing -- [x] 4.1 Create or update README with instructions for building and running the container with podman (including host network mode note for mDNS, volume mounts if needed) -- [x] 4.2 Test container build with podman build and verify image builds successfully -- [x] 4.3 Test container run and verify CUPS is accessible and printer is shared/discoverable (manual verification) -- [x] 4.4 Verify `podman run` with `PRINTER_IP` and a custom `PRINTER_NAME` creates the queue at the derived URI and advertises that name +## 4. Printer configuration +- [x] 4.1 Create the queue with the BR-Script3 PPD and a raw AppSocket URI by default (`socket://:9100`) +- [x] 4.2 Keep environment-based configuration (`PRINTER_IP`, `PRINTER_PROTOCOL`, `PRINTER_URI`, `PRINTER_NAME`, `PRINTER_DESCRIPTION`, `PPD_FILE`) +- [x] 4.3 Share the queue and set it as default; verify AirPrint capabilities (URF, sides, colour) + +## 5. Documentation and testing +- [x] 5.1 Update README for the PostScript/BR-Script3 approach and the client matrix (macOS, iOS/iPadOS, Linux, Windows) +- [x] 5.2 Build the image with podman and verify it builds +- [x] 5.3 Run the container and verify CUPS is reachable and the queue is shared/discoverable +- [x] 5.4 Submit a job (colour + duplex) and verify it prints correctly +- [x] 5.5 Verify `PRINTER_IP`/`PRINTER_PROTOCOL`/`PRINTER_NAME` derive the expected queue URI and advertised name + +## 6. Cleanup +- [x] 6.1 Add `.gitignore` for macOS metadata and stop tracking existing `.DS_Store` files +- [x] 6.2 Remove the unused Brother Linux vendor packages (`lpr-driver/`, `cupswrapper/`, `installer/`) from the repository +- [x] 6.3 Prune the obsolete `printserver:amd64` image, the test containers and scratch files