第二十二章:打印

在花了前面几章的时间操纵文本之后,是时候把文本放在纸上了。在本章中,我们将介绍用于打印文件和控制打印机操作的命令行工具。我们不会研究如何配置打印,因为打印因发行版而异,通常在安装过程中自动设置。请注意,我们需要一个可用的打印机配置来执行本章中的练习。

我们将讨论以下命令:

第二十二章:打印打印简史昏暗时代的印刷基于字符的打印机图形打印机使用Linux打印准备打印文件pr —— 转换文本文件以进行打印向打印机发送打印任务lpr —— 打印文件(伯克利风格)lp —— 打印文件(System V 风格)另一个选项:a2ps监视和控制打印作业lpstat —— 显示打印系统状态lpq —— 显示打印队列状态lprm/cancel —— 取消打印作业总结

打印简史

要充分了解类Unix操作系统中的打印功能,我们必须首先了解一些历史。在类Unix系统上打印可以追溯到操作系统的起源。在那些日子里,打印机及其使用方式与今天大不相同。

昏暗时代的印刷

dim —— 昏暗的

与计算机一样,前PC时代的打印机往往体积大、价格昂贵、集中化。1980年的典型计算机用户在一个连接到远处计算机的终端上工作。打印机位于计算机附近,处于计算机操作员的监视之下。

当打印机昂贵且集中时,就像Unix早期一样,许多用户共享一台打印机是一种常见的做法。为了识别属于特定用户的打印作业,通常在每个打印作业的开头打印显示用户姓名的横幅页面(banner page)。然后,计算机支持人员将装载一辆包含当天打印作业的购物车,并将其交付给个人用户。

基于字符的打印机

80年代的打印机技术在两个方面与今天大不相同。首先,那个时期的打印机几乎都是冲击式打印机(impact printers)。冲击式打印机使用一种机械机制,将色带撞击纸张,在页面上形成字符印记。当时流行的两种技术是菊花轮(daisy-wheel)印刷和点阵(dot-matrix)印刷。

【所以printer被译作印机?】

早期打印机的第二个也是更重要的特征是,打印机使用设备固有的一组固定字符。例如,菊花轮打印机只能打印实际模制在菊花轮花瓣上的字符。这使得打印机非常像高速打字机。与大多数打字机一样,它们使用等宽字体打印。这意味着每个字符的宽度相同。打印是在页面上的固定位置完成的,页面的可打印区域包含固定数量的字符。大多数打印机水平打印每英寸10个字符(characters per inch,CPI),垂直打印每英寸6行(lines per inch,LPI)。采用此方案,一张美国信纸的宽度为85个字符,高度为66行。考虑到每侧的小边距,80个字符被认为是打印行的最大宽度。这就解释了为什么终端显示器(和我们的终端模拟器)通常是80个字符宽。使用等宽字体和80个字符宽的终端,可以提供打印输出的所见即所得(What You See Is You Get,WYSIWYG)视图。

数据以包含要打印的字符的简单字节流的形式发送到类似打字机的打印机。例如,为了打印“a”,发送ASCII字符代码97。此外,低编号的ASCII控制码提供了一种移动打印机托架和纸张的方法,使用托架返回、换行、进纸等代码。使用控制码,可以通过让打印机打印一个字符、退格,然后再次打印字符,在页面上获得更暗的打印效果,从而实现一些有限的字体效果,如粗体(boldface)。如果我们使用 nroff 渲染手册页并使用 cat -a 检查输出,我们实际上可以看到这一点。

^H (Ctrl-H)字符是用于创建粗体效果的退格。同样,我们还可以看到用于产生下划线的退格/下划线序列。

图形打印机

GUI的发展导致了打印机技术的重大变化。随着计算机转向更多基于图片的显示,打印从基于字符的技术转向了图形技术。低成本激光打印机的出现促进了这一点,它可以在页面的可打印区域的任何地方打印小点,而不是打印固定字符。这使得打印比例字体(proportional fonts,如排版师使用的字体),甚至照片和高质量图表成为可能。

然而,从基于字符的方案转向图形方案带来了巨大的技术挑战。原因如下:使用基于字符的打印机填充一页所需的字节数可以这样计算(假设每页60行,每行包含80个字符):

