/* Copyright (C) 1999, 2000 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: gdevpdff.c,v 1.22.2.2.2.1 2003/01/17 00:49:01 giles Exp $ */
/* Font handling for pdfwrite driver. */
#include "ctype_.h"
#include "math_.h"
#include "memory_.h"
#include "string_.h"
#include <stdlib.h>
#include "gx.h"
#include "gserrors.h"
#include "gsmalloc.h"		/* for patching font memory */
#include "gsmatrix.h"
#include "gspath.h"
#include "gsutil.h"		/* for bytes_compare */
#include "gxfixed.h"		/* for gxfcache.h */
#include "gxfont.h"
#include "gxfcache.h"		/* for orig_fonts list */
#include "gxfcid.h"
#include "gxfont1.h"
#include "gxfont42.h"
#include "gxpath.h"		/* for getting current point */
#include "gdevpdfx.h"
#include "gdevpdff.h"
#include "gdevpdfo.h"
#include "gdevpsf.h"
#include "scommon.h"

/*
 * In our quest to work around Acrobat Reader quirks, we're resorting to
 * making all font names in the output unique by adding a suffix derived
 * from the PDF object number.  We hope to get rid of this someday....
 */
private const bool MAKE_FONT_NAMES_UNIQUE = true;

/* GC descriptors */
public_st_pdf_font();
public_st_pdf_char_proc();
public_st_pdf_font_descriptor();
private_st_pdf_encoding_element();
private ENUM_PTRS_WITH(pdf_encoding_elt_enum_ptrs, pdf_encoding_element_t *pe) {
    uint count = size / (uint)sizeof(*pe);

    if (index >= count)
	return 0;
    return ENUM_CONST_STRING(&pe[index].str);
} ENUM_PTRS_END
private RELOC_PTRS_WITH(pdf_encoding_elt_reloc_ptrs, pdf_encoding_element_t *pe)
    uint count = size / (uint)sizeof(*pe);
    uint i;

    for (i = 0; i < count; ++i)
	RELOC_CONST_STRING_VAR(pe[i].str);
RELOC_PTRS_END

/* Define the 14 standard built-in fonts. */
const pdf_standard_font_t pdf_standard_fonts[] = {
#define m(name, enc) {name, enc},
    pdf_do_std_fonts(m)
#undef m
    {0}
};

/* ---------------- Embedding status ---------------- */

/* Return the index of a standard font name, or -1 if missing. */
int
pdf_find_standard_font(const byte *str, uint size)
{
    const pdf_standard_font_t *ppsf;

    for (ppsf = pdf_standard_fonts; ppsf->fname; ++ppsf)
	if (strlen(ppsf->fname) == size &&
	    !strncmp(ppsf->fname, (const char *)str, size)
	    )
	    return ppsf - pdf_standard_fonts;
    return -1;
}

/*
 * If there is a standard font with the same appearance (CharStrings,
 * Private, WeightVector) as the given font, set *psame to the mask of
 * identical properties, and return the standard-font index; otherwise,
 * set *psame to 0 and return -1.
 */
private int
find_std_appearance(const gx_device_pdf *pdev, const gs_font_base *bfont,
		    int mask, int *psame)
{
    bool has_uid = uid_is_UniqueID(&bfont->UID) && bfont->UID.id != 0;
    const pdf_std_font_t *psf = pdev->std_fonts;
    int i;

    mask |= FONT_SAME_OUTLINES;
    for (i = 0; i < PDF_NUM_STD_FONTS; ++psf, ++i) {
	if (has_uid) {
	    if (!uid_equal(&bfont->UID, &psf->uid))
		continue;
	    if (!psf->font) {
		/*
		 * Identity of UIDs is supposed to guarantee that the
		 * fonts have the same outlines and metrics.
		 */
		*psame = FONT_SAME_OUTLINES | FONT_SAME_METRICS;
		return i;
	    }
	}
	if (psf->font) {
	    int same = *psame =
		bfont->procs.same_font((const gs_font *)bfont, psf->font,
				       mask);

	    if (same & FONT_SAME_OUTLINES)
		return i;
	}
    }
    *psame = 0;
    return -1;
}

/*
 * We register the fonts in pdev->std_fonts so that the pointers can
 * be weak (get set to 0 when the font is freed).
 */
private GS_NOTIFY_PROC(pdf_std_font_notify_proc);
typedef struct pdf_std_font_notify_s {
    gx_device_pdf *pdev;
    int index;			/* in std_fonts */
    gs_font *font;	/* for checking */
} pdf_std_font_notify_t;
gs_private_st_ptrs2(st_pdf_std_font_notify, pdf_std_font_notify_t,
		    "pdf_std_font_notify_t",
		    pdf_std_font_notify_enum_ptrs,
		    pdf_std_font_notify_reloc_ptrs,
		    pdev, font);
private int
pdf_std_font_notify_proc(void *vpsfn /*proc_data*/, void *event_data)
{
    pdf_std_font_notify_t *const psfn = vpsfn;
    gx_device_pdf *const pdev = psfn->pdev;
    gs_font *const font = psfn->font;

    if (event_data)
	return 0;		/* unknown event */
    if_debug4('_',
	      "[_]  notify 0x%lx: gs_font 0x%lx, id %ld, index=%d\n",
	      (ulong)psfn, (ulong)font, font->id, psfn->index);
#ifdef DEBUG
    if (pdev->std_fonts[psfn->index].font != font)
	lprintf3("pdf_std_font_notify font = 0x%lx, std_fonts[%d] = 0x%lx\n",
		 (ulong)font, psfn->index,
		 (ulong)pdev->std_fonts[psfn->index].font);
    else
#endif
	pdev->std_fonts[psfn->index].font = 0;
    gs_font_notify_unregister(font, pdf_std_font_notify_proc, vpsfn);
    gs_free_object(pdev->pdf_memory, vpsfn, "pdf_std_font_notify_proc");
    return 0;
}

/* Unregister the standard fonts when cleaning up. */
private void
pdf_std_font_unreg_proc(void *vpsfn /*proc_data*/)
{
    pdf_std_font_notify_proc(vpsfn, NULL);
}
void
pdf_unregister_fonts(gx_device_pdf *pdev)
{
    int j;

    for (j = 0; j < PDF_NUM_STD_FONTS; ++j)
	if (pdev->std_fonts[j].font != 0)
	    gs_notify_unregister_calling(&pdev->std_fonts[j].font->notify_list,
					 pdf_std_font_notify_proc, NULL,
					 pdf_std_font_unreg_proc);
}

