From: Fritz_Elfert@wue.maus.DE (Fritz Elfert)
Subject: bugs in fontutils-0.6/bzrto/ps
Organization: MausNet (Mitglied im IN e.V.)
Distribution: gnu
Date: Wed, 30 Dec 1992 14:08:00 GMT

Hi!

I think i found two bugs in the program 'bzrto' of the GNU font-utilities
Version 0.6:

I tested the the package by rendering the Times-Roman with Ghostscript 2.5.2
using a size of 60 points and a resolution of 1000 dpi. After converting the
bitmap with limn i used bzrto to produce an Adobe-Type-1 font to be compared
with the original Times. Two misalignmets of the characters could be seen:

  First all characters where shifted to the right except those with a
set-width smaller than then bounding-box (negative left-side-bearing).

  Second many characters are not positioned correctly in relation to the
baseline. Many accented characters have their accents positioned wrong.

All those error did not appear in Type-3 fonts, so i took a closer look to
the file 'pstype1.c' and found several things.

The reason for the first bug is that the hsbw-command at the start of each
character alrady defines the starting x-coordinate of the character, and this
value is repeated in the first rmoveto-command. The result is, that all
left-side-bearings are multiplied by two. I corrected that by replacing the
lsb-parameter at the first line of end_char() by a zero-constant.

The reason for the second bug is simply a cumulation of successive rounding-
errors during conversion of absolute real coordinates to relative integer
coordinates for the Type-1-Charstrings. I included three font-listings of
the character 'Agrave'

First here is the Type-3 listing:

/Agrave [ 7.251 0 0.132 -0.012 7.106 8.949 {
  4.228 7.227 moveto
  4.173 7.303  4.100 7.363  4.036 7.432  curveto
  3.505 7.974 lineto
  3.011 8.468 lineto
  2.801 8.678  2.486 9.099  2.144 8.903  curveto
  2.058 8.853  2.005 8.759  2.000 8.660  curveto
  1.994 8.542  2.041 8.422  2.121 8.335  curveto
  2.163 8.290  2.214 8.250  2.264 8.215  curveto
  3.300 7.559 lineto
  3.662 7.330 lineto
  3.708 7.301  3.755 7.264  3.806 7.242  curveto
  3.844 7.225  3.874 7.227  3.914 7.227  curveto
  4.228 7.227 lineto
  closepath
* 0.145 0.193 moveto
  0.145 0 lineto
  2.144 0 lineto
  2.144 0.193 lineto
  1.102 0.193  1.505 0.913  1.747 1.518  curveto
  1.997 2.140 lineto
  2.021 2.168  2.050 2.167  2.084 2.168  curveto
  4.517 2.168 lineto
  4.564 2.168  4.606 2.174  4.636 2.130  curveto
  4.684 2.063  4.710 1.955  4.744 1.879  curveto
  4.877 1.579  5.201 0.890  5.221 0.590  curveto
  5.248 0.197  4.806 0.193  4.529 0.193  curveto
  4.529 0 lineto
  7.094 0 lineto
  7.094 0.193 lineto
  6.877 0.210  6.706 0.243  6.552 0.415  curveto
  6.499 0.475  6.450 0.544  6.412 0.614  curveto
  6.109 1.166  5.881 1.773  5.627 2.349  curveto
  4.396 5.155 lineto
  3.853 6.396 lineto
  3.689 6.750 lineto
  3.668 6.770  3.641 6.769  3.613 6.769  curveto
  3.565 6.769 lineto
  3.529 6.769  3.495 6.773  3.470 6.741  curveto
  2.815 5.203 lineto
  1.703 2.578 lineto
  1.475 2.051  0.998 0.810  0.659 0.422  curveto
  0.519 0.262  0.348 0.209  0.145 0.193  curveto
  closepath
  3.336 5.324 moveto
  3.452 5.035 lineto
  3.644 4.577 lineto
  4.221 3.204 lineto
  4.481 2.578 lineto
  2.168 2.578 lineto
  2.992 4.553 lineto
  3.312 5.324 lineto
  3.336 5.324 lineto
  closepath
  fill } ] def

The y-component of the moveto following the first closepath. (line marked *)
is 0.193. Next here ist the decoded Type-1 listing of the same character
(the patch at the hsbw-command already is incuded):

