I need to determine the height of a string (in given scale and font) in 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
will print test centered horizontally (but not yet vertically) at (10,10). (to see this, I also show a small circle at 10,10). I also need to determine the string height to center the text vertically as well, but I cant find a function for it.
I was having terrible results using the above procedures with a dingbat font, then i realised they assume the text will really begin at the current point, which in some cases is wildly inaccurate.
My solution also relies on pathbbox
to calculate width and height but then it also uses X0 and Y0 to get to the origin in the first place.
%-- 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
Are you familiar with the PostScript code you're using? Or is it just blindly copied and pasted from someplace? If you want to understand it, you should google for "PostScript Language Reference" or "Red Book" or "PLRM". These resources are available as PDFs from Adobe.
Your PostScript snippet uses the following steps:
(test)
places the string "test" on the top of the stack.dup
duplicates the topmost item on the stack. (You'll now have the string twice on the stack.)stringwidth
. After this operator is executed, the topmost "test" string will have been consumed, and two values will have been added to the stack instead: the string's height (topmost) and the string's width (second from top). [Update: Actually, "string's height" isn't entirely correct -- it's rather the vertical offset of the current point after finishing to draw the string...]pop
. This simply deletes the topmost value on the stack. Now only string's width remains on the top of the stack. 2 div
divides that value by 2 and leaves the result (half the stringwidth). neg
negates the topmost value on the stack. Now that negative value is topmost on the stack.0
places the value "0" on top of the stack.rmoveto
then consumes the two topmost values on the stack and moves the current point by that distance (half the string's width) to the left.show
consumes the first "test" string that remained all the time at the bottom of the stack and "shows" it.So what would work to take into account the string's height? Try as your last line:
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"
To understand my changes look up the meaning of charpath
, div
, exch
, pathbbox
, roll
and sub
operators in The Red Book.
This command uses Ghostscript to create a PDF file on Windows from the code (easier to view and check results):
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"
On Linux use:
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"
Better readable forms are:
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"
and
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"
Just adding to pipitas answer:
/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
The method expects that some font is already set. It works over the selected font (setfont
) and its size (scalefont
).
I use (HÍpg) to get the biggest bounding box possible, using accentuated uppercase characters and "below line" characters. The result is good enough.
The alternative approach steals from dreamlax's answer -- some fonts do not support charpath
operator. (See How can you get the height metric of a string in PostScript?)
Saving and restoring the graphic context keeps the current point in place, so it has no impact over the "flow" of your document.
Hope I've helped.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With