什么是PostScript词典,以及如何访问它们(通过Ghostscript)?

sda*_*aau 7 stack dictionary postscript ghostscript

我通常将其ghostscript视为命令行工具; 然而,我从不停止对那里存在的大量设置和选项感到惊讶 - 这是因为这ghostscript是一个完整的PostScript语言解释器(我经常忘记).

例如,在查询Ghostscript中输出设备的默认选项/设置(例如'pdfwrite'或'tiffg4') ; 一个人学习如何检索给定输出设备的默认选项.但是,我想知道的是 - 这些选项是否与所谓的PostScript词典相关?

或者,换句话说 - 什么是PostScript词典; 什么设施ghostscript有,查询(并可能)修改这些数据?

Kur*_*fle 9

用最简单的术语来说:在PostScript中,字典是 键(名称)+值对的列表.字典允许PostScript解释器查找密钥是否存在并获取其值以在任何过程中使用它.解释器还可以创建键,存储或修改值,甚至可以创建完整的自定义词典(由其处理的PostScript代码决定).键通常是name类型(但它们可以是任何其他类型,但null除外).

对于PostScript解释器的任何实现,必须始终存在其中两个词典:

  • systemdict 这个包含预定义的PostScript运算符(以及使它们执行PostScript规范期望它们的实现).

  • userdict 这个包含PostScript程序的变量和过程(将'过程'视为由语言定义的运算符和程序定义的值和参数的组合构成的函数或子例程).

关于名称的一个词:名称是其他编程语言是uniq 标识符(它们区分大小写).这些标识符可以是变量或过程名称.它们可以由ASCII的256个字符的任意组合组成(但它们不是字符串).

您可能知道,PostScript是一种面向堆栈的语言.它使用几个堆栈:

  • 操作数堆栈 该堆栈保存每个操作数和中间操作的每个结果(将最后一个结果暂时转换为操作数堆栈的最顶层元素).

  • 字典堆栈 如名称所示:此堆栈仅包含字典.因此,堆栈定义任何键/名称查找的当前上下文.

  • 执行堆栈 这个包含可执行对象,即主要是当前正在执行的过程文件.如果解释器中断当前对象的执行,它会将中断的对象放入此堆栈.一个对象完全执行后,它将从堆栈中删除,并继续执行现在最顶层的对象.

  • 图形状态堆栈 此堆栈承载弹出图形元素的当前上下文:当前行宽设置,当前字体,当前颜色或灰度值,当前路径...当前图形状态可以保存(gsave)并grestore稍后恢复().最顶层的图形状态始终是当前的图形状态.

所有这些堆栈彼此独立.但是,操作数,字典和图形状态堆栈在PostScript程序的控制之下(也就是说,可以由它操纵).执行堆栈是解释器的唯一属性.

对于每个堆栈,存在某些限制(关于可以存储在其上的元素的数量等).的PostScript知道哪些可以操纵堆栈操作符:把堆栈上的新元素,去掉最上面的元素(pop),重复最上面的元素(dup),洗牌元素的顺序栈中的(roll),交换两个顶moste elements(exch)等等(PostScript编程的一个很好的介绍是来自Adobe 的'Bluebook').

正如我已经说过的,字典有自己的堆栈,其中包含PostScript解释器可能使用的所有字典.

在该叠层可以有字体的单独的字典,或者任意数量的词典的程序的PostScript想要创建(使用dict关键字)和私人使用,或一些字典特定于某个PostScript解释,如Ghostscript的.

systemdict始终是最底层的一个; 以上是这个userdict.这两个不能从字典堆栈中删除,因为所有其他的都可以受任何堆栈操作操作符(例如pop从堆栈中删除最顶层的元素).

每当解释器查找名称时,它会从词典中搜索该名称,从最顶层的词典开始.因此userdict之前被搜查过systemdict.一旦找到名称(一个键),解释器就会停止搜索并使用该键(或者更确切地说,它保存的值).这种架构的结果是PostScript程序员可能会覆盖systemdict使用自己的变体预定义的任何PostScript运算符.

