Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine string height in PostScript?

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.

like image 964
willem Avatar asked Sep 01 '10 12:09

willem


3 Answers

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
like image 188
Baraco Avatar answered Sep 20 '22 10:09

Baraco


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:

  1. (test) places the string "test" on the top of the stack.
  2. dup duplicates the topmost item on the stack. (You'll now have the string twice on the stack.)
  3. 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...]
  4. Next, you use pop. This simply deletes the topmost value on the stack. Now only string's width remains on the top of the stack.
  5. 2 div divides that value by 2 and leaves the result (half the stringwidth).
  6. neg negates the topmost value on the stack. Now that negative value is topmost on the stack.
  7. 0 places the value "0" on top of the stack.
  8. 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.
  9. 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"
like image 8
Kurt Pfeifle Avatar answered Nov 01 '22 23:11

Kurt Pfeifle


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.

like image 7
Ricardo Nolde Avatar answered Nov 01 '22 23:11

Ricardo Nolde