如何在PostScript中插入图片

6
我想在我的PostScript代码中添加图片。
%!PS-Adobe-3.0

/Times-Roman findfont
12 scalefont setfont

50 700 moveto
(text) show
showpage

但我不知道该怎么做。有人能帮忙吗?

你可能会喜欢在comp.lang.postscript中的这个讨论 - luser droog
2
不要忽视懒惰的方法,使用一些现有工具将图像转换为EPS并直接粘贴进去。 - agentp
你需要支持哪些图像格式? - lhf
3个回答

8

简化工作流程

如果只关注如何将一个随意的图片文件转换成嵌入式数据,可以跳过前面内容,从简化工作流程开始阅读。

在Postscript语言参考手册中有详细记录,但其中信息可能会让人有些吃力。就像Ken所说,你需要使用image操作符。我通常使用“老派”的形式:

width height bits-per-pixel matrix proc   image   ‐

对于任意随意的图片文件,你通常希望使用ImageMagick中的convert将其转换为文本格式。当然你也可以直接将其转换为EPS格式,但要学习的话,你必须亲自动手。

% convert image.png image.xbm

这将会给你生成一个类似于以下的文件:
  1 #define glasses_width 320
  2 #define glasses_height 240
  3 static char glasses_bits[] = {
  4   0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  5   0x00, 0x00, 0x00, 0x00, 0x45, 0x65, 0xDB, 0x65, 0xB5, 0x6F, 0xBF, 0xEF,
  6   0xFF, 0xFF, 0xFF, 0xBF, 0xB5, 0xED, 0x3C, 0xBF, 0xB3, 0xDB, 0xAD, 0xF6,
  7   0xE6, 0x4A, 0xAA, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
  8   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xA8, 0x66, 0xD6,
  9   0xDF, 0xF9, 0xF7, 0xBF, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0x7F, 0xFB, 0xEA,
 10   0xDD, 0x5A, 0x9A, 0x69, 0xB9, 0xBE, 0x55, 0x65, 0x00, 0x00, 0x00, 0x00,
...
803   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
804   };

所以,在vi中,要做一些事情,例如:
:%s/^#/%#/         #comment-out the #defines
:g/[{}]/d          #delete the array brackets
:%s/0x//g          #remove the 0x prefixes
:%s/, //g          #remove the spaces

给出类似这样的东西:
  1 %#define glasses_width 320
  2 %#define glasses_height 240 
  3   000000000003000000000000
  4   000000004565DB65B56FBFEF
  5   FFFFFFBFB5ED3CBFB3DBADF6
  6   E64AAABA0000000000020000
  7   000000000000000099A866D6
  8   DFF9F7BFFFFDFFFEFF7FFBEA
  9   DD5A9A69B9BE556500000000
 10   000C00000000000000000000
...
802   000000000000000000000000

然后您使用image调用中的数字,也要修剪这些行,并将数据直接插入到这些行之后

%width height depth [ x-scale x-skew y-skew y-scale x-offset y-offset ]=matrix 
320    240    1     [ 1       0      0      -1      0        240 ]
%  {proc-yielding-string-data}                  call(image)
   { currentfile 80 string readhexstring pop }  image