60 X 80 = 4,800 bytes

相比之下,每英寸300点(dot per inch,DPI)激光打印机(假设每页打印面积为8乘10英寸)需要这么多字节:

(8 X 300) X (10 X 300) / 8 = 900,000 bytes

许多速度较慢的PC网络根本无法处理在激光打印机上打印整页所需的近1兆字节的数据,因此很明显需要一项聪明的发明。

这项发明就是页面描述语言(page description language,PDL)。页面描述语言是一种描述页面内容的编程语言。基本上,它说,“转到这个位置,用10点Helvetica绘制字符'a',转到这个位置……”直到页面上的所有内容都被描述出来。第一个主要的PDL是Adobe Systems的PostScript,至今仍被广泛使用。PostScript语言是一种为排版和其他类型的图形和成像量身定制的完整编程语言。它包括对35种标准高质量字体的内置支持,以及在运行时接受其他字体定义的能力。起初,打印机本身就内置了对PostScript的支持。这解决了数据传输问题。虽然与基于字符的打印机的简单字节流相比,典型的PostScript程序非常冗长,但它比表示整个打印页面所需的字节数要少得多。

PostScript打印机接受PostScript程序作为输入。打印机包含自己的处理器和存储器(通常使打印机成为比它所连接的计算机更强大的计算机),并执行一个称为PostScript解释器的特殊程序,该程序读取传入的PostScript程序并将结果呈现到打印机的内部存储器中,从而形成将传输到纸张上的位(点)图案。将某物渲染成大位模式(称为位图,bitmap)的过程的通用名称是光栅图像处理器(raster image processor,RIP)。

随着时间的推移,计算机和网络都变得更快了。这允许RIP从打印机移动到主机,这反过来又允许高质量的打印机便宜得多。

如今,许多打印机仍然接受基于字符的流,但许多低成本打印机不接受。它们依赖于主机的RIP来提供比特流以打印为点。还有一些PostScript打印机。

使用Linux打印

现代Linux系统使用两个软件套件来执行和管理打印。第一个是通用Unix打印系统(Common Unix Printing System,CUPS),提供打印驱动程序和打印作业管理,第二个是PostScript解释器Ghostscript,充当RIP。

CUPS通过创建和维护打印队列(print queues)来管理打印机。正如我们在前面的历史课中讨论的那样,Unix打印最初是为了管理多个用户共享的集中式打印机而设计的。由于打印机天生就很慢,与给它们供电的计算机相比,打印系统需要一种安排多个打印作业并保持有序的方法。CUPS还能够识别不同类型的数据(在合理范围内),并将文件转换为可打印的形式。

准备打印文件

作为命令行用户,我们主要对打印文本感兴趣,尽管当然也可以打印其他数据格式。

pr —— 转换文本文件以进行打印

我们在上一章中稍微看了一下 pr 。现在,我们将研究与打印结合使用的许多选项中的一些。在我们的打印历史中,我们看到了基于字符的打印机如何使用等宽字体,从而导致每行字符数和每页行数固定。 pr 用于调整文本以适应特定的页面大小,具有可选的页眉和边距。下表总结了其最常用的选项。

选项描述
+first [:last]输出范围以 first 开头的页面,也可以选择以 last 结尾。
-columns将页面内容组织到由 columns 指定的列数中。
-a默认情况下,多列输出垂直列出。通过添加 -a (across)选项,内容将水平列出。
-d双倍空间输出。
-D "format"使用格式设置页眉中显示的日期的格式。有关格式字符串的描述,请参阅 date 命令的手册页。
-f使用表单提要而不是回车到单独的页面。
-h "header"在页眉的中心部分,使用 header 而不是正在处理的文件的名称。
-l length将页面长度设置为 length 。默认值为66(美国信纸,每英寸六行)
-n行号
-o offset创建一个字符宽的左边距偏移。
-w width将页面宽度设置为 width 。默认值为72。

pr 通常用作管道中的过滤器。在这个例子中,我们将生成 /usr/bin 的目录列表,并使用 pr 将其格式化为分页的三列输出:

