TeX 文件

TeX 文件

这可能是一个奇怪的问题,因为这是大多数用户都会做的事情不是想。

我想用 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 驱动程序(如dvipsdvipdfmx)将获取这些指令,并分别获取字体信息,然后生成实际输出。这是我希望它使用“原始”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 字体。因此,调用会产生 ,并且日志显示它正在使用位图字体(来自我们主目录中的目录)。-Vdvips -V hello.dvihello.pstexmf-var/fonts/pk/ljfour/public/cm/cmr10.600pk

这也是我们可以指定不同的 DPI 和模式的地方模式.mf,例如dvips -V -D100 -mode amiga foo.dvidvips -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 的精彩回答稍微看一下gfpk文件,然后看一下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)

反而。)

我们还可以调用pktogfpk文件,然后gftype在该gf文件上使用。在最大输出时,即使用选项时-images -mnemonicsgftype输出如下:

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

如您所见,它明显更暗。当然,在如此低的分辨率下比较光栅化是没有意义的;重点只是它们不同。它们不同的原因可能有很多;例如,存在哲学和实用方面的考虑,例如是否优化字体形状的准确性或易读性。

未解答的问题

  1. PostScript 文件中位图字体信息的格式是什么?周围是%DVIPSBitmapFont:注释吗?我找不到任何文档,但再多做一些实验可能会有所帮助。

  2. 如果我们要求 Ghostscript 以与生成位图的分辨率完全相同的分辨率呈现 PostScript 文件,那么是否可以保证它将对每个字符使用完全相同的位图(来自gfpk文件的位图)?我针对 中的五个不同字符中的每一个都验证了这一点hello.ps,但我不确定是否严格保证。(请注意,Ghostscript 很乐意以任何分辨率为任何字体生成 PBM — 这是它的用途 — 因此这是一个自然的问题。)如果不是,那么这个答案的最后一部分将无效,我们可能应该找到替代方案。但是,gs如果分辨率匹配,则似乎可以使用相同的位图。更多的实验应该可以证实这一点。

相关内容