假设您的位图数据是向下递增的。只要您可以获得一些原始样本的转储,就可以调整此方法以用于其他ASCII格式。嵌入具有解码器代码的压缩图像是一个大问题,建议您暂时避免这样做。(主要是因为我还不知道如何做到这一点。我一直像躲避一大罐虫子一样避免它。:D)
我检查了上面的建议,发现我忘记了一个大问题。Postscript 喜欢将其位图存储在大端字节中。也就是说,第7位是最左边的位,第0位是最右边的位。这与xbm格式相反。因此,上述完成的程序如下:
%!
%reverse the bits in a byte
/reverse {               % b
    dup 1 and            % b b0          % explode the bits
    1 index 2 and        % b b0 b1
    2 index 4 and        % b b0 b1 b2
    3 index 8 and        % b b0 b1 b2 b3
    4 index 16 and       % b b0 b1 b2 b3 b4
    5 index 32 and       % b b0 b1 b2 b3 b4 b5
    6 index 64 and       % b b0 b1 b2 b3 b4 b5 b6
    8 7 roll 128 and     % b0 b1 b2 b3 b4 b5 b6 b7
    -7 bitshift exch     % b0 b1 b2 b3 b4 b5 b7-7=0' b6  % shift and combine
    -5 bitshift or exch  % b0 b1 b2 b3 b4 b0'|b6-5=1' b5
    -3 bitshift or exch  % b0 b1 b2 b3 b0'|b1'|b5-3=2' b4
    -1 bitshift or exch  % b0 b1 b2 b0'|b1'|b2'|b4-1=3' b3
    1 bitshift or exch   % b0 b1 b0'|b1'|b2'|b3'|b3+1=4' b2
    3 bitshift or exch   % b0 b0'|b1'|b2'|b3'|b4'|b2+3=5' b1
    5 bitshift or exch   % b0'|b1'|b2'|b3'|b4'|b5'|b1+5=6' b0
    7 bitshift or        % b0'|b1'|b2'|b3'|b4'|b5'|b6'|b0+7=7'
} def

320 240 1  % width height bitdepth
[ 1 0 0 -1 0 240 ]  % 1-to-1 matrix with descending y, offset by max_y
{ %proc-yielding-string
    currentfile 80 string  % file string
    readhexstring pop  % string         read a line of hex data from THIS FILE
    0 1 2 index length 1 sub  % string 0 1 strlen-1
    {  % string index
        2 copy 2 copy  % str i str i str i
        get reverse    % str i str i rev(str_i)
        put  % str' i
        pop % str'                      % reverse each char (byte)
    } for                               % loop over chars in string
} image
  000000000003000000000000
  000000004565DB65B56FBFEF
  FFFFFFBFB5ED3CBFB3DBADF6
  E64AAABA0000000000020000
  000000000000000099A866D6
  DFF9F7BFFFFDFFFEFF7FFBEA
  ...

完整程序附带完整图像数据,请在此处下载。

更简单的工作流程

更简单但仍需"手动"操作的方法是将其转换为使用与后置脚本相同的位顺序约定的pbm格式。然后,xxd -ps将产生一个"后置脚本"十六进制转储。以下三个示例都使用此方式准备的十六进制数据。但是,此方法仍需要您手动测量标题的长度(使用xxd找到宽度和高度之后的空格字符字节偏移量)。

%!
% swar.ps
%
%image example
%image origin:  http://upload.wikimedia.org/wikipedia/commons/thumb/2/23/Spacewar%21-PDP-1-20070512.jpg/320px-Spacewar%21-PDP-1-20070512.jpg
%
% bash commands to prepare image file:
%
% $ wget http://upload.wikimedia.org/wikipedia/commons/thumb/2/23/Spacewar%21-PDP-1-20070512.jpg/320px-Spacewar%21-PDP-1-20070512.jpg
% $ identify 320px-Spacewar\!-PDP-1-20070512.jpg 
% $ convert 320px-Spacewar\!-PDP-1-20070512.jpg spacewar.pbm
% $ xxd -ps spacewar.pbm > spacewar.asc
% % gs swar.ps


/infile (spacewar.asc)(r)file def
/buf 256 string def

% use $ xxd spacewar.pbm | head
% to find the length of the header and read that length
% into the buffer and discard, leaving only samples.
infile buf 0 16#5d getinterval readhexstring pop pop 

320 215 1
[ 1 0 0 -1 0 215 ]
{ infile buf readhexstring pop } image

showpage

spacewar.asc 是一段相同的丑陋十六进制样本块。