/*
 * Scan a font directory for standard fonts.  Return true if any new ones
 * were found.
 */
private bool
scan_for_standard_fonts(gx_device_pdf *pdev, const gs_font_dir *dir)
{
    bool found = false;
    gs_font *orig = dir->orig_fonts;

    for (; orig; orig = orig->next) {
	gs_font_base *obfont;

	if (orig->FontType == ft_composite || !orig->is_resource)
	    continue;
	obfont = (gs_font_base *)orig;
	if (uid_is_UniqueID(&obfont->UID)) {
	    /* Is it one of the standard fonts? */
	    int i = pdf_find_standard_font(orig->key_name.chars,
					   orig->key_name.size);
	    pdf_std_font_t *psf;

	    if (i >= 0 && (psf = &pdev->std_fonts[i])->font == 0) {
		pdf_std_font_notify_t *psfn =
		    gs_alloc_struct(pdev->pdf_memory, pdf_std_font_notify_t,
				    &st_pdf_std_font_notify,
				    "scan_for_standard_fonts");

		if (psfn == 0)
		    continue;	/* can't register */
		psfn->pdev = pdev;
		psfn->index = i;
		psfn->font = orig;
		if_debug4('_',
			  "[_]register 0x%lx: gs_font 0x%lx, id %ld, index=%d\n",
			  (ulong)psfn, (ulong)orig, orig->id, i);
		gs_font_notify_register(orig, pdf_std_font_notify_proc, psfn);
		psf->font = orig;
		psf->orig_matrix = obfont->FontMatrix;
		psf->uid = obfont->UID;
		found = true;
	    }
	}
    }
    return found;
}

/*
 * Determine the embedding status of a font.  If the font is in the base
 * 14, store its index (0..13) in *pindex, otherwise store -1 there.
 */
private bool
font_is_symbolic(const gs_font *font)
{
    if (font->FontType == ft_composite)
	return true;		/* arbitrary */
    switch (((const gs_font_base *)font)->nearest_encoding_index) {
    case ENCODING_INDEX_STANDARD:
    case ENCODING_INDEX_ISOLATIN1:
    case ENCODING_INDEX_WINANSI:
    case ENCODING_INDEX_MACROMAN:
	return false;
    default:
	return true;
    }
}
private bool
embed_list_includes(const gs_param_string_array *psa, const byte *chars,
		    uint size)
{
    uint i;

    for (i = 0; i < psa->size; ++i)
	if (!bytes_compare(psa->data[i].data, psa->data[i].size, chars, size))
	    return true;
    return false;
}
pdf_font_embed_t
pdf_font_embed_status(gx_device_pdf *pdev, gs_font *font, int *pindex,
		      int *psame)
{
    const byte *chars = font->font_name.chars;
    uint size = font->font_name.size;

    /*
     * The behavior of Acrobat Distiller changed between 3.0 (PDF 1.2),
     * which will never embed the base 14 fonts, and 4.0 (PDF 1.3), which
     * doesn't treat them any differently from any other fonts.
     */
    if (pdev->CompatibilityLevel < 1.3) {
	/* Check whether the font is in the base 14. */
	int index = pdf_find_standard_font(chars, size);

	if (index >= 0) {
	    *pindex = index;
	    if (font->is_resource) {
		*psame = ~0;
		return FONT_EMBED_STANDARD;
	    } else if (font->FontType != ft_composite &&
		       find_std_appearance(pdev, (gs_font_base *)font, -1,
					   psame) == index)
		return FONT_EMBED_STANDARD;
	}
    }
    *pindex = -1;
    *psame = 0;
    /* Check the Embed lists. */
    if (embed_list_includes(&pdev->params.NeverEmbed, chars, size))
	return FONT_EMBED_NO;
    if (pdev->params.EmbedAllFonts || font_is_symbolic(font) ||
	embed_list_includes(&pdev->params.AlwaysEmbed, chars, size))
	return FONT_EMBED_YES;
    return FONT_EMBED_NO;
}

/* ---------------- Everything else ---------------- */

/*
 * Compute and return the orig_matrix of a font.
 */
int
pdf_font_orig_matrix(const gs_font *font, gs_matrix *pmat)
{
    switch (font->FontType) {
    case ft_composite:		/* subfonts have their own FontMatrix */
    case ft_TrueType:
    case ft_CID_TrueType:
	/* The TrueType FontMatrix is 1 unit per em, which is what we want. */
	gs_make_identity(pmat);
	break;
    case ft_encrypted:
    case ft_encrypted2:
    case ft_CID_encrypted:
	/*
         * Type 1 fonts are supposed to use a standard FontMatrix of
         * [0.001 0 0 0.001 0 0], with a 1000-unit cell.  However,
         * Windows NT 4.0 creates Type 1 fonts, apparently derived from
         * TrueType fonts, that use a 2048-unit cell and corresponding
         * FontMatrix. 
         *
         * Also some PS programs perform font scaling by replacing
         * FontMatrix like this :
         *
         *   /f12 /Times-Roman findfont
         *   copyfont	  % (remove FID)
         *   dup /FontMatrix [0.012 0 0 0.012 0 0] put
         *   definefont
         *   /f12 1 selectfont
         *
         * Such fonts are their own "base font", but the orig_matrix
         * must still be set to 0.001, not 0.012 .
         *
         * Detect and correct for this here.
	 */
	{
	    const gs_font *base_font = font;
	    double scale;

	    while (base_font->base != base_font)
		base_font = base_font->base;
	    if (base_font->FontMatrix.xx == 1.0/2048 &&
		base_font->FontMatrix.xy == 0 &&
		base_font->FontMatrix.yx == 0 &&
		base_font->FontMatrix.yy == 1.0/2048
		)
		scale = 1.0/2048;
	    else
		scale = 0.001;
	    gs_make_scaling(scale, scale, pmat);
	}
	break;
    default:
	return_error(gs_error_rangecheck);
    }
    return 0;
}

/*
 * Find the original (unscaled) standard font corresponding to an
 * arbitrary font, if any.  Return its index in standard_fonts, or -1.
 */