此外,一些词典可以用于PS程序"私有"(无访问,如字体词典)或"只读".


更新 - 更多答案:


Kur*_*fle 7

其他答案已经涵盖了"什么是字典?" 你问题的一部分.现在让我们转向"Ghostscript如何访问它们?"

也许问题应该是:"我怎么能(高级用户,开发人员,极客......)访问它们?"

您可以通过编写简单的PostScript程序单行程序打印出PostScript解释程序(可能是Ghostscript)已知的任何可访问字典的内容,或者通过简单地调用解释程序(Ghostscript)并将程序代码移交给命令行(-c ...).

您只需要知道相应字典的名称.

让我们看看一个有趣的内部Ghostscript字典,名为.distillersettings:

gs \
 -dNODISPLAY \
 -c ".distillersettings {exch ==only ( ) print ==} forall quit"
Run Code Online (Sandbox Code Playgroud)

结果:

/default -dict-
/prepress -dict-
/PSL2Printer -dict-
/ebook -dict-
/screen -dict-
/printer -dict-
Run Code Online (Sandbox Code Playgroud)

乍一看,这可能不会告诉你太多.但是你可能认识一些关键的名字在词典:/prepress,/printer,/screen,/ebook...

所有这些都可以在Ghostscript命令行上使用,以便在需要输出-sDEVICE=pdfwrite(Ghostscript'Distiller'相似的功能)时请求一组预定义的设置.要询问这样一组设置,只需添加-dPDFSETTINGS=/printer到命令行即可.

乍看之下,你会看到.distillersettings字典的内容基本上是一组6个字典.这是一本'词典词典'.

默认情况下不打印字典内容(不使用上面的PostScript代码).但是如果你想要它们,你可以在上面的命令中使用一个名为Ghostscript的程序===而不是标准的PostScript语言运算符==.此过程与==execpt的行为相同,它还会扩展字典并打印其中包含的所有键:值对.

要小心与===方法:-dict-你试图扩大可veeeeeery长,可能会导致你失去你的视力.:-)

在我们目前的情况下,它仍然是可管理的:

gs \
 -dNODISPLAY \
 -c ".distillersettings {exch ==only ( ) print ===} forall quit"
Run Code Online (Sandbox Code Playgroud)

