/* Copyright (C) 1990, 1992, 1993, 1997, 1998 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: gxhint1.c,v 1.2.6.1.2.1 2003/01/17 00:49:03 giles Exp $ */ /* Font level hints for Type 1 fonts */ #include "gx.h" #include "gserrors.h" #include "gxarith.h" #include "gxfixed.h" #include "gxmatrix.h" #include "gxfont.h" #include "gxfont1.h" #include "gxtype1.h" /* Define whether to use font hints. */ /* Only set this to false for debugging the hint machinery. */ static bool USE_HINTS = true; /* ------ Initialization ------ */ typedef zone_table(1) a_zone_table; typedef stem_table(1) a_stem_table; private void compute_snaps(P6(const gs_matrix_fixed *, const a_stem_table *, stem_snap_table *, int, int, const char *)); private alignment_zone * compute_zones(P6(const gs_matrix_fixed *, const font_hints *, const a_zone_table *, const a_zone_table *, alignment_zone *, int)); private int transform_zone(P4(const gs_matrix_fixed *, const font_hints *, const float *, alignment_zone *)); /* Reset the font-level hints. */ void reset_font_hints(font_hints * pfh, const gs_log2_scale_point * plog2_scale) { set_pixel_scale(&pfh->scale.x, plog2_scale->x); set_pixel_scale(&pfh->scale.y, plog2_scale->y); pfh->axes_swapped = pfh->x_inverted = pfh->y_inverted = false; pfh->use_x_hints = pfh->use_y_hints = false; pfh->snap_h.count = pfh->snap_v.count = 0; pfh->a_zone_count = 0; } /* Compute the font-level hints from the font and the matrix. */ /* We should cache this with the font/matrix pair.... */ void compute_font_hints(font_hints * pfh, const gs_matrix_fixed * pmat, const gs_log2_scale_point * plog2_scale, const gs_type1_data * pdata) { alignment_zone *zp = &pfh->a_zones[0]; reset_font_hints(pfh, plog2_scale); /* Figure out which hints, if any, to use, */ /* and the orientation of the axes. */ if (is_fzero(pmat->xy)) pfh->y_inverted = is_fneg(pmat->yy), pfh->use_y_hints = USE_HINTS; else if (is_fzero(pmat->xx)) pfh->y_inverted = is_fneg(pmat->xy), pfh->axes_swapped = true, pfh->use_y_hints = USE_HINTS; if (is_fzero(pmat->yx)) pfh->x_inverted = is_fneg(pmat->xx), pfh->use_x_hints = USE_HINTS; else if (is_fzero(pmat->yy)) pfh->x_inverted = is_fneg(pmat->yx), pfh->axes_swapped = true, pfh->use_x_hints = USE_HINTS; if_debug6('y', "[y]ctm=[%g %g %g %g %g %g]\n", pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty); if_debug7('y', "[y]scale=%d/%d, swapped=%d, x/y_hints=%d,%d, x/y_inverted=%d,%d\n", 1 << plog2_scale->x, 1 << plog2_scale->y, pfh->axes_swapped, pfh->use_x_hints, pfh->use_y_hints, pfh->x_inverted, pfh->y_inverted); /* Transform the actual hints. */ if (pfh->use_x_hints) { compute_snaps(pmat, (const a_stem_table *)&pdata->StdHW, &pfh->snap_h, 0, pfh->axes_swapped, "h"); compute_snaps(pmat, (const a_stem_table *)&pdata->StemSnapH, &pfh->snap_h, 0, pfh->axes_swapped, "h"); } if (pfh->use_y_hints) { gs_fixed_point vw; fixed *vp = (pfh->axes_swapped ? &vw.x : &vw.y); pixel_scale *psp = (pfh->axes_swapped ? &pfh->scale.x : &pfh->scale.y); /* Convert BlueFuzz to device pixels. */ if (gs_distance_transform2fixed(pmat, 0.0, (float)pdata->BlueFuzz, &vw) < 0 ) vw.x = vw.y = fixed_0; pfh->blue_fuzz = any_abs(*vp); /* * Decide whether to suppress overshoots. The formula in * section 5.6 of the "Adobe Type 1 Font Format" says that * at 300 dpi, if BlueScale = (P - 0.49) / 240, overshoot * suppression turns off at point sizes at least P, i.e.: * P >= BlueScale * 240 + 0.49. * At 300 dpi, P = |CTM.yy| / (300/72), so the condition is * equivalent to * |CTM.yy| >= BlueScale * 1000 + 2.0417, * or * BlueScale >= (|CTM.yy| - 2.0417) / 1000. * Since *pmat is the concatenation of the FontMatrix and * CTM, if we assume a 1000-unit scale, this is equivalent to * BlueScale >= |pmat->yy| - 0.00020417. * Since the constant term is slightly smaller than * fixed_epsilon, we just disregard it. * * According to the same section of the Adobe documentation, * there is a requirement that BlueScale times the maximum * alignment zone height must be less than 1. We enforced * this when the font was constructed (in zfont1.c). */ if (gs_distance_transform2fixed(pmat, 0.0, 1.0, &vw) < 0) vw.x = vw.y = fixed_0; pfh->suppress_overshoot = fixed2float(any_abs(*vp) >> psp->log2_unit) < pdata->BlueScale; if (gs_distance_transform2fixed(pmat, 0.0, pdata->BlueShift, &vw) < 0 ) vw.x = vw.y = fixed_0; pfh->blue_shift = any_abs(*vp); /* * Don't let the blue shift exceed half a pixel. * See the discussion of BlueShift in section 5.7 of the * "Adobe Type 1 Font Format" book. */ if (pfh->blue_shift > psp->half) pfh->blue_shift = psp->half; if_debug6('y', "[y]blue_fuzz=%d->%g, blue_scale=%g, blue_shift=%g->%g, sup_ov=%d\n", pdata->BlueFuzz, fixed2float(pfh->blue_fuzz), pdata->BlueScale, pdata->BlueShift, fixed2float(pfh->blue_shift), pfh->suppress_overshoot); zp = compute_zones(pmat, pfh, (const a_zone_table *)&pdata->BlueValues, (const a_zone_table *)&pdata->FamilyBlues, zp, 1); zp = compute_zones(pmat, pfh, (const a_zone_table *)&pdata->OtherBlues, (const a_zone_table *)&pdata->FamilyOtherBlues, zp, max_OtherBlues); compute_snaps(pmat, (const a_stem_table *)&pdata->StdVW, &pfh->snap_v, 1, !pfh->axes_swapped, "v"); compute_snaps(pmat, (const a_stem_table *)&pdata->StemSnapV, &pfh->snap_v, 1, !pfh->axes_swapped, "v"); } pfh->a_zone_count = zp - &pfh->a_zones[0]; } /* Transform one set of stem snap widths. */ private void compute_snaps(const gs_matrix_fixed * pmat, const a_stem_table * pst, stem_snap_table * psst, int from_y, int to_y, const char *tname) { gs_fixed_point wxy; fixed *wp = (to_y ? &wxy.y : &wxy.x); int i; int j = psst->count; for (i = 0; i < pst->count; i++) { float w = pst->values[i]; int code = (from_y ? gs_distance_transform2fixed(pmat, 0.0, w, &wxy) : gs_distance_transform2fixed(pmat, w, 0.0, &wxy) ); if (code < 0) continue; psst->data[j] = any_abs(*wp); if_debug3('y', "[y]snap_%s[%d]=%g\n", tname, j, fixed2float(psst->data[j])); j++; } psst->count = j; } /* Compute the alignment zones for one set of 'blue' values. */ private alignment_zone * compute_zones(const gs_matrix_fixed * pmat, const font_hints * pfh, const a_zone_table * blues, const a_zone_table * family_blues, alignment_zone * zp, int bottom_count) { int i; fixed fuzz = pfh->blue_fuzz; int inverted = (pfh->axes_swapped ? pfh->x_inverted : pfh->y_inverted); for (i = 0; i < blues->count; i += 2) { const float *vp = &blues->values[i]; zp->is_top_zone = i >> 1 >= bottom_count; if (transform_zone(pmat, pfh, vp, zp) < 0) continue; if_debug5('y', "[y]blues[%d]=%g,%g -> %g,%g\n", i >> 1, vp[0], vp[1], fixed2float(zp->v0), fixed2float(zp->v1)); if (i < family_blues->count) { /* Check whether family blues should supersede. */ alignment_zone fz; const float *fvp = &family_blues->values[i]; fixed unit = (pfh->axes_swapped ? pfh->scale.x.unit : pfh->scale.y.unit); fixed diff; if (transform_zone(pmat, pfh, fvp, &fz) < 0) continue; if_debug5('y', "[y]f_blues[%d]=%g,%g -> %g,%g\n", i >> 1, fvp[0], fvp[1], fixed2float(fz.v0), fixed2float(fz.v1)); diff = (zp->v1 - zp->v0) - (fz.v1 - fz.v0); if (diff > -unit && diff < unit) zp->v0 = fz.v0, zp->v1 = fz.v1; } /* Compute the flat position, and add the fuzz. */ if ((inverted ? zp->is_top_zone : !zp->is_top_zone)) zp->flat = zp->v1, zp->v0 -= fuzz; else zp->flat = zp->v0, zp->v1 += fuzz; zp++; } return zp; } /* Transform a single alignment zone to device coordinates, */ /* taking axis swapping into account. */ private int transform_zone(const gs_matrix_fixed * pmat, const font_hints * pfh, const float *vp, alignment_zone * zp) { gs_fixed_point p0, p1; fixed v0, v1; int code; if ((code = gs_point_transform2fixed(pmat, 0.0, vp[0], &p0)) < 0 || (code = gs_point_transform2fixed(pmat, 0.0, vp[1], &p1)) < 0 ) return code; if (pfh->axes_swapped) v0 = p0.x, v1 = p1.x; else v0 = p0.y, v1 = p1.y; if (v0 <= v1) zp->v0 = v0, zp->v1 = v1; else zp->v0 = v1, zp->v1 = v0; return 0; }