向打印机发送打印任务

CUPS打印套件支持两种历史上在类Unix系统上使用的打印方法。一种方法称为Berkeley或LPD(用于Unix的Berkeley软件发行版),使用 lpr 程序,而另一种方法名为SysV(来自Unix的System V版本),使用 lp 程序。这两个程序做的事情大致相同。根据个人品味任意选择。

lpr —— 打印文件(伯克利风格)

lpr程序可用于将文件发送到打印机。它也可用于管道,因为它接受标准输入。例如,要打印我们之前的多列目录列表的结果,我们可以这样做:

报告将被发送到系统的默认打印机。要将文件发送到其他打印机,可以这样使用 -P 选项:

lpr -P printer_name

这里,printer_name 是所需打印机的名称。要查看系统已知的打印机列表,请使用以下命令:

提示:许多Linux发行版允许您指定一个以可移植文档格式(PDF)输出文件的“打印机”,而不是在物理打印机上打印。这对于尝试打印命令非常方便。检查打印机配置程序是否支持此配置。在某些发行版上,您可能需要安装其他软件包(如 cups-pdf )来启用此功能。

下表描述了 lpr 的常见选项:

选项描述
-# number指定打印 number 份副本。
-p用带有日期、时间、作业名称和页码的阴影标题打印每页。
打印文本文件时可以使用这种所谓的“漂亮打印(pretty print)”选项。
-P printer指定用于输出的打印机的名称。
如果未指定打印机,则使用系统的默认打印机。
-r打印后删除文件。这对于生成临时打印机输出文件的程序非常有用。

lp —— 打印文件(System V 风格)

lpr 一样, lp 接受文件或标准输入进行打印。它与 lpr 的不同之处在于,它支持不同的(稍微复杂一些的)选项集。下表描述了常见选项:

选项描述
-d printer将目标(打印机)设置为 printer
如果未指定 d 选项,则使用系统默认打印机。
-n number指定打印的份数。
-o landscape将输出设置为横向。
-o fitplot缩放文件以适应页面。
这在打印图像(如JPEG文件)时非常有用。
-o scaling=number将文件缩放到 number
值100填充页面。小于100的值会缩小,而大于100的值则会导致文件跨多页打印。
-o cpi=number将每英寸的输出字符数设置为 number
默认值为10。
-o lpi=number将每英寸的输出行数设置为 number
默认值为6。
-o page-bottom=points
-o page-left=points
-o page-right=points
-o page-top=points
设置页边距。
值以 point 表示,点是排版测量单位。一英寸有72个点。
-P pages指定页面列表。
页面可以表示为逗号分隔的列表和/或范围,例如1、3、5、7-10。

我们将再次生成目录列表,这次打印12个CPI和8个LPI,左边距为半英寸。请注意,我们必须调整 pr 选项以适应新的页面大小:

此管道使用比默认值更小的类型生成一个四列列表。每英寸字符数的增加使我们能够在页面上容纳更多的列。

另一个选项:a2ps

a2ps 程序(在大多数存储库中都有)很有趣。从它的名字我们可以推测,它是一个格式转换程序,但它还有更多。它的名字最初的意思是“ASCII to PostScript”,用于准备在PostScript打印机上打印的文本文件。然而,多年来,该程序的功能不断增长,现在它的名字的意思是“Anything to PostScript”。虽然它的名字暗示了一个格式转换程序,但它实际上是一个打印程序。它将默认输出发送到系统的默认打印机,而不是标准输出。该程序的默认行为是“漂亮的打印机”,这意味着它改善了输出的外观。我们使用该程序在桌面上创建PostScript文件。

在这里,我们使用 -t 选项(省略页眉和页脚)使用 pr 过滤流,然后使用a2ps过滤流,指定输出文件( -o 选项)和每页66行( -L 选项)以匹配 pr 的输出分页。如果我们使用合适的文件查看器查看结果文件,我们将看到图7中的输出。

viewinga2psoutput

正如我们所看到的,默认的输出布局是“two-up”格式。这会导致每张纸上打印两页的内容。 a2ps 也应用了漂亮的页眉和页脚。

