// -*- related-file-name: "../include/efont/t1mm.hh" -*- /* t1mm.{cc,hh} -- Type 1 multiple master font information * * Copyright (c) 1998-2014 Eddie Kohler * * 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. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include namespace Efont { MultipleMasterSpace::MultipleMasterSpace(PermString fn, int na, int nm) : CharstringProgram(1000), _ok(false), _font_name(fn), _naxes(na), _nmasters(nm), _axis_types(na, PermString()), _axis_labels(na, PermString()), _design_vector(0), _norm_design_vector(0), _weight_vector(0) { } void MultipleMasterSpace::set_master_positions(const Vector &mp) { _master_positions = mp; } void MultipleMasterSpace::set_normalize(const Vector &nin, const Vector &nout) { _normalize_in = nin; _normalize_out = nout; } void MultipleMasterSpace::set_axis_type(int ax, PermString t) { _axis_types[ax] = t; } void MultipleMasterSpace::set_axis_label(int ax, PermString t) { _axis_labels[ax] = t; } void MultipleMasterSpace::set_design_vector(const NumVector &v) { _default_design_vector = v; } void MultipleMasterSpace::set_weight_vector(const NumVector &v) { _default_weight_vector = v; } PermString MultipleMasterSpace::axis_abbreviation(PermString atype) { if (atype == "Weight") return "wt"; else if (atype == "Width") return "wd"; else if (atype == "OpticalSize") return "op"; else if (atype == "Style") return "st"; else return atype; } bool MultipleMasterSpace::error(ErrorHandler *errh, const char *s, ...) const { if (errh) { char buf[1024]; va_list val; va_start(val, s); assert(strlen(s) < 800); sprintf(buf, (s[0] == ' ' ? "%.200s%s" : "%.200s: %s"), _font_name.c_str(), s); errh->vxmessage(ErrorHandler::e_error, buf, val); va_end(val); } return false; } bool MultipleMasterSpace::check(ErrorHandler *errh) { if (_ok) return true; if (_nmasters <= 0 || _nmasters > 16) return error(errh, "number of masters must be between 1 and 16"); if (_naxes <= 0 || _naxes > 4) return error(errh, "number of axes must be between 1 and 4"); if (_master_positions.size() != _nmasters) return error(errh, "bad BlendDesignPositions"); for (int i = 0; i < _nmasters; i++) if (_master_positions[i].size() != _naxes) return error(errh, "inconsistent BlendDesignPositions"); if (_normalize_in.size() != _naxes || _normalize_out.size() != _naxes) return error(errh, "bad BlendDesignMap"); for (int i = 0; i < _naxes; i++) if (_normalize_in[i].size() != _normalize_out[i].size()) return error(errh, "bad BlendDesignMap"); if (!_axis_types.size()) _axis_types.assign(_naxes, PermString()); if (_axis_types.size() != _naxes) return error(errh, "bad BlendAxisTypes"); if (!_axis_labels.size()) _axis_labels.assign(_naxes, PermString()); if (_axis_labels.size() != _naxes) return error(errh, "bad axis labels"); if (!_default_design_vector.size()) _default_design_vector.assign(_naxes, UNKDOUBLE); if (_default_design_vector.size() != _naxes) return error(errh, "inconsistent design vector"); if (!_default_weight_vector.size()) _default_weight_vector.assign(_nmasters, UNKDOUBLE); if (_default_weight_vector.size() != _nmasters) return error(errh, "inconsistent weight vector"); _ok = true; return true; } bool MultipleMasterSpace::check_intermediate(ErrorHandler *errh) { if (!_ok || _cdv) return true; for (int a = 0; a < _naxes; a++) for (int m = 0; m < _nmasters; m++) if (_master_positions[m][a] != 0 && _master_positions[m][a] != 1) { if (errh) errh->warning("%s requires intermediate master conversion programs", _font_name.c_str()); return false; } return true; } int MultipleMasterSpace::axis(PermString ax) const { for (int a = 0; a < _naxes; a++) if (_axis_types[a] == ax || _axis_labels[a] == ax) return a; return -1; } double MultipleMasterSpace::axis_low(int ax) const { return _normalize_in[ax][0]; } double MultipleMasterSpace::axis_high(int ax) const { return _normalize_in[ax].back(); } Vector MultipleMasterSpace::empty_design_vector() const { return Vector(_naxes, UNKDOUBLE); } bool MultipleMasterSpace::set_design(NumVector &design_vector, int ax, double value, ErrorHandler *errh) const { if (ax < 0 || ax >= _naxes) return error(errh, " has only %d axes", _naxes); if (value < axis_low(ax)) { value = axis_low(ax); if (errh) errh->warning("raising %s's %s to %g", _font_name.c_str(), _axis_types[ax].c_str(), value); } if (value > axis_high(ax)) { value = axis_high(ax); if (errh) errh->warning("lowering %s's %s to %g", _font_name.c_str(), _axis_types[ax].c_str(), value); } design_vector[ax] = value; return true; } bool MultipleMasterSpace::set_design(NumVector &design_vector, PermString ax_name, double val, ErrorHandler *errh) const { int ax = axis(ax_name); if (ax < 0) return error(errh, " has no `%s' axis", ax_name.c_str()); else return set_design(design_vector, ax, val, errh); } bool MultipleMasterSpace::normalize_vector(ErrorHandler *errh) const { NumVector &design = *_design_vector; NumVector &norm_design = *_norm_design_vector; for (int a = 0; a < _naxes; a++) if (!KNOWN(design[a])) { if (errh) errh->error("must specify %s's %s coordinate", _font_name.c_str(), _axis_types[a].c_str()); return false; } // Move to normalized design coordinates. norm_design.assign(_naxes, UNKDOUBLE); if (_ndv) { CharstringInterp ai; if (!ai.interpret(this, &_ndv)) return error(errh, "%s in NDV program", ai.error_string().c_str()); } else for (int a = 0; a < _naxes; a++) { double d = design[a]; double nd = UNKDOUBLE; const Vector &norm_in = _normalize_in[a]; const Vector &norm_out = _normalize_out[a]; if (d < norm_in[0]) nd = norm_out[0]; for (int i = 1; i < norm_in.size(); i++) if (d >= norm_in[i-1] && d < norm_in[i]) { nd = norm_out[i-1] + ((d - norm_in[i-1]) * (norm_out[i] - norm_out[i-1]) / (norm_in[i] - norm_in[i-1])); goto done; } if (d >= norm_in.back()) nd = norm_out.back(); done: norm_design[a] = nd; } for (int a = 0; a < _naxes; a++) if (!KNOWN(norm_design[a])) return error(errh, "bad normalization"); return true; } bool MultipleMasterSpace::convert_vector(ErrorHandler *errh) const { NumVector &norm_design = *_norm_design_vector; NumVector &weight = *_weight_vector; weight.assign(_nmasters, 1); if (_cdv) { CharstringInterp ai; if (!ai.interpret(this, &_cdv)) return error(errh, "%s in CDV program", ai.error_string().c_str()); } else for (int a = 0; a < _naxes; a++) for (int m = 0; m < _nmasters; m++) { if (_master_positions[m][a] == 0) weight[m] *= 1 - norm_design[a]; else if (_master_positions[m][a] == 1) weight[m] *= norm_design[a]; else return error(errh, " requires intermediate master conversion programs"); } return true; } bool MultipleMasterSpace::design_to_norm_design(const NumVector &design_in, NumVector &norm_design, ErrorHandler *errh) const { NumVector design(design_in); NumVector weight; _design_vector = &design; _norm_design_vector = &norm_design; _weight_vector = &weight; if (!normalize_vector(errh)) return false; _design_vector = _norm_design_vector = _weight_vector = 0; return true; } bool MultipleMasterSpace::design_to_weight(const NumVector &design_in, NumVector &weight, ErrorHandler *errh) const { NumVector design(design_in); NumVector norm_design; bool dirty = false; for (int i = 0; i < _naxes; i++) if (design[i] != _default_design_vector[i]) dirty = true; if (dirty) { _design_vector = &design; _norm_design_vector = &norm_design; _weight_vector = &weight; if (!normalize_vector(errh)) return false; if (!convert_vector(errh)) return false; _design_vector = _norm_design_vector = _weight_vector = 0; } else weight = _default_weight_vector; double sum = 0; for (int m = 0; m < _nmasters; m++) sum += weight[m]; if (sum < 0.9999 || sum > 1.0001) return error(errh, "bad conversion: weight vector doesn't sum to 1"); // adjust weight vector to max 4 decimal digits of precision, and make it // sum to exactly 1 sum = 0; for (int m = 0; m < _nmasters - 1; m++) { weight[m] = floor(weight[m] * 10000. + 0.5) / 10000.; sum += weight[m]; } weight[_nmasters - 1] = 1 - sum; return true; } Vector * MultipleMasterSpace::mm_vector(VectorType t, bool writable) const { if (t == VEC_WEIGHT) return _weight_vector; else if (t == VEC_NORM_DESIGN) return _norm_design_vector; else if (t == VEC_DESIGN && !writable) return _design_vector; else return 0; } }