[comp.lang.postscript] Variable characters

toms@fcs260c2.ncifcrf.gov (Tom Schneider) (10/09/90)

I would like to make some characters of a precise size, for example,
10.3 cm high.  I am being thwarted because fonts are not consistent between
PostScript printers.  That is, if I draw a character to fit just outside a
rectangle and show it on NeWS, then the same program makes the character
INSIDE the rectangle when printed to a LaserWriter ntxII.

This Is Frustrating! :-(

Is there any way to rescale the font so that it is the same size on all
printers?  For example, is there an easy way to get a hold of the exact
character size so as to correct for these non-standard effects?  (And I thought
PostScript was supposed to be a standard language, hruummph!  Aren't there
people working on making this more standard?)

Another way to do this would be to create my own fonts entirely from scratch.
Examples are at the end of the cookbook, but I'd hate to type them in (;-).
Does anyone have a copy or working example I could use?  (I can't use it if it
has a copyright restriction, but I always include original author information
in the program.)

If I could build a font from scratch, then it would solve another problem I
have, which is that in the font I prefer (Helvetica-Bold), some of the
characters reach above and below the line they rest on.  That is, relative to
an A, the letters C and G extend above and below the lines.  This is a royal
pain for what I'm doing.  So far, I have had to empirically figure out scale
factors to correct for this.  It would be nice to have a clean solution, like a
font which I have complete control over.

Thanks in advance,

  Tom Schneider
  National Cancer Institute
  Laboratory of Mathematical Biology
  Frederick, Maryland  21702-1201
  toms@ncifcrf.gov

ps
If you've gotten this far, you might want to know what it's for.  I want
to create stacks of letters.  The height of the stack is pre-defined,
as are the relative heights of each letter in the stack.  You can get
some examples by ftp from 'ncifcrf.gov' in 'pub/delila' get the compressed
PostScript files:
  lambcro.logo.Z     ribosome.logo.Z    t7.logo.Z     globin.logo.Z
(Please send me email if you do pick them up.)  These sacks are called
'sequence logos'.  They are a new way to represent DNA, RNA or protein sequence
patterns.  We have a paper in press in Nucleic Acids Research, preprints are
available.

kevina@apple.com (This space for rent) (10/10/90)

In article <1902@fcs280s.ncifcrf.gov> toms@fcs260c2.ncifcrf.gov (Tom 
Schneider) writes:
> I would like to make some characters of a precise size, for example,
> 10.3 cm high.  I am being thwarted because fonts are not consistent 
> between PostScript printers.  That is, if I draw a character to fit just
> outside a rectangle and show it on NeWS, then the same program makes the
> character INSIDE the rectangle when printed to a LaserWriter ntxII.
> 
> This Is Frustrating! :-(
> 
> Is there any way to rescale the font so that it is the same size on all
> printers?  For example, is there an easy way to get a hold of the exact
> character size so as to correct for these non-standard effects?  (And I
> thought PostScript was supposed to be a standard language, hruummph!
> Aren't there people working on making this more standard?)
> 
The problem facing you is that, while the PostScript language is more or 
less standard, the font shapes depend on the designer, type vendor, or 
language implementation.  The fonts used in NeWS are not exactly the same 
as those from Adobe, which are not the same as those from Bitstream, which 
are not the same as the original lead type, etc.  (This is an 
industry-wide issue.)  One way to compensate for this in PostScript is to 
use the charpath and pathbbox operators and scale appropriately.

This routine takes a fontname, a string, and a height in points, and 
scales the font so that every character in the string prints at the same 
height.  It also adjusts the currentpoint so that every character rests on 
the baseline (try it with "g"!).  The results are pretty hideous for 
anything except what you are trying to do:

    /heightshow {             % fontname string height  heightshow  -
      /ht exch def
      /str exch def
      /fnt exch def
      /s1 1 string def
      str {                   % for each char in the string
        s1 0 3 -1 roll put    % convert char to string
        gsave  
          newpath 
          0 0 moveto
          fnt findfont setfont
          s1 true charpath 
          flattenpath 
          pathbbox            % compute bounding box of 1 pt. char
          2 index sub         % ury - lly = char height
          ht exch div         % scale for char to match req. height 
          /scalefactor exch def
          pop                 % don't need urx
          scalefactor mul     % adjust lly so char sits on baseline
          /yadjust exch def 
          pop                 % don't need llx
        grestore
        fnt findfont scalefactor scalefont setfont
        0 yadjust neg rmoveto
        s1 show
        0 yadjust rmoveto
      } forall
    } bind def

    /ptspercm 72.0 2.54 div def
    36 36 moveto
    /Helvetica-Bold (C) 10.3 ptspercm mul heightshow
    showpage

> If I could build a font from scratch, then it would solve another problem 
> I have, which is that in the font I prefer (Helvetica-Bold), some of the
> characters reach above and below the line they rest on.  That is, relative
> to an A, the letters C and G extend above and below the lines.  This is a
> royal pain for what I'm doing.  So far, I have had to empirically figure
> out scale factors to correct for this.  It would be nice to have a clean
> solution, like a font which I have complete control over.
> 
Having rounded characters extend above and below the lines may be a royal 
pain for what you are doing, but it is essential for legible typography.  
(To see this, print something like "CHEOPS" with the heightshow routine... 
the rounded characters will appear smaller than the non-rounded 
characters!)

--Kevin Andresen [kevina@apple.com]
"Technically, Sen. Helms is a member of the arachnid family."

toms@fcs260c2.ncifcrf.gov (Tom Schneider) (10/11/90)

In article <10628@goofy.Apple.COM> kevina@apple.com (This space for rent)
writes:
>In article <1902@fcs280s.ncifcrf.gov> toms@fcs260c2.ncifcrf.gov (Tom 
>Schneider) writes:
>> I would like to make some characters of a precise size, for example,
>> 10.3 cm high.

>    /heightshow {             % fontname string height  heightshow  -

This works beautifully!  Thanks Kevin!

>--Kevin Andresen [kevina@apple.com]

  Tom Schneider
  National Cancer Institute
  Laboratory of Mathematical Biology
  Frederick, Maryland  21702-1201
  toms@ncifcrf.gov

shiva@well.sf.ca.us (Kenneth Porter) (10/11/90)

When I need to get characters to fit a pre-defined space (such
as is the case on my business card), I always calculate the
point size, sometimes with x and y computed independently. The
x size can be calculated by using stringwidth after setting the
font at 1 point.  The y size can be calculated by iterating
over the string and reading the metric info for each character.
If you need to set x and y scale independently, use makefont
instead of scalefont and provide a scale matrix.
 
This allows me to substitute fonts ad hoc without digging out
my calculator to recalculate all of the sizes.  Just let the PS
interpreter do the grunt work.
 
Ken (shiva@well.sf.ca.us)

marsh@linus.mitre.org (Ralph Marshall) (10/11/90)

In article <21131@well.sf.ca.us> shiva@well.sf.ca.us (Kenneth Porter) writes:
>
>
>When I need to get characters to fit a pre-defined space (such
>as is the case on my business card), I always calculate the
>point size, sometimes with x and y computed independently. The
>x size can be calculated by using stringwidth after setting the
>font at 1 point.  The y size can be calculated by iterating
>over the string and reading the metric info for each character.

Of course, you can also get the height of the string from stringwidth
(despite the somewhat misleading name).  It leaves both the width and
height of the string on the stack, taking into account all the usual
things such as scales and rotations, actual font, font size, etc., so
it should be suitable for most situations such as picking a scale size
that will allow the text to fit on a business card.

Ralph Marshall
marsh@linus.mitre.org

glenn@heaven.woodside.ca.us (Glenn Reid) (10/12/90)

In article <122961@linus.mitre.org> marsh@darwin.UUCP (Ralph Marshall 617 271-8784) writes:
>Of course, you can also get the height of the string from stringwidth
>(despite the somewhat misleading name).  It leaves both the width and
>height of the string on the stack, taking into account ....

No.  The "stringwidth" operator returns the amount by which the current
point will be displaced.  It has nothing to do with the toner marks
made on the page and how big they might be.  For essentially all Roman
fonts, "stringwidth" returns 0 for the Y component unless your coordinate
system is rotated or something.

You need to use "false charpath pathbbox" to get the actual visual width
and height of the characters.

/Glenn

-- 
 Glenn Reid				RightBrain Software
 glenn@heaven.woodside.ca.us		PostScript/NeXT developers
 ..{adobe,next}!heaven!glenn		415-851-1785

jpensar@ra.abo.fi (Johan Pensar RT) (10/12/90)

In article <122961@linus.mitre.org> marsh@darwin.UUCP (Ralph Marshall 617 271-8784) writes:
>Of course, you can also get the height of the string from stringwidth
>(despite the somewhat misleading name).  It leaves both the width and
>height of the string on the stack, taking into account all the usual
>things such as scales and rotations, actual font, font size, etc., so
>it should be suitable for most situations such as picking a scale size
>that will allow the text to fit on a business card.
>
>Ralph Marshall
>marsh@linus.mitre.org

Ooohhh??, I have allways belived that stringwidth leaves (in a simplified
vocabular) the changes of current point in x and y directions that would
occur if the string was printed :-)
That is, for normal characters, the lowest parameter left on the stack is
allways zero, would be something else if japanese was printed (i guess they
writes downwards?). Correct me if wrong.

--------------------------------------------------------------------------
Johan Pensar                                      Email: jpensar@ra.abo.fi
Process Control Laboratory
Abo Akademi University
FINLAND

marsh@linus.mitre.org (Ralph Marshall) (10/12/90)

In an earlier incarnation, I misspoke myself as follows:
>>Of course, you can also get the height of the string from stringwidth
>>(despite the somewhat misleading name).  It leaves both the width and
>>height of the string on the stack, taking into account ....
>

To which Glenn pointed out that PostScript should only be used by
professionals who know what they are doing, and not by those of us
watching out in net-land who were bound to burn, shock, or lacerate
ourselves due to the lack of proper training:

>No.  The "stringwidth" operator returns the amount by which the current
>point will be displaced.  It has nothing to do with the toner marks
>made on the page and how big they might be.  For essentially all Roman
>fonts, "stringwidth" returns 0 for the Y component unless your coordinate
>system is rotated or something.
>
>/Glenn

---------------------------------------------------------------------------
Ralph Marshall (marsh@linus.mitre.org)

Disclaimer:  Often wrong but never in doubt...
---------------------------------------------------------------------------




-- 
---------------------------------------------------------------------------
Ralph Marshall (marsh@linus.mitre.org)

Disclaimer:  Often wrong but never in doubt...

aas@boeygen.nr.no (Gisle Aas) (10/15/90)

In article <293@heaven.woodside.ca.us> glenn@heaven.woodside.ca.us (Glenn Reid) writes:

>                                               For essentially all Roman
>  fonts, "stringwidth" returns 0 for the Y component unless your coordinate
>  system is rotated or something.

Can anybody enlighten me on this subject. Why should stringwidth
return something else if your coordinate system is rotated or
something?
--
Gisle Aas               |  snail: Boks 114 Blindern, N-0314 Oslo, Norway
Norsk Regnesentral      |  X.400: G=Gisle;S=Aas;O=nr;P=uninett;C=no
voice: +47-2-453561     |  inet:  Gisle.Aas@nr.no

glenn@heaven.woodside.ca.us (Glenn Reid) (10/16/90)

In article <AAS.90Oct15092820@boeygen.nr.no> aas@boeygen.nr.no (Gisle Aas) writes:
>In article <293@heaven.woodside.ca.us> glenn@heaven.woodside.ca.us (Glenn Reid) writes:
>
>>                                               For essentially all Roman
>>  fonts, "stringwidth" returns 0 for the Y component unless your coordinate
>>  system is rotated or something.
>
>Can anybody enlighten me on this subject. Why should stringwidth
>return something else if your coordinate system is rotated or
>something?

Sure, I can enlighten you :-)  I was being brain-dead when I said that,
and I was wrong.  It doesn't matter if the coordinate system is rotated.
You get 0 unless the font itself has a Y component in the width of the
character, which is extremely rare in Roman fonts.

In a weak defense of my earlier posting, and to try to convince you that
I actually do test things before I post them to the net, I had
(approximately) the following dialog with my PostScript interpreter,
but failed to notice the negative exponent on the Y component.  It's
effectively 0, but due to arithmetic error and roundoff, it's not exactly 0:

    heaven> PS
    PostScript(r) Version 1006.24
    PS>60 rotate
    PS>/Helvetica-Bold 120 selectfont
    PS>0 0 moveto (TESTING) stringwidth
    PS>pstack
    6.10352e-05 
    520.08 
    PS>quit
    heaven>

Sorry about the misinformation.

(Glenn) cvn

-- 
 Glenn Reid				RightBrain Software
 glenn@heaven.woodside.ca.us		PostScript/NeXT developers
 ..{adobe,next}!heaven!glenn		415-851-1785

rcd@ico.isc.com (Dick Dunn) (10/16/90)

aas@boeygen.nr.no (Gisle Aas) writes:
> glenn@heaven.woodside.ca.us (Glenn Reid) writes:

> >                                               For essentially all Roman
> >  fonts, "stringwidth" returns 0 for the Y component unless your coordinate
> >  system is rotated or something.

> Can anybody enlighten me on this subject. Why should stringwidth
> return something else if your coordinate system is rotated or
> something?

Glenn didn't really mean the coordinate system (he knows better than
that:-) but rather the font transformation matrix.  Rotation the page coord-
inates doesn't affect anything, because stringwidth gives its result in page
coordinates.  However, if you start with a typical Roman font and transform
it with "makefont", using a font transformation matrix [a b c d e f] where
b is nonzero, you'll get a nonzero y component from stringwidth.  (Note
also that altering f *doesn't* give a nonzero y--although it displaces the
text in the y direction, it does so by a constant amount for the whole
string, so that the y "escapement" is still zero.)
-- 
Dick Dunn     rcd@ico.isc.com -or- ico!rcd       Boulder, CO   (303)449-2870
   ...Never offend with style when you can offend with substance.

