cpus打印服务
http://ip:631/
一键网络打工单
网络打印机
lpd协议 515 TCP 端口 打印机 konica bizhub C368
打印机地址: lpd://192.168.20.200/queue
同时上传驱动: KOC759SCX.ppd
ipp协议打印
-
创建打印队列 create-job 0x0006 #包含打印参数:纸张大小和文件名
-
发送打印文件 send-Document 0x0007 #包含文件名和内容
-
单次打印 Print-Job 0x0002 (常用)
查询纸张支持media-supported
cups
- 使用PageSize支持纸张
- 设置纸张宽高单位往往是 点(points)
papercut
-
实际打印为 PostScript(PS)文件 (建议pdf)
-
使用media-size中y-dimension与x-dimension 支持纸张 ??
-
纸张宽高单位微米
命令行打印
需要打印机支持文本打印
ipp打印
echo "100200300400500hello110220330440550" | lp -d Xprinter_XP-420B #打印到打印机
echo "100200300400500hello110220330440550" | lp -h 192.168.12.105:631 -d Xerox_Xprinter_XP-420B -o PageSize=Custom.120x170
echo "100200300400500hello110220330440550" | lp -h 192.168.12.105:631 -d Xerox_Xprinter_XP-420B #打印机内容
lp -h 192.168.12.105:631 -d Xerox_Xprinter_XP-420B -o PageSize=Custom.120x170 ~/d.pdf
lp -h 192.168.12.98:9163 -d "Xprinter XP-420B" ~/d.pdf
lp -d ipp://192.168.12.98:9163/printers/Xprinter%20XP-420B ~/d.pdf #????
lp -d Xprinter_XP-420B /mnt/d.pdf
lp -d EPSON_LQ-630KII document.pdf #打印文件
#远程打印
lp -h 192.168.12.105:631 -d Xerox_Xprinter_XP-420B ./d.pdf
lpq -h 192.168.12.105:631 -P Xerox_Xprinter_XP-420B #获取打印队列
lpq -h 192.168.12.98:9163 -P "Xprinter XP-420B" #支持
lp -d Xerox_Xprinter_XP-420B -o PageSize=Custom.120x160 /mnt/d.pdf #实际纸张40*60 小的在前面
ipp管理
lpstat -a #发现网络 打印机列表 ??
lpoptions -h 192.168.12.105:631 -d Xerox_Xprinter_XP-420B -l
lpoptions -h 192.168.12.98:9163 -d "Xprinter XP-420B" -l #不支持
lpstat -h 192.168.12.105:631 -a
lpstat -h 192.168.12.98:9163 -a #不支持 papercut
内容管理
pdfinfo d.pdf | grep "Page" #检查pdf文件逻辑大小
pdfinfo output.pdf | grep "Page" #检查pdf文件逻辑大小
ps2pdf file.ps temp.pdf #文件转换
裁剪pdf
gs -o out_40x60.pdf \
-sDEVICE=pdfwrite \
-dFIXEDMEDIA \
-dPDFFitPage \
-dDEVICEWIDTHPOINTS=113.39 \
-dDEVICEHEIGHTPOINTS=170.08 \
d.pdf
查看打印机
解压deb文件可以得到ppd驱动 例如
lpinfo -m #(已经支持的驱动)
lpinfo -v #(插入后,看到打印机)
lpstat -p -d #(安装完成后)
lpoptions -d XP-420B #打印机选项 -设置纸张自定义 -o PageSize=Custom.120x160
cupsenable XP-420B #启用打印机
cupsaccept XP-420B
lpstat -l -o #打印队列明细
ipp内容
version: 2.0
operation-id: Print-Job 0x0002
name: attributes-charset
charset value: 'utf-8'
name: attributes-natural-language
naturalLanguage value: 'zh-cn'
name: PageSize
nameWithoutLanguage value: 'Custom.120x170'
name: print-color-mode
keyword value: 'monochrome'
name: requesting-user-name
nameWithoutLanguage value: 'jack'
name: printer-uri
uri value: 'ipp://192.168.12.105:631/printers/Xerox_Xprinter_XP-420B'
环境管理
apt install lpr cups-client foomatic-db
dpkg-query -W libcups2
docker-compose -f /vol1/1000/cups/docker-compose.yml up -d
docker exec -ti ydkn-cups bash #最好自己构建
过滤器错误
XP-420B Idle - "Rendering completed" #打印机状态
stopped "Filter failed" #打印队列状态
依赖安装
apt install cups-ipp-utils
apt install libcupsimage2 # libcupsimage2t64 代替 libcupsimage2
apt install libcups2 cups-filters ghostscript libcupsimage2t64
apt remove printer-driver-xprinter #重装
安装驱动
dpkg --force-all -i /mnt/epsidm24-secc0014_1_2_4-1_amd64.deb
lpinfo -m | grep -i 630k
direct usb://EPSON/LQ-630KII?serial=52323130315E4A0505
Epson/eplq630kii-Epson_epsidm24-secc0014.ppd Epson LQ-630KII ESC/P2 secc0014, 1.2
安装驱动
lpadmin -p Xprinter_XP-420B -E -v usb://Xprinter/XP-420B?serial=S420BUH25A160664 -P /usr/share/cups/model/xprinter/XP-420B.ppd
构建
vi Dockerfile
# 基础镜像使用 Debian 12 (Bookworm),以确保使用最新的稳定版本和库
FROM debian:12-slim
# 维护者信息
LABEL maintainer="Your Name <your.email@example.com>"
# 设置环境变量,用于 CUPS 配置(与您的 compose 文件一致)
ENV CUPSADMIN=admin \
CUPSPASSWORD=admin \
TZ=Asia/Shanghai \
# 强制 CUPS 在前台运行,这是 Docker 容器的标准做法
CUPSD_FOREGROUND=yes \
# 设置默认配置目录
CUPS_SERVER_ROOT=/etc/cups \
# 设置 CUPS 状态文件目录
CUPS_STATE_DIR=/var/run/cups
# 刷新包列表并安装 CUPS 及其相关组件、系统工具
# - cups: CUPS 服务本身
# - cups-client: 客户端工具
# - cups-bsd: 兼容 BSD 的打印命令 (lpr)
# - printer-driver-all: 尽可能多的打印机驱动
# - ghostscript: 打印处理依赖
# - sudo: 用于创建 CUPS 用户的可能需求 (虽然在 Dockerfile 中不常用)
# - locales/tzdata: 用于时区和语言环境设置
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
cups libcupsimage2 libcupsimage2 \
cups-client \
cups-bsd \
printer-driver-all \
ghostscript \
sudo \
locales \
tzdata \
# 用于 USB 设备和 DBUS 依赖 (如果需要)
dbus \
udev \
# 清理 APT 缓存,减小镜像大小
&& rm -rf /var/lib/apt/lists/*
# 配置时区
RUN echo "${TZ}" > /etc/timezone && \
dpkg-reconfigure --frontend noninteractive tzdata
# 为 CUPS 创建必要的目录和权限
# /var/run/cups 和 /var/cache/cups 可能是动态的,但确保它们存在且权限正确
RUN mkdir -p /var/run/cups /var/cache/cups /var/log/cups /etc/cups/ppd /usr/lib/cups/backend && \
chmod -R 755 /var/run/cups /var/cache/cups /var/log/cups /etc/cups/ppd && \
# CUPS 默认运行的用户和组通常是 'lp' 和 'sys'
chown -R root:lp /var/run/cups /var/cache/cups /var/log/cups /etc/cups/ppd && \
# 确保 /etc/cups/cupsd.conf 的权限正确
chmod 644 /etc/cups/cupsd.conf
# 准备 CUPS 初始配置脚本
COPY setup_cups.sh /usr/local/bin/setup_cups.sh
RUN chmod +x /usr/local/bin/setup_cups.sh
# 暴露 CUPS 默认端口
EXPOSE 631
# 容器启动时运行的命令
# 1. 运行配置脚本,设置管理用户并配置 cupsd.conf
# 2. 以调试/前台模式启动 CUPS 守护进程,这是 Docker 容器保持运行的标准做法
CMD ["/bin/bash", "-c", "/usr/local/bin/setup_cups.sh && /usr/sbin/cupsd -f"]
# 在构建的同一目录下,您还需要创建配置脚本 setup_cups.sh
docker build -t my-cups .
启动配置sh
vi setup_cups.sh
#!/bin/bash
# 设置 CUPS 配置文件路径
CUPS_CONF="/etc/cups/cupsd.conf"
echo "### Starting CUPS initial configuration..."
# 检查 CUPS 管理员用户是否存在,如果不存在则创建
if ! id -u "$CUPSADMIN" >/dev/null 2>&1; then
echo "Creating CUPS admin user: $CUPSADMIN"
# 创建用户并设置密码
# -M: 不创建家目录
# -s /bin/false: 设置一个不可登录的shell
# -G lpadmin: 将用户添加到lpadmin组,使其成为CUPS管理员
useradd -r -M -s /bin/false -G lpadmin "$CUPSADMIN"
# 使用环境变量设置密码
echo "$CUPSADMIN:$CUPSPASSWORD" | chpasswd
echo "CUPS admin user created successfully."
else
echo "CUPS admin user $CUPSADMIN already exists."
fi
# 确保 CUPS 服务在前台运行的配置是启用状态
if [ "$CUPSD_FOREGROUND" = "yes" ]; then
echo "Setting up CUPS to listen on all interfaces (0.0.0.0:631) and enable web interface."
# 允许监听所有网络接口 (0.0.0.0:631)
sed -i 's/Listen localhost:631/Listen 0.0.0.0:631/' $CUPS_CONF
# 开放 web 界面访问
# 允许来自所有主机的远程管理(需要谨慎)
sed -i '/<Location \/>/,/<\/Location>/ s/Order deny,allow/Allow All\nOrder allow,deny/' $CUPS_CONF
sed -i '/<Location \/admin>/,/<\/Location>/ s/Order deny,allow/Allow All\nOrder allow,deny/' $CUPS_CONF
sed -i '/<Location \/admin>/,/<\/Location>/ s/Require user @SYSTEM/AuthType Basic\nRequire user @SYSTEM/' $CUPS_CONF
sed -i '/<Location \/admin\/conf>/,/<\/Location>/ s/Require user @SYSTEM/AuthType Basic\nRequire user @SYSTEM/' $CUPS_CONF
# 启用远程连接
if ! grep -q "ServerName" $CUPS_CONF; then
echo "ServerName 0.0.0.0" >> $CUPS_CONF
fi
echo "Restarting D-Bus service if running in host mode to ensure device access..."
# 尽管 network_mode: host 应该处理大部分 D-Bus,但我们尝试在容器内启动 D-Bus(如果需要)
# 在许多情况下,在 Docker 容器中正确运行 DBUS 是复杂的,但这一步是为了提高兼容性。
if [ -e /var/run/dbus/system_bus_socket ]; then
echo "DBus socket found, D-Bus may be functional or mounted from host."
else
# 尝试启动一个简单的 DBUS 会话,以满足 CUPS 的一些依赖
dbus-daemon --system --fork --nopidfile
fi
fi
echo "### CUPS configuration complete. Starting cupsd in foreground..."
# 检查配置文件是否已修改
grep "Listen 0.0.0.0:631" $CUPS_CONF || echo "Warning: Configuration change might not be applied correctly."
# 将控制权交回给 CMD 以启动 CUPS
exec "$@"
启动yaml
olbat/cupsd
services:
cups:
# 使用您刚才构建的镜像名称和标签
image: my-cups
container_name: ydkn-cups
# 必须使用 host 网络模式,才能正确与主机上的打印机和 USB/网络进行通信
network_mode: host
privileged: true # 推荐添加 privileged 权限以确保对 /dev/bus/usb 的完全访问
user: "0"
volumes:
# /var/run/dbus 用于系统服务间通信(如 udev/设备发现)
- /var/run/dbus:/var/run/dbus:ro
# 您的自定义挂载点
- /vol1/1000/cups/vmnt:/mnt
- /vol1/1000/cups/conf:/etc/cups
devices:
# 必须挂载整个 USB 总线以进行 USB 打印机发现
- /dev/bus/usb:/dev/bus/usb
environment:
# 这些变量将由 setup_cups.sh 脚本使用
- CUPSADMIN=admin
- CUPSPASSWORD=admin
- TZ=Asia/Shanghai
restart: always
测试访问
curl -k -u admin:admin https://localhost:631/ #忽略证书验证访问cups
docker-compose -f /vol1/1000/cups/docker-compose.yml up -d
cupsd配置
vim /etc/cups/cupsd.conf
添加
ServerName cups-host
Encryption Never
Encryption IfRequested
Listen 0.0.0.0:631
<Location /admin>
Encryption Never
Allow All
</Location>
https://192.168.12.105:631/admin
快速换镜像
apt install apt-transport-https ca-certificates curl
cat <<EOF > /etc/apt/sources.list
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-updates main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm-backports main contrib non-free non-free-firmware
EOF
cat <<EOF > /etc/apt/sources.list
deb http://deb.debian.org/debian bookworm main contrib non-free
deb http://deb.debian.org/debian bookworm-updates main contrib non-free
deb http://security.debian.org/debian-security bookworm-security main contrib non-free
EOF
抓包分析
用户获取不同打印机: 纸张大小参数
转发设置
netsh interface portproxy add v4tov4 listenport=631 listenaddress=0.0.0.0 connectport=631 connectaddress=192.168.12.105 protocol=tcp
包过滤条件
Wireshark
( ip.src == 192.168.12.98 and ip.dst == 192.168.12.105 ) && (ipp)
lpoptions -h 192.168.12.105:631 -p Xerox_Xprinter_XP-420B -l|grep -i page
lpoptions -h 192.168.12.98:9163 -p "Xprinter XP-420B" -l|grep -i page
一键打印
def 一键打印(data,pageSize):
req = b""
# IPP Header (version 2.0, Print-Job 0x0002, request-id 1)
req += struct.pack("!BBH", 0x02, 0x00, 0x0002)
req += ipp_int32(1)
# Operation Attributes
req += ipp_tag(0x01)
# attributes-charset
req += ipp_tag(0x47) + ipp_name("attributes-charset") + ipp_text("utf-8")
# attributes-natural-language
req += ipp_tag(0x48) + ipp_name("attributes-natural-language") + ipp_text("zh-cn")
# printer-uri
req += ipp_tag(0x45) + ipp_name("printer-uri") + ipp_text(
f"http://{cups_addr}/printers/{printer_name}"
)
# requesting-user-name
req += ipp_tag(0x42) + ipp_name("requesting-user-name") + ipp_text("jack")
# 纸张大小 PageSize
# size = "CustomSize4"
# size = "Custom.40x60"
# size = "Custom.113x170"
# size = "Custom.120x170"
# size = "Custom.120x170"
#size = "Custom.133x170"
#size = "Custom-10x15cm"
req += ipp_tag(0x44) + ipp_name("PageSize") + ipp_text(pageSize) #cups纸张参数
#req += ipp_tag(0x44) + ipp_name("mediaSizes") + ipp_text(size) #papercut纸张参数
#req += ipp_tag(0x44) + ipp_name("media") + ipp_text(size) #papercut纸张参数 media: "Custom-10x15cm"
# document-format = application/pdf (keyword tag = 0x44)
req += ipp_tag(0x44) + ipp_name("document-format") + ipp_text("application/pdf")
# End of attributes
req += ipp_tag(0x03)
#req += document_data
#req += pdf_res.content #document_data
req += data
# --------------------------
# Send IPP request
# --------------------------
url = f"http://{cups_addr}/printers/{ parse.quote(printer_name, safe=':/?&=') }"
headers = {
"Content-Type": "application/ipp"
}
response = requests.post(url, headers=headers, data=req)
return response
#----
with open(r"c:\downloaded_file.pdf", "rb") as f:
document_data = f.read()
response = 一键打印(pdf_res.content,"Custom.120x170")
print("HTTP Status:", response.status_code,int.from_bytes(response.content[2:4]))
pdf模板
基于模板的替换 生成新pdf
def 查找替换pdf( page ,kvls,font_size=10,font_path='C:\Windows\Fonts\simhei.ttf'):
""" pdf某一页,[["<<占位符>>","新内容"],] """
xp,yp = page.rect[2],page.rect[3]
add_buffer = BytesIO()
c = canvas.Canvas(add_buffer, pagesize=(xp,yp ))
pdfmetrics.registerFont(TTFont('ChineseFont', font_path))
c.setFont("ChineseFont", font_size)
for rp in kvls:
old_text = rp[0]
new_text = rp[1]
text_instances = page.search_for(old_text)
drawx = text_instances[0][0] #取得 第一个的x坐标
drawy = text_instances[-1][1] #最后一个 y坐标
#绘制
c.drawString(drawx, yp - drawy - font_size, new_text)# y轴起点不一样可能是反的 (简单减去字体大小)
# 替换找到的文本
#擦除
for inst in text_instances:
# 在原始文本位置添加一个白色矩形覆盖原文本
page.add_redact_annot(inst, fill=(1, 1, 1)) # 白色填充
c.showPage()
c.save()
# 合并
add_buffer.seek(0)
add_doc = fitz.open(stream=add_buffer, filetype="pdf")
page.apply_redactions()
page.show_pdf_page(page.rect, add_doc, 0) #合并第一页
return add_buffer #新pdf
watermark_buffer = 查找替换pdf(page,[["123456",'新文本123'],["__二维码__","请扫码"]])