现在的输出是:

 /default << /Optimize false /DoThumbnails false /PreserveEPSInfo true /ColorConversionStrategy /LeaveColorUnchanged /DownsampleMonoImages false /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments true /GrayACSImageDict << /HSamples [2 1 1 2] /VSamples [2 1 1 2] /QFactor 0.9 /Blend 1 >> /DownsampleColorImages false /PreserveOverprintSettings true /CreateJobTicket false /AutoRotatePages /PageByPage /NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats] /ColorACSImageDict << /HSamples [2 1 1 2] /VSamples [2 1 1 2] /QFactor 0.9 /Blend 1 >> /DownsampleGrayImages false /UCRandBGInfo /Preserve >>
 /prepress << /DoThumbnails true /MonoImageResolution 1200 /ColorImageDownsampleType /Bicubic /PreserveEPSInfo true /ColorConversionStrategy /LeaveColorUnchanged /GrayImageDownsampleType /Bicubic /EmbedAllFonts true /CannotEmbedFontPolicy /Error /PreserveOPIComments true /GrayImageResolution 300 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /ColorImageResolution 300 /PreserveOverprintSettings true /CreateJobTicket true /AutoRotatePages /None /MonoImageDownsampleType /Bicubic /NeverEmbed [] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /CompatibilityLevel 1.4 /UCRandBGInfo /Preserve >>
 /PSL2Printer << /DoThumbnails false /CompatibilityLevel 1.2 /TransferFunctionInfo /Preserve /MonoImageResolution 1200 /PreserveEPSInfo true /CompressFonts true /ColorImageDownsampleType /Bicubic /GrayImageDownsampleType /Bicubic /ColorConversionStrategy /LeaveColorUnchanged /EmbedAllFonts true /ColorACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /CannotEmbedFontPolicy /Error /PreserveOPIComments true /CompressPages true /GrayImageResolution 600 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.15 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /ColorImageResolution 600 /PreserveOverprintSettings true /AutoRotatePages /None /MonoImageDownsampleType /Bicubic /ASCII85EncodePages true /MaxViewerMemorySize 8000000 /NeverEmbed [] /PreserveHalftoneInfo true /UCRandBGInfo /Preserve >>
 /ebook << /DoThumbnails false /MonoImageResolution 300 /ColorImageDownsampleType /Bicubic /PreserveEPSInfo false /ColorConversionStrategy /sRGB /GrayImageDownsampleType /Bicubic /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments false /GrayImageResolution 150 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /ColorImageResolution 150 /PreserveOverprintSettings false /CreateJobTicket false /AutoRotatePages /All /MonoImageDownsampleType /Bicubic /NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /CompatibilityLevel 1.4 /UCRandBGInfo /Remove >>
 /screen << /DoThumbnails false /MonoImageResolution 300 /ColorImageDownsampleType /Average /PreserveEPSInfo false /ColorConversionStrategy /sRGB /GrayImageDownsampleType /Average /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments false /GrayImageResolution 72 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /ColorImageResolution 72 /PreserveOverprintSettings false /CreateJobTicket false /AutoRotatePages /PageByPage /MonoImageDownsampleType /Average /NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.76 /Blend 1 /HSamples [2 1 1 2] /VSamples [2 1 1 2] >> /CompatibilityLevel 1.3 /UCRandBGInfo /Remove >>
 /printer << /DoThumbnails false /MonoImageResolution 1200 /ColorImageDownsampleType /Bicubic /PreserveEPSInfo true /ColorConversionStrategy /UseDeviceIndependentColor /GrayImageDownsampleType /Bicubic /EmbedAllFonts true /CannotEmbedFontPolicy /Warning /PreserveOPIComments true /GrayImageResolution 300 /GrayACSImageDict << /ColorTransform 1 /QFactor 0.4 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /ColorImageResolution 300 /PreserveOverprintSettings true /CreateJobTicket true /AutoRotatePages /None /MonoImageDownsampleType /Bicubic /NeverEmbed [] /ColorACSImageDict << /ColorTransform 1 /QFactor 0.4 /Blend 1 /HSamples [1 1 1 1] /VSamples [1 1 1 1] >> /CompatibilityLevel 1.4 /UCRandBGInfo /Preserve >>
Run Code Online (Sandbox Code Playgroud)

仍然不那么好.所以让我们试着让它变得更好.我们可以这样做的方法是修改我们的PostScript代码:我们现在告诉它访问.distillersettings字典并从中获取其中一个键的值(让我们使用/screen).由于我们知道该值是另一个字典,我们知道我们将获得另一组键:值对,我们将能够以与之前相同的方式进行格式化:

gs \
 -q \
 -dNODISPLAY \
 -c ".distillersettings /screen get {exch ==only ( ) print ===} forall quit"
Run Code Online (Sandbox Code Playgroud)

现在这看起来更好,不是吗?看看自己:

/DoThumbnails false
/MonoImageResolution 300
/ColorImageDownsampleType /Average
/PreserveEPSInfo false
/ColorConversionStrategy /sRGB
/GrayImageDownsampleType /Average
/EmbedAllFonts true
/CannotEmbedFontPolicy /Warning
/PreserveOPIComments false
/GrayImageResolution 72
/GrayACSImageDict -dict-
/ColorImageResolution 72
/PreserveOverprintSettings false
/CreateJobTicket false
/AutoRotatePages /PageByPage
/MonoImageDownsampleType /Average
/NeverEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica     /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats]
/ColorACSImageDict -dict-
/CompatibilityLevel 1.3
/UCRandBGInfo /Remove
Run Code Online (Sandbox Code Playgroud)

正如你的敏锐眼睛已经发现的那样:一些关键值也是字典.你可以自由地再次使用上面的命令,这次===代替第二个命令==来解决/GrayACSImageDict -dict-可能一直隐藏的谜团......

