【问题标题】:Type 3 fonts conversionType 3 字体转换
【发布时间】:2018-07-11 18:46:50
【问题描述】:

我正在将 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ôíØªÄ~Ø•Œöí&´« +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

【问题讨论】:

    标签: pdf ghostscript acrobat postscript xpdf


    【解决方案1】:

    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,所以我们推送它,stringdupcurrentfileexchreadstring,所以我们的堆栈看起来像:

    /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 程序运行。

    【讨论】:

    • 预测器本身不是FlateDecode的一部分。 PDF 32000-1:2008 说“[LZW 和 Flate] 支持通过预测函数进行可选转换,这改进了采样图像数据的压缩。” /Predictor 键在 LZWDecodeFlateDecode 流描述符中是可选的。
    • 那是因为预测器信息在编码数据中,所以您不需要(对于 PNG 预测器)指定它。这就是为什么我没有说 PostScript FlateDecode 需要它。但是如果你想自己解压,你需要一些能够理解特定预测函数的东西。这也是为什么 PNG 预测器需要 PostScript 语言级别 3 的原因,早期版本的规范不允许这样做。 PDF 参考中关于它的内容并不完全相关,因为我们正在讨论转换为 PostScript。
    • 如何写入内联图像流,因为我可以在 或 中写入数据,显示 ascii85 字符串或其他但我的数据是原始数据。我如何在数据源中指定。数据在 pdf 中看起来像这样 /DP> ID xœUÒAkAðÿ¸bRÝŃ•R²iõn=L›)ö(^÷,=øZO ld”zìI¯ñØ£)YH%%ÁIÈAK6iÁÉ–Š]Í ¶¾™](ûÛÙyo÷½†«±ØD¶£°ü ¼®ÛS
    • 这取决于你,不是吗?您可以将其编写为 Ascii85,在这种情况下,您需要将该过滤器添加到链中,或者您可以编写一个十六进制字符串,或者您可以使用纯二进制。您可以将数据放在一个字符串中,或​​者只是从当前文件中读取它。选择完全由您决定。如果你想创建一个 PostScript 表示,那么你将不得不编写一些 PostScript。
    • 如果您是 PostScript 新手,那么我怀疑我能否进一步解释这一点。是的 BuildGlyh 执行 CharProc,但这仅仅意味着 PostScript 解释器执行存储在字体的 CharStrings 字典中的过程。该程序必须仍然有效,而您的程序无效。字符串的长度不是问题,这是您创建了一个过程的事实。我会尝试在答案中解释更多。
    猜你喜欢
    • 2011-01-06
    • 2020-09-21
    • 2022-10-17
    • 2012-06-10
    • 2011-04-08
    • 1970-01-01
    • 2017-02-23
    • 2016-05-31
    • 2015-09-24
    相关资源
    最近更新 更多