int
pdf_find_orig_font(gx_device_pdf *pdev, gs_font *font, gs_matrix *pfmat)
{
    bool scan = true;
    int i;

    if (font->FontType == ft_composite)
	return -1;
    for (;; font = font->base) {
	gs_font_base *bfont = (gs_font_base *)font;
	int same;

	/* Look for a standard font with the same appearance. */
	i = find_std_appearance(pdev, bfont, 0, &same);
	if (i >= 0)
	    break;
	if (scan) {
	    /* Scan for fonts with any of the standard names that */
	    /* have a UID. */
	    bool found = scan_for_standard_fonts(pdev, font->dir);

	    scan = false;
	    if (found) {
		i = find_std_appearance(pdev, bfont, 0, &same);
		if (i >= 0)
		    break;
	    }
	}
	if (font->base == font)
	    return -1;
    }
    *pfmat = pdev->std_fonts[i].orig_matrix;
    return i;
}

/*
 * Determine the resource type (resourceFont or resourceCIDFont) for
 * a font.
 */
pdf_resource_type_t
pdf_font_resource_type(const gs_font *font)
{
    switch (font->FontType) {
    case ft_CID_encrypted:	/* CIDFontType 0 */
    case ft_CID_user_defined:	/* CIDFontType 1 */
    case ft_CID_TrueType:	/* CIDFontType 2 */
    case ft_CID_bitmap:		/* CIDFontType 4 */
	return resourceCIDFont;
    default:
	return resourceFont;
    }
}

/*
 * Determine the chars_count (number of entries in chars_used and Widths)
 * for a font.  For CIDFonts, this is CIDCount.
 */
private int
pdf_font_chars_count(const gs_font *font)
{
    switch (font->FontType) {
    case ft_composite:
	/* Composite fonts don't use chars_used or Widths. */
	return 0;
    case ft_CID_encrypted:
	return ((const gs_font_cid0 *)font)->cidata.common.CIDCount;
    case ft_CID_TrueType:
	return ((const gs_font_cid2 *)font)->cidata.common.CIDCount;
    default:
	return 256;		/* Encoding size */
    }
}

/*
 * Determine the glyphs_count (number of entries in glyphs_used) for a font.
 * This is only non-zero for TrueType-based fonts.
 */
private int
pdf_font_glyphs_count(const gs_font *font)
{
    switch (font->FontType) {
    case ft_TrueType:
    case ft_CID_TrueType:
	return ((const gs_font_type42 *)font)->data.numGlyphs;
    default:
	return 0;
    }
}

/*
 * Allocate a font resource.  If pfd != 0, a FontDescriptor is allocated,
 * with its id, values, and chars_count taken from *pfd_in.
 * If font != 0, its FontType is used to determine whether the resource
 * is of type Font or of (pseudo-)type CIDFont; in this case, pfres->font
 * and pfres->FontType are also set.
 */
int
pdf_alloc_font(gx_device_pdf *pdev, gs_id rid, pdf_font_t **ppfres,
	       const pdf_font_descriptor_t *pfd_in, gs_font *font)
{
    gs_memory_t *mem = pdev->v_memory;
    pdf_font_descriptor_t *pfd = 0;
    pdf_resource_type_t rtype = resourceFont;
    gs_string chars_used, glyphs_used;
    int *Widths = 0;
    byte *widths_known = 0;
    ushort *CIDToGIDMap = 0;
    int code;
    pdf_font_t *pfres;

    chars_used.data = 0;
    glyphs_used.data = 0;
    if (pfd_in) {
	uint chars_count = pfd_in->chars_count;
	uint glyphs_count = pfd_in->glyphs_count;

	code = pdf_alloc_resource(pdev, resourceFontDescriptor,
				  pfd_in->rid, (pdf_resource_t **)&pfd, 0L);
	if (code < 0)
	    return code;
	chars_used.size = (chars_count + 7) >> 3;
	chars_used.data = gs_alloc_string(mem, chars_used.size,
					  "pdf_alloc_font(chars_used)");
	if (chars_used.data == 0)
	    goto vmfail;
	if (glyphs_count) {
	    glyphs_used.size = (glyphs_count + 7) >> 3;
	    glyphs_used.data = gs_alloc_string(mem, glyphs_used.size,
					       "pdf_alloc_font(glyphs_used)");
	    if (glyphs_used.data == 0)
		goto vmfail;
	    memset(glyphs_used.data, 0, glyphs_used.size);
	}
	memset(chars_used.data, 0, chars_used.size);
	pfd->values = pfd_in->values;
	pfd->chars_count = chars_count;
	pfd->chars_used = chars_used;
	pfd->glyphs_count = glyphs_count;
	pfd->glyphs_used = glyphs_used;
	pfd->do_subset = FONT_SUBSET_OK;
	pfd->FontFile_id = 0;
	pfd->base_font = 0;
	pfd->notified = false;
	pfd->written = false;
    }
    if (font) {
	uint chars_count = pdf_font_chars_count(font);
	uint widths_known_size = (chars_count + 7) >> 3;

	Widths = (void *)gs_alloc_byte_array(mem, chars_count, sizeof(*Widths),
					     "pdf_alloc_font(Widths)");
	widths_known = gs_alloc_bytes(mem, widths_known_size,
				      "pdf_alloc_font(widths_known)");
	if (Widths == 0 || widths_known == 0)
	    goto vmfail;
	if (font->FontType == ft_CID_TrueType) {
	    CIDToGIDMap = (void *)
		gs_alloc_byte_array(mem, chars_count, sizeof(*CIDToGIDMap),
				    "pdf_alloc_font(CIDToGIDMap)");
	    if (CIDToGIDMap == 0)
		goto vmfail;
	    memset(CIDToGIDMap, 0, chars_count * sizeof(*CIDToGIDMap));
	}
	memset(widths_known, 0, widths_known_size);
	rtype = pdf_font_resource_type(font);
    }
    code = pdf_alloc_resource(pdev, rtype, rid, (pdf_resource_t **)ppfres, 0L);
    if (code < 0)
	goto fail;
    pfres = *ppfres;
    memset((byte *)pfres + sizeof(pdf_resource_t), 0,
	   sizeof(*pfres) - sizeof(pdf_resource_t));
    pfres->font = font;
    if (font)
	pfres->FontType = font->FontType;
    pfres->index = -1;
    pfres->is_MM_instance = false;
    pfres->FontDescriptor = pfd;
    pfres->write_Widths = false;
    pfres->Widths = Widths;
    pfres->widths_known = widths_known;
    pfres->BaseEncoding = ENCODING_INDEX_UNKNOWN;
    pfres->Differences = 0;
    pfres->DescendantFont = 0;
    pfres->glyphshow_font = 0;
    pfres->CIDToGIDMap = CIDToGIDMap;
    pfres->char_procs = 0;
    return 0;
 vmfail:
    code = gs_note_error(gs_error_VMerror);
 fail:
    gs_free_object(mem, CIDToGIDMap, "pdf_alloc_font(CIDToGIDMap)");
    gs_free_object(mem, widths_known, "pdf_alloc_font(widths_known)");
    gs_free_object(mem, Widths, "pdf_alloc_font(Widths)");
    if (glyphs_used.data)
	gs_free_string(mem, glyphs_used.data, glyphs_used.size,
		       "pdf_alloc_font(glyphs_used)");
    if (chars_used.data)
	gs_free_string(mem, chars_used.data, chars_used.size,
		       "pdf_alloc_font(chars_used)");
    gs_free_object(mem, pfd, "pdf_alloc_font(descriptor)");
    return code;
}