toms@fcs260c2.ncifcrf.gov (Tom Schneider) (10/16/90)

Thanks to Kevin Andresen [kevina@apple.com] I now know that it is possible to
adjust character sizes.  Unfortunately every time I try to modify his program I
get tangled into some problem with one or the other postscript device I use.

Grrr #1.
NeWS (the old 1.0 kind I think) refuses to create small characters, and forces
them to be bigger.  This is design error, in which someone thought that it
would to 'help' the user.  By rotating my characters, I can to avoid the
problem some of the time.  Anyone at Sun know why this was done?  Does NeWS 2.0
still have this stupid 'feature'?

Grrr #2.
The other device I use is an Apple Laserwriter IIntx; which is in general a
great machine --- except that there is no way to reset the darn thing, short of
turning it off and on again.  (The best contender, control-d didn't seem to
help at all.)  I asked about this here about a month ago and have still not
gotten a satisfactory solution.  Is there anyone at Apple who KNOWS the
solution?  A satisfactory solution is one that works under Unix, and is
complete tested code.  (Ie, exactly what do I send to the printer to clear
its little brain?)

So here's the problem:  draw a character of a specified height in cm, with
its bounding box just to prove you have it under control.  Have it function
on any device.  When I run the following code on NeWS, it works fine.  When
I send it to the printer, it works fine the first time.  On the second shot
I get a blank page.  What am I doing wrong?  How do I tell the printer to
forget anything I ever told it before?????

  Tom Schneider
  National Cancer Institute
  Laboratory of Mathematical Biology
  Frederick, Maryland  21702-1201
  toms@ncifcrf.gov

