这可能是一个奇怪的问题,因为这是大多数用户都会做的事情不是想。
我想用 TeX 排版一些文本,并将排版页面的描述作为位图获取:一串 0 和 1,表示墨水应该流向何处(当然假设具有一定的分辨率)。此外,我想使用 Computer Modern 字体的光栅(Metafont)版本,而不是 cm-super 或 Latin Modern 等矢量版本。理想情况下,甚至可以改变分辨率(每英寸的点数)和其他打印机特性,就像 Metafont 可以做到的那样。
我该如何实现这个目标?据我所知,这应该是可能的 :-)(Knuth 开发 TeX 和 Metafont 时的目标是为数字排版机生成输出:为 XGP 等输出设备生成将出现在页面上的点图案(180–200 dpi)、多佛 (384 dpi) 和 Alphatype CRS (5333 dpi)。但也许他从未生成过明确的位图;只是以适合这些设备的格式输出。
如果我理解正确的话,DVI 文件包含类似“将字符 72 放在 (h, w) 位置”的指令。(我们可以用以下代码以文本形式查看 DVI 文件:dvitype
如果我理解正确的话,DVI 文件包含类似“将字符 72 放在位置 (h, w)”的指令。(我们也由 Knuth 撰写。)然后,DVI 驱动程序(如dvips
或dvipdfmx
)将获取这些指令,并分别获取字体信息,然后生成实际输出。这是我希望它使用“原始”Metafont(光栅)字体而不是其矢量等效字体的部分。
我的系统上似乎有一个dvipng
(TeX Live 的一部分),我可以使用。它甚至有--mode
和--bdpi
这样的选项,似乎很相关。问题是:
- 我不知道如何使用它:-)(例如如何指定分辨率)
- PNG 格式并不简单,我不确定如何从中得到简单的 0 和 1 序列。事实上,
dvipng
文档中提到了抗锯齿和--gamma
“颜色插值”之类的东西,这让我担心它不是严格意义上的位图,而是更复杂的光栅图像。
因此也许还有另一种选择:有什么好方法可以为页面获取一个简单的位图?
答案1
我想我找到了一个答案(遵循大家的宝贵建议):以下是我所理解的各种文件中包含的信息,以及从 TeX 源到渲染位图的路径。
TeX 文件
我们从一个 TeX 文件开始,比如说,一个hello.tex
只包含以下内容的文件:
hello
\bye
DVI 文件
使用 编译 TeX 文件tex hello.tex
将生成hello.dvi
,其中仅包含字体名称和字体中字符的放置位置说明。例如,这是hello.dvi
包含的内容( 的输出hexdump -v -C hello.dvi
):
00000000 f7 02 01 83 92 c0 1c 3b 00 00 00 00 03 e8 1b 20 |.......;....... |
00000010 54 65 58 20 6f 75 74 70 75 74 20 32 30 31 37 2e |TeX output 2017.|
00000020 30 33 2e 32 34 3a 31 32 35 35 8b 00 00 00 01 00 |03.24:1255......|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 00 ff ff ff ff 8d 9f f2 00 00 8e a0 02 83 |................|
00000060 33 da 8d a0 fd 86 cc 26 8d 91 14 00 00 f3 00 4b |3......&.......K|
00000070 f1 60 79 00 0a 00 00 00 0a 00 00 00 05 63 6d 72 |.`y..........cmr|
00000080 31 30 ab 68 65 6c 6c 6f 8e 8e 9f 18 00 00 8d 92 |10.hello........|
00000090 00 e8 60 a3 31 8e 8c f8 00 00 00 2a 01 83 92 c0 |..`.1......*....|
000000a0 1c 3b 00 00 00 00 03 e8 02 9b 33 da 01 d5 c1 47 |.;........3....G|
000000b0 00 02 00 01 f3 00 4b f1 60 79 00 0a 00 00 00 0a |......K.`y......|
000000c0 00 00 00 05 63 6d 72 31 30 f9 00 00 00 97 02 df |....cmr10.......|
000000d0 df df df df |....|
可以使用 获取此内容的人类可读版本dvitype
;相关部分是:
109: fntdef1 0: cmr10
130: fntnum0 current font is cmr10
131: setchar104 h:=1310720+364090=1674810, hh:=106
132: setchar101 h:=1674810+291271=1966081, hh:=124
133: setchar108 h:=1966081+182045=2148126, hh:=136
134: setchar108 h:=2148126+182045=2330171, hh:=148
135: setchar111 h:=2330171+327681=2657852, hh:=169
我认为这些是将字体 0 定义为“cmr10”、使用字体 0 以及在某些位置设置字符 (104、101、108、108、111)(对应于“hello”)的指令。这些hh
值根据-dpi
传递给dvitype
而变化(默认值为 300),但其余值是固定的。(我们也可以使用“歧义hello.dvi”或“dv2dthello.dvi" 以用于其他格式的人性化输出。) 因此,我们可以看到,dvi 文件中仍然没有任何字体形状信息。DVI 驱动程序接收这些指令,查找字体(“cmr10”),并将指定的字符放在指定的位置。
包含 Type 1 字体的 PS 文件
调用dvips hello.dvi
使用(至少在我的系统上)Type 1(矢量)字体,例如texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb
。注释中cmr10.pfb
说%Copyright: Copyright (c) 1997, 2009 American Mathematical Society
。我认为这对应于AMS 网站上有关 Type 1 字体的页面其中提到字体(轮廓版)由 Blue Sky Research 和 Y&Y Inc. 制作。(另请参阅自述文件.bluesky在字体/cm/ps-type1目录,Nelson Beebe 网站上的“Blue Sky Research 和 Computer Modern 字体”, 和卡雷尔·皮什卡的比较(PDF 草稿)其中与 Computer Modern 的另一个 Type 1 版本,即 BaKoMa(由 Basil K. Malyshev 编写)一起。
此文件中有一些二进制内容cmr10.pfb
,您可以使用将其转换为人类可读的形式t1disasm
。这表明,例如,字母的字形在代码e
中定义如下:cmr10.pfb
/e {
28 444 hsbw
67 4 callsubr
84 252 rmoveto
6 149 84 25 34 0 rrcurveto
103 10 -135 -39 hvcurveto
closepath
-238 -21 rmoveto
279 hlineto
22 3 0 21 hvcurveto
99 -54 97 -125 vhcurveto
-116 -92 -103 -125 hvcurveto
68 4 callsubr
-134 105 -97 115 vhcurveto
122 45 111 19 hvcurveto
10 -8 2 -5 vhcurveto
-9 0 -2 -6 -2 -8 rrcurveto
-35 -103 -90 0 -10 0 rrcurveto
-50 0 -40 30 -23 37 rrcurveto
-30 48 0 66 0 36 rrcurveto
closepath
endchar
} ND
这是大多数用户想要和使用的字体。它是一种矢量字体,而不是光栅字体,这意味着它指定了字符的“形状”,而不是位图。它最终在屏幕(或纸张)上变成像素的方式是,当您在解释器/查看器(如 Ghostscript)中打开 PostScript(或 PDF)文件时,那程序(查看器/渲染器)采用上面指定的形状,并尝试将该形状近似为查看缩放级别下的可用像素数。因此,相同的形状描述在所有缩放级别上都“有效”,这意味着渲染器会在您选择的任何缩放级别重新计算像素。它还可以使用字体指定的一些提示信息。(我在写作时学到了一些关于这一切(字体光栅化等)的知识这个答案,这是一个可以说明这种情况如何微妙的例子。)
要从这样的 PostScript 文件中获取位图,我们可以使用 Ghostscript 来生成PBM 文件它基本上是一个由 0 和 1 组成的矩阵:cat hello.ps | gs -sDEVICE=pbm -sOutputFile=hello.pbm -r600 -
。请注意“-r600”,它指定 600 dpi 的分辨率:无论您指定什么分辨率,gs
都会将 PostScript 文件“渲染”(光栅化)到该分辨率,从而生成位图。
正如我所说,这些字体是大多数用户想要的(见Heiko Oberdiek 的这个回答获得高度赞誉,出色的 的作者pkfix
),但在这里我想查看由 Metafont 生成的位图(并由 TeX 定位),而不是由查看器软件生成的位图,因此继续...
带位图字体的 PS 文件
我们可以使用选项“下载非驻留 PostScript 字体作为位图”,而不是dvips hello.dvi
使用 Type 1 字体。因此,调用会产生 ,并且日志显示它正在使用位图字体(来自我们主目录中的目录)。-V
dvips -V hello.dvi
hello.ps
texmf-var/fonts/pk/ljfour/public/cm/cmr10.600pk
这也是我们可以指定不同的 DPI 和模式的地方模式.mf,例如dvips -V -D100 -mode amiga foo.dvi
或dvips -V -D1200 -mode cgnszz foo.dvi
。(两者必须匹配,否则会打印类似 的警告mktexpk: Mismatched mode […] and resolution […]; ignoring mode
。)对于我们指定的每种模式,它要么从我们的本地目录中获取pk
文件,要么如果不存在,则自动调用 Metafont。例如,如果我们调用dvips -V -D406 -mode agfafzz hello.dvi
(并且这些选项是第一次使用),日志会显示:
kpathsea: Running mktexpk --mfmode agfafzz --bdpi 406 --mag 1+0/406 --dpi 406 cmr10
mktexpk: Running mf-nowin -progname=mf \mode:=agfafzz; mag:=1+0/406; nonstopmode; input cmr10
This is METAFONT, Version 2.7182818 (TeX Live 2015) (preloaded base=mf)
...
Font metrics written on cmr10.tfm.
Output written on cmr10.406gf (128 characters, 16756 bytes).
Transcript written on cmr10.log.
mktexpk: /Users/username/Library/texlive/2015/texmf-var/fonts/pk/agfafzz/public/cm/cmr10.406pk: successfully generated.
生成的pk
文件就是 PS 文件中使用的文件。请参阅David Carlisle 的精彩回答稍微看一下gf
和pk
文件,然后看一下modes.mf
。
pk
当使用此类文件时,ps
文件包含类似的部分(对于我们hello.dvi
上面的文件,只需要五个字符):
%DVIPSBitmapFont: Fa cmr10 10 5
/Fa 5 112 df<0001C0000003C0000007C000001FC00000FFC000FFFFC000FFFFC000FF
1FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC00000
1FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC00000
1FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC00000
1FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC00000
1FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC000001FC00000
3FE0007FFFFFF07FFFFFF07FFFFFF01C3879B72A>49 D<0007F800001FFF00007C0FC001
F803E003F001F007E001F80FC000F81F80007C1F80007C3F00007E3F00003E7F00003E7F
00003F7E00003FFE00003FFE00003FFE00003FFFFFFFFFFFFFFFFFFE000000FE000000FE
000000FE000000FE0000007E0000007E0000007F0000007F0000003F0000033F8000031F
8000070FC0000607C0000E07E0001C01F0003800F80070007E03E0001FFF800003FC0020
277EA525>101 D<03F000000000FFF000000000FFF000000000FFF0000000000FF00000
000003F00000000003F00000000003F00000000003F00000000003F00000000003F00000
000003F00000000003F00000000003F00000000003F00000000003F00000000003F00000
000003F00000000003F00000000003F00000000003F00000000003F00FF0000003F03FFC
000003F0F03F000003F1C01F800003F3800FC00003F7000FC00003FE000FC00003FC0007
E00003FC0007E00003F80007E00003F80007E00003F80007E00003F00007E00003F00007
E00003F00007E00003F00007E00003F00007E00003F00007E00003F00007E00003F00007
E00003F00007E00003F00007E00003F00007E00003F00007E00003F00007E00003F00007
E00003F00007E00003F00007E00003F00007E00003F00007E00003F00007E00003F00007
E00003F00007E00007F8000FF000FFFFC1FFFF80FFFFC1FFFF80FFFFC1FFFF80293A7EB9
2E>104 D<03F000FFF000FFF000FFF0000FF00003F00003F00003F00003F00003F00003
F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003
F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003
F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003F00003
F00003F00003F00003F00003F00003F00003F00003F00007F800FFFFC0FFFFC0FFFFC012
3A7EB917>108 D<0003FE0000000FFF8000003E03E00000F800F80001F0007C0003E000
3E0007C0001F000F80000F801F80000FC01F000007C03F000007E03F000007E07E000003
F07E000003F07E000003F07E000003F0FE000003F8FE000003F8FE000003F8FE000003F8
FE000003F8FE000003F8FE000003F8FE000003F8FE000003F87E000003F07E000003F07F
000007F03F000007E03F000007E01F80000FC00F80000F800FC0001F8007E0003F0003F0
007E0000F800F800007E03F000001FFFC0000003FE000025277EA52A>111
D E
%EndDVIPSBitmapFont
并且这会随着模式而变化:对于amiga
仅为 100 dpi 的模式,字体较少:
%DVIPSBitmapFont: Fa cmr10 10 5
/Fa 5 112 df<20E02020202020207004097F8807>49 D<7088F88088700506808506>
101 D<C04040407844444444EE070A808908>104 D<C04040404040404040E0030A8089
04>108 D<7884848484780606808507>111 D E
%EndDVIPSBitmapFont
我还没有设法直接逆向工程这些信息(如何从 到<7088F88088700506808506>
的字形e
)。但我们可以使用pktype
生成的pk
文件以人类可读的形式查看它。这是e
中字符 101(小写)的部分texmf-var/fonts/pk/amiga/public/cm/cmr10.100pk
:
635: Flag byte = 224 Character = 101 Packet length = 15
Dynamic packing variable = 14
TFM width = 466035 dx = 393216
Height = 6 Width = 5 X-offset = 0 Y-offset = 5
.***.
*...*
*****
*....
*...*
.***.
(除了像上面那样的非常低的分辨率,它不会打印这样的像素图像:对于 600 DPI,texmf-var/fonts/pk/ljfour/public/cm/cmr10.600pk
相应的输出看起来像
3304: Flag byte = 192 Character = 101 Packet length = 77
Dynamic packing variable = 12
TFM width = 466035 dx = 2424832
Height = 39 Width = 32 X-offset = -2 Y-offset = 37
(13)8(22)13(17)5(6)6(13)6(9)5(11)6(11)5(9)6(12)6(7)6(14)5(6)[1]6(16)5(4)6
(17)6(3)6(18)5(2)7(18)5(2)7(18)6(1)6(19)13[2](19)77[4](26)[1]6(26)[1]7(26)6
(22)2(2)7(21)2(3)6(20)3(4)6(19)2(6)5(18)3(6)6(16)3(9)5(14)3(11)5(12)3(13)6
(7)5(16)14(21)8(10)
反而。)
我们还可以调用pktogf
该pk
文件,然后gftype
在该gf
文件上使用。在最大输出时,即使用选项时-images -mnemonics
,gftype
输出如下:
1160: beginning of char 101: 0<=m<=5 0<=n<=5
(initially n=5) paint (1)3
1168: newrow 0 (n=4) paint 1(3)1
1172: newrow 0 (n=3) paint 5
1174: newrow 0 (n=2) paint 1
1176: newrow 0 (n=1) paint 1(3)1
1180: newrow 1 (n=0) paint 3
1182: eoc
.<--This pixel's lower left corner is at (0,6) in METAFONT coordinates
***
* *
*****
*
* *
***
.<--This pixel's upper left corner is at (0,0) in METAFONT coordinates
它完整地拼出了每个字符(字形)的位图。
(不确定输出中“左下”和“左上”的奇怪用法是(由 David Fuchs 和 Knuth 编写的)错误gftype
,还是某种人工制品/笑话。)
位图(例如 PBM 文件)
我第一次了解到 PBM 文件格式是从这篇博文。这非常方便,是一种避免处理每种图像格式的方法。从上面的 PostScript 文件中,Ghostscript 可以生成前面提到的 PBM 文件:
cat hello.ps | gs -sDEVICE=pbm -sOutputFile=hello.pbm -r100 -
生成的hello.pbm
文件开头如下:
P1
# Image generated by GPL Ghostscript (device=pbm)
850 1100
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
等等。上面的每个“块”(几行每行长度为 64,加上另一行上的长度稍长一些)有 850 个数字(0 或 1)。有 1100 个这样的块。(由于纸张尺寸为 8.5 英寸 x 11 英寸,并且我们指定了每英寸 100 个点,因此整个图像为 850 个点 x 1100 个点。)展开这些线并放大到具有与“hello”对应的位的区域后,我们得到该区域的以下位图:
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000110000000000001100110000000000000000000000000
00000000000000010000000000000100010000000000000000000000000
00000000000000010000000000000100010000000000000000000000000
00000000000000010000000000000100010000000000000000000000000
00000000000000011110000111000100010001111000000000000000000
00000000000000010001001000100100010010000100000000000000000
00000000000000010001001111100100010010000100000000000000000
00000000000000010001001000000100010010000100000000000000000
00000000000000010001001000100100010010000100000000000000000
00000000000000111011100111001110111001111000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
(如果这还不足以让您产生好感,请尝试远离屏幕。当然,使用这些位来控制哪些像素处于打开和关闭状态才是可视化它们的“真正”方式。)
无论如何,看起来通过这种方式我们可以得到排版页面的位图,无论在何种分辨率/Metafont 模式下。
值得注意的是,以下是我们从具有矢量(Type 1)字体的 PostScript 文件中获取的同一区域的位图,具有相同的分辨率(100 dpi):
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000111000000000011101110000000000000000000000000
00000000000000011000000000001100110000000000000000000000000
00000000000000011000000000001100110000000000000000000000000
00000000000000011000000110001100110001100000000000000000000
00000000000000011111001101001100110011011000000000000000000
00000000000000011001011001101100110110001100000000000000000
00000000000000011001011111101100110110001100000000000000000
00000000000000011001011000001100110110001100000000000000000
00000000000000011001011000101100110110001100000000000000000
00000000000000111111101101111111111011011000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000
如您所见,它明显更暗。当然,在如此低的分辨率下比较光栅化是没有意义的;重点只是它们不同。它们不同的原因可能有很多;例如,存在哲学和实用方面的考虑,例如是否优化字体形状的准确性或易读性。
未解答的问题
PostScript 文件中位图字体信息的格式是什么?周围是
%DVIPSBitmapFont:
注释吗?我找不到任何文档,但再多做一些实验可能会有所帮助。如果我们要求 Ghostscript 以与生成位图的分辨率完全相同的分辨率呈现 PostScript 文件,那么是否可以保证它将对每个字符使用完全相同的位图(来自
gf
或pk
文件的位图)?我针对 中的五个不同字符中的每一个都验证了这一点hello.ps
,但我不确定是否严格保证。(请注意,Ghostscript 很乐意以任何分辨率为任何字体生成 PBM — 这是它的用途 — 因此这是一个自然的问题。)如果不是,那么这个答案的最后一部分将无效,我们可能应该找到替代方案。但是,gs
如果分辨率匹配,则似乎可以使用相同的位图。更多的实验应该可以证实这一点。