PostScript 具有与 PDF 大致相同的过滤器。您不需要解压缩数据,只需使用 PostScript 中的 FlateDecode 过滤器,并保持压缩数据不变。
请注意,Predictor 15(或任何其他 PNG 预测器)需要语言 3 级,但这不成问题,18 年以来,3 级一直是标准。
否则,您需要实现支持 PNG 预测器的 FlateDecode 过滤器版本。我相信 zlib 完全可以做到这一点。
[编辑]
您的“PostScript 输出”不完整,您正在使用尚未提供定义的 PDF 运算符(q 和 Q)。除此之外,这使得无法通过解释器运行代码。请按要求提供一个完整简单的示例文件。不粘贴代码,我不打算自己去创建文件,而且二进制文件根本不会剪切和粘贴。
从桌面检查中我无法立即发现问题,但由于我无法运行代码,我很容易遗漏一些东西。
[编辑 2]
不出所料,该文件运行良好。
您尚未提供您正在创建的 PostScript 文件。通过查看您开始使用的 PDF 文件,我很难判断您创建的 PostScript 有什么问题。
当然,您可以使用 Ghostscript(我看到您已经使用它来创建 PDF 文件)来创建 PostScript 文件,然后查看其中包含的内容。如果设置 -dCompressFonts=false 则输出字体甚至不会被压缩。
例如:
37 0 4 -52 33 -1 d1
0.01 0 0 0.01 0 0 cm
q 2900 0 0 -5100 400 -99.9998 cm
BI
/IM true
/W 29
/H 51
/BPC 1
/D[1
0]
/F[/A85
/CCF]
/DP[null
<</K -1
/Columns 29>>]
ID
-D=,M5m+t^0_>op8\HM"Du]KKrr2rthqG/5qU_ik]$f$TlUslD91qoN93j0%dckk:ld^*DV25!+
!WX>~>
EI Q
当然,您需要查看序言以了解其中使用的所有过程是如何定义的,但是您可以自己做,当然不需要我做。请注意,图像掩码使用 CCITTFax 和 ASCII85 解码过滤器,添加其他过滤器很简单。由于数据保证为“单色”(它是一个掩码),因此 CCITT 过滤器通常会提供优于 Flate 的压缩率。
请注意,如果您真的使用的是 Ghostscript 9.05,那么您应该升级,也就是 6 岁。
如果您要解释为什么要从 PDF 中获取丑陋的位图 type 3 字体并从中制作丑陋的位图 type 3 PostScript 字体,这可能会有所帮助。
[编辑 3]
仔细查看您的 PostScript 文件,字形的定义与您在问题中提出的不匹配。实际内容是这样的:
/g10135{
88 0 4 -70 82 8 setcachedevice
q
[
0.01 0 0 0.01 0 0 ] M
q
[7800 0 0 -7800 400 800 ]M
<<
/ImageType 1
/Width 78
/Height 78
/ImageMatrix [ 78 0 0 -78 0 78]
/BitsPerComponent 1
/Decode [1
0]
/DataSource ....binary data.....
<< /Predictor 15
/Columns 78
/BitsPerComponent 1>>
/FlateDecode filter def
>> imagemask
Q
Q
}bind def
您没有提供文件、过程或字符串源作为字典中 DataSource 键的值。本质上,PostScript 解释器读取并标记/DataSource 键,然后继续将二进制文件作为 PostScript 处理。不出所料,这会在使用 Ghostscript 处理时导致错误“(二进制令牌,类型 = 156)中的语法错误”。
如果您已经解决了这个问题,那么您会发现 filter 运算符也接受一个数据源,而您也没有为此提供一个。
所以你需要为你的二进制数据创建一个数据源。如何做到这一点取决于您,但currentfile 是一种方式。或者 readstring 假设您知道字符串长度。
比如:
<<
/ImageType 1
/Width 29
/Height 51
/ImageMatrix [29 0 0 -51 0 51]
/BitsPerComponent 1
/Decode [1 0]
/DataSource
<length> string dup
currentfile exch readstring
.....binary data.....
<<
/Predictor 15
/Columns 29
>> /FlateDecode filter
>> imagemask
显然,您必须通过知道字符串长度来填写自己。在我看来,FlateDecode 的字典参数似乎不需要它。
[编辑 4]
我注意到这似乎是为了商业用途。这没有错,但我不会为你做所有的功课,如果你的工作是由你来学习语言以完成这项工作。
我将略略跳过下面的实际实现细节,试图概述您哪里出错了。实际情况稍微复杂一些,我没有讨论存储在 CharStrings 字典中的过程是如何创建的,或者与早期名称绑定(这是 PostScript 中的一个重要概念)的区别。
您现有的代码是:
/g10135{
88 0 4 -70 82 8 setcachedevice
q
[
0.01 0 0 0.01 0 0 ] M
q
[7800 0 0 -7800 400 800 ]M
<<
/ImageType 1
/Width 78
/Height 78
/ImageMatrix [ 78 0 0 -78 0 78]
/BitsPerComponent 1
/Decode [1
0]
/DataSource {417 string dup
currentfile exch readstring}
...binary data....
<< /Predictor 15
/Columns 78
>>/FlateDecode filter def
>> imagemask
Q
Q
}bind def
因此,PostScript 解释器一次读取一个字节,并将它们转换为标记。这要么产生一个可执行的令牌,它被执行,要么产生一个堆栈上的操作。
所以/g10135 由{ 字符终止,因为这是一个保留字符。 / 引入了一个名称对象,因此我们最终得到了名称对象 g10135,我们将其推入操作数堆栈。 { 字符引入了一个可执行数组,因此我们将 mark 放在操作数堆栈上。
接下来我们读取 88,以空格字符结尾。那是一个数字,所以我们将它存储在操作数堆栈中,就像其他数字一样。操作数堆栈现在包含:
/g10135
mark
88
0
4
-70
82
8
然后我们读取 setcachedevice,它以空格结尾。这不是标准标记,因此解释器开始查看字典堆栈上的字典,寻找定义。由于是标准运算符,我们在systemdict中找到并执行。这从操作数堆栈中消耗了 6 个操作数,它没有其他效果(实际上它确实有,但这有点特殊,因为我们在字体内执行,但我们现在将忽略它)。
接下来我们遇到q,再次在字典堆栈上的每个字典中查找它以找到定义。这在您自己的序言中定义为gsave,因此它不接受操作数并且不返回操作数,它只是保存图形状态,将保存深度增加 1。
剩下的我就不赘述了,这会很乏味,但是,最终我们到达了你的/DataSource,这是一个名字,所以我们将它压入操作数堆栈。接下来我们遇到的是{,这是一个过程定义,所以我们在操作数堆栈上压入一个标记。然后我们遇到417,所以我们推送它,string、dup、currentfile、exch 和 readstring,所以我们的堆栈看起来像:
/DataSource
mark
417
string
dup
currentfile
exch
readstring
然后我们得到字符},这是一个可执行数组的结束标记,所以我们创建数组并将其压入操作数堆栈:
/DataSource
{....}
然后我们返回程序并继续执行它。接下来我们找到一些二进制数据,因此我们尝试将其作为 PostScript 二进制标记来执行。因为它无效,解释器会抛出错误。
仅仅创建一个可执行数组并不足以实际执行它。如果您查看我在上面编辑 3 末尾发布的大纲代码,您会注意到我确实没有将readstring 等放在可执行数组中,我只是让解释器执行该代码立即。
通过这样做,readstring 作用于currentfile(在这种情况下是实际的 PostScript 程序)并从该文件中的当前点读取数据字节。当前点将在消耗终止readstring 的空白之后立即出现,即实际的二进制数据。 readstring 运算符从文件中读取足够的字节来填充字符串,将字符串留在操作数堆栈上。文件指针移动到二进制数据之后的字节,解释器在该点恢复令牌扫描。因此,它创建 FilterParams 字典,将 /FlateDecode 名称放入堆栈,然后执行 filter 运算符,该运算符使用名称、字典和字符串操作数,返回一个文件对象。然后,该文件对象成为与图像字典中的 DataSource 键关联的值,该值被传递给 imagemask 运算符。
虽然我没有测试过该代码,但它基本上是正确的。当然还有其他方法可以达到同样的目的。
这基本上是我准备好的,你需要去看看我写的东西,然后和你自己的程序比较。
请注意,调查此问题的最简单方法是获取 CharProc 的内容(不包括 setcachedevice),然后将其作为 PostScript 程序运行。