diff options
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/afm.cc')
-rw-r--r-- | Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/afm.cc | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/afm.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/afm.cc new file mode 100644 index 00000000000..76b07fbcfbd --- /dev/null +++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/afm.cc @@ -0,0 +1,586 @@ +// -*- related-file-name: "../include/efont/afm.hh" -*- + +/* afm.{cc,hh} -- Adobe Font Metrics files + * + * 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/afm.hh> +#include <efont/afmparse.hh> +#include <efont/t1cs.hh> /* for UNKDOUBLE */ +#include <lcdf/error.hh> +#include <ctype.h> +#include <assert.h> +namespace Efont { + +AfmReader::AfmReader(AfmParser &parser, Metrics *afm, AfmMetricsXt *afm_xt, + ErrorHandler *errh) + : _afm(afm), _afm_xt(afm_xt), _l(parser), + _composite_warned(false), _metrics_sets_warned(false), _y_width_warned(0) +{ + _errh = errh ? errh : ErrorHandler::silent_handler(); +} + +Metrics * +AfmReader::read(Slurper &slurp, ErrorHandler *errh) +{ + AfmParser p(slurp); + if (!p.ok()) + return 0; + + Metrics *afm = new Metrics; + AfmMetricsXt *afm_xt = new AfmMetricsXt; + afm->add_xt(afm_xt); + AfmReader reader(p, afm, afm_xt, errh); + + if (!reader.read()) { + delete afm; + return 0; + } else + return afm; +} + +Metrics * +AfmReader::read(const Filename &fn, ErrorHandler *errh) +{ + Slurper slurpy(fn); + return read(slurpy, errh); +} + + +void +AfmReader::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 +AfmReader::lerror(const char *format, ...) const +{ + va_list val; + va_start(val, format); + _errh->vxmessage(_l.landmark(), ErrorHandler::e_error, format, val); + va_end(val); +} + +GlyphIndex +AfmReader::find_err(PermString name, const char *) const +{ + GlyphIndex gi = _afm->find(name); + if (gi < 0) + lerror("character `%s' doesn't exist", name.c_str()); + return gi; +} + +void +AfmReader::composite_warning() const +{ + if (!_composite_warned) + lwarning("composite fonts not supported"); + _composite_warned = 1; +} + +void +AfmReader::metrics_sets_warning() const +{ + if (!_metrics_sets_warned) + lwarning("only metrics set 0 is supported"); + _metrics_sets_warned = 1; +} + +void +AfmReader::y_width_warning() const +{ + if (_y_width_warned < 40) { + lwarning("character has a nonzero Y width"); + _y_width_warned++; + if (_y_width_warned == 40) + lwarning("I won't warn you again."); + } +} + +void +AfmReader::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(); +} + + +bool +AfmReader::read() +{ + AfmParser &l = _l; + assert(_afm && _afm_xt); + + // 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)) + _afm_xt->opening_comments.push_back(comment); + else if (l.isall("StartFontMetrics %g", (double *)0)) + ; + else { + l.save_line(); + break; + } + } + + _afm->set_scale(1000); + unsigned invalid_lines = 0; + PermString s; + bool isbasefont; + int metrics_sets; + int direction; + + while (l.next_line()) + switch (l.first()) { + + case 'A': + if (l.isall("Ascender %g", &fd( fdAscender ))) + break; + goto invalid; + + case 'C': + if (l.isall("Characters %d", (int *)0)) + break; + if (l.isall("CapHeight %g", &fd( fdCapHeight ))) + break; + if (l.isall("CharacterSet %+s", (PermString *) 0)) + break; + if (l.isall("CharWidth %g %g", (double *)0, (double *)0)) + break; + if (l.isall("Comment %+s", (PermString *) 0)) + break; + goto invalid; + + case 'D': + if (l.isall("Descender %g", &fd( fdDescender ))) + break; + goto invalid; + + case 'E': + if (l.isall("EncodingScheme %+s", &_afm_xt->encoding_scheme)) + break; + if (l.isall("EndDirection")) + break; + if (l.isall("EndFontMetrics")) + goto done; + if (l.isall("EscChar %d", (int *)0)) { + composite_warning(); + break; + } + goto invalid; + + case 'F': + if (l.isall("FontName %+s", &s)) { + _afm->set_font_name(s); + break; + } + if (l.isall("FullName %+s", &s)) { + _afm->set_full_name(s); + break; + } + if (l.isall("FamilyName %+s", &s)) { + _afm->set_family(s); + 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("ItalicAngle %g", &fd( fdItalicAngle ))) + break; + if (l.isall("IsBaseFont %b", &isbasefont)) { + if (isbasefont == 0) + composite_warning(); + break; + } + if (l.isall("IsFixedV %b", (bool *)0)) { + metrics_sets_warning(); + break; + } + if (l.isall("IsFixedPitch %b", (bool *)0)) + break; + goto invalid; + + case 'M': + if (l.isall("MappingScheme %d", (int *)0)) { + composite_warning(); + break; + } + if (l.isall("MetricsSets %d", &metrics_sets)) { + if (metrics_sets != 0) + metrics_sets_warning(); + break; + } + goto invalid; + + case 'N': + if (l.isall("Notice %+s", &_afm_xt->notice)) + break; + goto invalid; + + case 'S': + if (l.isall("StartDirection %d", &direction)) { + if (direction != 0) + metrics_sets_warning(); + break; + } + if (l.isall("StartCharMetrics %d", (int *)0)) { + read_char_metrics(); + break; + } + if (l.isall("StartKernData")) { + read_kerns(); + break; + } + if (l.isall("StartComposites %d", (int *)0)) { + read_composites(); + break; + } + if (l.isall("StdHW %g", &fd( fdStdHW ))) + break; + if (l.isall("StdVW %g", &fd( fdStdVW ))) + break; + if (l.isall("StartFontMetrics %g", (double *)0)) + break; + goto invalid; + + case 'U': + if (l.isall("UnderlinePosition %g", &fd( fdUnderlinePosition ))) + break; + if (l.isall("UnderlineThickness %g", &fd( fdUnderlineThickness ))) + break; + goto invalid; + + case 'V': + if (l.isall("Version %+s", &s)) { + _afm->set_version(s); + break; + } + if (l.isall("VVector %g %g", (double *)0, (double *)0)) { + metrics_sets_warning(); + break; + } + goto invalid; + + case 'W': + if (l.isall("Weight %+s", &s)) { + _afm->set_weight(s); + break; + } + goto invalid; + + case 'X': + if (l.isall("XHeight %g", &fd( fdXHeight ))) + break; + goto invalid; + + default: + invalid: + invalid_lines++; + no_match_warning(); + + } + + done: + if (invalid_lines >= l.lineno() - 10) + return false; + else + return true; +} + + +static Vector<PermString> ligature_left; +static Vector<PermString> ligature_right; +static Vector<PermString> ligature_result; + +void +AfmReader::read_char_metric_data() const +{ + int c = -1; + double wx = UNKDOUBLE; + double bllx = UNKDOUBLE, blly = 0, burx = 0, bury = 0; + PermString n; + PermString ligright, ligresult; + + AfmParser &l = _l; + + l.is("C %d ; WX %g ; N %/s ; B %g %g %g %g ;", + &c, &wx, &n, &bllx, &blly, &burx, &bury); + + while (l.left()) { + + switch (l.first()) { + + case 'B': + if (l.is("B %g %g %g %g", &bllx, &blly, &burx, &bury)) + break; + goto invalid; + + case 'C': + if (l.is("C %d", &c)) + break; + if (l.is("CH <%x>", &c)) + break; + goto invalid; + + case 'E': + if (l.isall("EndCharMetrics")) + return; + goto invalid; + + case 'L': + if (l.is("L %/s %/s", &ligright, &ligresult)) { + if (!n) + lerror("ligature given, but character has no name"); + else { + ligature_left.push_back(n); + ligature_right.push_back(ligright); + ligature_result.push_back(ligresult); + } + break; + } + goto invalid; + + case 'N': + if (l.is("N %/s", &n)) + break; + goto invalid; + + case 'W': + if (l.is("WX %g", &wx) || + l.is("W0X %g", &wx)) + break; + if (l.is("W %g %g", &wx, (double *)0) || + l.is("W0 %g %g", &wx, (double *)0) || + l.is("W0Y %g", (double *)0)) { + y_width_warning(); + break; + } + if (l.is("W1X %g", (double *)0) || + l.is("W1Y %g", (double *)0) || + l.is("W1 %g %g", (double *)0, (double *)0)) { + metrics_sets_warning(); + break; + } + goto invalid; + + default: + invalid: + // always warn about unknown directives here! + no_match_warning("character metrics"); + l.skip_until(';'); + break; + + } + + l.is(";"); // get rid of any possible semicolon + } + + // create the character + if (!n) + lwarning("character without a name ignored"); + else { + if (_afm->find(n) != -1) + lwarning("character %s defined twice", n.c_str()); + + GlyphIndex gi = _afm->add_glyph(n); + + _afm->wd(gi) = wx; + _afm->lf(gi) = bllx; + _afm->rt(gi) = burx; + _afm->tp(gi) = bury; + _afm->bt(gi) = blly; + + if (c != -1) + _afm->set_code(gi, c); + } +} + + +void +AfmReader::read_char_metrics() const +{ + assert(!ligature_left.size()); + + while (_l.next_line()) + // Grok the whole line. Are we on a character metric data line? + switch (_l.first()) { + + case 'C': + if (isspace(_l[1]) || (_l[1] == 'H' && isspace(_l[2]))) { + read_char_metric_data(); + break; + } + if (_l.is("Comment")) + break; + goto invalid; + + case 'E': + if (_l.isall("EndCharMetrics")) + goto end_char_metrics; + goto invalid; + + default: + invalid: + no_match_warning(); + + } + + end_char_metrics: + + for (int i = 0; i < ligature_left.size(); i++) { + GlyphIndex leftgi = find_err(ligature_left[i], "ligature"); + GlyphIndex rightgi = find_err(ligature_right[i], "ligature"); + GlyphIndex resultgi = find_err(ligature_result[i], "ligature"); + if (leftgi >= 0 && rightgi >= 0 && resultgi >= 0) + if (_afm->add_lig(leftgi, rightgi, resultgi)) + lwarning("duplicate ligature; first ignored"); + } + ligature_left.clear(); + ligature_right.clear(); + ligature_result.clear(); +} + + +void +AfmReader::read_kerns() const +{ + double kx; + PermString left, right; + GlyphIndex leftgi, rightgi; + + AfmParser &l = _l; + // AFM files have reversed pair programs when read. + _afm->pair_program()->set_reversed(true); + + while (l.next_line()) + switch (l.first()) { + + case 'C': + if (l.is("Comment")) + break; + goto invalid; + + case 'E': + if (l.isall("EndKernPairs")) + break; + if (l.isall("EndKernData")) + return; + if (l.isall("EndTrackKern")) + break; + goto invalid; + + case 'K': + if (l.isall("KPX %/s %/s %g", &left, &right, &kx)) { + goto validkern; + } + if (l.isall("KP %/s %/s %g %g", &left, &right, &kx, (double *)0)) { + y_width_warning(); + goto validkern; + } + if (l.isall("KPY %/s %/s %g", &left, &right, (double *)0)) { + y_width_warning(); + break; + } + if (l.isall("KPH <%x> <%x> %g %g", (int *)0, (int *)0, + (double *)0, (double *)0)) { + lwarning("KPH not supported"); + break; + } + goto invalid; + + validkern: + leftgi = find_err(left, "kern"); + rightgi = find_err(right, "kern"); + if (leftgi >= 0 && rightgi >= 0) + // A kern with 0 amount is NOT useless! + // (Because of multiple masters.) + if (_afm->add_kern(leftgi, rightgi, _afm->add_kv(kx))) + lwarning("duplicate kern; first pair ignored"); + break; + + case 'S': + if (l.isall("StartKernPairs %d", (int *)0) || + l.isall("StartKernPairs0 %d", (int *)0)) + break; + if (l.isall("StartKernPairs1 %d", (int *)0)) { + metrics_sets_warning(); + break; + } + if (l.isall("StartTrackKern %d", (int *)0)) + break; + goto invalid; + + case 'T': + if (l.isall("TrackKern %g %g %g %g %g", (double *)0, (double *)0, + (double *)0, (double *)0, (double *)0)) + break; // FIXME: implement TrackKern + goto invalid; + + default: + invalid: + no_match_warning(); + break; + + } +} + + +void +AfmReader::read_composites() const +{ + while (_l.next_line()) + switch (_l.first()) { + + case 'C': + if (_l.is("Comment")) + break; + if (_l.is("CC")) + break; + goto invalid; + + case 'E': + if (_l.isall("EndComposites")) + return; + goto invalid; + + default: + invalid: + no_match_warning(); + break; + + } +} + +} |