a2ps 有很多选择。下表提供了摘要:

选项介绍
--center-title=text将中心页面标题设置为 text
--columns=number将页面排列成 number 列。默认值为2。
--footer=text将页脚设置为 text
--guess报告作为参数给出的文件类型。
由于 a2ps 试图转换和格式化所有类型的数据,因此此选项对于预测 a2ps 在给定特定文件时会做什么非常有用。
--left-footer=text将左侧页脚设置为 text
--left-title=text将左侧页面标题设置为文本 text
--line-numbers=interval为每个 interval 行的输出行编号。
--list=defaults显示默认设置。
--pages=range打印 range 内的页面。
--right-footer=text将右侧页脚设置为 text
--right-title=text将右侧页面标题设置为文本 text
--rows=number将页面排列成 number 行。默认值为1。
-B无页眉
-b text将页眉设置为 text
-f size使用 size 点字体。
-l number将每行字符设置为 number 。此选项和 -L 选项(见下一条目)可用于使使用其他程序(如 pr )分页的文件正确地适应页面。
-L number将每页行数设置为 number
-M name使用介质 name 。例如,A4。
-n number输出每页 number 份。
-o file将输出发送到 file 。若 file 指定为 - ,则使用标准输出。
-P printer使用 printer 。如果未指定打印机,则使用系统默认打印机。
-R纵向(portrait orientation)
-r横向(landscape orientation)
-T number为每 number 个字符设置制表符。
-u texttext 为水印。

这只是一个总结。 a2ps 还有更多选项。

注意:还有另一种输出格式化程序可用于将文本转换为PostScript。它被称为 enscript ,可以执行许多相同的格式化和打印技巧,但与 a2ps 不同,它只接受文本输入。

监视和控制打印作业

由于Unix打印系统被设计为处理来自多个用户的多个打印作业,CUPS也被设计为这样做。每台打印机都有一个打印队列,作业会停在那里,直到它们可以被后台打印到打印机。CUPS提供了几个用于管理打印机状态和打印队列的命令行程序。与 lprlp 程序一样,这些管理程序是按照伯克利和System V打印系统的相应程序建模的。

lpstat —— 显示打印系统状态

lpstat 程序可用于确定系统上打印机的名称和可用性。例如,如果我们有一个系统同时具有物理打印机(名为“printer”)和PDF虚拟打印机(称为“PDF”),我们可以这样检查它们的状态:

此外,我们可以通过这种方式确定打印系统配置的更详细描述:

在这个例子中,我们看到“printer”是系统的默认打印机,它是一个使用互联网打印协议(ipp://)连接到名为“print-server”的系统的网络打印机。

下表列出了常用的选项:

选项描述
-a [printer...]显示 printer 的打印机队列状态。
请注意,这是打印机队列接受作业的能力状态,而不是物理打印机的状态。
如果未指定打印机,则显示所有打印队列。
-d显示系统默认 printer 的名称。
-p [printer...]显示指定 printer 的状态。
如果未指定打印机,则显示所有打印机。
-r显示打印服务器的状态。
-s显示状态摘要。
-t显示完整的状态报告。

lpq —— 显示打印队列状态

要查看打印机队列的状态,可以使用 lpq 程序。这允许我们查看队列的状态及其包含的打印作业。以下是一个名为“打印机”的系统默认打印机的空队列示例:

如果不指定打印机(使用 -P 选项),则显示系统的默认打印机。如果我们将作业发送到打印机,然后查看队列,我们将看到它被列出:

lprm/cancel —— 取消打印作业

CUPS提供了两个程序,用于终止打印作业并将其从打印队列中删除。一种是伯克利风格(lprm),另一种是System V(cancel)。他们支持的选项略有不同,但基本上做的是一样的事情。以我们之前的打印作业为例,我们可以通过以下方式停止作业并将其删除:

每个命令都有选项用于指定删除属于特定用户、特定打印机和多个作业号的所有作业。他们各自的手册页上有所有的细节。

总结

在本章中,我们看到了过去的打印机是如何影响类Unix机器上打印系统的设计的,以及命令行上有多少控制不仅可以控制打印作业的调度和执行,还可以控制各种输出选项。