..........................................................................

(version = 2.00 of charbox.ps 1990 October 15 \n) print
initgraphics

/cmfactor { 72 2.54 div } def % defines points -> centimeters
/cm { cmfactor mul} def % defines centimeters

erasepage
12 cm 10 cm translate
90 rotate
4 4 scale

    /Helvetica-Bold findfont 30 scalefont setfont

gsave
    newpath
    0 0 moveto
    (G) true charpath 
    flattenpath 
    pathbbox % compute bounding box of 1 pt. char => lx ly ux uy
    % the path is here, but toss it away ...
grestore

pstack
    /uy exch def
    /ux exch def
    /ly exch def
    /lx exch def

1 setlinewidth
gsave
newpath
   0  0 moveto
  ux  0 lineto
  ux uy lineto
   0 uy lineto
   0  0 lineto
stroke
grestore

gsave
    0 0 moveto
    (G) show
grestore

showpage

stanley@fozzie.phoenix.com (John Stanley) (10/17/90)

shiva@well.sf.ca.us (Kenneth Porter) writes:

> This is not true.  stringwidth returns the change in current
> point after a show of the specified string.  For Roman
> typefaces, only the x component is non-zero.  The y component

   Only too true. I once wrote some ps functions to center strings on
specified points. Y was always 0.

