chiark / gitweb /
Rework PANOSE generation
authorBen Harris <bjh21@bjh21.me.uk>
Thu, 26 Sep 2024 23:54:20 +0000 (00:54 +0100)
committerBen Harris <bjh21@bjh21.me.uk>
Fri, 27 Sep 2024 09:12:56 +0000 (10:12 +0100)
The code predated the introduction of bold variants, so it was
significantly incorrect.  Also it appears that some software uses the
PANOSE classification to distinguish monospaced fonts.  After careful
study of the specification, I think all the variants of Bedstead can
be classified as text fonts, so I've removed the code that described
some variants as decorative.  Then I did some calculations and a lot
of playing around with GeoGebra to work out which parts of the design
space give which values of Stroke Variation, Letterform, and Midline.

bedstead.c

index 27c12d70dd100fa98f151df5bff225b8b41a2aad..7601d32a0d6546b4fdb38c50eeb4bab974910713 100644 (file)
@@ -2929,19 +2929,50 @@ static void
 dopanose(void)
 {
        /*
-        * PANOSE is a complex font categorisation scheme.  I suspect
-        * no-one uses it, but it's a mandatory part of the 'OS/2'
+        * PANOSE is a complex font categorisation scheme.  It's not
+        * entirely unused, and is a mandatory part of the 'OS/2'
         * table, so it would be nice to get it right.  This procedure
         * calculates the PANOSE code for a Bedstead variant based on
         * its design parameters.
         *
         * See <http://panose.com> for the full details.
+        *
+        * PANOSE has some rules that divert Latin Text fonts into the
+        * Latin Decorative category.  One applies if ConRat > 1.
+        * However, by definition ConRat is at most 1, so this never
+        * applies.  The other case is for very wide or narrow fonts,
+        * but this is checked after checking for monospaced fonts,
+        * and Bedstead is monospaced.  Thus all Bedstead variants can
+        * be treated as Latin Text fonts.
         */
        int panose[10];
        int stdvw;
-       /* Common digits */
-       /* WeightRat == 700/StdVW */
+       assert(YPIX == 100); /* Make sums easier. */
+       panose[0] = 2; /* Latin Text */
+       /*
+        * WStem(I) = stdvw
+        * FootWid = 2 * XPIX + stdvw
+        * FootRat = 2 * XPIX / stdvw
+        * FootRat exceeds 1.6, but A, E, H and N are sans-serif
+        * So re-calculate FootRat based on H
+        * WStem(H) = stdvw
+        * FootWid(H) = stdvw
+        * FootRat(H) = 1
+        * So Bedstead is Sans Serif
+        * Flared excluded by FootRat
+        * StemCor = 0
+        * RonRat = 0
+        * Rounded excluded by RonRat
+        * FootPitch = 0 deg
+        * Perpendicular Sans Serif excluded by FootPitch
+        * EWid = 4 * XPIX + stdvw
+        * EOut = 4 * XPIX + stdvw
+        * SerOb = 1
+        * So Normal Sans Serif.
+        */
+       panose[1] = 11; /* Normal Sans Serif */
        stdvw = XPIX * (100 + weight->weight) / 100;
+       /* WeightRat == 700/StdVW */
        if (stdvw <= 20) /* WeightRat >= 35 */
                panose[2] = 2; /* Very Light */
        else if (stdvw < 39) /* WeightRat > 17.9 */
@@ -2966,6 +2997,8 @@ dopanose(void)
         * To make life simpler, ignore diagonals when calculating
         * ConRat.
         */
+       /* J and M are the same width. */
+       panose[3] = 9; /* Monospaced */
        /* ConRat == min(StdVW / 100, 100 / StdVW) */
        if (stdvw > 80 && stdvw < 125)
                panose[4] = 2; /* None */
@@ -2984,53 +3017,49 @@ dopanose(void)
        else
                panose[4] = 9; /* Very High */
        /*
-        * First digit is overall style.  Bedstead is a text font, but
-        * PANOSE says that fonts with horizontal stress and extreme
-        * aspect ratios should be classified as decorative.
+        * Trying to work out Speed values by algebra made my head
+        * hurt, as did learning enough Maxima to get it to help.  So
+        * I sketched it in GeoGebra and played with the numbers.
+        * Speed varies with weight, but seems to be independent of
+        * XPIX.
         */
-       if (stdvw < 100 || /* ConRat > 1.00 */
-           XPIX > 164) { /* ORat < 0.85 */
-               panose[0] = 4; /* Latin Decorative */
-               panose[1] = 2; /* Derivative */
-               /* ORat == 140/XPIX */
-               if (XPIX <= 53)
-                       panose[3] = 2; /* Super Condensed */
-               else if (XPIX <= 66)
-                       panose[3] = 3; /* Very Condensed */
-               else if (XPIX <= 110)
-                       panose[3] = 4; /* Condensed */
-               else if (XPIX <= 152)
-                       panose[3] = 5; /* Normal */
-               else if (XPIX <= 155)
-                       panose[3] = 6; /* Extended */
-               else if (XPIX <= 164)
-                       panose[3] = 7; /* Very Extended */
+       assert(XQTR_S == 29 && YQTR_S == 29); /* Only tested these values. */
+       if (panose[4] == 2)
+               panose[5] = 2; /* No Variation */
+       else if (weight->weight < 12) { /* Speed < ~0.9602 */
+               if (stdvw > YPIX)
+                       panose[5] = 7; /* Rapid/Vertical */
                else
-                       panose[3] = 8; /* Super Extended */
-               panose[5] = 11; /* Normal Sans Serif */
-               panose[6] = 2; /* Standard Solid Fill */
-               panose[7] = 2; /* No Lining */
-               panose[8] = 3; /* Square */
-               panose[9] = 2; /* Extended Collection */
+                       panose[5] = 8; /* Rapid/Horizontal */
        } else {
-               panose[0] = 2; /* Latin Text */
-               panose[1] = 11; /* Normal Sans Serif */
-               /* J and M are the same width. */
-               panose[3] = 9; /* Monospaced */
-               if (panose[4] == 2)
-                       panose[5] = 2; /* No Variation */
-               else if (stdvw > 100)
+               if (stdvw > YPIX)
                        panose[5] = 5; /* Gradual/Vertical */
                else
-                       panose[5] = 6; /* Gradual/Hoizontal */
-               /* Unusually shaped 'A' means no fit here. */
-               panose[6] = 1; /* No Fit */
-               /* OutCurv == 0.791 */
-               /* FIXME: Only correct for weight == 0 */
-               panose[7] = 5; /* Normal/Boxed */
-               panose[8] = 3; /* Standard/Pointed */
-               panose[9] = 7; /* Ducking/Large */
+                       panose[5] = 6; /* Gradual/Horizontal */
        }
+       /* Unusually shaped 'A' means no fit here. */
+       panose[6] = 1; /* No Fit */
+       /*
+        * We declare that our O is not Off Center.
+        *
+        * OutCurv similarly seems to be independent of XPIX, and for
+        * all reasonable weights it falls within the "Boxed" range.
+        */
+       panose[7] = 5; /* Normal/Boxed */
+       /*
+        * TrimRat is independent of XPIX.
+        * TrimRat = ((100-2*XQTR_S) + weight->weight) / (100 + weight->weight)
+        *         = (42 + weight->weight) / (100 + weight->weight)
+        * So between 0 and 0.613.
+        */
+       if (weight->weight >= 45)
+               panose[8] = 2; /* Standard/Trimmed */
+       else
+               panose[8] = 3; /* Standard/Pointed */
+       /*
+        * DuckRat = XRat = 5/7
+        */
+       panose[9] = 7; /* Ducking/Large */
        printf("Panose: %d %d %d %d %d %d %d %d %d %d\n",
               panose[0], panose[1], panose[2], panose[3], panose[4], 
               panose[5], panose[6], panose[7], panose[8], panose[9]);