0

我正在将 Type3 字形字体从 Pdf 解析为 postscript。输入文件具有应用了数据流 flate 解码过滤器的内联图像。过滤器具有预测器 15。任何人都可以帮助我将图像流从 pdf 格式转换为 postscript。这就是在 pdf 中给出输入流的方式

32 0 obj 
    <<
    /Length 342
    >>
    stream
    37 0 4 -52 33 -1 d1
    0.01 0 0 0.01 0 0 concat
    gsave 2900 0 0 -5100 400 -100 concat
    BI
    /IM true
    /W 29
    /H 51
    /BPC 1
    /D[1
    0]
    /F/Fl
    /DP<</Predictor 15
    /Columns 29>>
    ID xœ=Ì¡
    Â`ÅñÿeÂLθ n`0>Ù`ñ
    f[¦DŒF_ÁhC1ì%Ä)¶o.¢Ÿ"†ßá†s®àì]^ÏŠÅS³tFËÂÚ3sç'Æi èÐÇ:j‹¹¨åìOTÿ ª•ÉÙÕÅŸ¨‡¹Ó$°ÆΚWèÁ!¯Cê
    ÷0&f    µtðV ©Ë÷iôíتÄ~Ø•Œöí&amp;´« +ro#Ê‚ûÏÅùlßG'
    EI gRestore

    endstream 
    endobj 

这就是我试图在 Postscript 的输出中写的内容

/g21 {
37 0 4 -52 33 -1 setcachedevice
q
[0.01 0 0 0.01 0 0] concat
q
[2900 0 0 -5100 400 -100] concat
[ xœ…ѱNÃ0à3©p'l` ¢abä*‰'@‚W`KP¡00öQ`d@ ¨CWž€u`‰štj4Ü]@ /ù¤œíÿ| ÂìÊüå7úŠ‰V'‚ª¦zò¡9à*´º
m1Õ`ñ—íü‹­‡½Gù@ãÝAVxc¥Ž®"6oFܬJHÃB3(æod¾…xFP†o$!v±Ã»·0—gØY÷J$û„`´#zÊ
Oí¼œÑ¸é`Ê}ü…ñ.Z¯›cF4\¡*O¤ÑPÒYòî¦/éG‘qÑç¼2>öq<Üœ<
B˜5‚²¢ºÎ/èqUTUàoÓ9͔Π܉ä²z ‡S×ÛÙC(PA²š7è­T¾ŽCGÈRaLéåksnˆÃ0z<zø:ž=
]
0
<<
  /ImageType 1
  /Width 29
  /Height 51
  /ImageMatrix [29 0 0 -51 0 51]
  /BitsPerComponent 1
  /Decode [1 0]
  /DataSource { 2 copy get exch 1 add exch }
    <</Predictor 15
    /Columns 29 
    >>
    /FlateDecode filter
>>
imagemask
pop pop
gRestore
gRestore
} def
4

1 回答 1

0

PostScript 具有与 PDF 大致相同的过滤器。您不需要解压缩数据,只需使用 PostScript 中的 FlateDecode 过滤器,并保持压缩数据不变。

请注意,Predictor 15(或任何其他 PNG 预测器)需要语言级别 3,但这应该不是问题,级别 3 已经是 18 年的标准。

否则,您需要实现支持 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我们推送到操作数堆栈的名称对象。该{字符引入了一个可执行数组,因此我们将 amark放在操作数堆栈中。

接下来我们读到 88,以一个空格字符结束。那是一个数字,所以我们将它存储在操作数堆栈中,就像其他数字一样。操作数堆栈现在包含:

/g10135
mark
88
0
4
-70
82
8

然后我们读取以空格结尾的 setcachedevice。这不是标准标记,因此解释器开始查看字典堆栈上的字典,寻找定义。由于是标准运算符,我们在systemdict中找到并执行。这消耗了操作数堆栈中的 6 个操作数,它没有其他效果(实际上它有,但这有点特殊,因为我们在字体内执行,但我们现在将忽略它)。

接下来我们遇到 a q,再次在字典堆栈上的每个字典中查找它以找到定义。这在您自己的序言中定义为 a gsave,因此它不接受操作数并且不返回任何操作数,它只是保存图形状态,将保存深度增加 1。

剩下的我就不赘述了,但是,最终我们到达了 your /DataSource,这是一个名字,所以我们将它压入操作数堆栈。接下来我们遇到的是{一个过程定义,所以我们在操作数堆栈上压入一个标记。然后我们遇到 a417所以我们推送那个 , string, dup, and currentfile,所以我们的堆栈看起来像:exchreadstring

/DataSource
mark
417
string
dup
currentfile
exch
readstring

然后我们得到字符}That 是可执行数组的结束标记,因此我们创建数组并将其压入操作数堆栈:

/DataSource
{....}

然后我们返回程序并继续执行它。接下来我们找到一些二进制数据,因此我们尝试将其作为 PostScript 二进制标记来执行。因为它无效,解释器会抛出错误。

仅仅创建一个可执行数组并不足以实际执行它。如果您查看我在上面编辑 3 末尾发布的大纲代码,您会注意到我没有readstring放在可执行数组中,我只是让解释器立即执行该代码。

通过这样做,readstring操作currentfile(在这种情况下为实际的 PostScript 程序)并从该文件中的当前点读取数据字节。当前点将在消耗终止 的空白之后立即出现readstring,即实际的二进制数据。运算符从文件中读取足够的readstring字节来填充字符串,将字符串留在操作数堆栈上。文件指针移动到二进制数据之后的字节,解释器在该点恢复令牌扫描。因此,它会创建 FilterParams 字典,将 /FlateDecode 名称放在堆栈上,然后执行filter使用名称、字典和字符串操作数的运算符,返回一个文件对象。然后,该文件对象成为与传递给操作员的图像字典中的 DataSource 键关联的值imagemask

虽然我没有测试过该代码,但它基本上是正确的。当然还有其他方法可以达到同样的目的。

基本上就我准备好了,你需要去看看我写的东西,并将它与你自己的程序进行比较。

请注意,调查此问题的最简单方法是获取 CharProc 的内容(不包括 setcachedevice),然后将其作为 PostScript 程序运行。

于 2018-02-01T14:30:57.573 回答