$ head spacewar.asc
50340a2346696c6520736f757263653a20687474703a2f2f636f6d6d6f6e
732e77696b696d656469612e6f72672f77696b692f46696c653a53706163
65776172212d5044502d312d32303037303531322e6a70670a3332302032
31350a007fffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffff007fffffffffffffffffffffffdfffffff
ffffffffffffffffffffffffffffffffffffffffffffff007fffffffffff
ffffffffffff803fffffffffffffffffffffffffffffffffffffffffffff
ffffff007ffffffffffffffffffffffff800ffffffffffffffffffffffff
ffffffffffffffffffffffffff007fffffffffffffffffffffff7fe007ff
ffffffffffffffffffffffffffffffffffffffffffffff007fffffffffff

只要解释器(或者Distiller)没有设置SAFER选项,这个样例块就可以作为外部文件使用,因为这会禁用文件访问操作符。

也可以像上面一样使用currentfile将其嵌入图像调用之后。

%!
/buf 256 string def

320 215 1
[ 1 0 0 -1 0 215 ]
{ currentfile buf readhexstring pop }
{ 
    infile buf 0 16#5d getinterval readhexstring pop pop  % discard header
    image 
} exec
50340a2346696c6520736f757263653a20687474703a2f2f636f6d6d6f6e
732e77696b696d656469612e6f72672f77696b692f46696c653a53706163
65776172212d5044502d312d32303037303531322e6a70670a3332302032
31350a007fffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffff007fffffffffffffffffffffffdfffffff
ffffffffffffffffffffffffffffffffffffffffffffff007fffffffffff
ffffffffffff803fffffffffffffffffffffffffffffffffffffffffffff
ffffff007ffffffffffffffffffffffff800ffffffffffffffffffffffff
ffffffffffffffffffffffffff007fffffffffffffffffffffff7fe007ff
ffffffffffffffffffffffffffffffffffffffffffffff007fffffffffff
%...

或者,只要数据少于64k,你就可以将其读入字符串中。【注意:这是字符串大小的历史性实现限制。我已经收到通知,当前版本的Ghostscript可以很好地处理更大的字符串。】如果您想在文档中多次重复使用图像,则这是有用的。

%!
/imgbuf 320 215 mul 8 div ceiling cvi string def   % create a string for byte storage (<64k)

{ 
    currentfile imgbuf 0 16#5d getinterval readhexstring pop pop  % read header
    currentfile imgbuf readhexstring pop pop                 % read data (discarding header data)
} exec
50340a2346696c6520736f757263653a20687474703a2f2f636f6d6d6f6e
732e77696b696d656469612e6f72672f77696b692f46696c653a53706163
65776172212d5044502d312d32303037303531322e6a70670a3332302032
31350a007fffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffff007fffffffffffffffffffffffdfffffff
ffffffffffffffffffffffffffffffffffffffffffffff007fffffffffff
ffffffffffff803fffffffffffffffffffffffffffffffffffffffffffff
ffffff007ffffffffffffffffffffffff800ffffffffffffffffffffffff
ffffffffffffffffffffffffff007fffffffffffffffffffffff7fe007ff
ffffffffffffffffffffffffffffffffffffffffffffff007fffffffffff
%...

320 215 1
[ 1 0 0 -1 0 215 ]
{ imgbuf }
image

之前我略过的那一部分(矩阵参数)...

在上述所有代码中,我对PLRM的解释有了很大的自由,基本上忽略了一些常见建议,因为这通常会妨碍理解过程,但是这里有一个建议...

使用image运算符的推荐方法是使用不同于上面所示的矩阵类型。实际上,该矩阵由image运算符解释为反向映射。也就是说,要进行放大操作,需要使数值变小,而要进行缩小操作,则要增加数值。你应该使用以下方式(修改上面的最终示例,因为它最短且易于理解,即假设imgbuf已经像上面使用readhexstring函数那样填充了数据)。

这个调用应该更准确地写成:

320 215 scale  % scale 1x1 image to proper dimensions
320 215 1          % "data" dimensions: w h bit-depth
[ 320 0 0 -215 0 215 ]              % inverse mapping
{ imgbuf }   % data-acquisition (yield data in a string)
image

