]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/lib/font/bitmap/bitscale.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / lib / font / bitmap / bitscale.c
diff --git a/Xserver/lib/font/bitmap/bitscale.c b/Xserver/lib/font/bitmap/bitscale.c
new file mode 100644 (file)
index 0000000..ce407e2
--- /dev/null
@@ -0,0 +1,1914 @@
+/* $TOG: bitscale.c /main/31 1997/06/09 11:21:46 barstow $ */
+
+/*
+
+Copyright (c) 1991, 1994  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the X Consortium.
+
+*/
+/* $XFree86: xc/lib/font/bitmap/bitscale.c,v 3.4.2.1 1997/06/11 12:08:40 dawes Exp $ */
+
+/*
+ * Author:  Keith Packard, MIT X Consortium
+ */
+
+#include "fntfilst.h"
+#include "bitmap.h"
+#ifdef _XOPEN_SOURCE
+#include <math.h>
+#else
+#define _XOPEN_SOURCE  /* to get prototype for hypot on some systems */
+#include <math.h>
+#undef _XOPEN_SOURCE
+#endif
+
+#ifndef MAX
+#define   MAX(a,b)    (((a)>(b)) ? a : b)
+#endif
+
+extern Atom MakeAtom();
+
+void bitmapUnloadScalable();
+
+enum scaleType {
+    atom, truncate_atom, pixel_size, point_size, resolution_x,
+    resolution_y, average_width, scaledX, scaledY, unscaled, fontname,
+    raw_ascent, raw_descent, raw_pixelsize, raw_pointsize,
+    raw_average_width, uncomputed
+};
+
+typedef struct _fontProp {
+    char       *name;
+    Atom        atom;
+    enum scaleType type;
+} fontProp;
+
+static FontPtr BitmapScaleBitmaps();
+static FontPtr PrinterScaleBitmaps();
+
+typedef FontPtr (*ScaleFunc) ();
+/* These next two arrays must be kept in step with the renderer array */
+ScaleFunc scale[] =
+{
+    BitmapScaleBitmaps,
+    BitmapScaleBitmaps,
+    BitmapScaleBitmaps,
+    BitmapScaleBitmaps,
+    BitmapScaleBitmaps,
+    BitmapScaleBitmaps,
+    PrinterScaleBitmaps,
+};
+
+static FontEntryPtr FindBestToScale();
+static FontEntryPtr FindPmfToScale();
+
+typedef FontEntryPtr (*FindToScale) ();
+FindToScale find_scale[] =
+{
+    FindBestToScale,
+    FindBestToScale,
+    FindBestToScale,
+    FindBestToScale,
+    FindBestToScale,
+    FindBestToScale,
+    FindPmfToScale,
+};
+
+static unsigned long fontGeneration = 0;       /* initialization flag */
+
+static fontProp fontNamePropTable[] = {
+    "FOUNDRY", 0, atom,
+    "FAMILY_NAME", 0, atom,
+    "WEIGHT_NAME", 0, atom,
+    "SLANT", 0, atom,
+    "SETWIDTH_NAME", 0, atom,
+    "ADD_STYLE_NAME", 0, atom,
+    "PIXEL_SIZE", 0, pixel_size,
+    "POINT_SIZE", 0, point_size,
+    "RESOLUTION_X", 0, resolution_x,
+    "RESOLUTION_Y", 0, resolution_y,
+    "SPACING", 0, atom,
+    "AVERAGE_WIDTH", 0, average_width,
+    "CHARSET_REGISTRY", 0, atom,
+    "CHARSET_ENCODING", 0, truncate_atom,
+    "FONT", 0, fontname,
+    "RAW_ASCENT", 0, raw_ascent,
+    "RAW_DESCENT", 0, raw_descent,
+    "RAW_PIXEL_SIZE", 0, raw_pixelsize,
+    "RAW_POINT_SIZE", 0, raw_pointsize,
+    "RAW_AVERAGE_WIDTH", 0, raw_average_width
+};
+
+#define TRANSFORM_POINT(matrix, x, y, dest) \
+       ((dest)[0] = (matrix)[0] * (x) + (matrix)[2] * (y), \
+        (dest)[1] = (matrix)[1] * (x) + (matrix)[3] * (y))
+
+#define CHECK_EXTENT(lsb, rsb, desc, asc, data) \
+       ((lsb) > (data)[0] ? (lsb) = (data)[0] : 0 , \
+        (rsb) < (data)[0] ? (rsb) = (data)[0] : 0, \
+        (-desc) > (data)[1] ? (desc) = -(data)[1] : 0 , \
+        (asc) < (data)[1] ? (asc) = (data)[1] : 0)
+
+#define NPROPS (sizeof(fontNamePropTable) / sizeof(fontProp))
+
+/* Warning: order of the next two tables is critically interdependent.
+   Location of "unscaled" properties at the end of fontPropTable[]
+   is important. */
+
+static fontProp fontPropTable[] = {
+    "MIN_SPACE", 0, scaledX,
+    "NORM_SPACE", 0, scaledX,
+    "MAX_SPACE", 0, scaledX,
+    "END_SPACE", 0, scaledX,
+    "AVG_CAPITAL_WIDTH", 0, scaledX,
+    "AVG_LOWERCASE_WIDTH", 0, scaledX,
+    "QUAD_WIDTH", 0, scaledX,
+    "FIGURE_WIDTH", 0, scaledX,
+    "SUPERSCRIPT_X", 0, scaledX,
+    "SUPERSCRIPT_Y", 0, scaledY,
+    "SUBSCRIPT_X", 0, scaledX,
+    "SUBSCRIPT_Y", 0, scaledY,
+    "SUPERSCRIPT_SIZE", 0, scaledY,
+    "SUBSCRIPT_SIZE", 0, scaledY,
+    "SMALL_CAP_SIZE", 0, scaledY,
+    "UNDERLINE_POSITION", 0, scaledY,
+    "UNDERLINE_THICKNESS", 0, scaledY,
+    "STRIKEOUT_ASCENT", 0, scaledY,
+    "STRIKEOUT_DESCENT", 0, scaledY,
+    "CAP_HEIGHT", 0, scaledY,
+    "X_HEIGHT", 0, scaledY,
+    "ITALIC_ANGLE", 0, unscaled,
+    "RELATIVE_SETWIDTH", 0, unscaled,
+    "RELATIVE_WEIGHT", 0, unscaled,
+    "WEIGHT", 0, unscaled,
+    "DESTINATION", 0, unscaled,
+    "PCL_FONT_NAME", 0, unscaled,
+    "_ADOBE_POSTSCRIPT_FONTNAME", 0, unscaled
+};
+
+static fontProp rawFontPropTable[] = {
+    "RAW_MIN_SPACE", 0, 0,
+    "RAW_NORM_SPACE", 0, 0,
+    "RAW_MAX_SPACE", 0, 0,
+    "RAW_END_SPACE", 0, 0,
+    "RAW_AVG_CAPITAL_WIDTH", 0, 0,
+    "RAW_AVG_LOWERCASE_WIDTH", 0, 0,
+    "RAW_QUAD_WIDTH", 0, 0,
+    "RAW_FIGURE_WIDTH", 0, 0,
+    "RAW_SUPERSCRIPT_X", 0, 0,
+    "RAW_SUPERSCRIPT_Y", 0, 0,
+    "RAW_SUBSCRIPT_X", 0, 0,
+    "RAW_SUBSCRIPT_Y", 0, 0,
+    "RAW_SUPERSCRIPT_SIZE", 0, 0,
+    "RAW_SUBSCRIPT_SIZE", 0, 0,
+    "RAW_SMALL_CAP_SIZE", 0, 0,
+    "RAW_UNDERLINE_POSITION", 0, 0,
+    "RAW_UNDERLINE_THICKNESS", 0, 0,
+    "RAW_STRIKEOUT_ASCENT", 0, 0,
+    "RAW_STRIKEOUT_DESCENT", 0, 0,
+    "RAW_CAP_HEIGHT", 0, 0,
+    "RAW_X_HEIGHT", 0, 0,
+};
+
+static void
+initFontPropTable()
+{
+    int         i;
+    fontProp   *t;
+
+    i = sizeof(fontNamePropTable) / sizeof(fontProp);
+    for (t = fontNamePropTable; i; i--, t++)
+       t->atom = MakeAtom(t->name, (unsigned) strlen(t->name), TRUE);
+
+    i = sizeof(fontPropTable) / sizeof(fontProp);
+    for (t = fontPropTable; i; i--, t++)
+       t->atom = MakeAtom(t->name, (unsigned) strlen(t->name), TRUE);
+
+    i = sizeof(rawFontPropTable) / sizeof(fontProp);
+    for (t = rawFontPropTable; i; i--, t++)
+       t->atom = MakeAtom(t->name, (unsigned) strlen(t->name), TRUE);
+}
+
+static FontEntryPtr
+GetScalableEntry (fpe, name)
+    FontPathElementPtr fpe;
+    FontNamePtr                name;
+{
+    FontDirectoryPtr   dir;
+
+    dir = (FontDirectoryPtr) fpe->private;
+    return FontFileFindNameInDir (&dir->scalable, name);
+}
+
+static double
+get_matrix_horizontal_component(matrix)
+    double *matrix;
+{
+    return hypot(matrix[0], matrix[1]);
+}
+
+static double
+get_matrix_vertical_component(matrix)
+    double *matrix;
+{
+    return hypot(matrix[2], matrix[3]);
+}
+
+
+static Bool
+ComputeScaleFactors(from, to, dx, dy, sdx, sdy, rescale_x)
+    FontScalablePtr from,
+                to;
+    double     *dx, *sdx,
+               *dy, *sdy,
+              *rescale_x;
+{
+    double srcpixelset, destpixelset, srcpixel, destpixel;
+
+    srcpixelset = get_matrix_horizontal_component(from->pixel_matrix);
+    destpixelset = get_matrix_horizontal_component(to->pixel_matrix);
+    srcpixel = get_matrix_vertical_component(from->pixel_matrix);
+    destpixel = get_matrix_vertical_component(to->pixel_matrix);
+
+    if (srcpixelset >= EPS)
+    {
+       *dx = destpixelset / srcpixelset;
+       *sdx = 1000.0 / srcpixelset;
+    }
+    else
+       *sdx = *dx = 0;
+
+    *rescale_x = 1.0;
+
+    /* If client specified a width, it overrides setsize; in this
+       context, we interpret width as applying to the font before any
+       rotation, even though that's not what is ultimately returned in
+       the width field. */
+    if (from->width > 0 && to->width > 0 && fabs(*dx) > EPS)
+    {
+       double rescale = (double)to->width / (double)from->width;
+
+       /* If the client specified a transformation matrix, the rescaling
+          for width does *not* override the setsize.  Instead, just check
+          for consistency between the setsize from the matrix and the
+          setsize that would result from rescaling according to the width.
+          This assumes (perhaps naively) that the width is correctly
+          reported in the name.  As an interesting side effect, this test
+          may result in choosing a different source bitmap (one that
+          scales consistently between the setsize *and* the width) than it
+          would choose if a width were not specified.  Sort of a hidden
+          multiple-master functionality. */
+       if ((to->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY ||
+           (to->values_supplied & POINTSIZE_MASK) == POINTSIZE_ARRAY)
+       {
+           /* Reject if resulting width difference is >= 1 pixel */
+           if (fabs(rescale * from->width - *dx * from->width) >= 10)
+               return FALSE;
+       }
+       else
+       {
+           *rescale_x = rescale/(*dx);
+           *dx = rescale;
+       }
+    }
+
+    if (srcpixel >= EPS)
+    {
+       *dy = destpixel / srcpixel;
+       *sdy = 1000.0 / srcpixel;
+    }
+    else
+       *sdy = *dy = 0;
+
+    return TRUE;
+}
+
+/* favor enlargement over reduction because of aliasing resulting
+   from reduction */
+#define SCORE(m,s) \
+if (m >= 1.0) { \
+    if (m == 1.0) \
+        score += (16 * s); \
+    else if (m == 2.0) \
+        score += (4 * s); \
+    else \
+        score += (int)(((double)(3 * s)) / m); \
+} else { \
+        score += (int)(((double)(2 * s)) * m); \
+}
+
+/* don't need to favor enlargement when looking for bitmap that can
+   be used unscalable */
+#define SCORE2(m,s) \
+if (m >= 1.0) \
+    score += (int)(((double)(8 * s)) / m); \
+else \
+    score += (int)(((double)(8 * s)) * m);
+
+static FontEntryPtr
+FindBestToScale(fpe, entry, vals, best, dxp, dyp, sdxp, sdyp, fpep)
+    FontPathElementPtr fpe;
+    FontEntryPtr       entry;
+    FontScalablePtr    vals,
+                       best;
+    double             *dxp, *sdxp,
+                       *dyp, *sdyp;
+    FontPathElementPtr *fpep;
+{
+    FontScalableRec temp;
+    int                    source, i;
+    int                    best_score, best_unscaled_score,
+                   score;
+    double         dx, sdx, dx_amount,
+                   dy, sdy, dy_amount,
+                   best_dx, best_sdx, best_dx_amount,
+                   best_dy, best_sdy, best_dy_amount,
+                   best_unscaled_sdx, best_unscaled_sdy,
+                   rescale_x, best_rescale_x,
+                   best_unscaled_rescale_x;
+    FontEntryPtr    zero;
+    FontNameRec            zeroName;
+    char           zeroChars[MAXFONTNAMELEN];
+    FontDirectoryPtr   dir;
+    FontScaledPtr   scaled;
+    FontScalableExtraPtr   extra;
+    FontScaledPtr   best_scaled, best_unscaled;
+    FontPathElementPtr best_fpe, best_unscaled_fpe;
+    FontEntryPtr    bitmap = NULL;
+    FontEntryPtr    result;
+    int                    aliascount = 20;
+    FontPathElementPtr bitmap_fpe;
+    FontNameRec            xlfdName;
+
+    /* find the best match */
+    rescale_x = 1.0;
+    best_scaled = 0;
+    best_score = 0;
+    best_unscaled = 0;
+    best_unscaled_score = -1;
+    memcpy (zeroChars, entry->name.name, entry->name.length);
+    zeroChars[entry->name.length] = '\0';
+    zeroName.name = zeroChars;
+    FontParseXLFDName (zeroChars, &temp, FONT_XLFD_REPLACE_ZERO);
+    zeroName.length = strlen (zeroChars);
+    zeroName.ndashes = entry->name.ndashes;
+    xlfdName.name = vals->xlfdName;
+    xlfdName.length = strlen(xlfdName.name);
+    xlfdName.ndashes = FontFileCountDashes(xlfdName.name, xlfdName.length);
+    restart_bestscale_loop: ;
+    /*
+     * Look through all the registered bitmap sources for
+     * the same zero name as ours; entries along that one
+     * can be scaled as desired.
+     */
+    for (source = 0; source < FontFileBitmapSources.count; source++)
+    {
+       /* There might already be a bitmap that satisfies the request
+          but didn't have a zero name that was found by the scalable
+          font matching logic.  Keep track if there is.  */
+       if (bitmap == NULL && vals->xlfdName != NULL)
+       {
+           bitmap_fpe = FontFileBitmapSources.fpe[source];
+           dir = (FontDirectoryPtr) bitmap_fpe->private;
+           bitmap = FontFileFindNameInDir (&dir->nonScalable, &xlfdName);
+           if (bitmap && bitmap->type != FONT_ENTRY_BITMAP)
+           {
+               if (bitmap->type == FONT_ENTRY_ALIAS && aliascount > 0)
+               {
+                   aliascount--;
+                   xlfdName.name = bitmap->u.alias.resolved;
+                   xlfdName.length = strlen(xlfdName.name);
+                   xlfdName.ndashes = FontFileCountDashes(xlfdName.name,
+                                                          xlfdName.length);
+                   bitmap = NULL;
+                   goto restart_bestscale_loop;
+               }
+               else
+                   bitmap = NULL;
+           }
+       }
+
+       if (FontFileBitmapSources.fpe[source] == fpe)
+           zero = entry;
+       else
+       {
+           dir = (FontDirectoryPtr) FontFileBitmapSources.fpe[source]->private;
+           zero = FontFileFindNameInDir (&dir->scalable, &zeroName);
+           if (!zero)
+               continue;
+       }
+       extra = zero->u.scalable.extra;
+       for (i = 0; i < extra->numScaled; i++)
+       {
+           FontScalableRec tmpvals;
+           scaled = &extra->scaled[i];
+           if (!scaled->bitmap)
+               continue;
+           if (!ComputeScaleFactors(&scaled->vals, vals, &dx, &dy, &sdx, &sdy,
+                                    &rescale_x))
+               continue;
+           score = 0;
+           dx_amount = dx;
+           dy_amount = dy;
+           SCORE(dy_amount, 10);
+           SCORE(dx_amount, 1);
+           if ((score > best_score) ||
+                   ((score == best_score) &&
+                    ((dy_amount < best_dy_amount) ||
+                     ((dy_amount == best_dy_amount) &&
+                      (dx_amount < best_dx_amount))))) 
+           {
+               best_fpe = FontFileBitmapSources.fpe[source];
+               best_scaled = scaled;
+               best_score = score;
+               best_dx = dx;
+               best_dy = dy;
+               best_sdx = sdx;
+               best_sdy = sdy;
+               best_dx_amount = dx_amount;
+               best_dy_amount = dy_amount;
+               best_rescale_x = rescale_x;
+           }
+           /* Is this font a candidate for use without ugly rescaling? */
+           if (fabs(dx) > EPS && fabs(dy) > EPS &&
+               fabs(vals->pixel_matrix[0] * rescale_x -
+                    scaled->vals.pixel_matrix[0]) < 1 &&
+               fabs(vals->pixel_matrix[1] * rescale_x -
+                    scaled->vals.pixel_matrix[1]) < EPS &&
+               fabs(vals->pixel_matrix[2] -
+                    scaled->vals.pixel_matrix[2]) < EPS &&
+               fabs(vals->pixel_matrix[3] -
+                    scaled->vals.pixel_matrix[3]) < 1)
+           {
+               /* Yes.  The pixel sizes are close on the diagonal and
+                  extremely close off the diagonal. */
+               score = 0;
+               SCORE2(vals->pixel_matrix[3] /
+                      scaled->vals.pixel_matrix[3], 10);
+               SCORE2(vals->pixel_matrix[0] * rescale_x /
+                      scaled->vals.pixel_matrix[0], 1);
+               if (score > best_unscaled_score)
+               {
+                   best_unscaled_fpe = FontFileBitmapSources.fpe[source];
+                   best_unscaled = scaled;
+                   best_unscaled_sdx = sdx / dx;
+                   best_unscaled_sdy = sdy / dy;
+                   best_unscaled_score = score;
+                   best_unscaled_rescale_x = rescale_x;
+               }
+           }
+       }
+    }
+    if (best_unscaled)
+    {
+       *best = best_unscaled->vals;
+       *fpep = best_unscaled_fpe;
+       *dxp = 1.0;
+       *dyp = 1.0;
+       *sdxp = best_unscaled_sdx;
+       *sdyp = best_unscaled_sdy;
+       rescale_x = best_unscaled_rescale_x;
+       result = best_unscaled->bitmap;
+    }
+    else if (best_scaled)
+    {
+       *best = best_scaled->vals;
+       *fpep = best_fpe;
+       *dxp = best_dx;
+       *dyp = best_dy;
+       *sdxp = best_sdx;
+       *sdyp = best_sdy;
+       rescale_x = best_rescale_x;
+       result = best_scaled->bitmap;
+    }
+    else
+       result = NULL;
+
+    if (bitmap != NULL && (result == NULL || *dxp != 1.0 || *dyp != 1.0))
+    {
+       *fpep = bitmap_fpe;
+       FontParseXLFDName (bitmap->name.name, best, FONT_XLFD_REPLACE_NONE);
+       if (ComputeScaleFactors(best, best, dxp, dyp, sdxp, sdyp, &rescale_x))
+           result = bitmap;
+       else
+           result = NULL;
+    }
+
+    if (result && rescale_x != 1.0)
+    {
+       /* We have rescaled horizontally due to an XLFD width field.  Change
+          the matrix appropriately */
+       vals->pixel_matrix[0] *= rescale_x;
+       vals->pixel_matrix[1] *= rescale_x;
+#ifdef NOTDEF
+       /* This would force the pointsize and pixelsize fields in the
+          FONT property to display as matrices to more accurately
+          report the font being supplied.  It might also break existing
+          applications that expect a single number in that field. */
+       vals->values_supplied =
+           vals->values_supplied & ~(PIXELSIZE_MASK | POINTSIZE_MASK) |
+           PIXELSIZE_ARRAY;
+#else /* NOTDEF */
+       vals->values_supplied = vals->values_supplied & ~POINTSIZE_MASK;
+#endif /* NOTDEF */
+       /* Recompute and reround the FontScalablePtr values after
+          rescaling for the new width. */
+       FontFileCompleteXLFD(vals, vals);
+    }
+
+    return result;
+}
+
+static FontEntryPtr
+FindPmfToScale(fpe, entry, vals, best, dxp, dyp, sdxp, sdyp, fpep)
+    FontPathElementPtr fpe;
+    FontEntryPtr       entry;
+    FontScalablePtr    vals,
+                       best;
+    double             *dxp, *sdxp,
+                       *dyp, *sdyp;
+    FontPathElementPtr *fpep;
+{
+    FontEntryPtr    result = NULL;
+    FontScaledPtr   scaled;
+    FontScalableExtraPtr   extra;
+    int i;
+
+    extra = entry->u.scalable.extra;
+    for (i = 0; i < extra->numScaled; i++)
+    {
+       double rescale_x;
+
+       scaled = &extra->scaled[i];
+       if (!scaled->bitmap)
+           continue;
+       if (!ComputeScaleFactors(&scaled->vals, vals, dxp, dyp, sdxp, sdyp,
+                                &rescale_x))
+           continue;
+       *best = scaled->vals;
+       *fpep = fpe;
+       result = scaled->bitmap;
+       if (rescale_x != 1.0)
+       {
+           /* We have rescaled horizontally due to an XLFD width field.  Change
+              the matrix appropriately */
+           vals->pixel_matrix[0] *= rescale_x;
+           vals->pixel_matrix[1] *= rescale_x;
+#ifdef NOTDEF
+           /* This would force the pointsize and pixelsize fields in the
+              FONT property to display as matrices to more accurately
+              report the font being supplied.  It might also break existing
+              applications that expect a single number in that field. */
+           vals->values_supplied =
+               vals->values_supplied & ~(PIXELSIZE_MASK | POINTSIZE_MASK) |
+               PIXELSIZE_ARRAY;
+#else /* NOTDEF */
+           vals->values_supplied = vals->values_supplied & ~POINTSIZE_MASK;
+#endif /* NOTDEF */
+           /* Recompute and reround the FontScalablePtr values after
+              rescaling for the new width. */
+           FontFileCompleteXLFD(vals, vals);
+       }
+       break;
+    }
+    return result;
+}
+
+static long
+doround(x)
+double x;
+{
+    return (x >= 0) ? (long)(x + .5) : (long)(x - .5);
+}
+
+static int
+computeProps(pf, wasStringProp, npf, isStringProp, nprops, xfactor, yfactor,
+            sXfactor, sYfactor)
+    FontPropPtr pf;
+    char       *wasStringProp;
+    FontPropPtr npf;
+    char       *isStringProp;
+    unsigned int nprops;
+    double      xfactor, sXfactor,
+                yfactor, sYfactor;
+{
+    int         n;
+    int         count;
+    fontProp   *t;
+    double      rawfactor;
+
+    for (count = 0; nprops > 0; nprops--, pf++, wasStringProp++) {
+       n = sizeof(fontPropTable) / sizeof(fontProp);
+       for (t = fontPropTable; n && (t->atom != pf->name); n--, t++);
+       if (!n)
+           continue;
+
+       switch (t->type) {
+       case scaledX:
+           npf->value = doround(xfactor * (double)pf->value);
+           rawfactor = sXfactor;
+           break;
+       case scaledY:
+           npf->value = doround(yfactor * (double)pf->value);
+           rawfactor = sYfactor;
+           break;
+       case unscaled:
+           npf->value = pf->value;
+           npf->name = pf->name;
+           npf++;
+           count++;
+           *isStringProp++ = *wasStringProp;
+           break;
+       }
+       if (t->type != unscaled)
+       {
+           npf->name = pf->name;
+           npf++;
+           count++;
+           npf->value = doround(rawfactor * (double)pf->value);
+           npf->name = rawFontPropTable[t - fontPropTable].atom;
+           npf++;
+           count++;
+           *isStringProp++ = *wasStringProp;
+           *isStringProp++ = *wasStringProp;
+       }
+    }
+    return count;
+}
+
+
+static int
+ComputeScaledProperties(sourceFontInfo, name, vals, dx, dy, sdx, sdy,
+                       sWidth, pProps, pIsStringProp)
+    FontInfoPtr     sourceFontInfo;    /* the font to be scaled */
+    char           *name;              /* name of resulting font */
+    FontScalablePtr vals;
+    double         dx, sdx,
+                   dy, sdy;    /* scale factors in x and y directions */
+    long           sWidth;     /* 1000-pixel average width */
+    FontPropPtr            *pProps;    /* returns properties; preallocated */
+    char           **pIsStringProp;  /* return booleans; preallocated */
+{
+    int         n;
+    char       *ptr1,
+               *ptr2;
+    char       *ptr3;
+    FontPropPtr fp;
+    fontProp   *fpt;
+    extern int  serverGeneration;
+    char       *isStringProp;
+    int                nProps;
+
+    if (fontGeneration != serverGeneration) {
+       initFontPropTable();
+       fontGeneration = serverGeneration;
+    }
+    nProps = NPROPS + 1 + sizeof(fontPropTable) / sizeof(fontProp) +
+                         sizeof(rawFontPropTable) / sizeof(fontProp);
+    fp = (FontPropPtr) xalloc(sizeof(FontPropRec) * nProps);
+    *pProps = fp;
+    if (!fp)
+       return 1;
+    isStringProp = (char *) xalloc (nProps);
+    *pIsStringProp = isStringProp;
+    if (!isStringProp)
+    {
+       xfree (fp);
+       return 1;
+    }
+    ptr2 = name;
+    for (fpt = fontNamePropTable, n = NPROPS;
+        n;
+        fp++, fpt++, n--, isStringProp++)
+    {
+
+       if (*ptr2)
+       {
+           ptr1 = ptr2 + 1;
+           if (!(ptr2 = strchr(ptr1, '-'))) ptr2 = strchr(ptr1, '\0');
+       }
+
+       *isStringProp = 0;
+       switch (fpt->type) {
+       case atom:
+           fp->value = MakeAtom(ptr1, ptr2 - ptr1, TRUE);
+           *isStringProp = 1;
+           break;
+       case truncate_atom:
+           for (ptr3 = ptr1; *ptr3; ptr3++)
+               if (*ptr3 == '[')
+                   break;
+           if (!*ptr3) ptr3 = ptr2;
+           fp->value = MakeAtom(ptr1, ptr3 - ptr1, TRUE);
+           *isStringProp = 1;
+           break;
+       case pixel_size:
+           fp->value = doround(vals->pixel_matrix[3]);
+           break;
+       case point_size:
+           fp->value = doround(vals->point_matrix[3] * 10.0);
+           break;
+       case resolution_x:
+           fp->value = vals->x;
+           break;
+       case resolution_y:
+           fp->value = vals->y;
+           break;
+       case average_width:
+           fp->value = vals->width;
+           break;
+       case fontname:
+           fp->value = MakeAtom(name, strlen(name), TRUE);
+           *isStringProp = 1;
+           break;
+       case raw_ascent:
+           fp->value = sourceFontInfo->fontAscent * sdy;
+           break;
+       case raw_descent:
+           fp->value = sourceFontInfo->fontDescent * sdy;
+           break;
+       case raw_pointsize:
+           fp->value = (long)(72270.0 / (double)vals->y + .5);
+           break;
+       case raw_pixelsize:
+           fp->value = 1000;
+           break;
+       case raw_average_width:
+           fp->value = sWidth;
+           break;
+       }
+       fp->name = fpt->atom;
+    }
+    n = NPROPS;
+    n += computeProps(sourceFontInfo->props, sourceFontInfo->isStringProp,
+                     fp, isStringProp, sourceFontInfo->nprops, dx, dy,
+                     sdx, sdy);
+    return n;
+}
+
+static void ScaleBitmap();
+
+static int
+compute_xform_matrix(vals, dx, dy, xform, inv_xform, xmult, ymult)
+    FontScalablePtr    vals;
+    double             dx, dy, *inv_xform, *xmult, *ymult;
+    register double    *xform;
+{
+    double det, sintheta, costheta, tanphi;
+    double pixel = get_matrix_vertical_component(vals->pixel_matrix);
+    double pixelset = get_matrix_horizontal_component(vals->pixel_matrix);
+
+    if (pixel < EPS || pixelset < EPS) return 0;
+
+    /* Initialize the transformation matrix to the scaling factors */
+    xform[0] = dx / pixelset;
+    xform[1] = xform[2] = 0.0;
+    xform[3] = dy / pixel;
+
+/* Inline matrix multiply -- somewhat ugly to minimize register usage */
+#define MULTIPLY_XFORM(a,b,c,d) \
+{ \
+  register double aa = (a), bb = (b), cc = (c), dd = (d); \
+  register double temp; \
+  temp =     aa * xform[0] + cc * xform[1]; \
+  aa =       aa * xform[2] + cc * xform[3]; \
+  xform[1] = bb * xform[0] + dd * xform[1]; \
+  xform[3] = bb * xform[2] + dd * xform[3]; \
+  xform[0] = temp; \
+  xform[2] = aa; \
+}
+
+    /* Rescale the transformation matrix for size of source font */
+    MULTIPLY_XFORM(vals->pixel_matrix[0],
+                  vals->pixel_matrix[1],
+                  vals->pixel_matrix[2],
+                  vals->pixel_matrix[3]);
+
+    *xmult = xform[0];
+    *ymult = xform[3];
+
+
+    if (inv_xform == NULL) return 1;
+
+    /* Compute the determinant for use in inverting the matrix. */
+    det = xform[0] * xform[3] - xform[1] * xform[2];
+
+    /* If the determinant is tiny or zero, give up */
+    if (fabs(det) < EPS) return 0;
+
+    /* Compute the inverse */
+    inv_xform[0] = xform[3] / det;
+    inv_xform[1] = -xform[1] / det;
+    inv_xform[2] = -xform[2] / det;
+    inv_xform[3] = xform[0] / det;
+
+    return 1;
+}
+
+/*
+ *  ScaleFont
+ *  returns a pointer to the new scaled font, or NULL (due to AllocError).
+ */
+static FontPtr
+ScaleFont(opf, widthMult, heightMult, sWidthMult, sHeightMult, vals,
+         newWidthMult, newHeightMult, sWidth)
+    FontPtr     opf;           /* originating font */
+    double      widthMult;     /* glyphs width scale factor */
+    double      heightMult;    /* glyphs height scale factor */
+    double      sWidthMult;    /* scalable glyphs width scale factor */
+    double      sHeightMult;   /* scalable glyphs height scale factor */
+    FontScalablePtr    vals;
+    double      *newWidthMult; /* return: X component of glyphs width
+                                  scale factor */
+    double      *newHeightMult;        /* return: Y component of glyphs height
+                                  scale factor */
+    long       *sWidth;        /* return: average 1000-pixel width */
+{
+    FontPtr     pf;
+    FontInfoPtr pfi,
+                opfi;
+    BitmapFontPtr  bitmapFont,
+                obitmapFont;
+    CharInfoPtr pci,
+                opci;
+    int         nchars;                /* how many characters in the font */
+    int        *scratch;
+    int         i;
+    int         glyph;
+    int                firstCol, lastCol, firstRow, lastRow;
+    double     xform[4], inv_xform[4];
+    double     xmult, ymult;
+    int                totalwidth = 0, totalchars = 0;
+    int                inkindex1, inkindex2;
+#define OLDINDEX(i) (((i)/(lastCol - firstCol + 1) + \
+                     firstRow - opf->info.firstRow) * \
+                    (opf->info.lastCol - opf->info.firstCol + 1) + \
+                    (i)%(lastCol - firstCol + 1) + \
+                    firstCol - opf->info.firstCol)
+
+    extern int  bitmapGetBitmaps();
+    extern int  bitmapGetExtents();
+    extern int  bitmapGetGlyphs();
+    extern int  bitmapGetMetrics();
+
+    *sWidth = 0;
+
+    opfi = &opf->info;
+    glyph = opf->glyph;
+    obitmapFont = (BitmapFontPtr) opf->fontPrivate;
+
+    bitmapFont = 0;
+    pf = (FontPtr) xalloc(sizeof(FontRec));
+    if (!pf)
+       goto bail;
+    pf->refcnt = 0;
+    pf->maxPrivate = -1;
+    pf->devPrivates = (pointer *) 0;
+    pf->bit = opf->bit;
+    pf->byte = opf->byte;
+    pf->glyph = opf->glyph;
+    pf->scan = opf->scan;
+
+    pf->get_glyphs = bitmapGetGlyphs;
+    pf->get_metrics = bitmapGetMetrics;
+    pf->unload_font = bitmapUnloadScalable;
+    pf->unload_glyphs = NULL;
+
+    pfi = &pf->info;
+    *pfi = *opfi;
+    /* If charset subsetting specified in vals, determine what our range
+       needs to be for the output font */
+    if (vals->nranges)
+    {
+       int i;
+
+       pfi->allExist = 0;
+       firstCol = 255;
+       lastCol = 0;
+       firstRow = 255;
+       lastRow = 0;
+
+       for (i = 0; i < vals->nranges; i++)
+       {
+           if (vals->ranges[i].min_char_high != vals->ranges[i].max_char_high)
+           {
+               firstCol = opfi->firstCol;
+               lastCol = opfi->lastCol;
+           }
+           if (firstCol > vals->ranges[i].min_char_low)
+               firstCol = vals->ranges[i].min_char_low;
+           if (lastCol < vals->ranges[i].max_char_low)
+               lastCol = vals->ranges[i].max_char_low;
+           if (firstRow > vals->ranges[i].min_char_high)
+               firstRow = vals->ranges[i].min_char_high;
+           if (lastRow < vals->ranges[i].max_char_high)
+               lastRow = vals->ranges[i].max_char_high;
+       }
+
+       if (firstCol > lastCol || firstRow > lastRow)
+           goto bail;
+
+       if (firstCol < opfi->firstCol)
+           firstCol = opfi->firstCol;
+       if (lastCol > opfi->lastCol)
+           lastCol = opfi->lastCol;
+       if (firstRow < opfi->firstRow)
+           firstRow = opfi->firstRow;
+       if (lastRow > opfi->lastRow)
+           lastRow = opfi->lastRow;
+    }
+    else
+    {
+       firstCol = opfi->firstCol;
+       lastCol = opfi->lastCol;
+       firstRow = opfi->firstRow;
+       lastRow = opfi->lastRow;
+    }
+
+    bitmapFont = (BitmapFontPtr) xalloc(sizeof(BitmapFontRec));
+    if (!bitmapFont)
+       goto bail;
+    nchars = (lastRow - firstRow + 1) * (lastCol - firstCol + 1);
+    pfi->firstRow = firstRow;
+    pfi->lastRow = lastRow;
+    pfi->firstCol = firstCol;
+    pfi->lastCol = lastCol;
+    pf->fontPrivate = (pointer) bitmapFont;
+    bitmapFont->version_num = obitmapFont->version_num;
+    bitmapFont->num_chars = nchars;
+    bitmapFont->num_tables = obitmapFont->num_tables;
+    bitmapFont->metrics = 0;
+    bitmapFont->ink_metrics = 0;
+    bitmapFont->bitmaps = 0;
+    bitmapFont->encoding = 0;
+    bitmapFont->bitmapExtra = 0;
+    bitmapFont->pDefault = 0;
+    bitmapFont->metrics = (CharInfoPtr) xalloc(nchars * sizeof(CharInfoRec));
+    if (!bitmapFont->metrics)
+       goto bail;
+    bitmapFont->encoding = (CharInfoPtr *) xalloc(nchars * sizeof(CharInfoPtr));
+    if (!bitmapFont->encoding)
+       goto bail;
+
+#undef MAXSHORT
+#define MAXSHORT    32767
+#undef MINSHORT
+#define MINSHORT    -32768
+
+    pfi->anamorphic = FALSE;
+    if (heightMult != widthMult)
+       pfi->anamorphic = TRUE;
+    pfi->cachable = TRUE;
+
+    if (!compute_xform_matrix(vals, widthMult, heightMult, xform,
+                             inv_xform, &xmult, &ymult))
+       goto bail;
+
+    pfi->fontAscent = opfi->fontAscent * ymult;
+    pfi->fontDescent = opfi->fontDescent * ymult;
+
+    pfi->minbounds.leftSideBearing = MAXSHORT;
+    pfi->minbounds.rightSideBearing = MAXSHORT;
+    pfi->minbounds.ascent = MAXSHORT;
+    pfi->minbounds.descent = MAXSHORT;
+    pfi->minbounds.characterWidth = MAXSHORT;
+    pfi->minbounds.attributes = MAXSHORT;
+
+    pfi->maxbounds.leftSideBearing = MINSHORT;
+    pfi->maxbounds.rightSideBearing = MINSHORT;
+    pfi->maxbounds.ascent = MINSHORT;
+    pfi->maxbounds.descent = MINSHORT;
+    pfi->maxbounds.characterWidth = MINSHORT;
+    pfi->maxbounds.attributes = MINSHORT;
+
+    /* Compute the transformation and inverse transformation matrices.
+       Can fail if the determinant is zero. */
+
+    inkindex1 = 0;
+    pci = bitmapFont->metrics;
+    for (i = 0; i < nchars; i++)
+    {
+       if (opci = obitmapFont->encoding[inkindex2 = OLDINDEX(i)])
+       {
+           double newlsb, newrsb, newdesc, newasc, point[2];
+
+#define minchar(p) ((p).min_char_low + ((p).min_char_high << 8))
+#define maxchar(p) ((p).max_char_low + ((p).max_char_high << 8))
+
+           if (vals->nranges)
+           {
+               int row = i / (lastCol - firstCol + 1) + firstRow;
+               int col = i % (lastCol - firstCol + 1) + firstCol;
+               int ch = (row << 8) + col;
+               int j;
+               for (j = 0; j < vals->nranges; j++)
+                   if (ch >= minchar(vals->ranges[j]) &&
+                       ch <= maxchar(vals->ranges[j]))
+                       break;
+               if (j == vals->nranges)
+               {
+                   bitmapFont->encoding[i] = 0;
+                   continue;
+               }
+           }
+
+           if (opci->metrics.leftSideBearing == 0 &&
+               opci->metrics.rightSideBearing == 0 &&
+               opci->metrics.ascent == 0 &&
+               opci->metrics.descent == 0 &&
+               opci->metrics.characterWidth == 0)
+           {
+               bitmapFont->encoding[i] = 0;
+               continue;
+           }
+
+           bitmapFont->encoding[i] = pci;
+
+           /* Compute new extents for this glyph */
+           TRANSFORM_POINT(xform,
+                           opci->metrics.leftSideBearing,
+                           -opci->metrics.descent,
+                           point);
+           newlsb = point[0];
+           newrsb = newlsb;
+           newdesc = -point[1];
+           newasc = -newdesc;
+           TRANSFORM_POINT(xform,
+                           opci->metrics.leftSideBearing,
+                           opci->metrics.ascent,
+                           point);
+           CHECK_EXTENT(newlsb, newrsb, newdesc, newasc, point);
+           TRANSFORM_POINT(xform,
+                           opci->metrics.rightSideBearing,
+                           -opci->metrics.descent,
+                           point);
+           CHECK_EXTENT(newlsb, newrsb, newdesc, newasc, point);
+           TRANSFORM_POINT(xform,
+                           opci->metrics.rightSideBearing,
+                           opci->metrics.ascent,
+                           point);
+           CHECK_EXTENT(newlsb, newrsb, newdesc, newasc, point);
+
+           pci->metrics.leftSideBearing = (int)floor(newlsb);
+           pci->metrics.rightSideBearing = (int)floor(newrsb + .5);
+           pci->metrics.descent = (int)ceil(newdesc);
+           pci->metrics.ascent = (int)floor(newasc + .5);
+           /* Accumulate total width of characters before transformation,
+              to ascertain predominant direction of font. */
+           totalwidth += opci->metrics.characterWidth;
+           pci->metrics.characterWidth =
+               doround((double)opci->metrics.characterWidth * xmult);
+           pci->metrics.attributes =
+               doround((double)opci->metrics.characterWidth * sWidthMult);
+           if (!pci->metrics.characterWidth)
+           {
+               /* Since transformation may shrink width, height, and
+                  escapement to zero, make sure existing characters
+                  are not mistaken for undefined characters. */
+
+               if (pci->metrics.rightSideBearing ==
+                   pci->metrics.leftSideBearing)
+                   pci->metrics.rightSideBearing++;
+               if (pci->metrics.ascent == -pci->metrics.descent)
+                   pci->metrics.ascent++;
+           }
+    
+           pci++;
+       }
+       else
+           bitmapFont->encoding[i] = 0;
+    }
+
+
+    /*
+     * For each character, set the per-character metrics, scale the glyph, and
+     * check per-font minbounds and maxbounds character information.
+     */
+
+    pci = bitmapFont->metrics;
+    for (i = 0; i < nchars; i++)
+    {
+       CharInfoRec temppci;
+       if ((pci = bitmapFont->encoding[i]) &&
+           (opci = obitmapFont->encoding[OLDINDEX(i)]))
+       {
+           pci = bitmapFont->encoding[i];
+           totalchars++;
+           *sWidth += abs((int)(INT16)pci->metrics.attributes);
+#define MINMAX(field) \
+           if (pfi->minbounds.field > pci->metrics.field) \
+               pfi->minbounds.field = pci->metrics.field; \
+           if (pfi->maxbounds.field < pci->metrics.field) \
+               pfi->maxbounds.field = pci->metrics.field
+    
+           MINMAX(leftSideBearing);
+           MINMAX(rightSideBearing);
+           MINMAX(ascent);
+           MINMAX(descent);
+           MINMAX(characterWidth);
+
+           /* Hack: Cast attributes into a signed quantity.  Tread lightly
+              for now and don't go changing the global Xproto.h file */
+           if ((INT16)pfi->minbounds.attributes >
+               (INT16)pci->metrics.attributes)
+               pfi->minbounds.attributes = pci->metrics.attributes;
+           if ((INT16)pfi->maxbounds.attributes <
+               (INT16)pci->metrics.attributes)
+               pfi->maxbounds.attributes = pci->metrics.attributes;
+#undef MINMAX
+       }
+    }
+    pfi->ink_minbounds = pfi->minbounds;
+    pfi->ink_maxbounds = pfi->maxbounds;
+    if (totalchars)
+    {
+       *sWidth = (*sWidth * 10 + totalchars / 2) / totalchars;
+       if (totalwidth < 0)
+       {
+           /* Dominant direction is R->L */
+           *sWidth = -*sWidth;
+       }
+
+       if (pfi->minbounds.characterWidth == pfi->maxbounds.characterWidth)
+           vals->width = pfi->minbounds.characterWidth * 10;
+       else
+           vals->width = doround((double)*sWidth * vals->pixel_matrix[0] /
+                                 1000.0);
+    }
+    else
+    {
+       vals->width = 0;
+       *sWidth = 0;
+    }
+    FontComputeInfoAccelerators (pfi);
+
+    if (pfi->defaultCh != (unsigned short) NO_SUCH_CHAR) {
+       unsigned int r,
+                   c,
+                   cols;
+
+       r = pfi->defaultCh >> 8;
+       c = pfi->defaultCh & 0xFF;
+       if (pfi->firstRow <= r && r <= pfi->lastRow &&
+               pfi->firstCol <= c && c <= pfi->lastCol) {
+           cols = pfi->lastCol - pfi->firstCol + 1;
+           r = r - pfi->firstRow;
+           c = c - pfi->firstCol;
+           bitmapFont->pDefault = bitmapFont->encoding[r * cols + c];
+       }
+    }
+
+    *newWidthMult = xmult;
+    *newHeightMult = ymult;
+    return pf;
+bail:
+    if (pf)
+       xfree(pf);
+    if (bitmapFont) {
+       xfree(bitmapFont->metrics);
+       xfree(bitmapFont->ink_metrics);
+       xfree(bitmapFont->bitmaps);
+       xfree(bitmapFont->encoding);
+    }
+    return NULL;
+}
+
+static int
+lcm(a, b)                      /* least common multiple */
+    int         a,
+                b;
+{
+    register int m;
+    register int larger,
+                smaller;
+
+    if (a > b) {
+       m = larger = a;
+       smaller = b;
+    } else {
+       m = larger = b;
+       smaller = a;
+    }
+
+    while (m % smaller)
+       m += larger;
+    return m;
+}
+
+static void
+ScaleBitmap(pFont, opci, pci, inv_xform, widthMult, heightMult)
+    FontPtr     pFont;
+    CharInfoPtr opci;
+    CharInfoPtr pci;
+    double     *inv_xform;
+    double     widthMult;
+    double     heightMult;
+{
+    register char  *bitmap,            /* The bits */
+               *newBitmap;
+    register int   bpr,                        /* Padding information */
+               newBpr;
+    int         width,                 /* Extents information */
+                height,
+                newWidth,
+                newHeight;
+    register int row,                  /* Loop variables */
+               col;
+    INT32      deltaX,                 /* Increments for resampling loop */
+               deltaY;
+    INT32      xValue,                 /* Subscripts for resampling loop */
+               yValue;
+    double     point[2];
+    unsigned char *char_grayscale = 0;
+    INT32      *diffusion_workspace, *thisrow, *nextrow, pixmult;
+    int                box_x, box_y;
+
+    static unsigned char masklsb[] =
+       { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };
+    static unsigned char maskmsb[] =
+       { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
+    unsigned char      *mask = (pFont->bit == LSBFirst ? masklsb : maskmsb);
+
+
+    bitmap = opci->bits;
+    newBitmap = pci->bits;
+    width = GLYPHWIDTHPIXELS(opci);
+    height = GLYPHHEIGHTPIXELS(opci);
+    newWidth = GLYPHWIDTHPIXELS(pci);
+    newHeight = GLYPHHEIGHTPIXELS(pci);
+    if (!newWidth || !newHeight || !width || !height)
+       return;
+
+    bpr = BYTES_PER_ROW(width, pFont->glyph);
+    newBpr = BYTES_PER_ROW(newWidth, pFont->glyph);
+
+    if (widthMult > 0.0 && heightMult > 0.0 &&
+       (widthMult < 1.0 || heightMult < 1.0))
+    {
+       /* We are reducing in one or both dimensions.  In an attempt to
+          reduce aliasing, we'll antialias by passing the original
+          glyph through a low-pass box filter (which results in a
+          grayscale image), then use error diffusion to create bitonal
+          output in the resampling loop.  */
+
+       /* First compute the sizes of the box filter */
+       widthMult = ceil(1.0 / widthMult);
+       heightMult = ceil(1.0 / heightMult);
+       box_x = width / 2;
+       box_y = height / 2;
+       if (widthMult < (double)box_x) box_x = (int)widthMult;
+       if (heightMult < (double)box_y) box_y = (int)heightMult;
+       /* The pixmult value (below) is used to darken the image before
+          we perform error diffusion: a necessary concession to the
+          fact that it's very difficult to generate readable halftoned
+          glyphs.  The degree of darkening is proportional to the size
+          of the blurring filter, hence inversely proportional to the
+          darkness of the lightest gray that results from antialiasing.
+          The result is that characters that exercise this logic (those
+          generated by reducing from a larger source font) tend to err
+          on the side of being too bold instead of being too light to
+          be readable. */
+       pixmult = box_x * box_y * 192;
+
+       if (box_x > 1 || box_y > 1)
+       {
+           /* Looks like we need to anti-alias.  Create a workspace to
+              contain the grayscale character plus an additional row and
+              column for scratch */
+           char_grayscale =
+               (unsigned char *)xalloc((width + 1) * (height + 1));
+           if (char_grayscale)
+           {
+               diffusion_workspace =
+                   (INT32 *)xalloc((newWidth + 2) * 2 * sizeof(int));
+               if (!diffusion_workspace)
+               {
+                   xfree(char_grayscale);
+                   char_grayscale = (unsigned char *)0;
+               }
+               /* Initialize our error diffusion workspace for later use */
+               bzero((char *)diffusion_workspace + sizeof(INT32),
+                     (newWidth + 3) * sizeof(int));
+               thisrow = diffusion_workspace + 1;
+               nextrow = diffusion_workspace + newWidth + 3;
+           }
+       }
+    }
+
+    if (char_grayscale)
+    {
+       /* We will be doing antialiasing.  First copy the bitmap into
+          our buffer, mapping input range [0,1] to output range
+          [0,255].  */
+       register unsigned char *srcptr, *dstptr;
+       srcptr = (unsigned char *)bitmap;
+       dstptr = char_grayscale;
+       for (row = 0; row < height; row++)
+       {
+           for (col = 0; col < width; col++)
+               *dstptr++ = (srcptr[col >> 3] & mask[col & 0x7]) ? 255 : 0;
+           srcptr += bpr;      /* On to next row of source */
+           dstptr++;           /* Skip scratch column in dest */
+       }
+       if (box_x > 1)
+       {
+           /* Our box filter has a width > 1... let's filter the rows */
+
+           int right_width = box_x / 2;
+           int left_width = box_x - right_width - 1;
+
+           for (row = 0; row < height; row++)
+           {
+               int sum = 0;
+               int left_size = 0, right_size = 0;
+
+               srcptr = char_grayscale + (width + 1) * row;
+               dstptr = char_grayscale + (width + 1) * height; /* scratch */
+
+               /* We've computed the shape of our full box filter.  Now
+                  compute the right-hand part of the moving sum */
+               for (right_size = 0; right_size < right_width; right_size++)
+                   sum += srcptr[right_size];
+
+               /* Now start moving the sum, growing the box filter, and
+                  dropping averages into our scratch buffer */
+               for (left_size = 0; left_size < left_width; left_size++)
+               {
+                   sum += srcptr[right_width];
+                   *dstptr++ = sum / (left_size + right_width + 1);
+                   srcptr++;
+               }
+
+               /* The box filter has reached full width... continue
+                  computation of moving average until the right side
+                  hits the wall. */
+               for (col = left_size; col + right_size < width; col++)
+               {
+                   sum += srcptr[right_width];
+                   *dstptr++ = sum / box_x;
+                   sum -= srcptr[-left_width];
+                   srcptr++;
+               }
+
+               /* Collapse the right side of the box filter */
+               for (; right_size > 0; right_size--)
+               {
+                   *dstptr++ = sum / (left_width + right_size);
+                   sum -= srcptr[-left_width];
+                   srcptr++;
+               }
+
+               /* Done with the row... copy dest back over source */
+               memmove(char_grayscale + (width + 1) * row,
+                       char_grayscale + (width + 1) * height,
+                       width);
+           }
+       }
+       if (box_y > 1)
+       {
+           /* Our box filter has a height > 1... let's filter the columns */
+
+           int bottom_height = box_y / 2;
+           int top_height = box_y - bottom_height - 1;
+
+           for (col = 0; col < width; col++)
+           {
+               int sum = 0;
+               int top_size = 0, bottom_size = 0;
+
+               srcptr = char_grayscale + col;
+               dstptr = char_grayscale + width;         /* scratch */
+
+               /* We've computed the shape of our full box filter.  Now
+                  compute the bottom part of the moving sum */
+               for (bottom_size = 0;
+                    bottom_size < bottom_height;
+                    bottom_size++)
+                   sum += srcptr[bottom_size * (width + 1)];
+
+               /* Now start moving the sum, growing the box filter, and
+                  dropping averages into our scratch buffer */
+               for (top_size = 0; top_size < top_height; top_size++)
+               {
+                   sum += srcptr[bottom_height * (width + 1)];
+                   *dstptr = sum / (top_size + bottom_height + 1);
+                   dstptr += width + 1;
+                   srcptr += width + 1;
+               }
+
+               /* The box filter has reached full height... continue
+                  computation of moving average until the bottom
+                  hits the wall. */
+               for (row = top_size; row + bottom_size < height; row++)
+               {
+                   sum += srcptr[bottom_height * (width + 1)];
+                   *dstptr = sum / box_y;
+                   dstptr += width + 1;
+                   sum -= srcptr[-top_height * (width + 1)];
+                   srcptr += width + 1;
+               }
+
+               /* Collapse the bottom of the box filter */
+               for (; bottom_size > 0; bottom_size--)
+               {
+                   *dstptr = sum / (top_height + bottom_size);
+                   dstptr += width + 1;
+                   sum -= srcptr[-top_height * (width + 1)];
+                   srcptr += width + 1;
+               }
+
+               /* Done with the column... copy dest back over source */
+
+               dstptr = char_grayscale + col;
+               srcptr = char_grayscale + width;         /* scratch */
+               for (row = 0; row < height; row++)
+               {
+                   *dstptr = *srcptr;
+                   dstptr += width + 1;
+                   srcptr += width + 1;
+               }
+           }
+       }
+
+       /* Increase the grayvalue to increase ink a bit */
+       srcptr = char_grayscale;
+       for (row = 0; row < height; row++)
+       {
+           for (col = 0; col < width; col++)
+           {
+               register int pixvalue = (int)*srcptr * pixmult / 256;
+               if (pixvalue > 255) pixvalue = 255;
+               *srcptr = pixvalue;
+               srcptr++;
+           }
+           srcptr++;
+       }
+    }
+
+    /* Compute the increment values for the resampling loop */
+    TRANSFORM_POINT(inv_xform, 1, 0, point);
+    deltaX = (INT32)(point[0] * 65536.0);
+    deltaY = (INT32)(-point[1] * 65536.0);
+
+    /* Resampling loop:  resamples original glyph for generation of new
+       glyph in transformed coordinate system. */
+
+    for (row = 0; row < newHeight; row++)
+    {
+       /* Compute inverse transformation for start of this row */
+       TRANSFORM_POINT(inv_xform,
+                       (double)(pci->metrics.leftSideBearing) + .5,
+                       (double)(pci->metrics.ascent - row) - .5,
+                       point);
+
+       /* Adjust for coordinate system to get resampling point */
+       point[0] -= opci->metrics.leftSideBearing;
+       point[1] = opci->metrics.ascent - point[1];
+
+       /* Convert to integer coordinates */
+       xValue = (INT32)(point[0] * 65536.0);
+       yValue = (INT32)(point[1] * 65536.0);
+
+       if (char_grayscale)
+       {
+           INT32 *temp;
+           for (col = 0; col < newWidth; col++)
+           {
+               register int x = xValue >> 16, y = yValue >> 16;
+               int pixvalue, error;
+    
+               pixvalue = ((x >= 0 && x < width && y >= 0 && y < height) ?
+                           char_grayscale[x + y * (width + 1)] : 0) +
+                          thisrow[col] / 16;
+               if (pixvalue > 255) pixvalue = 255;
+               else if (pixvalue < 0) pixvalue = 0;
+
+               /* Choose the bit value and set resulting error value */
+               if (pixvalue >= 128)
+               {
+                   newBitmap[(col >> 3) + row * newBpr] |= mask[col & 0x7];
+                   error = pixvalue - 255;
+               }
+               else
+                   error = -pixvalue;
+
+               /* Diffuse the error */
+               thisrow[col + 1] += error * 7;
+               nextrow[col - 1] += error * 3;
+               nextrow[col] += error * 5;
+               nextrow[col + 1] = error;
+
+               xValue += deltaX;
+               yValue += deltaY;
+           }
+
+           /* Add in error values that fell off either end */
+           nextrow[0] += nextrow[-1];
+           nextrow[newWidth - 2] += thisrow[newWidth];
+           nextrow[newWidth - 1] += nextrow[newWidth];
+           nextrow[newWidth] = 0;
+
+           temp = nextrow;
+           nextrow = thisrow;
+           thisrow = temp;
+           nextrow[-1] = nextrow[0] = 0;
+       }
+       else
+       {
+           for (col = 0; col < newWidth; col++)
+           {
+               register int x = xValue >> 16, y = yValue >> 16;
+    
+               if (x >= 0 && x < width && y >= 0 && y < height)
+               {
+                   /* Use point-sampling for rescaling. */
+
+                   if (bitmap[(x >> 3) + y * bpr] & mask[x & 0x7])
+                       newBitmap[(col >> 3) + row * newBpr] |= mask[col & 0x7];
+               }
+
+               xValue += deltaX;
+               yValue += deltaY;
+           }
+       }
+    }
+
+
+    if (char_grayscale)
+    {
+       xfree(char_grayscale);
+       xfree(diffusion_workspace);
+    }
+}
+
+static FontPtr
+BitmapScaleBitmaps(pf, opf, widthMult, heightMult, vals)
+    FontPtr     pf;            /* scaled font */
+    FontPtr     opf;           /* originating font */
+    double      widthMult;     /* glyphs width scale factor */
+    double      heightMult;    /* glyphs height scale factor */
+    FontScalablePtr    vals;
+{
+    register int i;
+    int                nchars;
+    char       *glyphBytes;
+    BitmapFontPtr  bitmapFont,
+                  obitmapFont;
+    CharInfoPtr pci,
+               opci;
+    FontInfoPtr pfi;
+    int         glyph;
+    unsigned    bytestoalloc = 0;
+    int                firstCol, lastCol, firstRow, lastRow;
+
+    double     xform[4], inv_xform[4];
+    double     xmult, ymult;
+
+    bitmapFont = (BitmapFontPtr) pf->fontPrivate;
+    obitmapFont = (BitmapFontPtr) opf->fontPrivate;
+
+    if (!compute_xform_matrix(vals, widthMult, heightMult, xform,
+                             inv_xform, &xmult, &ymult))
+       goto bail;
+
+    pfi = &pf->info;
+    firstCol = pfi->firstCol;
+    lastCol = pfi->lastCol;
+    firstRow = pfi->firstRow;
+    lastRow = pfi->lastRow;
+
+    nchars = (lastRow - firstRow + 1) * (lastCol - firstCol + 1);
+    glyph = pf->glyph;
+    for (i = 0; i < nchars; i++)
+    {
+       if (pci = bitmapFont->encoding[i])
+           bytestoalloc += BYTES_FOR_GLYPH(pci, glyph);
+    }
+
+    /* Do we add the font malloc stuff for VALUE ADDED ? */
+    /* Will need to remember to free in the Unload routine */
+
+
+    bitmapFont->bitmaps = (char *) xalloc(bytestoalloc);
+    if (!bitmapFont->bitmaps)
+       goto bail;
+    bzero(bitmapFont->bitmaps, bytestoalloc);
+
+    glyphBytes = bitmapFont->bitmaps;
+    for (i = 0; i < nchars; i++)
+    {
+       if ((pci = bitmapFont->encoding[i]) &&
+           (opci = obitmapFont->encoding[OLDINDEX(i)]))
+       {
+           pci->bits = glyphBytes;
+           ScaleBitmap (pf, opci, pci, inv_xform,
+                        widthMult, heightMult);
+           glyphBytes += BYTES_FOR_GLYPH(pci, glyph);
+       }
+    }
+    return pf;
+
+bail:
+    if (pf)
+       xfree(pf);
+    if (bitmapFont) {
+       xfree(bitmapFont->metrics);
+       xfree(bitmapFont->ink_metrics);
+       xfree(bitmapFont->bitmaps);
+       xfree(bitmapFont->encoding);
+    }
+    return NULL;
+}
+
+static FontPtr
+PrinterScaleBitmaps(pf, opf, widthMult, heightMult, vals)
+    FontPtr     pf;            /* scaled font */
+    FontPtr     opf;           /* originating font */
+    double      widthMult;     /* glyphs width scale factor */
+    double      heightMult;    /* glyphs height scale factor */
+    FontScalablePtr    vals;
+{
+    register int i;
+    int                nchars;
+    char       *glyphBytes;
+    BitmapFontPtr  bitmapFont,
+                  obitmapFont;
+    CharInfoPtr pci,
+               opci;
+    FontInfoPtr pfi;
+    int         glyph;
+    unsigned    bytestoalloc = 0;
+    int                firstCol, lastCol, firstRow, lastRow;
+
+    double     xform[4], inv_xform[4];
+    double     xmult, ymult;
+
+    bitmapFont = (BitmapFontPtr) pf->fontPrivate;
+    obitmapFont = (BitmapFontPtr) opf->fontPrivate;
+
+    if (!compute_xform_matrix(vals, widthMult, heightMult, xform,
+                             inv_xform, &xmult, &ymult))
+       goto bail;
+
+    pfi = &pf->info;
+    firstCol = pfi->firstCol;
+    lastCol = pfi->lastCol;
+    firstRow = pfi->firstRow;
+    lastRow = pfi->lastRow;
+
+    nchars = (lastRow - firstRow + 1) * (lastCol - firstCol + 1);
+    glyph = pf->glyph;
+    for (i = 0; i < nchars; i++)
+    {
+       if (pci = bitmapFont->encoding[i])
+           bytestoalloc = MAX(bytestoalloc,BYTES_FOR_GLYPH(pci, glyph));
+    }
+
+    /* Do we add the font malloc stuff for VALUE ADDED ? */
+    /* Will need to remember to free in the Unload routine */
+
+
+    bitmapFont->bitmaps = (char *) xalloc(bytestoalloc);
+    if (!bitmapFont->bitmaps)
+       goto bail;
+    bzero(bitmapFont->bitmaps, bytestoalloc);
+
+    glyphBytes = bitmapFont->bitmaps;
+    for (i = 0; i < nchars; i++)
+    {
+       if ((pci = bitmapFont->encoding[i]) &&
+           (opci = obitmapFont->encoding[OLDINDEX(i)]))
+       {
+           pci->bits = glyphBytes;
+       }
+    }
+    return pf;
+
+bail:
+    if (pf)
+       xfree(pf);
+    if (bitmapFont) {
+       xfree(bitmapFont->metrics);
+       xfree(bitmapFont->ink_metrics);
+       xfree(bitmapFont->bitmaps);
+       xfree(bitmapFont->encoding);
+    }
+    return NULL;
+}
+
+#ifdef NOTDEF
+/*
+ *     exported interfaces
+ */
+
+FontFileLoadName(dirs, ndirs, name, pfont, format, fmask)
+    FontFileDirPtr *dirs;
+    int         ndirs;
+    char       *name;
+    FontPtr    *pfont;
+    fsBitmapFormat format;
+    fsBitmapFormatMask fmask;
+{
+    FontFileNamePtr fname;
+    char        full_name[1024];
+    int         ret = BadFontName;
+    int         i;
+
+    i = 0;
+    while (i < ndirs) {
+       if (fname = FontFileFindNameInDir(dirs[i], name)) {
+           if (!fname->alias) {
+               if (!fname->font) {
+                   strcpy(full_name, dirs[i]->dir);
+                   strcat(full_name, fname->file);
+                   ret = FontFileLoad(pfont, full_name, format, fmask);
+                   if (ret == Successful) {
+                       fname->font = *pfont;
+                       (*pfont)->fpePrivate = (pointer) fname;
+                   }
+                   return ret;
+               }
+               *pfont = fname->font;
+               return Successful;
+           }
+           name = fname->file;
+           i = 0;
+       } else
+           i++;
+    }
+    return BadFontName;
+}
+#endif
+
+/* ARGSUSED */
+BitmapOpenScalable (fpe, pFont, flags, entry, fileName, vals, format, fmask,
+                   non_cachable_font)
+    FontPathElementPtr fpe;
+    FontPtr            *pFont;
+    int                        flags;
+    FontEntryPtr       entry;
+    char               *fileName;  /* unused */
+    FontScalablePtr    vals;
+    fsBitmapFormat     format;
+    fsBitmapFormatMask fmask;
+    FontPtr            non_cachable_font;      /* We don't do licensing */
+{
+    FontScalableRec    best;
+    FontPtr            font = NullFont;
+    double             dx, sdx,
+                       dy, sdy,
+                       savedX, savedY;
+    FontPropPtr                props;
+    char               *isStringProp;
+    int                        propCount;
+    int                        status;
+    long               sWidth;
+
+    FontEntryPtr       scaleFrom;
+    FontPathElementPtr scaleFPE;
+    FontPtr            sourceFont;
+    char               fontName[MAXFONTNAMELEN];
+
+    /* Can't deal with mix-endian fonts yet */
+
+#ifdef NOTDEF /* XXX need better test */
+    if ((format & BitmapFormatByteOrderMask) !=
+           (format & BitmapFormatBitOrderMask))
+       return NullFontFileName;
+#endif
+
+    /* Reject outrageously small font sizes to keep the math from
+       blowing up. */
+    if (get_matrix_vertical_component(vals->pixel_matrix) < 1.0 ||
+       get_matrix_horizontal_component(vals->pixel_matrix) < 1.0)
+       return BadFontName;
+
+    scaleFrom = (*find_scale[BitmapGetRenderIndex(entry->u.bitmap.renderer)]) 
+                   (fpe, entry, vals, &best, &dx, &dy, &sdx, &sdy, &scaleFPE);
+
+    if (!scaleFrom)
+       return BadFontName;
+
+    status = FontFileOpenBitmap(scaleFPE, &sourceFont, LoadAll, scaleFrom,
+                               format, fmask);
+
+    if (status != Successful)
+       return BadFontName;
+
+    if (!vals->width)
+       vals->width = best.width * dx;
+
+    /* Compute the scaled font */
+
+    savedX = dx;
+    savedY = dy;
+    font = ScaleFont(sourceFont, dx, dy, sdx, sdy, vals, &dx, &dy, &sWidth);
+    if (font)
+       font = (*scale[ BitmapGetRenderIndex(entry->u.bitmap.renderer) ]) 
+                       (font, sourceFont, savedX, savedY, vals);
+
+    if (!font)
+    {
+       if (!sourceFont->refcnt)
+           FontFileCloseFont((FontPathElementPtr) 0, sourceFont);
+       return AllocError;
+    }
+
+    /* Prepare font properties for the new font */
+
+    strcpy (fontName, scaleFrom->name.name);
+    FontParseXLFDName (fontName, vals, FONT_XLFD_REPLACE_VALUE);
+
+    propCount = ComputeScaledProperties(&sourceFont->info, fontName, vals,
+                                       dx, dy, sdx, sdy, sWidth, &props,
+                                       &isStringProp);
+
+    if (!sourceFont->refcnt)
+       FontFileCloseFont((FontPathElementPtr) 0, sourceFont);
+
+    if (propCount && (!props || !isStringProp))
+    {
+       font->info.nprops = 0;
+       font->info.props = (FontPropPtr)0;
+       font->info.isStringProp = (char *)0;
+       bitmapUnloadScalable(font);
+       return AllocError;
+    }
+
+    font->info.props = props;
+    font->info.nprops = propCount;
+    font->info.isStringProp = isStringProp;
+
+    *pFont = font;
+    return Successful;
+}
+
+BitmapGetInfoScalable (fpe, pFontInfo, entry, fontName, fileName, vals)
+    FontPathElementPtr fpe;
+    FontInfoPtr                pFontInfo;
+    FontEntryPtr       entry;
+    FontNamePtr                fontName;
+    char               *fileName;
+    FontScalablePtr    vals;
+{
+    FontPtr pfont;
+    int flags = 0;
+    long format = 0;  /* It doesn't matter what format for just info */
+    long fmask = 0;
+    int ret;
+
+    ret = BitmapOpenScalable(fpe, &pfont, flags, entry, fileName, vals,
+                            format, fmask, NULL);
+    if (ret != Successful)
+        return ret;
+    *pFontInfo = pfont->info;
+
+    pfont->info.props = NULL;
+    pfont->info.isStringProp = NULL;
+
+    (*pfont->unload_font)(pfont);
+    return Successful;
+}
+
+void
+bitmapUnloadScalable (pFont)
+    FontPtr        pFont;
+{
+    BitmapFontPtr   bitmapFont;
+    FontInfoPtr            pfi;
+
+    bitmapFont = (BitmapFontPtr) pFont->fontPrivate;
+    pfi = &pFont->info;
+    xfree (pfi->props);
+    xfree (pfi->isStringProp);
+    xfree (bitmapFont->encoding);
+    xfree (bitmapFont->bitmaps);
+    xfree (bitmapFont->ink_metrics);
+    xfree (bitmapFont->metrics);
+    xfree (pFont->fontPrivate);
+    xfree (pFont->devPrivates);
+    xfree (pFont);
+}