# 11.5.LPD（行式打印机程序）

打印文件到后台的过程称为 *排队打印*（spooling）。排队打印允许用户继续使用计算机上的其他程序，而无需等待打印机缓慢完成打印任务。

FreeBSD 包含一个排队打印程序，叫做 [lpd(8)](https://man.freebsd.org/cgi/man.cgi?query=lpd\&sektion=8\&format=html)。打印任务是通过 [lpr(1)](https://man.freebsd.org/cgi/man.cgi?query=lpr\&sektion=1\&format=html) 提交的。

## 11.5.1. 初始设置

创建一个存储打印任务的目录，设置目录的所有权，并设置权限以防止其他用户查看这些文件的内容：

```sh
# mkdir -p /var/spool/lpd/lp
# chown daemon:daemon /var/spool/lpd/lp
# chmod 770 /var/spool/lpd/lp
```

打印机在 /etc/printcap 中定义。每个打印机的条目包括名称、连接的端口以及其他各种设置。创建 /etc/printcap 文件并添加以下内容：

```ini
lp:\				①	
	:lp=/dev/unlpt0:\	②
	:sh:\			③
	:mx#0:\			④
	:sd=/var/spool/lpd/lp:\	⑤
	:lf=/var/log/lpd-errs:	⑥
```

* ① 这是打印机的名称。[lpr(1)](https://man.freebsd.org/cgi/man.cgi?query=lpr\&sektion=1\&format=html) 将打印任务发送到 `lp` 打印机，除非使用 `-P` 指定了其他打印机，因此默认打印机应命名为 `lp`。
* ② 打印机连接的设备。将此行替换为适合所示连接类型的相应行。
* ③ 在打印任务开始时抑制打印页头。
* ④ 不限制打印任务的最大大小。
* ⑤ 此打印机的排队目录路径。每个打印机使用自己的排队目录。
* ⑥ 报告此打印机错误的日志文件路径。

创建 /etc/printcap 后，使用 [chkprintcap(8)](https://man.freebsd.org/cgi/man.cgi?query=chkprintcap\&sektion=8\&format=html) 测试是否存在错误：

```sh
# chkprintcap
```

在继续之前，修复任何报告的问题。

在 /etc/rc.conf 中启用 [lpd(8)](https://man.freebsd.org/cgi/man.cgi?query=lpd\&sektion=8\&format=html)：

```sh
lpd_enable="YES"
```

启动服务：

```sh
# service lpd start
```

## 11.5.2. 使用 [lpr(1)](https://man.freebsd.org/cgi/man.cgi?query=lpr\&sektion=1\&format=html) 打印

通过 `lpr` 将文档发送到打印机。可以在命令行中指定要打印的文件，或者通过管道将文件内容传递给 `lpr`。这两个命令是等效的，将 doc.txt 的内容发送到默认打印机：

```sh
% lpr doc.txt
% cat doc.txt | lpr
```

可以使用 `-P` 来选择打印机。要打印到名为 *laser* 的打印机：

```sh
% lpr -Plaser doc.txt
```

## 11.5.3. 过滤器

到目前为止的例子直接将文本文件的内容发送到打印机。只要打印机能够理解这些文件的内容，输出就会正确地打印出来。

一些打印机无法打印纯文本，而且输入文件可能甚至不是纯文本。

*过滤器* 允许对文件进行转换或处理。典型的用途是将一种类型的输入（如纯文本）转换成打印机可以理解的形式，如 PostScript® 或 `PCL`。过滤器还可以用于提供附加功能，比如添加页码或高亮显示源代码以使其更易于阅读。

这里讨论的过滤器是 *输入过滤器* 或 *文本过滤器*。这些过滤器将传入的文件转换为不同的形式。在创建文件之前，使用 [su(1)](https://man.freebsd.org/cgi/man.cgi?query=su\&sektion=1\&format=html) 变更为 `root` 用户。

过滤器在 /etc/printcap 中通过 `if=` 标识符进行指定。要使用 /usr/local/libexec/lf2crlf 作为过滤器，可以像这样修改 /etc/printcap：

```ini
lp:\
	:lp=/dev/unlpt0:\
	:sh:\
	:mx#0:\
	:sd=/var/spool/lpd/lp:\
	:if=/usr/local/libexec/lf2crlf:\   ①
	:lf=/var/log/lpd-errs:
```

* ① `if=` 标识符指定将用于传入文本的 *输入过滤器*。

```sh
lp:lp=/dev/unlpt0:sh:mx#0:sd=/var/spool/lpd/lp:if=/usr/local/libexec/lf2crlf:lf=/var/log/lpd-errs:
```

### 11.5.3.1. 防止纯文本打印机出现阶梯状打印

典型的 FreeBSD 文本文件每行末尾只包含一个换行符。在标准打印机上，这些行会出现“阶梯”现象：

```sh
A printed file looks
                    like the steps of a staircase
                                                 scattered by the wind
```

可以使用过滤器将换行符转换为回车符和换行符。回车符使打印机在每行结束后返回左边。创建 /usr/local/libexec/lf2crlf，内容如下：

```sh
#!/bin/sh
CR=$'\r'
/usr/bin/sed -e "s/$/${CR}/g"
```

设置权限并使其可执行：

```sh
# chmod 555 /usr/local/libexec/lf2crlf
```

修改 /etc/printcap 使用新的过滤器：

```sh
:if=/usr/local/libexec/lf2crlf:\
```

通过打印相同的纯文本文件来测试过滤器。回车符将使每一行从页面的左侧开始打印。

### 11.5.3.2. 使用 [print/enscript](https://cgit.freebsd.org/ports/tree/print/enscript/) 在 PostScript® 打印机上打印精美的纯文本

GNU Enscript 将纯文本文件转换为格式良好的 PostScript® 文件，用于在 PostScript® 打印机上打印。它添加了页码、自动换行，并提供了许多其他功能，使打印出来的文本文件更易于阅读。根据本地纸张大小，从 Ports 安装 [print/enscript-letter](https://cgit.freebsd.org/ports/tree/print/enscript-letter/) 或 [print/enscript-a4](https://cgit.freebsd.org/ports/tree/print/enscript-a4/)。

创建 /usr/local/libexec/enscript 文件，并写入以下内容：

```sh
#!/bin/sh
/usr/local/bin/enscript -o -
```

设置权限并使其可执行：

```sh
# chmod 555 /usr/local/libexec/enscript
```

修改 /etc/printcap 使用新的过滤器：

```ini
:if=/usr/local/libexec/enscript:\
```

通过打印一个纯文本文件来测试过滤器。

### 11.5.3.3. 将 PostScript® 打印到 `PCL` 打印机

许多程序生成 PostScript® 文档。然而，廉价的打印机通常只理解纯文本或 `PCL`。这个过滤器将 PostScript® 文件转换为 `PCL`，然后发送到打印机。

从 Ports 安装 Ghostscript PostScript® 解释器 [print/ghostscript9-base](https://cgit.freebsd.org/ports/tree/print/ghostscript9-base/)。

创建 /usr/local/libexec/ps2pcl 文件，并写入以下内容：

```sh
#!/bin/sh
/usr/local/bin/gs -dSAFER -dNOPAUSE -dBATCH -q -sDEVICE=ljet4 -sOutputFile=- -
```

设置权限并使其可执行：

```sh
# chmod 555 /usr/local/libexec/ps2pcl
```

发送到该脚本的 PostScript® 输入将在发送到打印机之前被渲染并转换为 `PCL`。

修改 /etc/printcap 使用这个新的输入过滤器：

```ini
:if=/usr/local/libexec/ps2pcl:\
```

通过发送一个小的 PostScript® 程序来测试过滤器：

```sh
% printf "%%\!PS \n /Helvetica findfont 18 scalefont setfont \
72 432 moveto (PostScript printing successful.) show showpage \004" | lpr
```

### 11.5.3.4. 智能过滤器

一个可以检测输入类型并自动将其转换为适合打印机的正确格式的过滤器非常方便。PostScript® 文件的前两个字符通常是 `%!`。过滤器可以检测这两个字符。PostScript® 文件可以直接发送到 PostScript® 打印机而不做任何更改。文本文件可以使用 Enscript 转换为 PostScript®，如前所示。创建 /usr/local/libexec/psif 文件，并写入以下内容：

```sh
#!/bin/sh
#
#  psif - 在 PostScript 打印机上打印 PostScript 或纯文本
#
IFS="" read -r first_line
first_two_chars=`expr "$first_line" : '\(..\)'`

case "$first_two_chars" in
%!)
    # %! : PostScript 作业，直接打印。
    echo "$first_line" && cat && exit 0
    exit 2
    ;;
*)
    # 否则，使用 enscript 格式化
    ( echo "$first_line"; cat ) | /usr/local/bin/enscript -o - && exit 0
    exit 2
    ;;
esac
```

设置权限并使其可执行：

```sh
# chmod 555 /usr/local/libexec/psif
```

修改 /etc/printcap 使用这个新的输入过滤器：

```sh
:if=/usr/local/libexec/psif:\
```

通过打印 PostScript® 和纯文本文件来测试过滤器。

## 11.5.4. 多个队列

/etc/printcap 中的条目实际上是 *队列* 的定义。一个打印机可以有多个队列。结合过滤器使用时，多个队列可以为用户提供更多的打印作业控制。

例如，考虑一个办公室中的网络化 PostScript® 激光打印机。大多数用户希望打印纯文本，但一些高级用户希望能够直接打印 PostScript® 文件。可以为同一台打印机在 /etc/printcap 中创建两个条目：

```ini
textprinter:\
	:lp=9100@officelaser:\
	:sh:\
	:mx#0:\
	:sd=/var/spool/lpd/textprinter:\
	:if=/usr/local/libexec/enscript:\
	:lf=/var/log/lpd-errs:

psprinter:\
	:lp=9100@officelaser:\
	:sh:\
	:mx#0:\
	:sd=/var/spool/lpd/psprinter:\
	:lf=/var/log/lpd-errs:
```

发送到 `textprinter` 的文档将由之前示例中的 /usr/local/libexec/enscript 过滤器进行格式化。高级用户可以在 `psprinter` 上打印 PostScript® 文件，那里不会进行任何过滤。

这种多个队列技术可以用来提供对打印机所有功能的直接访问。例如，一台配有双面打印器的打印机可以使用两个队列，一个用于普通的单面打印，另一个使用一个过滤器，该过滤器发送命令序列以启用双面打印，然后发送传入的文件。

## 11.5.5. 监视与控制打印

有几种工具可用于监视打印作业、检查和控制打印机操作。

### 11.5.5.1. [lpq(1)](https://man.freebsd.org/cgi/man.cgi?query=lpq\&sektion=1\&format=html)

[lpq(1)](https://man.freebsd.org/cgi/man.cgi?query=lpq\&sektion=1\&format=html) 用于显示用户的打印作业状态。其他用户的打印作业不会显示。

显示当前用户在单个打印机上的待处理作业：

```sh
% lpq -Plp
Rank   Owner      Job  Files                                 Total Size
1st    jsmith     0    (standard input)                      12792 bytes
```

显示当前用户在所有打印机上的待处理作业：

```sh
% lpq -a
lp:
Rank   Owner      Job  Files                                 Total Size
1st    jsmith     1    (standard input)                      27320 bytes

laser:
Rank   Owner      Job  Files                                 Total Size
1st    jsmith     287  (standard input)                      22443 bytes
```

### 11.5.5.2. [lprm(1)](https://man.freebsd.org/cgi/man.cgi?query=lprm\&sektion=1\&format=html)

[lprm(1)](https://man.freebsd.org/cgi/man.cgi?query=lprm\&sektion=1\&format=html) 用于删除打印作业。普通用户只能删除自己的作业。`root` 用户可以删除任何作业或所有作业。

从打印机删除所有待处理作业：

```sh
# lprm -Plp -
dfA002smithy dequeued
cfA002smithy dequeued
dfA003smithy dequeued
cfA003smithy dequeued
dfA004smithy dequeued
cfA004smithy dequeued
```

从打印机删除单个作业。可以使用 [lpq(1)](https://man.freebsd.org/cgi/man.cgi?query=lpq\&sektion=1\&format=html) 查找作业编号。

```sh
% lpq
Rank   Owner      Job  Files                                 Total Size
1st    jsmith     5    (standard input)                      12188 bytes

% lprm -Plp 5
dfA005smithy dequeued
cfA005smithy dequeued
```

### 11.5.5.3. [lpc(8)](https://man.freebsd.org/cgi/man.cgi?query=lpc\&sektion=8\&format=html)

[lpc(8)](https://man.freebsd.org/cgi/man.cgi?query=lpc\&sektion=8\&format=html) 用于检查和修改打印机状态。`lpc` 后跟命令和可选的打印机名称。可以使用 `all` 替代特定的打印机名称，命令将应用于所有打印机。普通用户可以使用 [lpc(8)](https://man.freebsd.org/cgi/man.cgi?query=lpc\&sektion=8\&format=html) 查看状态，只有 `root` 用户可以使用修改打印机状态的命令。

显示所有打印机的状态：

```sh
% lpc status all
lp:
	queuing is enabled
	printing is enabled
	1 entry in spool area
	printer idle
laser:
	queuing is enabled
	printing is enabled
	1 entry in spool area
	waiting for laser to come up
```

禁止打印机接受新作业，然后再次开始接受新作业：

```sh
# lpc disable lp
lp:
	queuing disabled
# lpc enable lp
lp:
	queuing enabled
```

停止打印，但继续接受新作业。然后开始打印：

```sh
# lpc stop lp
lp:
	printing disabled
# lpc start lp
lp:
	printing enabled
	daemon started
```

在出现错误条件后重新启动打印机：

```sh
# lpc restart lp
lp:
	no daemon to abort
	printing enabled
	daemon restarted
```

关闭打印队列并禁用打印，显示问题信息给用户：

```sh
# lpc down lp Repair parts will arrive on Monday
lp:
	printer and queuing disabled
	status message is now: Repair parts will arrive on Monday
```

重新启用停机的打印机：

```sh
# lpc up lp
lp:
	printing enabled
	daemon started
```

更多命令和选项请参见 [lpc(8)](https://man.freebsd.org/cgi/man.cgi?query=lpc\&sektion=8\&format=html)。

## 11.5.6. 共享打印机

打印机通常在企业和学校中被多个用户共享。为使共享打印机更加便利，提供了附加功能。

### 11.5.6.1. 别名

打印机名称在 `/etc/printcap` 文件的第一行中设置。可以在该名称后添加附加名称，或称为 *别名*。别名通过竖线分隔：

```sh
lp|repairsprinter|salesprinter:\
```

别名可以代替打印机名称。例如，销售部门的用户打印到他们的打印机时使用：

```sh
% lpr -Psalesprinter sales-report.txt
```

维修部门的用户打印到 *他们的* 打印机时使用：

```sh
% lpr -Prepairsprinter repairs-report.txt
```

所有文档都会打印到这台共享打印机上。当销售部门发展到需要自己的打印机时，可以从共享打印机条目中删除别名，并将其用作新打印机的名称。两个部门的用户继续使用相同的命令，但销售文档将发送到新打印机。

### 11.5.6.2. 页头页

在繁忙的共享打印机打印出来的文档堆中，用户可能很难找到自己的文档。*页头页*（有时也称为 *横幅页* 或 *分隔页*）旨在解决此问题。在每个打印作业之前打印带有用户名和文档名称的页头页。

启用页头页的方式取决于打印机是通过 `USB`、并行端口或串行端口直接连接到计算机，还是通过网络远程连接。

对于直接连接的打印机，通过从 `/etc/printcap` 文件中的条目中删除 `:sh:\`（禁止页头）行来启用页头页。这些页头页仅使用换行符进行换行。一些打印机可能需要 `/usr/share/examples/printing/hpif` 过滤器，以防止阶梯式文本。该过滤器配置 `PCL` 打印机，在接收到换行符时打印回车符和换行符。

对于网络打印机，页头页必须在打印机本身上配置。`/etc/printcap` 文件中的页头页条目将被忽略。设置通常可以通过打印机前面板或通过浏览器访问的配置网页进行调整。

## 11.5.7. 参考资料

示例文件：`/usr/share/examples/printing/`。

*4.3BSD Line Printer Spooler Manual*，`/usr/share/doc/smm/07.lpd/paper.ascii.gz`。

手册页：[printcap(5)](https://man.freebsd.org/cgi/man.cgi?query=printcap\&sektion=5\&format=html)，[lpd(8)](https://man.freebsd.org/cgi/man.cgi?query=lpd\&sektion=8\&format=html)，[lpr(1)](https://man.freebsd.org/cgi/man.cgi?query=lpr\&sektion=1\&format=html)，[lpc(8)](https://man.freebsd.org/cgi/man.cgi?query=lpc\&sektion=8\&format=html)，[lprm(1)](https://man.freebsd.org/cgi/man.cgi?query=lprm\&sektion=1\&format=html)，[lpq(1)](https://man.freebsd.org/cgi/man.cgi?query=lpq\&sektion=1\&format=html)。