/*
 * Create a new pdf_font for a gs_font.  This procedure is only intended
 * to be called from a few places in gdevpdft.c.
 */
int
pdf_create_pdf_font(gx_device_pdf *pdev, gs_font *font, const gs_matrix *pomat,
		    pdf_font_t **pppf)
{
    int index = -1;
    int ftemp_Widths[256];
    byte ftemp_widths_known[256/8];
    int BaseEncoding = ENCODING_INDEX_UNKNOWN;
    int same = 0, base_same = 0;
    pdf_font_embed_t embed =
	pdf_font_embed_status(pdev, font, &index, &same);
    bool have_widths = false;
    bool is_standard = false;
    long ffid = 0;
    pdf_font_descriptor_t *pfd = 0;
    pdf_std_font_t *psf = 0;
    gs_font *base_font = font;
    gs_font *below;
    pdf_font_descriptor_t fdesc;
    pdf_font_t *ppf;
    int code;
#define BASE_UID(fnt) (&((const gs_font_base *)(fnt))->UID)

    /* Find the "lowest" base font that has the same outlines. */
    while ((below = base_font->base) != base_font &&
	   base_font->procs.same_font(base_font, below,
				      FONT_SAME_OUTLINES))
	base_font = below;
    /*
     * set_base is the head of a logical loop; we return here if we
     * decide to change the base_font to one registered as a resource.
     */
 set_base:
    if (base_font == font)
	base_same = same;
    else
	embed = pdf_font_embed_status(pdev, base_font, &index, &base_same);
    if (embed == FONT_EMBED_STANDARD) {
	psf = &pdev->std_fonts[index];
	if (psf->font != 0 || psf->pfd != 0) {
	    /*
	     * Use the standard font as the base font.  Either base_font
	     * or pfd may be zero, but not both.
	     */
	    base_font = psf->font;
	    pfd = psf->pfd;
	}
	is_standard = true;
    } else if (embed == FONT_EMBED_YES &&
	       base_font->FontType != ft_composite &&
	       uid_is_valid(BASE_UID(base_font)) &&
	       !base_font->is_resource
	       ) {
	/*
	 * The base font has a UID, but it isn't a resource.  Look for a
	 * resource with the same UID, in the hope that that will be
	 * longer-lived.
	 */
	gs_font *orig = base_font->dir->orig_fonts;

	for (; orig; orig = orig->next)
	    if (orig != base_font && orig->FontType == base_font->FontType &&
		orig->is_resource &&
		uid_equal(BASE_UID(base_font), BASE_UID(orig))
		) {
		/* Use this as the base font instead. */
		base_font = orig;
		/*
		 * Recompute the embedding status of the base font.  This
		 * can't lead to a loop, because base_font->is_resource is
		 * now known to be true.
		 */
		goto set_base;
	    }
    }
	 
    /* Composite fonts don't have descriptors. */
    if (font->FontType == ft_composite) {
	code = pdf_alloc_font(pdev, font->id, &ppf, NULL, font);
	if (code < 0)
	    return code;
	if_debug2('_',
		  "[_]created ft_composite pdf_font_t 0x%lx, id %ld\n",
		  (ulong)ppf, pdf_resource_id((pdf_resource_t *)ppf));
	ppf->index = -1;
	code = pdf_register_font(pdev, font, ppf);
	*pppf = ppf;
	return code;
    }

    /* See if we already have a descriptor for this base font. */
    if (pfd == 0)		/* if non-zero, was standard font */
	pfd = (pdf_font_descriptor_t *)
	    pdf_find_resource_by_gs_id(pdev, resourceFontDescriptor,
				       base_font->id);
    if (pfd != 0 && pfd->base_font != base_font && pfd->base_font != 0)
	pfd = 0;

    /* Create an appropriate font and descriptor. */

    switch (embed) {
    case FONT_EMBED_YES:
	/*
	 * HACK: Acrobat Reader 3 has a bug that makes cmap formats 4
	 * and 6 not work in embedded TrueType fonts.  Consequently, it
	 * can only handle embedded TrueType fonts if all the glyphs
	 * referenced by the Encoding have numbers 0-255.  Check for
	 * this now.
	 */
	if ((font->FontType == ft_TrueType ||
	     font->FontType == ft_CID_TrueType) &&
	    pdev->CompatibilityLevel <= 1.2
	    ) {
	    int i;

	    for (i = 0; i <= 0xff; ++i) {
		gs_glyph glyph =
		    font->procs.encode_char(font, (gs_char)i,
					    GLYPH_SPACE_INDEX);

		if (glyph == gs_no_glyph ||
		    (glyph >= gs_min_cid_glyph &&
		     glyph <= gs_min_cid_glyph + 0xff)
		    )
		    continue;
		/* Can't embed, punt. */
		return_error(gs_error_rangecheck);
	    }
	}
	if (!pfd) {
	    code = pdf_compute_font_descriptor(pdev, &fdesc, font, NULL);
	    if (code < 0)
		return code;
	    ffid = pdf_obj_ref(pdev);
	}
	/* The font isn't standard: make sure we write the Widths. */
	same &= ~FONT_SAME_METRICS;
	break;
    case FONT_EMBED_NO:
	/*
	 * Per the PDF 1.3 documentation, there are only 3 BaseEncoding
	 * values allowed for non-embedded fonts.  Pick one here.
	 */
	BaseEncoding =
	    ((const gs_font_base *)base_font)->nearest_encoding_index;
	switch (BaseEncoding) {
	default:
	    BaseEncoding = ENCODING_INDEX_WINANSI;
	case ENCODING_INDEX_WINANSI:
	case ENCODING_INDEX_MACROMAN:
	case ENCODING_INDEX_MACEXPERT:
	    break;
	}
	code = pdf_compute_font_descriptor(pdev, &fdesc, font, NULL);
	if (code < 0)
	    return code;
	/* The font isn't standard: make sure we write the Widths. */
	same &= ~FONT_SAME_METRICS;
	break;
    case FONT_EMBED_STANDARD:
	break;
    }
    if (~same & (FONT_SAME_METRICS | FONT_SAME_ENCODING)) {
	/*
	 * Before allocating the font resource, check that we can
	 * get all the widths for non-CID-keyed fonts.
	 */
	switch (font->FontType) {
	case ft_composite:
	case ft_CID_encrypted:
	case ft_CID_TrueType:
	    break;
	default:
	    {
		pdf_font_t ftemp;
		int i;

		memset(&ftemp, 0, sizeof(ftemp));
		pdf_font_orig_matrix(font, &ftemp.orig_matrix);
		ftemp.Widths = ftemp_Widths;
		ftemp.widths_known = ftemp_widths_known;
		memset(ftemp.widths_known, 0, sizeof(ftemp_widths_known));
		for (i = 0; i <= 255; ++i) {
		    code = pdf_char_width(&ftemp, i, font, NULL);
		    if (code < 0 && code != gs_error_undefined)
			return code;
		}
		have_widths = true;
	    }
	}
    }
    if (pfd) {
	code = pdf_alloc_font(pdev, font->id, &ppf, NULL, font);
	if (code < 0)
	    return code;
	if_debug4('_',
		  "[_]created pdf_font_t 0x%lx, id %ld, FontDescriptor 0x%lx, id %ld (old)\n",
		  (ulong)ppf, pdf_resource_id((pdf_resource_t *)ppf),
		  (ulong)pfd, pdf_resource_id((pdf_resource_t *)pfd));
	ppf->FontDescriptor = pfd;
    } else {
	int name_index = index;

	fdesc.rid = base_font->id;
	fdesc.chars_count = pdf_font_chars_count(base_font);
	fdesc.glyphs_count = pdf_font_glyphs_count(base_font);
	code = pdf_alloc_font(pdev, font->id, &ppf, &fdesc, font);
	if (code < 0)
	    return code;
	pfd = ppf->FontDescriptor;
	if_debug4('_',
		  "[_]created pdf_font_t 0x%lx, id %ld, FontDescriptor 0x%lx, id %ld (new)\n",
		  (ulong)ppf, pdf_resource_id((pdf_resource_t *)ppf),
		  (ulong)pfd, pdf_resource_id((pdf_resource_t *)pfd));
	if (index < 0) {
	    int ignore_same;
	    const gs_font_name *pfname = &base_font->font_name;

	    /* CIDFonts may have a key_name but no font_name. */
	    if (pfname->size == 0)
		pfname = &base_font->key_name;
	    memcpy(pfd->FontName.chars, pfname->chars, pfname->size);
	    pfd->FontName.size = pfname->size;
	    pfd->FontFile_id = ffid;
	    pfd->base_font = base_font;
	    /* Don't allow non-standard fonts with standard names. */
	    pdf_font_embed_status(pdev, base_font, &name_index,
				  &ignore_same);
	} else {
	    /* Use the standard name. */
	    const pdf_standard_font_t *ppsf = &pdf_standard_fonts[index];
	    const char *fnchars = ppsf->fname;
	    uint fnsize = strlen(fnchars);

	    memcpy(pfd->FontName.chars, fnchars, fnsize);
	    pfd->FontName.size = fnsize;
	    memset(&pfd->values, 0, sizeof(&pfd->values));
	}
	pfd->orig_matrix = *pomat;
	if (is_standard) {
	    psf->pfd = pfd;
	    if (embed == FONT_EMBED_STANDARD)
		pfd->base_font = 0;
	} else {
	    code = pdf_adjust_font_name(pdev, pfd, name_index >= 0);
	    if (code < 0)
		return code;
	}
    } /* end else (!pfd) */
    ppf->index = index;

    switch (font->FontType) {
    case ft_encrypted:
    case ft_encrypted2:
	ppf->is_MM_instance =
	    ((const gs_font_type1 *)font)->data.WeightVector.count > 0;
	break;
    case ft_CID_encrypted:
    case ft_CID_TrueType:
	/*
	 * Write the CIDSystemInfo now, so we don't try to access it after
	 * the font may no longer be available.
	 */
	{
	    long cidsi_id = pdf_begin_separate(pdev);

	    pdf_write_CIDFont_system_info(pdev, font);
	    pdf_end_separate(pdev);
	    ppf->CIDSystemInfo_id = cidsi_id;
	}
    default:
	DO_NOTHING;
    }

    ppf->BaseEncoding = BaseEncoding;
    ppf->fname = pfd->FontName;
    ppf->orig_matrix = *pomat;
    if (~same & FONT_SAME_METRICS) {
	/*
	 * Contrary to the PDF 1.3 documentation, FirstChar and
	 * LastChar are *not* simply a way to strip off initial and
	 * final entries in the Widths array that are equal to
	 * MissingWidth.  Acrobat Reader assumes that characters
	 * with codes less than FirstChar or greater than LastChar
	 * are undefined, without bothering to consult the Encoding.
	 * Therefore, the implicit value of MissingWidth is pretty
	 * useless, because there must be explicit Width entries for
	 * every character in the font that is ever used.
	 * Furthermore, if there are several subsets of the same
	 * font in a document, it appears to be random as to which
	 * one Acrobat Reader uses to decide what the FirstChar and
	 * LastChar values are.  Therefore, we must write the Widths
	 * array for the entire font even for subsets.
	 */
	ppf->write_Widths = true;
	/*
	 * If the font is being downloaded incrementally, the range we
	 * determine here will be too small.  The character encoding
	 * loop in pdf_process_string takes care of expanding it.
	 */
	pdf_find_char_range(font, &ppf->FirstChar, &ppf->LastChar);
    }
    if (have_widths) {
	memcpy(ppf->Widths, ftemp_Widths, sizeof(ftemp_Widths));
	memcpy(ppf->widths_known, ftemp_widths_known,
	       sizeof(ftemp_widths_known));
    }
    code = pdf_register_font(pdev, font, ppf);

    *pppf = ppf;
    return code;
}