/Agrave
{0 725 hsbw 422 722 rmoveto -5 7 -7 6 -6 6 rrcurveto -53 54 rlineto -49 49
rlineto -20 20 -31 42 -34 -19 rrcurveto -8 -4 -5 -9 0 -9 rrcurveto 0 -11 4 -12
7 -8 rrcurveto 4 -4 5 -4 5 -3 rrcurveto 103 -65 rlineto 36 -22 rlineto 4 -2 4
-3 5 -2 rrcurveto 3 -1 3 0 4 0 rrcurveto 31 hlineto closepath -408 -703
*
rmoveto -19 vlineto 199 hlineto 19 vlineto -104 0 40 72 24 60 rrcurveto 25 62
rlineto 2 2 2 0 3 0 rrcurveto 243 hlineto 4 0 4 0 3 -4 rrcurveto 4 -6 2 -10 3
-7 rrcurveto 13 -30 32 -68 2 -29 rrcurveto 2 -39 -44 0 -27 0 rrcurveto -19
vlineto 256 hlineto 19 vlineto -21 1 -17 3 -15 17 rrcurveto -5 6 -4 6 -3 7
rrcurveto -30 55 -22 60 -25 57 rrcurveto -123 280 rlineto -54 124 rlineto -16
35 rlineto -2 1 -2 0 -2 0 rrcurveto -4 hlineto -3 0 -3 0 -2 -3 rrcurveto -65
-153 rlineto -111 -262 rlineto -22 -52 -47 -124 -33 -38 rrcurveto -13 -15 -17
-5 -20 -1 rrcurveto closepath 319 513 rmoveto 11 -28 rlineto 19 -45 rlineto 57
-137 rlineto 25 -62 rlineto -231 hlineto 82 197 rlineto 31 77 rlineto 2
hlineto closepath -333 -532 rmoveto endchar } def

By simply adding all y-components until the Asterisk-mark one should get 19.
Unfortunately all rounding errors cumulate yielding a result of 25. The result
is, that the character is 'raised' above the baseline. I corrected this bug by
using absolute_cs_number() at any place where previously cs_number() was used.
The conversion to 'Adobe-Unis' had to be done before the difference of two
consecutive points is calculated. The result can be seen in the following
listing of the character which has been produced by the completely patched
program:

/Agrave
{0 725 hsbw 422 722 rmoveto -5 8 -8 6 -6 7 rrcurveto -53 54 rlineto -49 49
rlineto -21 21 -32 42 -34 -19 rrcurveto -9 -5 -5 -10 -1 -9 rrcurveto 0 -12 5
-12 8 -9 rrcurveto 4 -4 5 -4 5 -4 rrcurveto 104 -66 rlineto 36 -22 rlineto 4
-3 5 -4 5 -2 rrcurveto 4 -2 3 0 4 0 rrcurveto 31 hlineto closepath -408 -703
rmoveto -19 vlineto 200 hlineto 19 vlineto -104 0 40 72 24 60 rrcurveto 25 63
rlineto 3 2 2 0 4 0 rrcurveto 243 hlineto 5 0 4 1 3 -4 rrcurveto 5 -7 3 -11 3
-8 rrcurveto 13 -30 33 -69 2 -29 rrcurveto 2 -40 -44 0 -28 0 rrcurveto -19
vlineto 257 hlineto 19 vlineto -22 2 -17 3 -15 17 rrcurveto -6 6 -4 7 -4 7
rrcurveto -31 55 -22 61 -26 57 rrcurveto -123 281 rlineto -54 124 rlineto -17
36 rlineto -2 1 -2 0 -3 0 rrcurveto -5 hlineto -4 0 -3 1 -3 -3 rrcurveto -65
-154 rlineto -111 -263 rlineto -23 -52 -48 -124 -34 -39 rrcurveto -14 -16 -17
-6 -20 -1 rrcurveto closepath 319 513 rmoveto 12 -29 rlineto 19 -46 rlineto 58
-137 rlineto 26 -63 rlineto -232 hlineto 83 198 rlineto 32 77 rlineto 2
hlineto closepath -333 -532 rmoveto endchar } def


Finally here is the context-diff of the patched (pstype1x.c) and distributed
(pstype1o.c) versions of pstype1.c:

*** pstype1o.c  Wed Dec 30 14:52:40 1992
--- pstype1x.c  Tue Dec 29 15:56:52 1992
***************
*** 105,111 ****
--- 105,114 ----
  static void end_char (real, real);
  static void cs_byte (one_byte, variable_string *);
  static variable_string cs_encrypt (variable_string);
+ /* Replaced by absolute_cs_number
+    F. Elfert, 29. Dec 1992
  static void cs_number (real, variable_string *);
+ */
  static void init_charstring_buffers (void);
  static void out_bb_char
    (real_bounding_box_type *, real_coordinate_type *, bzr_char_type);