在任何情况下,现在您只需使用-dPDFSETTINGS=/screen而不是枚举此/screen字典中嵌入的所有单个参数,就可以知道您在键入时节省的内容...

如果您想要一般的"屏幕"质量输出,您还知道需要覆盖哪个单值,但是所有字体都嵌入了差异 :

gs \
 -o out.pdf \
 -sDEVICE=pdfwrite \
 -dPDFSETTINGS=/screen \
 -c "<</NeverEmbed [ ] /AlwaysEmbed [/Courier /Courier-Bold /Courier-Oblique /Courier-BoldOblique /Helvetica /Helvetica-Bold /Helvetica-Oblique /Helvetica-BoldOblique /Times-Roman /Times-Bold /Times-Italic /Times-BoldItalic /Symbol /ZapfDingbats]>> setdistillerparams" \
 -f input.pdf
Run Code Online (Sandbox Code Playgroud)

如果只知道它使用的词典的名称,你可以通过这种方式探索很多有趣的Ghostscript内部.:-)


lus*_*oog 5

已经有很多好的答案,但是没人提到这个:

在调用ghostscript时,-d-s选项在systemdict中创建初始定义.这允许您对postscript程序进行参数化调用.

用于-dname[=token]将值设置为null或数字(或任何其他单个postscript标记).使用-sname=string设置一个字符串值(在大多数情况下工作,以及作为名字).

并且您可以使用正确的运算符在一定程度上操纵所有堆栈.

  • token 从字符串或文件推送到操作数堆栈(这是解释器循环用于使用程序流的内容,因此无论是通过文件输入代码还是直接从键盘输入代码,这都是您正在使用的内容)
  • pop 从操作数堆栈中丢弃
  • begin 推到dict堆栈
  • end 从dict堆栈弹出
  • run,exec,%procedure-invocation推到堆栈EXEC
  • exit,stop弹出或清除exec堆栈
  • gsave 在图形堆栈上推送gstate
  • grestore 弹出图形堆栈
  • save 推送所有VM内容的副本(所有dicts和数组,但不是字符串)
  • restore 将内存倒回到已保存状态(将所有dicts和数组恢复到以前的状态)

作为复合对象的字典继承了所有复合对象共有的许多运算符.

  • -typename- 创建对象,例如 dict
  • length 报告对象的大小
  • put 插入一个元素
  • get 检索一个元素
  • copy 使用另一个对象的内容填充对象
  • forall 为每个元素做点什么
  • *loadalternate元素(对于字典,load使用where然后执行搜索get;对于数组,aload在操作数堆栈上溢出数组的全部内容)
  • *storealternate insert元素(对于字典,store使用where然后执行搜索,put如果找不到;或者def如果没有;对于数组,astore从堆栈中的对象填充数组)

对于这个套件,词典添加

  • def 放入当前字典(dict堆栈顶部)
  • known 查询元素的字典
  • where 查询元素的所有词典
  • maxlength PS Level 2添加自动扩展词典和gc后不再有趣
  • dictstack 将dictstack复制到一个数组中(也许你想自下而上搜索,你可以!)
  • names not preceded by a slash /会自动load编辑,如果可执行,则执行
  • //token构造postscript对象时,任何以双斜杠开头的名称都被load编辑并替换到过程数组中.这非常强大,因为您可以模仿Lisp宏.

编辑:还有一件事.创建字典时,在选择字典大小时会有时间/空间权衡.字典几乎肯定是作为哈希表实现的(除了最简单的解释器之外),大多数哈希函数可以避免在表大约半满时发生冲突(经验法则:使用双倍大小的dicts来表示速度).当然,自第2级起,当你添加大小+ 1个元素时,字典会自动增长,大概是通过分配一个k*大小的新字典(其中k大概是1.5或2); 但手动控制尺寸可以提高速度.在1级,如果你不乘引用您的字典,你可以安装一个替代dictfullerrordict成长的字典并重新执行看跌期权(或DEF或其他).由于level-2在内部执行此操作,因此它可以替换所有引用.

  • 真棒,@ luserdroog - 非常感谢您的澄清!干杯! (2认同)