/*
 * Determine whether a font is a subset font by examining the name.
 */
bool
pdf_has_subset_prefix(const byte *str, uint size)
{
    int i;

    if (size < SUBSET_PREFIX_SIZE || str[SUBSET_PREFIX_SIZE - 1] != '+')
	return false;
    for (i = 0; i < SUBSET_PREFIX_SIZE - 1; ++i)
	if ((uint)(str[i] - 'A') >= 26)
	    return false;
    return true;
}

/*
 * Make the prefix for a subset font from the font's resource ID.
 */
void
pdf_make_subset_prefix(const gx_device_pdf *pdev, byte *str, ulong id)
{
    int i;
    ulong v;

    /* We disregard the id and generate a random prefix. */
    v = (ulong)(pdev->random_offset + rand());
    for (i = 0; i < SUBSET_PREFIX_SIZE - 1; ++i, v /= 26)
	str[i] = 'A' + (v % 26);
    str[SUBSET_PREFIX_SIZE - 1] = '+';
}

/*
 * Adjust the FontName of a newly created FontDescriptor so that it is
 * unique if necessary.
 */
int
pdf_adjust_font_name(const gx_device_pdf *pdev, pdf_font_descriptor_t *pfd,
		     bool is_standard)
{
    int code = 0;

    if (MAKE_FONT_NAMES_UNIQUE) {
	/* Check whether this name has already been used. */
	int j = 0;
	pdf_font_descriptor_t *old;
	byte *chars = pfd->FontName.chars;
	uint size = pfd->FontName.size;

#define SUFFIX_CHAR '~'
	/*
	 * If the name looks as though it has one of our unique suffixes,
	 * remove the suffix.
	 */
	{
	    int i;

	    for (i = size;
		 i > 0 && isxdigit(chars[i - 1]);
		 --i)
		DO_NOTHING;
	    if (i < size && i > 0 && chars[i - 1] == SUFFIX_CHAR) {
		do {
		    --i;
		} while (i > 0 && chars[i - 1] == SUFFIX_CHAR);
		size = i + 1;
	    }
	    code = size != pfd->FontName.size;
	}
	/*
	 * Non-standard fonts with standard names must always use a suffix
	 * to avoid being confused with the standard fonts.
	 */
	if (!is_standard)
	    for (; j < NUM_RESOURCE_CHAINS; ++j)
		for (old = (pdf_font_descriptor_t *)pdev->resources[resourceFontDescriptor].chains[j];
		     old != 0; old = old->next
		     ) {
		    const byte *old_chars = old->FontName.chars;
		    uint old_size = old->FontName.size;

		    if (old == pfd)
			continue;
		    if (pdf_has_subset_prefix(old_chars, old_size))
			old_chars += SUBSET_PREFIX_SIZE,
			    old_size -= SUBSET_PREFIX_SIZE;
		    if (!bytes_compare(old_chars, old_size, chars, size))
			goto found; /* do a double 'break' */
		}
    found:			/* only used for double 'break' above */
	if (j < NUM_RESOURCE_CHAINS) {
	    /* Create a unique name. */
	    char suffix[sizeof(long) * 2 + 2];
	    uint suffix_size;

	    sprintf(suffix, "%c%lx", SUFFIX_CHAR,
		    pdf_resource_id((pdf_resource_t *)pfd));
	    suffix_size = strlen(suffix);
	    if (size + suffix_size > sizeof(pfd->FontName.chars))
		return_error(gs_error_rangecheck);
	    memcpy(chars + size, (const byte *)suffix, suffix_size);
	    size += suffix_size;
	    code = 1;
	}
	pfd->FontName.size = size;
#undef SUFFIX_CHAR
    }
    return code;
}

