summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-2.98/otftotfm/secondary.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.98/otftotfm/secondary.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-2.98/otftotfm/secondary.cc742
1 files changed, 742 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.98/otftotfm/secondary.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.98/otftotfm/secondary.cc
new file mode 100644
index 00000000000..a951bc7d304
--- /dev/null
+++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.98/otftotfm/secondary.cc
@@ -0,0 +1,742 @@
+/* secondary.{cc,hh} -- code for generating fake glyphs
+ *
+ * Copyright (c) 2003-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.
+ */
+
+#include <config.h>
+#include "secondary.hh"
+#include "metrics.hh"
+#include "automatic.hh"
+#include "otftotfm.hh"
+#include "util.hh"
+#include <efont/t1bounds.hh>
+#include <efont/t1font.hh>
+#include <efont/t1rw.hh>
+#include <efont/otfname.hh>
+#include <efont/otfos2.hh>
+#include <efont/otfpost.hh>
+#include <efont/t1csgen.hh>
+#include <efont/t1unparser.hh>
+#include <efont/ttfcs.hh>
+#include <lcdf/straccum.hh>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+#include <algorithm>
+
+enum {
+ U_EXCLAMDOWN = 0x00A1, // U+00A1 INVERTED EXCLAMATION MARK
+ U_DEGREE = 0x00B0, // U+00B0 DEGREE SIGN
+ U_QUESTIONDOWN = 0x00BF, // U+00BF INVERTED QUESTION MARK
+ U_IJ = 0x0132, // U+0132 LATIN CAPITAL LIGATURE IJ
+ U_ij = 0x0133, // U+0133 LATIN SMALL LIGATURE IJ
+ U_DOTLESSJ = 0x0237, // U+0237 LATIN SMALL LETTER DOTLESS J
+ U_RINGABOVE = 0x02DA, // U+02DA RING ABOVE
+ U_COMBININGRINGABOVE = 0x030A, // U+030A COMBINING RING ABOVE
+ U_CWM = 0x200C, // U+200C ZERO WIDTH NON-JOINER
+ U_ENDASH = 0x2013, // U+2013 EN DASH
+ U_PERTENTHOUSAND = 0x2031, // U+2031 PER TEN THOUSAND SIGN
+ U_INTERROBANG = 0x203D, // U+203D INTERROBANG
+ U_FRACTION = 0x2044, // U+2044 FRACTION SLASH
+ U_CENTIGRADE = 0x2103, // U+2103 DEGREE CELSIUS
+ U_ASTERISKMATH = 0x2217, // U+2217 ASTERISK OPERATOR
+ U_BARDBL = 0x2225, // U+2225 PARALLEL TO
+ U_VISIBLESPACE = 0x2423, // U+2423 OPEN BOX
+ U_DBLBRACKETLEFT = 0x27E6, // U+27E6 MATHEMATICAL LEFT WHITE SQUARE BRACKET
+ U_DBLBRACKETRIGHT = 0x27E7, // U+27E7 MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+ U_INTERROBANGDOWN = 0x2E18, // U+2E18 INVERTED INTERROBANG
+ U_EMPTYSLOT = 0xD801, // invalid Unicode (not handled by Secondary)
+ U_ALTSELECTOR = 0xD802, // invalid Unicode
+ U_CAPITALCWM = 0xD809, // invalid Unicode
+ U_ASCENDERCWM = 0xD80A, // invalid Unicode
+ U_TWELVEUDASH = 0xD80C, // invalid Unicode
+ U_RINGFITTED = 0xD80D, // invalid Unicode
+
+ // BEGIN BACKWARDS COMPATIBILITY -- newer texglyphlist.txt does not include
+ // these code points
+ U_SS = 0xD800, // invalid Unicode
+ U_SSSMALL = 0xD803, // invalid Unicode
+ U_FFSMALL = 0xD804, // invalid Unicode
+ U_FISMALL = 0xD805, // invalid Unicode
+ U_FLSMALL = 0xD806, // invalid Unicode
+ U_FFISMALL = 0xD807, // invalid Unicode
+ U_FFLSMALL = 0xD808, // invalid Unicode
+ // END BACKWARDS COMPATIBILITY
+
+ U_USE_KERNX = 0xD80E, // invalid Unicode, not in maps
+ U_VS1 = 0xFE00,
+ U_VS16 = 0xFE0F,
+ U_VS17 = 0xE0100,
+ U_VS256 = 0xE01FF,
+ U_DOTLESSJ_2 = 0xF6BE,
+ U_THREEQUARTERSEMDASH = 0xF6DE,
+ U_FSMALL = 0xF766,
+ U_ISMALL = 0xF769,
+ U_LSMALL = 0xF76C,
+ U_SSMALL = 0xF773,
+ U_MATHDOTLESSJ = 0x1D6A5 // U+1D6A5 MATHEMATICAL ITALIC SMALL DOTLESS J
+};
+
+
+FontInfo::FontInfo(const Efont::OpenType::Font *otf_, ErrorHandler *errh)
+ : otf(otf_), cmap(0), cff_file(0), cff(0), post(0), name(0), _nglyphs(-1),
+ _got_glyph_names(false), _ttb_program(0), _override_is_fixed_pitch(false),
+ _override_italic_angle(false)
+{
+ cmap = new Efont::OpenType::Cmap(otf->table("cmap"), errh);
+ assert(cmap->ok());
+
+ if (String cff_string = otf->table("CFF")) {
+ cff_file = new Efont::Cff(cff_string, errh);
+ if (!cff_file->ok())
+ return;
+ Efont::Cff::FontParent *fp = cff_file->font(PermString(), errh);
+ if (!fp || !fp->ok())
+ return;
+ if (!(cff = dynamic_cast<Efont::Cff::Font *>(fp))) {
+ errh->error("CID-keyed fonts not supported");
+ return;
+ }
+ _nglyphs = cff->nglyphs();
+ }
+
+ if (!cff) {
+ post = new Efont::OpenType::Post(otf->table("post"), errh);
+ // read number of glyphs from 'maxp' -- should probably be elsewhere
+ if (Efont::OpenType::Data maxp = otf->table("maxp"))
+ if (maxp.length() >= 6)
+ _nglyphs = maxp.u16(4);
+ if (_nglyphs < 0 && post->ok())
+ _nglyphs = post->nglyphs();
+ }
+
+ name = new Efont::OpenType::Name(otf->table("name"), errh);
+}
+
+FontInfo::~FontInfo()
+{
+ delete cmap;
+ delete cff_file;
+ delete cff;
+ delete post;
+ delete name;
+ delete _ttb_program;
+}
+
+bool
+FontInfo::ok() const
+{
+ if (cff)
+ return cmap->ok() && cff->ok();
+ else
+ return post && post->ok() && name && name->ok();
+}
+
+bool
+FontInfo::glyph_names(Vector<PermString> &glyph_names) const
+{
+ program()->glyph_names(glyph_names);
+ return true;
+}
+
+int
+FontInfo::glyphid(PermString name) const
+{
+ if (cff)
+ return cff->glyphid(name);
+ else {
+ if (!_got_glyph_names) {
+ glyph_names(_glyph_names);
+ _got_glyph_names = true;
+ }
+ PermString *found = std::find(_glyph_names.begin(), _glyph_names.end(), name);
+ if (found == _glyph_names.end())
+ return 0;
+ return found - _glyph_names.begin();
+ }
+}
+
+String
+FontInfo::family_name() const
+{
+ if (cff)
+ return cff->dict_string(Efont::Cff::oFamilyName);
+ else
+ return name->english_name(Efont::OpenType::Name::N_FAMILY);
+}
+
+String
+FontInfo::postscript_name() const
+{
+ if (cff)
+ return cff->font_name();
+ else
+ return name->english_name(Efont::OpenType::Name::N_POSTSCRIPT);
+}
+
+const Efont::CharstringProgram *
+FontInfo::program() const
+{
+ if (cff)
+ return cff;
+ else {
+ if (!_ttb_program)
+ _ttb_program = new Efont::TrueTypeBoundsCharstringProgram(otf);
+ return _ttb_program;
+ }
+}
+
+bool
+FontInfo::is_fixed_pitch() const
+{
+ if (_override_is_fixed_pitch)
+ return _is_fixed_pitch;
+ else if (cff) {
+ double d;
+ return (cff->dict_value(Efont::Cff::oIsFixedPitch, &d) && d);
+ } else
+ return post->is_fixed_pitch();
+}
+
+double
+FontInfo::italic_angle() const
+{
+ if (_override_italic_angle)
+ return _italic_angle;
+ else if (cff) {
+ double d;
+ (void) cff->dict_value(Efont::Cff::oItalicAngle, &d);
+ return d;
+ } else
+ return post->italic_angle();
+}
+
+
+/* */
+
+Secondary::~Secondary()
+{
+}
+
+bool
+Secondary::encode_uni(int code, PermString name,
+ const uint32_t *uni_begin, const uint32_t *uni_end,
+ Metrics &metrics, ErrorHandler *errh)
+{
+ if (uni_begin + 1 == uni_end)
+ return encode_uni(code, name, *uni_begin, metrics, errh);
+ else {
+ Vector<Setting> v;
+ int max_s = 0;
+ while (uni_begin != uni_end) {
+ Vector<Setting> subv;
+ int s = setting(*uni_begin, subv, metrics, errh);
+ if (s == 0)
+ return false;
+ if (subv.size() && v.size())
+ v.push_back(Setting(Setting::KERN));
+ for (Vector<Setting>::const_iterator it = subv.begin();
+ it != subv.end(); ++it)
+ v.push_back(*it);
+ max_s = (max_s > s ? max_s : s);
+ ++uni_begin;
+ }
+ metrics.encode_virtual(code, name, 0, v, max_s > 1);
+ return true;
+ }
+}
+
+bool
+Secondary::encode_uni(int code, PermString name, uint32_t uni, Metrics &metrics, ErrorHandler *errh)
+{
+ Vector<Setting> v;
+ if (int s = setting(uni, v, metrics, errh)) {
+ metrics.encode_virtual(code, name, uni, v, s > 1);
+ return true;
+ } else if (_next)
+ return _next->encode_uni(code, name, uni, metrics, errh);
+ else
+ return false;
+}
+
+T1Secondary::T1Secondary(const FontInfo &finfo, const String &font_name,
+ const String &otf_file_name)
+ : _finfo(finfo), _font_name(font_name), _otf_file_name(otf_file_name),
+ _units_per_em(finfo.units_per_em()),
+ _xheight(font_x_height(finfo, Transform())),
+ _spacewidth(_units_per_em)
+{
+ int bounds[4], width;
+ if (char_bounds(bounds, width, finfo, Transform(), ' '))
+ _spacewidth = width;
+}
+
+int
+Secondary::setting(uint32_t uni, Vector<Setting> &v, Metrics &metrics, ErrorHandler *errh)
+{
+ if (_next)
+ return _next->setting(uni, v, metrics, errh);
+ else
+ return 0;
+}
+
+bool
+T1Secondary::char_setting(Vector<Setting> &v, Metrics &metrics, int uni, ...)
+{
+ Vector<int> codes;
+
+ // collect codes
+ va_list val;
+ va_start(val, uni);
+
+ int kerntype = Setting::KERN;
+ if (uni == U_USE_KERNX) {
+ kerntype = Setting::KERNX;
+ uni = va_arg(val, int);
+ }
+
+ for (; uni; uni = va_arg(val, int)) {
+ int code = metrics.unicode_encoding(uni);
+ if (code < 0) {
+ Glyph glyph = _finfo.cmap->map_uni(uni);
+ if (glyph == 0 || (code = metrics.force_encoding(glyph)) < 0)
+ return false;
+ }
+ codes.push_back(code);
+ }
+ va_end(val);
+
+ // generate setting
+ for (int i = 0; i < codes.size(); i++) {
+ if (i)
+ v.push_back(Setting(kerntype));
+ v.push_back(Setting(Setting::SHOW, codes[i], metrics.base_glyph(codes[i])));
+ }
+ return true;
+}
+
+bool
+T1Secondary::encode_uni(int code, PermString name, uint32_t uni, Metrics &metrics, ErrorHandler *errh)
+{
+ if (uni == U_ALTSELECTOR
+ || (uni >= U_VS1 && uni <= U_VS16)
+ || (uni >= U_VS17 && uni <= U_VS256)) {
+ Vector<Setting> v;
+ setting(uni, v, metrics, errh);
+ int which = (uni == U_ALTSELECTOR ? 0 : (uni <= U_VS16 ? uni - U_VS1 + 1 : uni - U_VS17 + 17));
+ metrics.encode_virtual(code, (which ? permprintf("<vs%d>", which) : PermString("<altselector>")), uni, v, false);
+ metrics.add_altselector_code(code, which);
+ return true;
+ } else
+ return Secondary::encode_uni(code, name, uni, metrics, errh);
+}
+
+
+static String dotlessj_file_name;
+
+static String
+dotlessj_dvips_include(const String &, const FontInfo &, ErrorHandler *)
+{
+ return "<" + pathname_filename(dotlessj_file_name);
+}
+
+int
+T1Secondary::dotlessj_font(Metrics &metrics, ErrorHandler *errh, Glyph &dj_glyph)
+{
+ if (!_font_name || !_finfo.otf || !_finfo.cff)
+ return -1;
+
+ String dj_name;
+ bool install_metrics;
+ // XXX make sure dotlessj is for the main font?
+ if ((dj_name = installed_metrics_font_name(_font_name, "dotlessj")))
+ install_metrics = false;
+ else {
+ dj_name = suffix_font_name(_font_name, "--lcdfj");
+ install_metrics = true;
+ }
+
+ // is dotlessj already mapped?
+ for (int i = 0; i < metrics.n_mapped_fonts(); i++)
+ if (metrics.mapped_font_name(i) == dj_name)
+ return i;
+
+ if (String filename = installed_type1_dotlessj(_otf_file_name, _finfo.cff->font_name(), (output_flags & G_DOTLESSJ), errh)) {
+
+ // check for special case: "\0" means the font's "j" is already
+ // dotless
+ if (filename == String("\0", 1))
+ return J_NODOT;
+
+ // open dotless-j font file
+ FILE *f = fopen(filename.c_str(), "rb");
+ if (!f) {
+ errh->error("%s: %s", filename.c_str(), strerror(errno));
+ return -1;
+ }
+
+ // read font
+ Efont::Type1Reader *reader;
+ int c = getc(f);
+ ungetc(c, f);
+ if (c == 128)
+ reader = new Efont::Type1PFBReader(f);
+ else
+ reader = new Efont::Type1PFAReader(f);
+ Efont::Type1Font *font = new Efont::Type1Font(*reader);
+ delete reader;
+
+ if (!font->ok()) {
+ errh->error("%s: no glyphs in dotless-J font", filename.c_str());
+ delete font;
+ return -1;
+ }
+
+ // find dotless-J character
+ Vector<PermString> glyph_names;
+ font->glyph_names(glyph_names);
+ Vector<PermString>::iterator g = std::find(glyph_names.begin(), glyph_names.end(), "uni0237");
+ if (g == glyph_names.end()) {
+ errh->error("%s: dotless-J font has no %<uni0237%> glyph", filename.c_str());
+ delete font;
+ return -1;
+ }
+ dj_glyph = g - glyph_names.begin();
+
+ // create metrics for dotless-J
+ if (install_metrics) {
+ Metrics dj_metrics(font, 256);
+ dj_metrics.encode('j', U_DOTLESSJ, dj_glyph);
+ ::dotlessj_file_name = filename;
+ output_metrics(dj_metrics, font->font_name(), -1, _finfo, String(), String(), dj_name, dotlessj_dvips_include, errh);
+ } else if (verbose)
+ errh->message("using %<%s%> for dotless-J font metrics", dj_name.c_str());
+
+ // add font to metrics
+ return metrics.add_mapped_font(font, dj_name);
+
+ } else
+ return -1;
+}
+
+int
+T1Secondary::setting(uint32_t uni, Vector<Setting> &v, Metrics &metrics, ErrorHandler *errh)
+{
+ Transform xform;
+ int vsize = v.size();
+ extern int letterspace;
+
+ if (char_setting(v, metrics, uni, 0))
+ return 1;
+
+ switch (uni) {
+
+ case U_CWM:
+ case U_ALTSELECTOR:
+ v.push_back(Setting(Setting::RULE, 0, _xheight));
+ return 1;
+
+ case U_CAPITALCWM:
+ v.push_back(Setting(Setting::RULE, 0, font_cap_height(_finfo, xform)));
+ return 1;
+
+ case U_ASCENDERCWM:
+ v.push_back(Setting(Setting::RULE, 0, font_ascender(_finfo, xform)));
+ return 1;
+
+ case U_VISIBLESPACE:
+ v.push_back(Setting(Setting::MOVE, 50, -150));
+ v.push_back(Setting(Setting::RULE, 40, 150));
+ v.push_back(Setting(Setting::RULE, _spacewidth, 40));
+ v.push_back(Setting(Setting::RULE, 40, 150));
+ v.push_back(Setting(Setting::MOVE, 50, 150));
+ return 2;
+
+ case U_SS:
+ if (char_setting(v, metrics, 'S', 'S', 0))
+ return 1;
+ break;
+
+ case U_SSSMALL:
+ if (char_setting(v, metrics, U_SSMALL, U_SSMALL, 0))
+ return 1;
+ else if (char_setting(v, metrics, 's', 's', 0))
+ return 1;
+ break;
+
+ case U_FFSMALL:
+ if (char_setting(v, metrics, U_FSMALL, U_FSMALL, 0))
+ return 1;
+ else if (char_setting(v, metrics, 'f', 'f', 0))
+ return 1;
+ break;
+
+ case U_FISMALL:
+ if (char_setting(v, metrics, U_FSMALL, U_ISMALL, 0))
+ return 1;
+ else if (char_setting(v, metrics, 'f', 'i', 0))
+ return 1;
+ break;
+
+ case U_FLSMALL:
+ if (char_setting(v, metrics, U_FSMALL, U_LSMALL, 0))
+ return 1;
+ else if (char_setting(v, metrics, 'f', 'l', 0))
+ return 1;
+ break;
+
+ case U_FFISMALL:
+ if (char_setting(v, metrics, U_FSMALL, U_FSMALL, U_ISMALL, 0))
+ return 1;
+ else if (char_setting(v, metrics, 'f', 'f', 'i', 0))
+ return 1;
+ break;
+
+ case U_FFLSMALL:
+ if (char_setting(v, metrics, U_FSMALL, U_FSMALL, U_LSMALL, 0))
+ return 1;
+ else if (char_setting(v, metrics, 'f', 'f', 'l', 0))
+ return 1;
+ break;
+
+ case U_IJ:
+ if (char_setting(v, metrics, 'I', 'J', 0))
+ return 1;
+ break;
+
+ case U_ij:
+ if (char_setting(v, metrics, 'i', 'j', 0))
+ return 1;
+ break;
+
+ case U_DOTLESSJ:
+ case U_DOTLESSJ_2:
+ case U_MATHDOTLESSJ: {
+ Glyph dj_glyph;
+ int which = dotlessj_font(metrics, errh, dj_glyph);
+ if (which >= 0) {
+ v.push_back(Setting(Setting::FONT, which));
+ v.push_back(Setting(Setting::SHOW, 'j', dj_glyph));
+ return 2;
+ } else if (which == J_NODOT && char_setting(v, metrics, 'j', 0))
+ return 1;
+ break;
+ }
+
+ case U_DBLBRACKETLEFT:
+ if (char_setting(v, metrics, '[', 0)) {
+ if (!_finfo.is_fixed_pitch()) {
+ double d = char_one_bound(_finfo, xform, 4, true, 0, '[', 0);
+ v.push_back(Setting(Setting::MOVE, (int) (-0.666 * d - letterspace), 0));
+ }
+ char_setting(v, metrics, '[', 0);
+ return 1;
+ }
+ break;
+
+ case U_DBLBRACKETRIGHT:
+ if (char_setting(v, metrics, ']', 0)) {
+ if (!_finfo.is_fixed_pitch()) {
+ double d = char_one_bound(_finfo, xform, 4, true, 0, ']', 0);
+ v.push_back(Setting(Setting::MOVE, (int) (-0.666 * d - letterspace), 0));
+ }
+ char_setting(v, metrics, ']', 0);
+ return 1;
+ }
+ break;
+
+ case U_BARDBL:
+ if (char_setting(v, metrics, '|', 0)) {
+ if (!_finfo.is_fixed_pitch()) {
+ double d = char_one_bound(_finfo, Transform(), 4, true, 0, '|', 0);
+ v.push_back(Setting(Setting::MOVE, (int) (-0.333 * d - letterspace), 0));
+ }
+ char_setting(v, metrics, '|', 0);
+ return 1;
+ }
+ break;
+
+ case U_ASTERISKMATH: {
+ int bounds[5];
+ double dropdown = 0;
+ if (char_bounds(bounds, bounds[4], _finfo, xform, '*'))
+ dropdown += std::max(bounds[3], 0) + std::min(bounds[1], 0);
+ if (char_bounds(bounds, bounds[4], _finfo, xform, '('))
+ dropdown -= std::max(bounds[3], 0) + std::min(bounds[1], 0);
+ v.push_back(Setting(Setting::MOVE, 0, (int) (-dropdown / 2)));
+ if (char_setting(v, metrics, '*', 0)) {
+ v.push_back(Setting(Setting::MOVE, 0, -(int) (-dropdown / 2)));
+ return 1;
+ }
+ break;
+ }
+
+ case U_TWELVEUDASH:
+ if (char_setting(v, metrics, U_ENDASH, 0)) {
+ if (!_finfo.is_fixed_pitch()) {
+ double d = char_one_bound(_finfo, xform, 4, true, 0, U_ENDASH, 0);
+ v.push_back(Setting(Setting::MOVE, (int) (667 - 2 * d - letterspace), 0));
+ }
+ char_setting(v, metrics, U_ENDASH, 0);
+ return 1;
+ }
+ break;
+
+ case U_THREEQUARTERSEMDASH:
+ if (char_setting(v, metrics, U_ENDASH, 0)) {
+ if (!_finfo.is_fixed_pitch()) {
+ double d = char_one_bound(_finfo, xform, 4, true, 0, U_ENDASH, 0);
+ v.push_back(Setting(Setting::MOVE, (int) (750 - 2 * d - letterspace), 0));
+ }
+ char_setting(v, metrics, U_ENDASH, 0);
+ return 1;
+ }
+ break;
+
+ case U_CENTIGRADE:
+ // TODO: set italic correction to that of a 'C'
+ if (char_setting(v, metrics, U_USE_KERNX, U_DEGREE, 'C', 0))
+ return 1;
+ break;
+
+ case U_INTERROBANG: {
+ double exclam_offset =
+ (char_one_bound(_finfo, xform, 4, true, 0, '?', 0)
+ - char_one_bound(_finfo, xform, 4, true, 0, '!', 0)) * 0.5 + 50;
+ v.push_back(Setting(Setting::PUSH));
+ v.push_back(Setting(Setting::MOVE, (int) exclam_offset, 0));
+ if (char_setting(v, metrics, '!', 0)) {
+ v.push_back(Setting(Setting::POP));
+ if (char_setting(v, metrics, '?', 0))
+ return 1;
+ }
+ break;
+ }
+
+ case U_INTERROBANGDOWN: {
+ double exclam_offset =
+ (char_one_bound(_finfo, xform, 4, true, 0, U_QUESTIONDOWN, 0)
+ - char_one_bound(_finfo, xform, 4, true, 0, U_EXCLAMDOWN, 0)) * 0.5 + 50;
+ v.push_back(Setting(Setting::PUSH));
+ v.push_back(Setting(Setting::MOVE, (int) exclam_offset, 0));
+ if (char_setting(v, metrics, U_EXCLAMDOWN, 0)) {
+ v.push_back(Setting(Setting::POP));
+ if (char_setting(v, metrics, U_QUESTIONDOWN, 0))
+ return 1;
+ }
+ break;
+ }
+
+ case U_PERTENTHOUSAND:
+ if (char_setting(v, metrics, U_USE_KERNX, 0xF661, U_FRACTION, 0xF655, 0xF655, 0xF655, 0))
+ return 1;
+ break;
+
+ case U_RINGFITTED: {
+ int A_width = char_one_bound(_finfo, xform, 4, true, -_units_per_em, 'A', 0);
+ uint32_t ring_char = U_RINGABOVE;
+ int ring_width = char_one_bound(_finfo, xform, 4, true, -_units_per_em, ring_char, 0);
+ if (ring_width <= -_units_per_em) {
+ ring_char = U_COMBININGRINGABOVE;
+ ring_width = char_one_bound(_finfo, xform, 4, true, -_units_per_em, ring_char, 0);
+ }
+ if (A_width > -_units_per_em && ring_width > -_units_per_em) {
+ int offset = (A_width - ring_width) / 2;
+ v.push_back(Setting(Setting::MOVE, offset, 0));
+ if (char_setting(v, metrics, ring_char, 0)) {
+ v.push_back(Setting(Setting::MOVE, A_width - ring_width - offset, 0));
+ return 1;
+ }
+ }
+ break;
+ }
+
+ }
+
+ // didn't find a good setting, restore v to pristine state
+ while (v.size() > vsize)
+ v.pop_back();
+
+ // variant selectors get the same setting as ALTSELECTOR
+ if ((uni >= U_VS1 && uni <= U_VS16) || (uni >= U_VS17 && uni <= U_VS256))
+ return setting(U_ALTSELECTOR, v, metrics, errh);
+
+ // otherwise, try other secondaries
+ return Secondary::setting(uni, v, metrics, errh);
+}
+
+
+bool
+char_bounds(int bounds[4], int &width, const FontInfo &finfo,
+ const Transform &transform, uint32_t uni)
+{
+ if (Efont::OpenType::Glyph g = finfo.cmap->map_uni(uni))
+ return Efont::CharstringBounds::bounds(transform, finfo.program()->glyph_context(g), bounds, width);
+ else
+ return false;
+}
+
+int
+char_one_bound(const FontInfo &finfo, const Transform &transform,
+ int dimen, bool max, int best, uint32_t uni, ...)
+{
+ int bounds[5];
+ va_list val;
+ va_start(val, uni);
+ while (uni != 0) {
+ if (char_bounds(bounds, bounds[4], finfo, transform, uni))
+ if (max ? bounds[dimen] > best : bounds[dimen] < best)
+ best = bounds[dimen];
+ uni = va_arg(val, uint32_t);
+ }
+ va_end(val);
+ return best;
+}
+
+int
+font_x_height(const FontInfo &finfo, const Transform &font_xform)
+{
+ try {
+ Efont::OpenType::Os2 os2(finfo.otf->table("OS/2"));
+ return os2.x_height();
+ } catch (Efont::OpenType::Bounds) {
+ // XXX what if 'x', 'm', 'z' were subject to substitution?
+ return char_one_bound(finfo, font_xform, 3, false, finfo.units_per_em(),
+ 'x', 'm', 'z', 0);
+ }
+}
+
+int
+font_cap_height(const FontInfo &finfo, const Transform &font_xform)
+{
+ try {
+ Efont::OpenType::Os2 os2(finfo.otf->table("OS/2"));
+ return os2.cap_height();
+ } catch (Efont::OpenType::Bounds) {
+ // XXX what if 'H', 'O', 'B' were subject to substitution?
+ return char_one_bound(finfo, font_xform, 3, false, finfo.units_per_em(),
+ 'H', 'O', 'B', 0);
+ }
+}
+
+int
+font_ascender(const FontInfo &finfo, const Transform &font_xform)
+{
+ try {
+ Efont::OpenType::Os2 os2(finfo.otf->table("OS/2"));
+ return os2.typo_ascender();
+ } catch (Efont::OpenType::Bounds) {
+ // XXX what if 'd', 'l' were subject to substitution?
+ return char_one_bound(finfo, font_xform, 3, true,
+ font_x_height(finfo, font_xform),
+ 'd', 'l', 0);
+ }
+}