也就是说,矩阵会将图像倒置(这里是一种诗意的“扭曲”感觉,而不是技术上的意义)成一个1单位X 1单位的正方形,这样可以(必须)缩放坐标系以获得每个像素1pt的图像渲染。正确的方法使您拥有更多灵活性:现在您有了320 215 scale行,可以进行合理的缩放计算——但如果您只想要一个1像素对1点的映射,则需要重复自己。
使用正确的代码来将图像的尺寸加倍,只需将320 215 scale替换为640 430 scale(或添加2 2 scale),这是很简单的事情。
320 215 scale
2 2 scale % == 640 430 scale
320 215 1                           % w h bit-depth
[ 320 0 0 -215 0 215 ]              % inverse mapping
{ imgbuf }   % data-acquisition
image

但是通过“hackish”(即粗糙的)方法,你实际上需要将矩阵“halve”(即减半)才能得到增长的倒数。:D

320 215 1
[ .5 0 0 -.5 0 430 ] % "doubled,inverted (ie. halved) with double-offset" matrix
{ imgbuf }
image

xxd -ps 可以为任何文件提供十六进制表示,适用于包含在 PostScript 程序中。 - lhf
@lhf,所以我可以修改这种方法并跳过vi的部分,但是我必须先转换为原始位图格式,对吗?那样就可以让我使用“短格式”调用image了。 - luser droog
文件读取示例可以简化为直接使用.pbm文件,跳过xxd步骤。只需将所有的readhexstring调用替换为readstring即可。这也适用于内联方法。但是,结果是一个包含嵌入式原始二进制数据的非ASCII干净的PS文件。 - luser droog
在此处直接阅读.pbm的示例链接 - luser droog
与部分工作的p{b,g,p}m阅读器相关的问题在此处 - luser droog

5
请参阅PostScript语言参考手册,这不是一个简单的主题,您需要仔细阅读。从第4.10节开始阅读,至少阅读处理类型1图像的部分(4.10.1至4.10.5节)。
以下是一个简单的示例:
/OneComponentString <007ff700> def

/OneComponentImage1
{
<<
/ImageType 1
/Width 2
/Height 2
/ImageMatrix [2 0 0 -2 0 2]
/BitsPerComponent 8
/Decode [0 1]
/DataSource OneComponentString
>>
} bind def

gsave
0 0 moveto
20 20 scale
/DeviceGray setcolorspace
OneComponentImage1 image
grestore

showpage

很好的例子,谢谢!但是无论您在哪里使用moveto进行调整,都不会影响ghostscript,它总是显示在(0, 0)。我必须使用currentpoint translate才能将其放置在其他位置。这是gs的一个错误吗? - jcomeau_ictx
1
嗯,moveto不是用来移动图像位置的,通常你需要通过调整字典中的矩阵来实现。moveto只是标准操作,以避免nocurrentpoint错误。translate同样可以工作,因为它会移动介质上0,0的位置。 - KenS

2

我建议一种简单的方法 - 运行EPS文件。
首先,将图像转换为EPS格式,例如myimage.eps,将其保存在myfolder中,然后修改您的代码以定义placeEPS运算符,并使用它来显示myimage.eps

%!PS-Adobe-3.0

% Place EPS image definition
% Syntax: <file> <scale> <x> <y> placeEPS
% =============================================================
/placeEPS 
{
    userdict begin

    /__be_state     save            def
    /__ds_count     countdictstack  def
    /__os_count     count 5 sub     def
    /showpage       {}              def

    initgraphics
    translate 
    dup scale 
    run

    count           __os_count sub  { pop } repeat
    countdictstack  __ds_count sub  { end } repeat
                    __be_state              restore
    end
} bind def
% =============================================================

% Your old code starts here
/Times-Roman findfont
12 scalefont setfont
50 700 moveto
(text) show

% Now mark myimage.eps scaled to 100% at position 100, 100
(myfolder/myimage.eps) 1 100 100 placeEPS

showpage

现在使用Acrobat Distiller或GhostScript进行PDF转换。
你也可以使用这种方法将全页画布放置到文档中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接