/* Add an encoding difference to a font. */
int
pdf_add_encoding_difference(gx_device_pdf *pdev, pdf_font_t *ppf, int chr,
			    gs_font_base *bfont, gs_glyph glyph)
{
    pdf_encoding_element_t *pdiff = ppf->Differences;
    /*
     * Since the font Widths are indexed by character code, changing the
     * encoding also changes the Widths.
     */
    int width;
    int code = pdf_glyph_width(ppf, glyph, (gs_font *)bfont, &width);

    if (code < 0)
	return code;
    if (pdiff == 0) {
	pdiff = gs_alloc_struct_array(pdev->pdf_memory, 256,
				      pdf_encoding_element_t,
				      &st_pdf_encoding_element,
				      "Differences");
	if (pdiff == 0)
	    return_error(gs_error_VMerror);
	memset(pdiff, 0, sizeof(pdf_encoding_element_t) * 256);
	ppf->Differences = pdiff;
    }
    pdiff[chr].glyph = glyph;
    pdiff[chr].str.data = (const byte *)
	bfont->procs.callbacks.glyph_name(glyph, &pdiff[chr].str.size);
    ppf->Widths[chr] = width;
    if (code == 0)
	ppf->widths_known[chr >> 3] |= 0x80 >> (chr & 7);
    else
	ppf->widths_known[chr >> 3] &= ~(0x80 >> (chr & 7));
    return 0;
}

/*
 * Get the width of a given character in a (base) font.  May add the width
 * to the widths cache (ppf->Widths).
 */
int
pdf_char_width(pdf_font_t *ppf, int ch, gs_font *font,
	       int *pwidth /* may be NULL */)
{
    if (ch < 0 || ch > 255)
	return_error(gs_error_rangecheck);
    if (!(ppf->widths_known[ch >> 3] & (0x80 >> (ch & 7)))) {
	gs_font_base *bfont = (gs_font_base *)font;
	gs_glyph glyph = bfont->procs.encode_char(font, (gs_char)ch,
						  GLYPH_SPACE_INDEX);
	int width = 0;
	int code = pdf_glyph_width(ppf, glyph, font, &width);

	if (code < 0)
	    return code;
	ppf->Widths[ch] = width;
	if (code == 0)
	    ppf->widths_known[ch >> 3] |= 0x80 >> (ch & 7);
    }
    if (pwidth)
	*pwidth = ppf->Widths[ch];
    return 0;
}

/*
 * Get the width of a glyph in a (base) font.  Return 1 if the width was
 * defaulted to MissingWidth.
 */