>  
> If metric info is not available in the font dictionary, you can
> use "charpath flattenpath pathbbox", but I think the resulting

  The results are in drawing coordinates. I.e. moving 1/2 the x value to
the left will center the string l/r.

  Unfortunately, either charpath or flattenpath has problems with certain 
fonts (Courier is on of them) which prohibits its use. I forget which
command is the problem, but it kept me from using my nice routines in a
production environment.


This is my signature. It doesn't contain my name at all!

shiva@well.sf.ca.us (Kenneth Porter) (10/17/90)

marsh@linus.mitre.org (Ralph Marshall) writes:
 
> Of course, you can also get the height of the string from
> stringwidth...
 
This is not true.  stringwidth returns the change in current
point after a show of the specified string.  For Roman
typefaces, only the x component is non-zero.  The y component
is zero because the baseline doesn't change (as it might in a
Japanese font).  Actually, the resulting width is NOT the
bounding box; it must be adjusted by the side bearings of the
first and last characters. This is only a problem, however, if
you need a pretty close fit around the string.
 
If metric info is not available in the font dictionary, you can
use "charpath flattenpath pathbbox", but I think the resulting
bounding box is relative to device coordinates, so you want to
do the pathbbox in a coordinate system that is aligned to
device space (ie. not rotated or skewed).
 
