rem: if (char_bounds(bounds, width, cff, cmap, 'x') && bounds[3] < xheight)
add: if (char_bounds(bounds, width, cff, cmap, 'x', font_xform) && bounds[3] < xheight)
context: output_pl(const OpenType::Font &otf, Cff::Font *cff,	  const Metrics &metrics, int boundary_char,	  const Vector<PermString> &,	  bool vpl, FILE *f){    // XXX check DESIGNSIZE and DESIGNUNITS for correctness    fprintf(f, "(COMMENT Created by '%s'%s)\n", invocation.c_str(), current_time.c_str());    // calculate a TeX FAMILY name using afm2tfm's algorithm    String family_name = String("TeX-") + cff->font_name();    if (family_name.length() > 19)	family_name = family_name.substring(0, 9) + family_name.substring(-10);    fprintf(f, "(FAMILY %s)\n", family_name.c_str());    int design_units = 1000;    if (metrics.coding_scheme()) {	fprintf(f, "(CODINGSCHEME %.39s)\n", String(metrics.coding_scheme()).c_str());	design_units = 1;    } else if (out_encoding_name)	fprintf(f, "(CODINGSCHEME %.39s)\n", out_encoding_name.c_str());    if (design_size <= 0)	design_size = get_design_size(otf);    fprintf(f, "(DESIGNSIZE R %.1f)\n"	    "(DESIGNUNITS R %d.0)\n"	    "(COMMENT DESIGNSIZE (1 em) IS IN POINTS)\n"	    "(COMMENT OTHER DIMENSIONS ARE MULTIPLES OF DESIGNSIZE/%d)\n"	    "(FONTDIMEN\n", design_size, design_units, design_units);    // figure out font dimensions    Transform font_xform;    if (extend)	font_xform.scale(1, extend);    if (slant)	font_xform.shear(slant);    int bounds[4], width;    double du = (design_units == 1000 ? 1. : design_units / 1000.);        double val;    if (cff->dict_value(Efont::Cff::oItalicAngle, 0, &val) && val)	fprintf(f, "   (SLANT R %g)\n", -tan(val * 3.1415926535 / 180.0));    OpenType::Cmap cmap(otf.table("cmap"));        if (char_bounds(bounds, width, cff, cmap, ' ')) {	// advance space width by letterspacing	width += letterspace;	fprint_real(f, "   (SPACE", width, du);	if (cff->dict_value(Efont::Cff::oIsFixedPitch, 0, &val) && val) {	    // fixed-pitch: no space stretch or shrink	    fprint_real(f, "   (STRETCH", 0, du);	    fprint_real(f, "   (SHRINK", 0, du);	    fprint_real(f, "   (EXTRASPACE", width, du);	} else {	    fprint_real(f, "   (STRETCH", width / 2., du);	    fprint_real(f, "   (SHRINK", width / 3., du);	    fprint_real(f, "   (EXTRASPACE", width / 6., du);	}    }    // XXX what if 'x', 'm', 'z' were subject to substitution?    int xheight = 1000;    static const int xheight_unis[] = { 'x', 'm', 'z', 0 };    for (const int *x = xheight_unis; *x; x++)	if (char_bounds(bounds, width, cff, cmap, 'x') && bounds[3] < xheight)	    xheight = bounds[3];    if (xheight < 1000)	fprint_real(f, "   (XHEIGHT", xheight, du);        fprint_real(f, "   (QUAD", 1000, du);    fprintf(f, "   )\n");    if (boundary_char >= 0)	fprintf(f, "(BOUNDARYCHAR D %d)\n", boundary_char);    // write MAPFONT    if (vpl)	fprintf(f, "(MAPFONT D 0\n   (FONTNAME %s)\n   (FONTDSIZE R %.1f)\n   )\n", make_base_font_name(font_name).c_str(), design_size);        // figure out the proper names and numbers for glyphs    Vector<String> glyph_ids;    Vector<String> glyph_comments(257, String());    Vector<String> glyph_base_comments(257, String());    for (int i = 0; i < 256; i++) {	if (metrics.glyph(i)) {	    PermString name = metrics.code_name(i), expected_name;	    if (i >= '0' && i <= '9')		expected_name = digit_names[i - '0'];	    else if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z'))		expected_name = PermString((char)i);	    	    if (expected_name		&& name.length() >= expected_name.length()		&& memcmp(name.c_str(), expected_name.c_str(), expected_name.length()) == 0)		glyph_ids.push_back("C " + String((char)i));	    else		glyph_ids.push_back("D " + String(i));	    	    if (name != expected_name)		glyph_comments[i] = " (COMMENT " + String(name) + ")";	    int base = metrics.base_code(i);	    if (base != i)		glyph_base_comments[i] = " (COMMENT " + String(metrics.code_name(base)) + ")";	    else		glyph_base_comments[i] = glyph_comments[i];	    	} else	    glyph_ids.push_back("X");    }    // finally, BOUNDARYCHAR    glyph_ids.push_back("BOUNDARYCHAR");    // LIGTABLE    fprintf(f, "(LIGTABLE\n");    Vector<int> lig_code2, lig_outcode, lig_context, kern_code2, kern_amt;    // don't print KRN x after printing LIG x    uint32_t used[8];    for (int i = 0; i <= 256; i++)	if (metrics.glyph(i)) {	    int any_lig = metrics.ligatures(i, lig_code2, lig_outcode, lig_context);	    int any_kern = metrics.kerns(i, kern_code2, kern_amt);	    if (any_lig || any_kern) {		fprintf(f, "   (LABEL %s)%s\n", glyph_ids[i].c_str(), glyph_comments[i].c_str());		memset(&used[0], 0, 32);		for (int j = 0; j < lig_code2.size(); j++) {		    fprintf(f, "   (%s %s %s)%s%s\n",			    lig_context_str(lig_context[j]),			    glyph_ids[lig_code2[j]].c_str(),			    glyph_ids[lig_outcode[j]].c_str(),			    glyph_comments[lig_code2[j]].c_str(),			    glyph_comments[lig_outcode[j]].c_str());		    used[lig_code2[j] >> 5] |= (1 << (lig_code2[j] & 0x1F));		}		for (Vector<int>::const_iterator k2 = kern_code2.begin(); k2 < kern_code2.end(); k2++)		    if (!(used[*k2 >> 5] & (1 << (*k2 & 0x1F)))) {			double this_kern = kern_amt[k2 - kern_code2.begin()];			if (fabs(this_kern) >= minimum_kern) {			    fprintf(f, "   (KRN %s", glyph_ids[*k2].c_str());			    fprint_real(f, "", this_kern, du, "");			    fprintf(f, ")%s\n", glyph_comments[*k2].c_str());			}		    }		fprintf(f, "   (STOP)\n");	    }	}    fprintf(f, "   )\n");        // CHARACTERs    Vector<Setting> settings;    StringAccum sa;        for (int i = 0; i < 256; i++)	if (metrics.setting(i, settings)) {	    fprintf(f, "(CHARACTER %s%s\n", glyph_ids[i].c_str(), glyph_comments[i].c_str());	    // unparse settings into DVI commands	    sa.clear();	    CharstringBounds boundser(font_xform);	    for (int j = 0; j < settings.size(); j++) {		Setting &s = settings[j];		if (s.op == Setting::SHOW) {		    boundser.char_bounds(cff->glyph_context(metrics.base_glyph(s.x)));		    sa << "      (SETCHAR " << glyph_ids[s.x] << ')' << glyph_base_comments[s.x] << "\n";		} else if (s.op == Setting::MOVE && vpl) {		    boundser.translate(s.x, s.y);		    if (s.x)			sa << "      (MOVERIGHT R " << real_string(s.x, du) << ")\n";		    if (s.y)			sa << "      (MOVEUP R " << real_string(s.y, du) << ")\n";		} else if (s.op == Setting::RULE && vpl) {		    boundser.mark(Point(0, 0));		    boundser.mark(Point(s.x, s.y));		    boundser.translate(s.x, 0);		    sa << "      (SETRULE R " << real_string(s.y, du) << " R " << real_string(s.x, du) << ")\n";		}	    }	    // output information	    boundser.output(bounds, width);	    fprint_real(f, "   (CHARWD", width, du);	    if (bounds[3] > 0)		fprint_real(f, "   (CHARHT", bounds[3], du);	    if (bounds[1] < 0)		fprint_real(f, "   (CHARDP", -bounds[1], du);	    if (bounds[2] > width)		fprint_real(f, "   (CHARIC", bounds[2] - width, du);	    if (vpl && (settings.size() > 1 || settings[0].op != Setting::SHOW))		fprintf(f, "   (MAP\n%s      )\n", sa.c_str());	    fprintf(f, "   )\n");	}}