***************
*** 356,363 ****
  static void
  end_char (real lsb, real set_width)
  {
!   cs_number (lsb, &pre_charstring_buffer);
!   cs_number (set_width, &pre_charstring_buffer);
    cs_byte (T1_HSBW, &pre_charstring_buffer);

    cs_byte (T1_ENDCHAR, &charstring_buffer);
--- 359,374 ----
  static void
  end_char (real lsb, real set_width)
  {
! /*
!    Changed because lsb defines starting-point and this is point
!    is output by the first move-command of the char. I'm not shure
!    whether ist better to eliminate the first move-command or setting
!    the lsb to zero. However ist easier doing it here.
!    F. Elfert 29. Dec 1992
! */
!   absolute_cs_number (/*lsb*/ 0, &pre_charstring_buffer);
!
!   absolute_cs_number (points_to_adobes(set_width), &pre_charstring_buffer);
    cs_byte (T1_HSBW, &pre_charstring_buffer);

    cs_byte (T1_ENDCHAR, &charstring_buffer);
***************
*** 527,534 ****
  static void
  out_point (real_coordinate_type *current, real_coordinate_type p)
  {
!   cs_number (p.x - current->x, &charstring_buffer);
!   cs_number (p.y - current->y, &charstring_buffer);
    *current = p;
  }

--- 538,545 ----
  static void
  out_point (real_coordinate_type *current, real_coordinate_type p)
  {
!   absolute_cs_number (points_to_adobes(p.x) - points_to_adobes(current->x),
&charstring_buffer);
!   absolute_cs_number (points_to_adobes(p.y) - points_to_adobes(current->y),
&charstring_buffer);
    *current = p;
  }

***************
*** 571,577 ****
           algorithm, perhaps.)  */
        if (!y_equal)
          {
!           cs_number (p.y - current->y, &charstring_buffer);
            cs_byte (v_opcode, &charstring_buffer);
            current->y = p.y;
          }
--- 582,588 ----
           algorithm, perhaps.)  */
        if (!y_equal)
          {
!           absolute_cs_number (points_to_adobes(p.y) -
points_to_adobes(current->y), &charstring_buffer);
            cs_byte (v_opcode, &charstring_buffer);
            current->y = p.y;
          }
***************
*** 578,584 ****
      }
    else if (y_equal)
      {
!       cs_number (p.x - current->x, &charstring_buffer);
        cs_byte (h_opcode, &charstring_buffer);
        current->x = p.x;
      }
--- 589,595 ----
      }
    else if (y_equal)
      {
!       absolute_cs_number (points_to_adobes(p.x) -
points_to_adobes(current->x), &charstring_buffer);
        cs_byte (h_opcode, &charstring_buffer);
        current->x = p.x;
      }
***************
*** 600,609 ****
    if (epsilon_equal (current->x, CONTROL1 (s).x)
        && epsilon_equal (CONTROL2 (s).y, END_POINT (s).y))
      {
!       cs_number (CONTROL1 (s).y - current->y, &charstring_buffer);
        current->y = CONTROL1 (s).y;
        out_point (current, CONTROL2 (s));
!       cs_number (END_POINT (s).x - current->x, &charstring_buffer);
        current->x = END_POINT (s).x;
        cs_byte (T1_VHCURVETO, &charstring_buffer);
      }
--- 611,620 ----
    if (epsilon_equal (current->x, CONTROL1 (s).x)
        && epsilon_equal (CONTROL2 (s).y, END_POINT (s).y))
      {
!       absolute_cs_number (points_to_adobes(CONTROL1 (s).y) -
points_to_adobes(current->y), &charstring_buffer);
        current->y = CONTROL1 (s).y;
        out_point (current, CONTROL2 (s));
!       absolute_cs_number (points_to_adobes(END_POINT (s).x) -
points_to_adobes(current->x), &charstring_buffer);
        current->x = END_POINT (s).x;
        cs_byte (T1_VHCURVETO, &charstring_buffer);
      }
***************
*** 612,621 ****
    else if (epsilon_equal (current->y, CONTROL1 (s).y)
        && epsilon_equal (CONTROL2 (s).x, END_POINT (s).x))
      {
!       cs_number (CONTROL1 (s).x - current->x, &charstring_buffer);
        current->x = CONTROL1 (s).x;
        out_point (current, CONTROL2 (s));
!       cs_number (END_POINT (s).y - current->y, &charstring_buffer);
        current->y = END_POINT (s).y;
        cs_byte (T1_HVCURVETO, &charstring_buffer);
      }
--- 623,632 ----
    else if (epsilon_equal (current->y, CONTROL1 (s).y)
        && epsilon_equal (CONTROL2 (s).x, END_POINT (s).x))
      {
!       absolute_cs_number (points_to_adobes(CONTROL1 (s).x) -
points_to_adobes(current->x), &charstring_buffer);
        current->x = CONTROL1 (s).x;
        out_point (current, CONTROL2 (s));
!       absolute_cs_number (points_to_adobes(END_POINT (s).y) -
points_to_adobes(current->y), &charstring_buffer);
        current->y = END_POINT (s).y;
        cs_byte (T1_HVCURVETO, &charstring_buffer);
      }
***************
*** 652,657 ****
--- 663,672 ----
  /* This appends to BUFFER the charstring encoding of the number N,
     assumed to be in printer's points.  */

+ /*
+    This routine now is not nacessary any more.
+    F. Elfert, 29. Dec 1992
+
  static void
  cs_number (real n, variable_string *buffer)
  {
***************
*** 659,665 ****

    absolute_cs_number (n_in_adobes, buffer);
  }
!

  /* See section 6.2 of the book Adobe Type 1 Font Format.  */

--- 674,680 ----

    absolute_cs_number (n_in_adobes, buffer);
  }
! */

  /* See section 6.2 of the book Adobe Type 1 Font Format.  */

=============================================================================

I hope this helps you improving these excellent tools.

Happy new Year!

-Fritz (fritz_elfert@wue.maus.de)

PS: Messages in the german .maus domain are restricted to 16k per mail. PLEASE
    do NOT send large listing etc. Thank you!


Tschau
  Fritz

Newsgroups: gnu.utils.bug