Top

D19:常见任务和基本工具之九

打印

目录:

打印简史

在类Unix系统上打印可以追溯到操作系统的早期。

混沌时代的打印

在前PC时代,打印机和计算机一样,体积巨大、价格昂贵,都是集中管理。
1980年的典型计算机用户是在一个于计算机相隔一定距离的终端上工作。而打印机则位于计算机附近,且在计算机操作员的监视下。
当时打印机价格昂贵且集中化管理,许多用户通常会共用一台打印机,就像早期的Unix一样。
为了识别不同用户的打印作业,通常会在每个打印作业的开头打印一个显示用户名的抬头。然后,计算机支持人员会将当天的打印作业装到小车里再逐一交付给各个用户。

基于字符的打印机

80年代的打印技术与现代不同。主要有两点:
  • 当时的打印机几乎都是冲击式(impact)打印机,即使用一种机械装置将色带撞击纸张,以在纸张上形成字符印模。
    流行的冲击式打印机主要是雏菊轮(daisy-wheel)打印和点阵(dot-matrix)打印。
  • 打印机使用设备固有的一组固定字符。
    例如雏菊轮打印机只能打印字模上的字符。这使得打印机很像高速打字机。和大多数打字机一样,它们使用单间距(固定宽度)字体打印。
    大多数打印机水平打印每英寸10个字符(10 characters per inch, CPI),垂直打印每英寸6行字符(6 lines per inch, LPI)。
    按此方案,一张美国信纸(US-Letter)宽85个字符,高66行。考虑到每一边都有一个小边距,则打印行的最大宽度被定义为80个字符。这与终端显示器保持一致。
    使用单间距字体和80个字符宽的终端可以提供打印输出的所见即所得(What You See Is What You Get,WYSIWYG)视图。
  • 数据作为简单字节流发送到类似于打字机的打印机,字节流里包含了要打印的字符。
    例如要打印“a”,就发送ASCII字符代码97。
    此外,编号较低的ASCII控制代码提供了移动打印机机架和纸张的方法,使用机架返回、换行、换页等代码。
    使用控制代码可以通过让打印机打印字符、退格并再次打印字符,在页面上获得较暗的打印效果,实现一些有限的字体效果,例如黑体。
    以下例子使用nroff呈现一个手册页,并使用cat -a检查输出:
    [me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | nroff -man | cat -A | head
    LS(1) User Commands LS(1)
    $
    $
    $
    N^HNA^HAM^HME^HE$
            ls - list directory contents$
    $
    S^HSY^HYN^HNO^HOP^HPS^HSI^HIS^HS$
            l^Hls^Hs [_^HO_^HP_^HT_^HI_^HO_^HN]... [_^HF_^HI_^HL_^HE]...$	
    
    ^H(Ctrl-H)字符是用于创建粗体效果的退格。同样,我们也可以看到用于生成下划线的退格/下划线序列。

    图形打印机

    低成本的激光打印机可以实现在打印区域内的任何地方打印小点,而不是打印固定字符。这使得打印比例字体甚至照片和高质量成为可能。

    基于字符的打印机填充页面所需的字节数为:60X80=4,800字节;而对于300DPI精度的激光打印机,一张8X10英寸的打印区域则需要(8X300)X(10X300)/8=900,000字节。
    许多速度较慢的PC网络根本无法处理在激光打印机上打印一整页所需的1M字节的数据。于是出现了page description language(页面描述语言,PDL),用于描述页面内容的编程语言。
    第一个主要的PDL是Adobe System的PostScript,至今仍广泛使用。它是一种为排版和其他类型的图形和图像定制的完整编程语言。包括了对35种标准、高质量字体的内置支持,以及在运行时接受其他字体定义的能力。

    起初,打印机本身内置了对PostScript的支持,这解决了数据传输问题。虽然比基于字符的打印机的简单字节流要冗长,但远小于表示整个打印页面所需的字节数。

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

    随着计算机和网络都变得更快,RIP从打印机转移到了主机,这使得高质量打印机的成本大大降低。

    如今许多打印机仍能接受基于字符的流,但许多低成本打印机不能接受,他们依靠主机的RIP提供一个比特流来打印成点。

    使用Linux打印

    现代Linux系统使用两个软件套件来执行和管理打印:
  • 第一个通用Unix打印系统(Common Unix Printing System,CUPS)提供打印驱动程序和打印作业管理
  • 第二个Ghostscript是PostScript解释器,充当RIP
  • CUPS通过创建和维护打印队列来管理打印机。CUPS还能识别不同类型的数据,并可以将文本转换为可打印形式。

    为打印准备文件

    pr —— 为打印转换文本文件

    pr用于调整文本以适应特定的页面大小,带有可选的页眉和页边距。下表是一些常用的选项:
    选项 说明
    +first[:last] 输出一系列页眉,从first开始,可选以last结束。
    -columns columns指定的列数组织页眉内容。
    -a 默认情况下,多列输出垂直列出。通过添加-a(across)选项,内容将水平列出。
    -d 双空间输出。
    -D "format" 使用"format"设置页眉中显示的日期的格式。格式参阅date命令的手册页。
    -f 使用表单馈送,而不是回车符返回到单独的页眉。
    实测加了此选项后执行命令需要先按回车才能显示结果。不太清除此选项的实际用途。
    -h "header" 在页眉的中间部分,使用页眉而不是正在处理的文件名。
    -l length 将页面长度设置为length。默认为66(美国字母,每英寸6行)。
    实测,若使用 -n 20,则每页显示10行。
    -n 每一项前面添加序号。
    -o offset 左边距为offset个字符宽度。
    -w width 将页宽设置为width。默认为72。
    pr通常在管道中用作过滤器。以下例子将生成/usr/bin的目录列表,并使用pr将其格式化为三列输出:
    [me@linuxbox ~]$ ls /usr/bin | pr -3 -w 65 | head
    
    2016-02-18 14:00                         Page 1
    [                   apturl         bsd-write
    411toppm            ar             bsh
    a2p                 arecord        btcflash
    a2ps                arecordmidi    bug-buddy
    a2ps-lpr-wrapper    ark            buildhash	
    	

    发送打印任务到打印机

    CUPS打印套件支持两种以前的类Unix系统上使用的打印方法。
  • 一种方法称为Berkeley或LPD(用于Unix的Berkeley软件发行版),使用lpr程序。
  • 另一种方法称为SysV(来自Unix的System V版本),使用lp程序。
  • 以上两个程序的作用大致相同,根据个人品味选择其一。

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

    lpr可以用于向打印机发送文件。也可以用于管道以接收标准输入。
    例如,要打印上一个多列目录列表的结果,可以使用以下命令:
    [me@linuxbox ~]$ ls /usr/bin | pr -3 | lpr
    结果将被发送到系统默认的打印机。要发送到其他打印机,可以使用-P选项:
    lpr -p printer_name
    若系统没有打印机,则会提示:
    lpr: lp: unknown printer
    要查看系统已连接的打印机列表,可以使用以下命令:
    [me@linuxbox ~]$ lpstat -a
    下表介绍lpr常用选项:
    选项 说明
    -# number 设置打印number个副本。
    -p 打印每页,页眉用阴影标出日期、时间、任务名称和页码。打印文本文件时可以使用这种所谓的“漂亮打印(pretty print)”选项。
    -P printer 指定打印机。如果不指定,则使用系统默认的打印机。
    -r 打印后删除文件。这对于生成临时打印机输出文件的程序很有用。

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

    lpr一样,lp接受文件或标准输入进行打印。不同之处在于,它支持一个不同的更复杂的选项集。常用选项如下表:
    选项 说明
    -d printer 指定目标打印机,未指定则使用默认打印机。
    -n number 设置打印的副本数量。
    -o landscape 横向打印
    -o fitplot 缩放文件以适应页面。
    -o scalint=number 设置缩放比例,number为百分比,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
    设置页面边距。数值用(points)点表示,点是印刷测量的单位。一英寸有72个点。
    -P pages 指定页面列表。页面可以表示为逗号分隔的列表和/或范围,例如1,3,5,7-10。
    以下例子再次生成目录列表,这次打印12个CPI和8个LPI,左边距为半英寸。注意,必须调整pr选项以适应新的页面大小:
    [me@linuxbox ~]$ ls /usr/bin | pr -4 -w 90 -l 88 | lp -o page-left=36 -o cpi=12 -o lpi=8
    此管道使用比默认值更小的类型生成四列列表。每英寸字符数的增加使我们可以在页面上容纳更多的列。

    其他选项: a2ps

    a2ps可在大多数存储库中使用。它是一个格式转换程序。其名字最初的意思是“ASCII to PostScript”,用于准备文本文件以便在PostScript打印机上打印。
    随着此项目的不断增强,它的名字变成了“Anything to PostScript”。
    虽然它的名字意味着一个格式转换程序,但实际上是一个打印程序。它将默认输出发送到系统的默认打印机,而不是标准输出。
    该程序的默认行为是“漂亮的打印机”(pretty printer)。这意味着它改善了输出的外观。

    以下例子在FreeBSD下实验不成功,提示a2ps: unknown medium 'libpaper'(另,FreeBSD需要安装a2ps)
    [me@linuxbox ~]$ ls /usr/bin | pr -3 -t | a2ps -o ~/Desktop/ls.ps -L 66
    [stdin (plain): 11 pages on 6 sheets]
    [Total: 11 pages on 6 sheets] saved into the file `/home/me/Desktop/ls.ps'	
    
    以上例子:
    使用pr过滤流,使用-t选项(省略页眉和页脚)
    然后使用a2ps,指定输出文件(-o选项)和每页66行(-L选项),以匹配pr的输出分页。
    可以使用合适的文件查看器查看结果文件。

    a2ps常用选项:
    选项 说明
    --center-title=text 设置中间页标题为text
    --columns=number 将页面排列成number列。默认值为2。
    --footer=text 设置页脚为text
    --guess 报告作为参数给出的文件类型。
    由于a2ps尝试转换和格式化所有类型的数据,因此此选项可用于预测a2ps在给定特定文件时的操作。
    --left-footer=text 设置左页脚为text
    --left-title=text 设置左页抬头为text
    --line-nmubers=interval 每隔interval行输出行数。
    --list=defaults 显示默认设置。
    --pages=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 指定介质,比如A4。
    -n number 输出每页的副本数量。
    -o file 输出到文件。如果file指定为-,则使用标准输出。
    -P printer 指定打印机。不指定则使用系统默认打印机。
    -R 肖像定位。
    -r 景观导向。
    -T number 将制表符设置为每个number字符。
    -u text 设置水印为text
    另有一个enscript,可以执行许多相同的格式化和打印技巧,但其只接受文本输入。

    监视和控制打印任务

    lpstat —— 显示打印系统状态

    lpstat程序可用于确定系统上打印机的名称和可用性。
    例如,如果我们有一个同时带有物理打印机(名为“printer”)和PDF虚拟打印机(名为“PDF”)的系统,我们可以像这样检查它们的状态:
    [me@linuxbox ~]$ lpstat -a
    PDF accepting requests since Mon 08 Dec 2017 03:05:59 PM EST
    printer accepting requests since Tue 24 Feb 2018 08:43:22 AM EST	
    
    此外,我们可以通过以下方式确定打印系统配置的更详细描述:
    [me@linuxbox ~]$ lpstat -s
    system default destination: printer
    device for PDF: cups-pdf:/
    device for printer: ipp://print-server:631/printers/printer	
    
    在本例中,我们看到“printer”是系统的默认打印机,它是使用Internet打印协议(ipp://)连接到名为“printserver”的系统的网络打印机。

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

    lpq —— 显示打印机队列状态

    lpq可以查看打印机队列的状态,机器包含的打印作业。例如:
    [me@linuxbox ~]$ lpq
    printer is ready
    no entries	
    
    同样的,如果没有指定打印机(使用-P选项),则显示系统默认打印机。
    如果向打印机发送作业,则可以看到打印队列:
    [me@linuxbox ~]$ ls *.txt | pr -3 | lp
    request id is printer-603 (1 file(s))
    [me@linuxbox ~]$ lpq
    printer is ready and printing
    Rank     Owner   Job    File(s)   Total Size
    active   me      603    (stdin)   1024 bytes	
    

    lprm/cancel —— 取消打印任务

    CUPS提供两个程序用来终止打印作业并将其从打印队列中删除。
    一种是Berkeley风格的lprm,一种是System V风格的cencel
    例如,取消以上例子中的作业:
    [me@linuxbox ~]$ cancel 603
    [me@linuxbox ~]$ lpq
    printer is ready
    no entries