summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/amfm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/amfm.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/amfm.cc850
1 files changed, 850 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/amfm.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/amfm.cc
new file mode 100644
index 00000000000..b38532d55fb
--- /dev/null
+++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/libefont/amfm.cc
@@ -0,0 +1,850 @@
+// -*- related-file-name: "../include/efont/amfm.hh" -*-
+
+/* amfm.{cc,hh} -- Adobe Multiple-Master Font Metrics
+ *
+ * Copyright (c) 1998-2012 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 <config.h>
+#endif
+#include <efont/amfm.hh>
+#include <efont/afm.hh>
+#include <efont/afmparse.hh>
+#include <lcdf/error.hh>
+#include <efont/findmet.hh>
+#include <efont/t1cs.hh>
+#include <lcdf/straccum.hh>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+namespace Efont {
+
+AmfmMetrics::AmfmMetrics(MetricsFinder *finder)
+ : _finder(finder),
+ _fdv(fdLast, UNKDOUBLE),
+ _nmasters(-1), _naxes(-1), _masters(0), _mmspace(0),
+ _primary_fonts(0), _sanity_afm(0), _uses(0)
+{
+}
+
+AmfmMetrics::~AmfmMetrics()
+{
+ assert(_uses == 0);
+ for (int m = 0; m < _nmasters; m++)
+ if (_masters[m].afm)
+ _masters[m].afm->unuse();
+ delete[] _masters;
+ delete _mmspace;
+ while (_primary_fonts) {
+ AmfmPrimaryFont *pf = _primary_fonts;
+ _primary_fonts = _primary_fonts->next;
+ delete pf;
+ }
+}
+
+
+bool
+AmfmMetrics::sanity(ErrorHandler *errh) const
+{
+ if (!_mmspace) {
+ errh->error("AMFM sanity: no multiple master interpolation information");
+ return false;
+ }
+
+ bool ok = true;
+ for (int m = 0; m < _nmasters; m++)
+ if (!_masters[m].font_name
+ || _masters[m].weight_vector.size() != _nmasters) {
+ errh->error("AMFM sanity: no information for master %d", m);
+ ok = false;
+ }
+
+ if (!_mmspace->check(errh))
+ ok = false;
+
+ return ok;
+}
+
+
+int
+AmfmMetrics::primary_label_value(int ax, PermString label) const
+{
+ assert(ax >= 0 && ax < _naxes);
+ for (AmfmPrimaryFont *pf = _primary_fonts; pf; pf = pf->next) {
+ if (pf->labels[ax] == label)
+ return pf->design_vector[ax];
+ }
+ return -1;
+}
+
+
+inline static bool
+strcompat(PermString a, PermString b)
+{
+ return !a || !b || a == b;
+}
+
+Metrics *
+AmfmMetrics::master(int m, ErrorHandler *errh)
+{
+ AmfmMaster &master = _masters[m];
+
+ if (!master.loaded) {
+ master.loaded = true;
+ DirectoryMetricsFinder directory_finder(_directory);
+ _finder->add_finder(&directory_finder);
+ Metrics *afm = _finder->find_metrics(master.font_name);
+
+ if (!afm) {
+ if (errh)
+ errh->error("%s: can't find AFM file for master `%s'",
+ _font_name.c_str(), master.font_name.c_str());
+
+ } else if (!strcompat(afm->font_name(), master.font_name)
+ || !strcompat(afm->family(), master.family)
+ || !strcompat(afm->full_name(), master.full_name)
+ || !strcompat(afm->version(), master.version)) {
+ if (errh)
+ errh->error("%s: AFM for master `%s' doesn't match AMFM",
+ _font_name.c_str(), master.font_name.c_str());
+
+ } else if (!_sanity_afm) {
+ master.afm = afm;
+ _sanity_afm = afm;
+ afm->use();
+
+ } else {
+ PairProgram *sanity_pairp = _sanity_afm->pair_program();
+ PairProgram *pairp = afm->pair_program();
+ char buf[1024];
+ buf[0] = 0;
+
+ if (afm->nglyphs() != _sanity_afm->nglyphs())
+ sprintf(buf, "glyph count (%d vs. %d)", afm->nglyphs(), _sanity_afm->nglyphs());
+ if (afm->nfd() != _sanity_afm->nfd())
+ sprintf(buf, "fd count (%d vs. %d)", afm->nfd(), _sanity_afm->nfd());
+ if (afm->nkv() != _sanity_afm->nkv())
+ sprintf(buf, "kv count (%d vs. %d)", afm->nkv(), _sanity_afm->nkv());
+ if (pairp->op_count() != sanity_pairp->op_count())
+ sprintf(buf, "pair op count (%d vs. %d)", pairp->op_count(), sanity_pairp->op_count());
+
+ if (!buf[0]) {
+ master.afm = afm;
+ afm->use();
+ } else if (errh)
+ errh->error("%s: AFM for master `%s' failed sanity checks (%s)", _font_name.c_str(), master.font_name.c_str(), buf);
+ }
+ }
+
+ return master.afm;
+}
+
+
+AmfmPrimaryFont *
+AmfmMetrics::find_primary_font(const Vector<double> &design_vector) const
+{
+ assert(design_vector.size() == _naxes);
+ for (AmfmPrimaryFont *pf = _primary_fonts; pf; pf = pf->next) {
+
+ for (int a = 0; a < _naxes; a++)
+ if ((int)design_vector[a] != pf->design_vector[a])
+ goto loser;
+ return pf;
+
+ loser: ;
+ }
+ return 0;
+}
+
+
+Metrics *
+AmfmMetrics::interpolate(const Vector<double> &design_vector,
+ const Vector<double> &weight_vector,
+ ErrorHandler *errh)
+{
+ assert(design_vector.size() == _naxes);
+ assert(weight_vector.size() == _nmasters);
+
+ // FIXME: check masters for correspondence.
+
+ /* 0.
+ * Make sure all necessary AFMs have been loaded. */
+ int m;
+ for (m = 0; m < _nmasters; m++)
+ if (weight_vector[m])
+ if (!master(m, errh))
+ return 0;
+
+ /* 1.
+ * Use the design vector to generate new FontName and FullName. */
+
+ AmfmPrimaryFont *pf = find_primary_font(design_vector);
+ // The primary font is useless to us if it doesn't have axis labels.
+ if (pf && !pf->labels.size())
+ pf = 0;
+
+ StringAccum font_name_sa, full_name_sa;
+ font_name_sa << _font_name;
+ full_name_sa << _full_name;
+ for (int a = 0; a < _naxes; a++) {
+ double dv = design_vector[a];
+ font_name_sa << '_' << dv;
+ full_name_sa << (a == 0 ? '_' : ' ') << dv;
+
+ PermString label;
+ if (pf)
+ label = pf->labels[a];
+ if (!label)
+ label = _mmspace->axis_abbreviation(a);
+ if (label)
+ full_name_sa << ' ' << label;
+ }
+ // Multiple master fonts require an underscore AFTER the font name too
+ font_name_sa << '_';
+
+ /* 2.
+ * Set up the new AFM with the special constructor. */
+
+ // Find the first master with a non-zero component.
+ for (m = 0; m < _nmasters && weight_vector[m] == 0; m++)
+ ;
+ Metrics *afm = new Metrics(font_name_sa.c_str(), full_name_sa.c_str(), *_masters[m].afm);
+ if (MetricsXt *xt = _masters[m].afm->find_xt("AFM")) {
+ AfmMetricsXt *new_xt = new AfmMetricsXt((AfmMetricsXt &)*xt);
+ afm->add_xt(new_xt);
+ }
+
+ /* 2.
+ * Interpolate the old AFM data into the new. */
+
+ afm->interpolate_dimens(*_masters[m].afm, weight_vector[m], false);
+
+ for (m++; m < _nmasters; m++)
+ if (weight_vector[m])
+ afm->interpolate_dimens(*_masters[m].afm, weight_vector[m], true);
+
+ return afm;
+}
+
+
+/*****
+ * AmfmReader
+ **/
+
+AmfmReader::AmfmReader(AfmParser &afmp, AmfmMetrics *amfm, ErrorHandler *errh)
+ : _amfm(amfm), _finder(amfm->_finder), _l(afmp),
+ _mmspace(amfm->_mmspace)
+{
+ _errh = errh ? errh : ErrorHandler::silent_handler();
+}
+
+AmfmMetrics *
+AmfmReader::read(Slurper &slurper, MetricsFinder *finder, ErrorHandler *errh)
+{
+ AfmParser parser(slurper);
+ if (!parser.ok()) return 0;
+ AmfmMetrics *amfm = new AmfmMetrics(finder);
+ AmfmReader reader(parser, amfm, errh);
+ if (!reader.read()) {
+ delete amfm;
+ return 0;
+ } else
+ return amfm;
+}
+
+AmfmMetrics *
+AmfmReader::read(const Filename &fn, MetricsFinder *finder, ErrorHandler *errh)
+{
+ Slurper slurper(fn);
+ return read(slurper, finder, errh);
+}
+
+void
+AmfmReader::add_amcp_file(Slurper &slurper, AmfmMetrics *amfm, ErrorHandler *errh)
+{
+ AfmParser parser(slurper);
+ if (!parser.ok()) return;
+ AmfmReader reader(parser, amfm, errh);
+ reader.read_amcp_file();
+}
+
+
+void
+AmfmReader::lwarning(const char *format, ...) const
+{
+ va_list val;
+ va_start(val, format);
+ _errh->vxmessage(_l.landmark(), ErrorHandler::e_warning, format, val);
+ va_end(val);
+}
+
+void
+AmfmReader::lerror(const char *format, ...) const
+{
+ va_list val;
+ va_start(val, format);
+ _errh->vxmessage(_l.landmark(), ErrorHandler::e_error, format, val);
+ va_end(val);
+}
+
+void
+AmfmReader::no_match_warning(const char *context) const
+{
+ // keyword() will fail (and a warning won't get printed) only if the string
+ // is all whitespace, which the spec allows
+ PermString keyword = _l.keyword();
+ if (!keyword) return;
+ if (_l.key_matched()) {
+ lwarning(context ? "bad `%s' command in %s:"
+ : "bad `%s' command:", keyword.c_str(), context);
+ lwarning("field %d %s", _l.fail_field(), _l.message().c_str());
+ } else
+ lwarning(context ? "unknown command `%s' in %s"
+ : "unknown command `%s'", keyword.c_str(), context);
+ _l.clear_message();
+}
+
+
+void
+AmfmReader::check_mmspace()
+{
+ if (!_mmspace && _amfm->_naxes >= 0 && _amfm->_nmasters >= 0
+ && _amfm->_font_name) {
+ _mmspace = _amfm->_mmspace =
+ new MultipleMasterSpace(_amfm->_font_name, _amfm->_naxes, _amfm->_nmasters);
+ }
+}
+
+
+bool
+AmfmReader::read()
+{
+ assert(_amfm != 0);
+ _mmspace = _amfm->_mmspace;
+
+ AfmParser &l = _l;
+ _amfm->_directory = l.filename().directory();
+
+ // First, read all opening comments into an array so we can print them out
+ // later.
+ PermString comment;
+ while (l.next_line()) {
+ if (l.isall("Comment %+s", &comment))
+ _amfm->_opening_comments.push_back(comment);
+ else if (l.isall("StartMasterFontMetrics %g", (double *)0))
+ ;
+ else {
+ l.save_line();
+ break;
+ }
+ }
+
+ int master = 0, axis = 0;
+
+ while (l.next_line())
+ switch (l[0]) {
+
+ case 'A':
+ if (l.isall("Ascender %g", &fd( fdAscender )))
+ break;
+ if (l.isall("Axes %d", &_amfm->_naxes)) {
+ check_mmspace();
+ break;
+ }
+ goto invalid;
+
+ case 'B':
+ if (l.is("BlendDesignPositions")) {
+ read_positions();
+ break;
+ }
+ if (l.is("BlendDesignMap")) {
+ read_normalize();
+ break;
+ }
+ if (l.is("BlendAxisTypes")) {
+ read_axis_types();
+ break;
+ }
+ goto invalid;
+
+ case 'C':
+ if (l.isall("CapHeight %g", &fd( fdCapHeight )))
+ break;
+ if (l.is("Comment"))
+ break;
+ goto invalid;
+
+ case 'D':
+ if (l.isall("Descender %g", &fd( fdDescender )))
+ break;
+ goto invalid;
+
+ case 'E':
+ if (l.isall("EncodingScheme %+s", &_amfm->_encoding_scheme))
+ break;
+ if (l.isall("EndMasterFontMetrics"))
+ goto done;
+ goto invalid;
+
+ case 'F':
+ if (l.isall("FontName %+s", &_amfm->_font_name)) {
+ check_mmspace();
+ break;
+ }
+ if (l.isall("FullName %+s", &_amfm->_full_name))
+ break;
+ if (l.isall("FamilyName %+s", &_amfm->_family))
+ break;
+ if (l.isall("FontBBox %g %g %g %g",
+ &fd( fdFontBBllx ), &fd( fdFontBBlly ),
+ &fd( fdFontBBurx ), &fd( fdFontBBury )))
+ break;
+ goto invalid;
+
+ case 'I':
+ if (l.isall("IsFixedPitch %b", (bool *)0))
+ break;
+ if (l.isall("ItalicAngle %g", &fd( fdItalicAngle )))
+ break;
+ goto invalid;
+
+ case 'M':
+ if (l.isall("Masters %d", &_amfm->_nmasters)) {
+ check_mmspace();
+ break;
+ }
+ goto invalid;
+
+ case 'N':
+ if (l.isall("Notice %+s", &_amfm->_notice))
+ break;
+ goto invalid;
+
+ case 'S':
+ if (l.isall("StartAxis")) {
+ read_axis(axis++);
+ break;
+ }
+ if (l.isall("StartMaster")) {
+ read_master(master++);
+ break;
+ }
+ if (l.isall("StartPrimaryFonts %d", (int *)0)) {
+ read_primary_fonts();
+ break;
+ }
+ if (l.isall("StartConversionPrograms %d %d", (int *)0, (int *)0)) {
+ read_conversion_programs();
+ break;
+ }
+ if (l.isall("StartMasterFontMetrics %g", (double *)0))
+ break;
+ goto invalid;
+
+ case 'U':
+ if (l.isall("UnderlinePosition %g", &fd( fdUnderlinePosition )))
+ break;
+ else if (l.isall("UnderlineThickness %g", &fd( fdUnderlineThickness )))
+ break;
+ goto invalid;
+
+ case 'V':
+ if (l.isall("Version %+s", &_amfm->_version))
+ break;
+ goto invalid;
+
+ case 'W':
+ if (l.isall("Weight %+s", &_amfm->_weight))
+ break;
+ if (l.is("WeightVector")) {
+ Vector<double> wv;
+ if (!read_simple_array(wv) || !_mmspace)
+ lerror("bad WeightVector");
+ else
+ _mmspace->set_weight_vector(wv);
+ break;
+ }
+ goto invalid;
+
+ case 'X':
+ if (l.isall("XHeight %g", &fd( fdXHeight )))
+ break;
+ goto invalid;
+
+ default:
+ invalid:
+ no_match_warning();
+
+ }
+
+ done:
+ if (!_mmspace) {
+ _errh->error("`%s' is not an AMFM file", String(_l.landmark().file()).c_str());
+ return false;
+ }
+
+ LandmarkErrorHandler pin_errh(_errh, _l.landmark());
+ if (!_amfm->sanity(&pin_errh)) {
+ _errh->lerror(_l.landmark().whole_file(),
+ "bad AMFM file (missing or inconsistent information)");
+ return false;
+ }
+
+ if (!_mmspace->check_intermediate() && _l.filename().directory()) {
+ String name = l.filename().base() + ".amcp";
+ Slurper slurp(_l.filename().from_directory(name));
+ add_amcp_file(slurp, _amfm, _errh);
+ }
+
+ return true;
+}
+
+
+void
+AmfmReader::read_amcp_file()
+{
+ int lines_read = 0;
+
+ while (_l.next_line()) {
+ lines_read++;
+ switch (_l[0]) {
+
+ case 'C':
+ if (_l.is("Comment"))
+ break;
+ goto invalid;
+
+ case 'S':
+ if (_l.isall("StartConversionPrograms %d %d", (int *)0, (int *)0)) {
+ read_conversion_programs();
+ break;
+ }
+ goto invalid;
+
+ default:
+ invalid:
+ no_match_warning("AMCP file");
+
+ }
+ }
+
+ if (_mmspace && !_mmspace->ndv() && !_mmspace->cdv() && lines_read)
+ lwarning("no conversion programs in .amcp file");
+}
+
+
+bool
+AmfmReader::read_simple_array(Vector<double> &vec) const
+{
+ if (!_l.is("[")) return false;
+
+ vec.clear();
+ double d;
+ while (_l.is("%g", &d))
+ vec.push_back(d);
+
+ return _l.is("]");
+}
+
+
+void
+AmfmReader::read_positions() const
+{
+ if (nmasters() < 2 || naxes() < 1) return;
+ Vector<NumVector> positions;
+ if (!_l.is("[") || !_mmspace) goto error;
+
+ for (int i = 0; i < nmasters(); i++) {
+ positions.push_back(NumVector());
+ if (!read_simple_array(positions.back()))
+ goto error;
+ }
+
+ if (!_l.is("]")) goto error;
+ _mmspace->set_master_positions(positions);
+ return;
+
+ error:
+ lerror("bad BlendDesignPositions");
+}
+
+
+void
+AmfmReader::read_normalize() const
+{
+ if (naxes() < 1) return;
+ Vector<NumVector> normalize_in, normalize_out;
+ if (!_l.is("[") || !_mmspace) goto error;
+
+ for (int a = 0; a < naxes(); a++) {
+ if (!_l.is("[")) goto error;
+ normalize_in.push_back(NumVector());
+ normalize_out.push_back(NumVector());
+ double v1, v2;
+ while (_l.is("[-%g %g-]", &v1, &v2)) {
+ normalize_in[a].push_back(v1);
+ normalize_out[a].push_back(v2);
+ }
+ if (!_l.is("]")) goto error;
+ }
+
+ if (!_l.is("]")) goto error;
+ _mmspace->set_normalize(normalize_in, normalize_out);
+ return;
+
+ error:
+ lerror("bad BlendDesignPositions");
+}
+
+
+void
+AmfmReader::read_axis_types() const
+{
+ PermString s;
+ int ax = 0;
+ Vector<PermString> types;
+ if (naxes() < 1) return;
+ if (!_l.is("[") || !_mmspace) goto error;
+ _mmspace->check();
+
+ while (_l.is("/%/s", &s))
+ _mmspace->set_axis_type(ax++, s);
+
+ if (!_l.is("]")) goto error;
+ return;
+
+ error:
+ lerror("bad BlendAxisTypes");
+}
+
+
+void
+AmfmReader::read_axis(int ax) const
+{
+ bool ok = _mmspace && ax < naxes();
+ if (!ok)
+ lerror("bad axis number %d", ax);
+ else
+ _mmspace->check();
+
+ PermString s;
+ while (_l.next_line())
+ switch (_l[0]) {
+
+ case 'A':
+ if (_l.is("AxisType %+s", &s)) {
+ if (ok)
+ _mmspace->set_axis_type(ax, s);
+ break;
+ }
+ if (_l.is("AxisLabel %+s", &s)) {
+ if (ok)
+ _mmspace->set_axis_label(ax, s);
+ break;
+ }
+ goto invalid;
+
+ case 'C':
+ if (_l.is("Comment"))
+ break;
+ goto invalid;
+
+ case 'E':
+ if (_l.isall("EndAxis"))
+ goto endaxis;
+ goto invalid;
+
+ default:
+ invalid:
+ no_match_warning();
+
+ }
+
+ endaxis: ;
+}
+
+
+void
+AmfmReader::read_master(int m) const
+{
+ AmfmMaster *amfmm;
+ AmfmMaster dummy;
+ if (m >= nmasters()) {
+ lerror("too many masters");
+ amfmm = &dummy;
+ } else {
+ if (!_amfm->_masters)
+ _amfm->_masters = new AmfmMaster[ nmasters() ];
+ amfmm = &_amfm->_masters[m];
+ }
+
+ while (_l.next_line())
+ // Grok the whole line. Are we on a character metric data line?
+ switch (_l[0]) {
+
+ case 'C':
+ if (_l.is("Comment"))
+ break;
+ goto invalid;
+
+ case 'E':
+ if (_l.isall("EndMaster"))
+ goto endmaster;
+ goto invalid;
+
+ case 'F':
+ if (_l.isall("FontName %+s", &amfmm->font_name))
+ break;
+ if (_l.isall("FullName %+s", &amfmm->full_name))
+ break;
+ if (_l.isall("FamilyName %+s", &amfmm->family))
+ break;
+ goto invalid;
+
+ case 'V':
+ if (_l.isall("Version %+s", &amfmm->version))
+ break;
+ goto invalid;
+
+ case 'W':
+ if (_l.is("WeightVector")) {
+ if (!(read_simple_array(amfmm->weight_vector) &&
+ amfmm->weight_vector.size() == nmasters())) {
+ lerror("bad WeightVector");
+ amfmm->weight_vector.clear();
+ }
+ break;
+ }
+ goto invalid;
+
+ default:
+ invalid:
+ no_match_warning();
+
+ }
+
+ endmaster: ;
+}
+
+
+void
+AmfmReader::read_one_primary_font() const
+{
+ AmfmPrimaryFont *pf = new AmfmPrimaryFont;
+ pf->design_vector.resize(naxes());
+ pf->labels.resize(naxes());
+
+ while (_l.left()) {
+
+ if (_l.is("PC")) {
+ for (int a = 0; a < naxes(); a++)
+ if (!_l.is("%d", &pf->design_vector[a]))
+ goto error;
+ } else if (_l.is("PL")) {
+ for (int a = 0; a < naxes(); a++)
+ if (!_l.is("(-%/s-)", &pf->labels[a]))
+ goto error;
+ } else if (_l.is("PN %(", &pf->name))
+ ;
+ else
+ no_match_warning("primary font");
+
+ _l.is(";"); // get rid of any possible semicolon
+ }
+
+ pf->next = _amfm->_primary_fonts;
+ _amfm->_primary_fonts = pf;
+ return;
+
+ error:
+ delete pf;
+}
+
+
+void
+AmfmReader::read_primary_fonts() const
+{
+ while (_l.next_line())
+ switch (_l[0]) {
+
+ case 'C':
+ if (_l.is("Comment"))
+ break;
+ goto invalid;
+
+ case 'E':
+ if (_l.isall("EndPrimaryFonts"))
+ goto end_primary_fonts;
+ goto invalid;
+
+ case 'P':
+ if (_l[1] == 'C' && isspace(_l[2])) {
+ read_one_primary_font();
+ break;
+ }
+ goto invalid;
+
+ default:
+ invalid:
+ no_match_warning();
+
+ }
+
+ end_primary_fonts: ;
+}
+
+
+void
+AmfmReader::read_conversion_programs() const
+{
+ String ndv, cdv, s;
+
+ while (_l.next_line())
+ switch (_l[0]) {
+
+ case 'C':
+ if (_l.isall("CDV %<", &s)) {
+ cdv += s;
+ break;
+ }
+ goto invalid;
+
+ case 'E':
+ if (_l.isall("EndConversionPrograms"))
+ goto end_conversion_programs;
+ goto invalid;
+
+ case 'N':
+ if (_l.isall("NDV %<", &s)) {
+ ndv += s;
+ break;
+ }
+ goto invalid;
+
+ default:
+ invalid:
+ no_match_warning();
+ break;
+
+ }
+
+ end_conversion_programs:
+ if (_mmspace) {
+ _mmspace->set_ndv(Type1Charstring(ndv));
+ _mmspace->set_cdv(Type1Charstring(cdv));
+ }
+}
+
+}