Ken (shiva@well.sf.ca.us)

toms@fcs260c2.ncifcrf.gov (Tom Schneider) (11/07/90)

In article <21131@well.sf.ca.us> shiva@well.sf.ca.us (Kenneth Porter) writes:
>
>When I need to get characters to fit a pre-defined space (such
>as is the case on my business card), I always calculate the
>point size, sometimes with x and y computed independently. The
>x size can be calculated by using stringwidth after setting the
>font at 1 point.  The y size can be calculated by iterating
>over the string and reading the metric info for each character.
>If you need to set x and y scale independently, use makefont
>instead of scalefont and provide a scale matrix.
> 
>This allows me to substitute fonts ad hoc without digging out
>my calculator to recalculate all of the sizes.  Just let the PS
>interpreter do the grunt work.
> 
>Ken (shiva@well.sf.ca.us)

Without an exact program, it is next to impossible to replicate what you do.  I
have ALMOST solved my problem, but not quite!  To prove that I am able to
control the character sizes, I decided to make the characters appear inside
boxes.  Unfortunately, they don't match the boxes exactly, and I don't know
why.  Following is my latest attempt.  Can you tell me why the characters STILL
don't fit the boxes exactly?  What is the problem with this code, and how do I
fix it?  (I print this on a LaserWriter ntxII.  NeWS 1.0 fails utterly.)

  Tom Schneider
  National Cancer Institute
  Laboratory of Mathematical Biology
  Frederick, Maryland  21702-1201
  toms@ncifcrf.gov

- snip - snip - snip - snip - snip - snip - snip - snip - snip - snip

(version = 2.31 of charbox.ps TDS 1990 November 6 \n) dup print
% Show the 4 characters ACGT surrounded by boxes that exactly match
% the characters.  Unfortunately, this version of the program ALMOST
% works.  The C and G characters are displaced downward by about 1mm
% relative to A and T.  The reason for this is unknown, can you figure
% it out?
%  Tom Schneider
%  National Cancer Institute
%  Laboratory of Mathematical Biology
%  Frederick, Maryland  21702-1201
%  toms@ncifcrf.gov

/cmfactor {72 2.54 div} def % defines points -> centimeters conversion
/cm {cmfactor mul} def % defines centimeters

% Show the version number on the page
initgraphics
erasepage
gsave
/Helvetica findfont 14 scalefont setfont
1 cm 25 cm moveto show
grestore

1 setlinewidth % define the width of the dashed lines
1 setflat % define the accurcy with which curved path segments
          % are to be rendered on the raster output device.
          % Even setting this to 0.01 makes no difference!

% Set up the font for the graphics
/fontsize 30 def
/Helvetica-Bold findfont fontsize scalefont setfont