int
pdf_glyph_width(pdf_font_t *ppf, gs_glyph glyph, gs_font *font,
		int *pwidth /* must not be NULL */)
{
    int wmode = font->WMode;
    gs_glyph_info_t info;
    /*
     * orig_matrix.xx is 1.0 for TrueType, 0.001 or 1.0/2048 for Type 1.
     */
    double scale = ppf->orig_matrix.xx * 1000.0;
    int code;

    if (glyph != gs_no_glyph &&
	(code = font->procs.glyph_info(font, glyph, NULL,
				       GLYPH_INFO_WIDTH0 << wmode,
				       &info)) >= 0
	) {
	double w, v;

	if (wmode && (w = info.width[wmode].y) != 0)
	    v = info.width[wmode].x;
	else
	    w = info.width[wmode].x, v = info.width[wmode].y;
	if (v != 0)
	    return_error(gs_error_rangecheck);
	*pwidth = (int)(w * scale);
	/*
	 * If the character is .notdef, don't cache the width,
	 * just in case this is an incrementally defined font.
	 */
	return (gs_font_glyph_is_notdef((gs_font_base *)font, glyph) ? 1 : 0);
    } else {
	/* Try for MissingWidth. */
	gs_point scale2;
	const gs_point *pscale = 0;
	gs_font_info_t finfo;

	if (scale != 1)
	    scale2.x = scale2.y = scale, pscale = &scale2;
	code = font->procs.font_info(font, pscale, FONT_INFO_MISSING_WIDTH,
				     &finfo);
	if (code < 0)
	    return code;
	*pwidth = finfo.MissingWidth;
	/*
	 * Don't mark the width as known, just in case this is an
	 * incrementally defined font.
	 */
	return 1;
    }
}

/*
 * Find the range of character codes that includes all the defined
 * characters in a font.  This is a separate procedure only for
 * readability: it is only called from one place in pdf_update_text_state.
 */
void
pdf_find_char_range(gs_font *font, int *pfirst, int *plast)
{
    gs_glyph notdef = gs_no_glyph;
    int first = 0, last = 255;
    gs_glyph glyph;

    switch (font->FontType) {
    case ft_encrypted:
    case ft_encrypted2: {
	/* Scan the Encoding vector looking for .notdef. */
	gs_font_base *const bfont = (gs_font_base *)font;
	int ch;

	for (ch = 0; ch <= 255; ++ch) {
	    gs_glyph glyph =
		font->procs.encode_char(font, (gs_char)ch,
					GLYPH_SPACE_INDEX);

	    if (glyph == gs_no_glyph)
		continue;
	    if (gs_font_glyph_is_notdef(bfont, glyph)) {
		notdef = glyph;
		break;
	    }
	}
	break;
    }
    default:
	DO_NOTHING;
    }
    while (last >= first &&
	   ((glyph =
	     font->procs.encode_char(font, (gs_char)last,
				     GLYPH_SPACE_INDEX))== gs_no_glyph ||
	    glyph == notdef || glyph == gs_min_cid_glyph)
	   )
	--last;
    while (first <= last &&
	   ((glyph =
	     font->procs.encode_char(font, (gs_char)first,
				     GLYPH_SPACE_INDEX))== gs_no_glyph ||
	    glyph == notdef || glyph == gs_min_cid_glyph)
	   )
	++first;
    if (first > last)
	last = first;	/* no characters used */
    *pfirst = first;
    *plast = last;
}

