如何在PostScript中确定字符串高度?

13

我需要在PostScript中确定一个字符串(在给定的比例和字体下)的高度。

/Helvetic-Oblique findfont
10 scalefont
setfont
10 10 1 0 360 arc fill
10 10 moveto (test) dup stringwidth pop 2 div neg 0 rmoveto show

将会在(10,10)处水平居中打印“test”(但还没有垂直居中)。为了查看此效果,我还在(10,10)处显示了一个小圆圈。我还需要确定字符串高度以便将文本垂直居中,但我找不到相应的函数。

4个回答

8
您是否熟悉您正在使用的PostScript代码?还是只是从某个地方盲目复制粘贴的?如果您想了解它,应该谷歌搜索“PostScript语言参考”或“红皮书”或“PLRM”。这些资源可以作为Adobe的PDF文件获得。
您的PostScript片段使用以下步骤:
1. (test) 将字符串“test”放置在堆栈顶部。 2. dup 复制堆栈上最顶部的项目。(现在您在堆栈上有两个相同的字符串。) 3. stringwidth。执行此运算符后,最顶部的“test”字符串将被消耗,并且将添加两个值到堆栈中:字符串的高度(最顶部)和字符串的宽度(次顶部)。[更新:实际上,“字符串的高度”并不完全正确 - 它实际上是绘制字符串后当前点的垂直偏移量...] 4. 接下来,您使用pop。这只是删除堆栈上最顶部的值。现在堆栈顶部仅剩字符串的宽度。 5. 2 div 将该值除以2,并留下结果(字符串宽度的一半)。 6. neg 取反堆栈上最顶部的值。现在,负值位于堆栈顶部。 7. 0 将值“0”放置在堆栈顶部。 8. rmoveto 然后消耗堆栈上最顶部的两个值,并将当前点移动该距离(字符串宽度的一半)向左。 9. show 消耗了始终保留在堆栈底部的第一个“test”字符串,并“显示”它。
那么如何考虑字符串的高度才能起作用?尝试将以下行作为最后一行:
200 700 moveto (test) dup true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll sub 2 div exch 200 700 moveto rmoveto show"

要理解我的更改,请查阅《红皮书》中的charpathdivexchpathbboxrollsub运算符的含义。
此命令在Windows上使用Ghostscript从代码创建PDF文件(更易于查看和检查结果)。
 gswin32c.exe ^
      -o my.pdf ^
      -sDEVICE=pdfwrite ^
      -c "/Helvetic-Oblique findfont 10 scalefont setfont 200 700 1 0 360 arc fill 0 0 moveto (test test) dup true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll sub 2 div exch 200 700 moveto rmoveto show"

在Linux上使用:
 gs \
      -o my.pdf \
      -sDEVICE=pdfwrite \
      -c "/Helvetic-Oblique findfont 10 scalefont setfont 200 700 1 0 360 arc fill 0 0 moveto (test test) dup true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll sub 2 div exch 200 700 moveto rmoveto show"

更易读的表单应该是这样的:
  gswin32c ^
     -o my.pdf ^
     -sDEVICE=pdfwrite ^
     -c "/Helvetic-Oblique findfont 10 scalefont setfont" ^
     -c "200 700 1 0 360 arc fill 0 0 moveto (test test) dup" ^
     -c "true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll" ^
     -c "sub 2 div exch 200 700 moveto rmoveto show"

并且

  gs \
     -o my.pdf \
     -sDEVICE=pdfwrite \
     -c "/Helvetic-Oblique findfont 10 scalefont setfont" \
     -c "200 700 1 0 360 arc fill 0 0 moveto (test test) dup" \
     -c "true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll" \
     -c "sub 2 div exch 200 700 moveto rmoveto show"

charpath的文档说明它不包括任何作为图像或掩码的字形部分。是否有一种增强功能可以包括这些类型的极端情况? - codeshot

7

补充一下pipitas的回答:

/textheight { 
    gsave                                  % save graphic context
    {                            
        100 100 moveto                     % move to some point 
        (HÍpg) true charpath pathbbox      % gets text path bounding box (LLx LLy URx URy)
        exch pop 3 -1 roll pop             % keeps LLy and URy
        exch sub                           % URy - LLy
    }
    stopped                                % did the last block fail?
    {
        pop pop                            % get rid of "stopped" junk
        currentfont /FontMatrix get 3 get  % gets alternative text height
    }
    if
    grestore                               % restore graphic context
} bind def

/jumpTextLine { 
    textheight 1.25 mul                    % gets textheight and adds 1/4
    0 exch neg rmoveto                     % move down only in Y axis
} bind def

该方法期望已经设置了某种字体。它适用于所选字体(setfont)及其大小(scalefont)。
我使用(HÍpg)来获取最大的边界框,使用带重音的大写字符和“下划线”字符。结果已经足够好了。
另一种方法来自于dreamlax的答案 - 有些字体不支持charpath运算符。(请参见如何在PostScript中获取字符串的高度指标?
保存和恢复图形上下文会保持当前点的位置,因此不会对文档的“流程”产生影响。
希望我已经帮到你了。

此外,您可以使用 currentfont /FontBBox get 来获取边界框,而不是使用 charpath pathbbox 来获取它... 在我的情况下,在 GhostScript 下它完美地工作;然而,当打印机解释时结果并不好。 - Ricardo Nolde

4

以下是一个简短的回答,以补充pipitas深入的解释。

这个过程将字符串居中显示在指定点上。

/ceshow { % (string) fontsize fontname x y
    gsave
        moveto findfont exch scalefont setfont % s
        gsave
            dup false charpath flattenpath pathbbox % s x0 y0 x1 y1
        grestore
        3 -1 roll sub % s x0 x1 dy
        3 1 roll sub % s dy -dx
        2 div exch % s -dx/2 dy
        -2 div % s -dx/2 -dy/2
        rmoveto show
    grestore
} bind def

2

我曾经尝试使用以上方法来处理一种叫做dingbat的字体,但是后来我发现这些方法都默认文本从当前点开始,而在某些情况下这个点的位置是非常不准确的,导致结果很糟糕。

我的解决方案也依赖于pathbbox来计算宽度和高度,但同时也使用了X0和Y0来首先到达原点。

%-- to make things nicer
/hmoveto { 0 rmoveto } def
/vmoveto { 0 exch rmoveto } def
%-- cshow means something else...
/ccshow {
    dup %-- charpath consumes the string
    gsave
    newpath %-- else there's a strange line somewhere
    0 0 moveto
    true charpath flattenpath pathbbox
    grestore
    2 index sub -2 div vmoveto
    2 index sub -2 div hmoveto
    neg vmoveto
    neg hmoveto
    show
} def

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