/charparams { % char charparams => uy ux ly lx
% takes a single character and returns the coordinates that (supposedly)
% defines the outer bounds of where the ink goes
  gsave
    newpath
    0 0 moveto
    % take the character off the stack and use it here:
    true charpath 
    flattenpath 
    pathbbox % compute bounding box of 1 pt. char => lx ly ux uy
    % the path is here, but toss it away ...
  grestore
  /uy exch def
  /ux exch def
  /ly exch def
  /lx exch def
% print the parameters to the user:
(lx) lx
(ly) ly
(ux) ux
(uy) uy
pstack
clear % clean up the stack, having printed all that
} bind def

/box { % xsize ysize box -
% draw a dashed box of xsize by ysize
  /ysize exch def % the x size of the box
  /xsize exch def % the y size of the box
  gsave
    newpath
    0 0 moveto
    xsize 0 lineto
    xsize ysize lineto
    0 ysize lineto
    0 0 lineto
%
    [3] 0 setdash
    stroke
  grestore
} bind def

/boxshow { % xsize ysize char boxshow
% show the character with a box around it
gsave
  /tc exch def % define the character
  /ysize exch def % the x size of the character
  /xsize exch def % the y size of the character

  xsize ysize box

  tc charparams

  ysize % desired size of character in points
  uy ly sub % height of character in points
  div % factor by which to scale up the character
  /ymulfactor exch def

  xsize % desired size of character in points
  ux lx sub % height of character in points
  div % factor by which to scale up the character
  /xmulfactor exch def

  /xmove xmulfactor lx mul neg def
  /ymove ymulfactor ly mul neg def

  newpath
  xmove ymove moveto
  xmulfactor ymulfactor scale
  tc show
grestore
} def

gsave

    2 cm 2 cm translate

    /size 22 def

    3 cm 0 translate
    0 0 moveto
    1 cm size cm (A) boxshow

    3 cm 0 translate
    0 0 moveto
    1 cm size cm (C) boxshow

    3 cm 0 translate
    0 0 moveto
    1 cm size cm (G) boxshow

    3 cm 0 translate
    0 0 moveto
    1 cm size cm (T) boxshow

grestore

showpage

shiva@well.sf.ca.us (Kenneth Porter) (11/13/90)

toms@fcs260c2.ncifcrf.gov (Tom Schneider) provided some code
demonstrating that scaling up a font bbox doesn't work.  After
some experimentation with Tom's sample code, I found that fonts
don't scale uniformly.  It is necessary to calculate the bbox
at the desired size, which leads to a chicken-and-egg problem.
 
Try setting the fontsize parameter in his code to first, 1
point, then 825 points.  In the first case, the characters
don't fit their expected bbox at all.  In the second case, they
fit quite well.  The number 825 is a compromise among the
ymulfactors of the final characters.
 
My error in assuming simple scaling would work partly stems
from my ignorance that Tom needed this much accuracy.  In my
own applications I could tolerate a fair amount of slop and I
was using small point sizes.
 
Apparently, to get a good fit, it is necessary to iterate the
bbox-calculating procedure a couple of times, using the result
from each iteration to compute the font scale factor for
scalefont for the next iteration.  A couple of iterations
should get you pretty close to a good fit.
 
In pseudo-code:
 
fontscale = 1
do {
  calculate character bbox
  fontscale = fontscale * desired_bbox / calculated_bbox
} while abs(desired_bbox-calculated_bbox) > acceptable_error
 
Ken (shiva@well.sf.ca.us)

jef@well.sf.ca.us (Jef Poskanzer) (11/13/90)

In the referenced message, shiva@well.sf.ca.us (Kenneth Porter) wrote:
}fontscale = 1
}do {
}  calculate character bbox
}  fontscale = fontscale * desired_bbox / calculated_bbox
}} while abs(desired_bbox-calculated_bbox) > acceptable_error

That's an excellent idea, except for one thing: if you want precisely-sized
characters that are BIG, you'll get a limitcheck.  I think you have to
add in some code to put a cap on the trial font scale, and then run some
tests to find a good value for the cap.
---
Jef

  Jef Poskanzer  jef@well.sf.ca.us  {ucbvax, apple, hplabs}!well!jef
         A professor is one who talks in someone else's sleep.