/* Compute the FontDescriptor.values for a font. */
private int
font_char_bbox(gs_rect *pbox, gs_glyph *pglyph, gs_font *font, int ch,
	       const gs_matrix *pmat)
{
    gs_glyph glyph;
    gs_glyph_info_t info;
    int code;

    glyph = font->procs.encode_char(font, (gs_char)ch, GLYPH_SPACE_INDEX);
    if (glyph == gs_no_glyph)
	return gs_error_undefined;
    code = font->procs.glyph_info(font, glyph, pmat, GLYPH_INFO_BBOX, &info);
    if (code < 0)
	return code;
    *pbox = info.bbox;
    if (pglyph)
	*pglyph = glyph;
    return 0;
}
int
pdf_compute_font_descriptor(gx_device_pdf *pdev, pdf_font_descriptor_t *pfd,
			    gs_font *font, const byte *used /*[32]*/)
{
    gs_font_base *bfont = (gs_font_base *)font;
    gs_glyph glyph, notdef;
    int index;
    int wmode = font->WMode;
    int members = (GLYPH_INFO_WIDTH0 << wmode) |
	GLYPH_INFO_BBOX | GLYPH_INFO_NUM_PIECES;
    gs_glyph letters[52];
    int num_letters = 0;
    pdf_font_descriptor_values_t desc;
    gs_matrix smat;
    gs_matrix *pmat = NULL;
    int fixed_width = 0;
    int small_descent = 0, small_height = 0;
    int code;

    memset(&desc, 0, sizeof(desc));
    desc.FontType = font->FontType;
    desc.FontBBox.p.x = desc.FontBBox.p.y = max_int;
    desc.FontBBox.q.x = desc.FontBBox.q.y = min_int;
    /*
     * Embedded TrueType fonts use a 1000-unit character space, but the
     * font itself uses a 1-unit space.  Compensate for this here.
     */
    switch (font->FontType) {
    case ft_TrueType:
    case ft_CID_TrueType:
	gs_make_scaling(1000.0, 1000.0, &smat);
	pmat = &smat;
    default:
	break;
    }
    /*
     * See the note on FONT_IS_ADOBE_ROMAN / FONT_USES_STANDARD_ENCODING
     * in gdevpdff.h for why the following substitution is made.
     */
#if 0
#  define CONSIDER_FONT_SYMBOLIC(font) font_is_symbolic(font)
#else
#  define CONSIDER_FONT_SYMBOLIC(font)\
  ((font)->FontType == ft_composite ||\
   ((const gs_font_base *)(font))->encoding_index != ENCODING_INDEX_STANDARD)
#endif
    if (CONSIDER_FONT_SYMBOLIC(font))
	desc.Flags |= FONT_IS_SYMBOLIC;
    else {
	/*
	 * Look at various specific characters to guess at the remaining
	 * descriptor values (CapHeight, ItalicAngle, StemV, XHeight,
	 * and flags SERIF, SCRIPT, ITALIC, ALL_CAPS, and SMALL_CAPS).
	 * The algorithms are pretty crude.
	 */
	/*
	 * Look at the glyphs for the lower-case letters.  If they are
	 * all missing, this is an all-cap font; if any is present, check
	 * the relative heights to determine whether this is a small-cap font.
	 */
	bool small_present = false;
	int ch;
	int x_height = min_int;
	int cap_height = 0;
	gs_rect bbox, bbox2;

	desc.Flags |= FONT_IS_ADOBE_ROMAN; /* required if not symbolic */
	for (ch = 'a'; ch <= 'z'; ++ch) {
	    int y0, y1;

	    code =
		font_char_bbox(&bbox, &letters[num_letters], font, ch, pmat);
	    if (code < 0)
		continue;
	    ++num_letters;
	    rect_merge(desc.FontBBox, bbox);
	    small_present = true;
	    y0 = (int)bbox.p.y;
	    y1 = (int)bbox.q.y;
	    switch (ch) {
	    case 'b': case 'd': case 'f': case 'h':
	    case 'k': case 'l': case 't': /* ascender */
		small_height = max(small_height, y1);
	    case 'i':		/* anomalous ascent */
		break;
	    case 'j':		/* descender with anomalous ascent */
		small_descent = min(small_descent, y0);
		break;
	    case 'g': case 'p': case 'q': case 'y': /* descender */
		small_descent = min(small_descent, y0);
	    default:		/* no ascender or descender */
		x_height = max(x_height, y1);		
	    }
	}
	desc.XHeight = (int)x_height;
	if (!small_present)
	    desc.Flags |= FONT_IS_ALL_CAPS;
	for (ch = 'A'; ch <= 'Z'; ++ch) {
	    code =
		font_char_bbox(&bbox, &letters[num_letters], font, ch, pmat);
	    if (code < 0)
		continue;
	    ++num_letters;
	    rect_merge(desc.FontBBox, bbox);
	    cap_height = max(cap_height, (int)bbox.q.y);
	}
	desc.CapHeight = cap_height;
	/*
	 * Look at various glyphs to determine ItalicAngle, StemV,
	 * SERIF, SCRIPT, and ITALIC.
	 */
	if ((code = font_char_bbox(&bbox, NULL, font, ':', pmat)) >= 0 &&
	    (code = font_char_bbox(&bbox2, NULL, font, '.', pmat)) >= 0
	    ) {
	    /* Calculate the dominant angle. */
	    int angle = 
		(int)(atan2((bbox.q.y - bbox.p.y) - (bbox2.q.y - bbox2.p.y),
			    (bbox.q.x - bbox.p.x) - (bbox2.q.x - bbox2.p.x)) *
		      radians_to_degrees) - 90;

	    /* Normalize to [-90..90]. */
	    while (angle > 90)
		angle -= 180;
	    while (angle < -90)
		angle += 180;
	    if (angle < -30)
		angle = -30;
	    else if (angle > 30)
		angle = 30;
	    /*
	     * For script or embellished fonts, we can get an angle that is
	     * slightly off from zero even for non-italic fonts.
	     * Compensate for this now.
	     */
	    if (angle <= 2 && angle >= -2)
		angle = 0;
	    desc.ItalicAngle = angle;
	}
	if (desc.ItalicAngle)
	    desc.Flags |= FONT_IS_ITALIC;
	if (code >= 0) {
	    double wdot = bbox2.q.x - bbox2.p.x;

	    if ((code = font_char_bbox(&bbox2, NULL, font, 'I', pmat)) >= 0) {
		double wcolon = bbox.q.x - bbox.p.x;
		double wI = bbox2.q.x - bbox2.p.x;

		desc.StemV = (int)wdot;
		if (wI > wcolon * 2.5 || wI > (bbox2.q.y - bbox2.p.y) * 0.25)
		    desc.Flags |= FONT_IS_SERIF;
	    }
	}
    }
    /*
     * Scan the entire glyph space to compute Ascent, Descent, FontBBox,
     * and the fixed width if any.  Avoid computing the bounding box of
     * letters a second time.
     */
    num_letters = psf_sort_glyphs(letters, num_letters);
    desc.Ascent = desc.FontBBox.q.y;
    notdef = gs_no_glyph;
    for (index = 0;
	 (code = font->procs.enumerate_glyph(font, &index, GLYPH_SPACE_INDEX, &glyph)) >= 0 &&
	     index != 0;
	 ) {
	gs_glyph_info_t info;

	if (psf_sorted_glyphs_include(letters, num_letters, glyph)) {
	    /* We don't need the bounding box. */
	    code = font->procs.glyph_info(font, glyph, pmat,
					  members - GLYPH_INFO_BBOX, &info);
	    if (code < 0)
		return code;
	} else {
	    code = font->procs.glyph_info(font, glyph, pmat, members, &info);
	    if (code < 0)
		return code;
	    rect_merge(desc.FontBBox, info.bbox);
	    if (!info.num_pieces)
		desc.Ascent = max(desc.Ascent, info.bbox.q.y);
	}
	if (notdef == gs_no_glyph && gs_font_glyph_is_notdef(bfont, glyph)) {
	    notdef = glyph;
	    desc.MissingWidth = info.width[wmode].x;
	}
	if (info.width[wmode].y != 0)
	    fixed_width = min_int;
	else if (fixed_width == 0)
	    fixed_width = info.width[wmode].x;
	else if (info.width[wmode].x != fixed_width)
	    fixed_width = min_int;
    }
    if (code < 0)
	return code;
    if (desc.Ascent == 0)
	desc.Ascent = desc.FontBBox.q.y;
    desc.Descent = desc.FontBBox.p.y;
    if (!(desc.Flags & (FONT_IS_SYMBOLIC | FONT_IS_ALL_CAPS)) &&
	(small_descent > desc.Descent / 3 || desc.XHeight > small_height * 0.9)
	)
	desc.Flags |= FONT_IS_SMALL_CAPS;
    if (fixed_width > 0) {
	desc.Flags |= FONT_IS_FIXED_WIDTH;
	desc.AvgWidth = desc.MaxWidth = desc.MissingWidth = fixed_width;
    }
    if (desc.CapHeight == 0)
	desc.CapHeight = desc.Ascent;
    if (desc.StemV == 0)
	desc.StemV = (int)(desc.FontBBox.q.x * 0.15);
    pfd->values = desc;
    return 0;
}
