summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-src/otftotfm/otftotfm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-src/otftotfm/otftotfm.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-src/otftotfm/otftotfm.cc2449
1 files changed, 0 insertions, 2449 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-src/otftotfm/otftotfm.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-src/otftotfm/otftotfm.cc
deleted file mode 100644
index 913e4cb68e4..00000000000
--- a/Build/source/texk/lcdf-typetools/lcdf-typetools-src/otftotfm/otftotfm.cc
+++ /dev/null
@@ -1,2449 +0,0 @@
-/* otftotfm.cc -- driver for translating OpenType fonts to TeX metrics
- *
- * Copyright (c) 2003-2019 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
-#ifdef WIN32
-# define _USE_MATH_DEFINES
-#endif
-#include <efont/psres.hh>
-#include <efont/t1rw.hh>
-#include <efont/t1font.hh>
-#include <efont/t1item.hh>
-#include <efont/t1bounds.hh>
-#include <efont/otfcmap.hh>
-#include <efont/otfname.hh>
-#include <efont/otfgsub.hh>
-#include <efont/ttfkern.hh>
-#include "glyphfilter.hh"
-#include "metrics.hh"
-#include "dvipsencoding.hh"
-#include "automatic.hh"
-#include "secondary.hh"
-#include "kpseinterface.h"
-#include "util.hh"
-#include "otftotfm.hh"
-#include <lcdf/md5.h>
-#include <lcdf/clp.h>
-#include <lcdf/error.hh>
-#include <lcdf/hashmap.hh>
-#include <lcdf/straccum.hh>
-#include <efont/cff.hh>
-#include <efont/otf.hh>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <errno.h>
-#include <signal.h>
-#include <algorithm>
-#include <math.h>
-/* M_PI isn't defined in C99 */
-#ifndef M_PI
-# define M_PI 3.14159265358979323846264338327
-#endif
-#ifdef HAVE_CTIME
-# include <time.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#ifdef HAVE_FCNTL_H
-# include <fcntl.h>
-#endif
-#ifdef _MSC_VER
-# include <io.h>
-#endif
-
-using namespace Efont;
-
-#define VERSION_OPT 301
-#define HELP_OPT 302
-#define QUERY_SCRIPTS_OPT 303
-#define QUERY_FEATURES_OPT 304
-#define KPATHSEA_DEBUG_OPT 305
-
-#define SCRIPT_OPT 311
-#define FEATURE_OPT 312
-#define ENCODING_OPT 313
-#define LITERAL_ENCODING_OPT 314
-#define EXTEND_OPT 315
-#define SLANT_OPT 316
-#define LETTERSPACE_OPT 317
-#define LIGKERN_OPT 318
-#define CODINGSCHEME_OPT 319
-#define UNICODING_OPT 320
-#define BOUNDARY_CHAR_OPT 321
-#define DESIGN_SIZE_OPT 322
-#define MINIMUM_KERN_OPT 323
-#define ALTSELECTOR_CHAR_OPT 324
-#define INCLUDE_ALTERNATES_OPT 325
-#define EXCLUDE_ALTERNATES_OPT 326
-#define CLEAR_ALTERNATES_OPT 327
-#define ALTSELECTOR_FEATURE_OPT 328
-#define DEFAULT_LIGKERN_OPT 329
-#define NO_ECOMMAND_OPT 330
-#define LETTER_FEATURE_OPT 331
-#define INCLUDE_SUBS_OPT 332
-#define EXCLUDE_SUBS_OPT 333
-#define CLEAR_SUBS_OPT 334
-#define SUBS_FILTER_OPT 335
-#define ALTERNATES_FILTER_OPT 336
-#define SPACE_FACTOR_OPT 337
-#define MATH_SPACING_OPT 338
-#define POSITION_OPT 339
-#define WARN_MISSING_OPT 340
-#define BASE_ENCODINGS_OPT 341
-#define FIXED_PITCH_OPT 342
-#define ITALIC_ANGLE_OPT 343
-#define PROPORTIONAL_WIDTH_OPT 344
-#define X_HEIGHT_OPT 345
-
-#define AUTOMATIC_OPT 350
-#define FONT_NAME_OPT 351
-#define QUIET_OPT 352
-#define GLYPHLIST_OPT 353
-#define VENDOR_OPT 354
-#define TYPEFACE_OPT 355
-#define NOCREATE_OPT 356
-#define VERBOSE_OPT 357
-#define FORCE_OPT 358
-
-#define VIRTUAL_OPT 360
-#define PL_OPT 361
-#define TFM_OPT 362
-#define MAP_FILE_OPT 363
-#define OUTPUT_ENCODING_OPT 364
-
-#define DIR_OPTS 380
-#define ENCODING_DIR_OPT (DIR_OPTS + O_ENCODING)
-#define TFM_DIR_OPT (DIR_OPTS + O_TFM)
-#define PL_DIR_OPT (DIR_OPTS + O_PL)
-#define VF_DIR_OPT (DIR_OPTS + O_VF)
-#define VPL_DIR_OPT (DIR_OPTS + O_VPL)
-#define TYPE1_DIR_OPT (DIR_OPTS + O_TYPE1)
-#define TYPE42_DIR_OPT (DIR_OPTS + O_TYPE42)
-#define TRUETYPE_DIR_OPT (DIR_OPTS + O_TRUETYPE)
-#define DIR_OPT (DIR_OPTS + NUMODIR)
-
-#define NO_OUTPUT_OPTS 400
-#define NO_ENCODING_OPT (NO_OUTPUT_OPTS + G_ENCODING)
-#define NO_TYPE1_OPT (NO_OUTPUT_OPTS + G_TYPE1)
-#define NO_DOTLESSJ_OPT (NO_OUTPUT_OPTS + G_DOTLESSJ)
-#define NO_UPDMAP_OPT (NO_OUTPUT_OPTS + G_UPDMAP)
-#define UPDMAP_SYS_OPT (NO_OUTPUT_OPTS + G_UPDMAP_USER)
-
-#define YES_OUTPUT_OPTS 2000
-#define TRUETYPE_OPT (YES_OUTPUT_OPTS + G_TRUETYPE)
-#define TYPE42_OPT (YES_OUTPUT_OPTS + G_TYPE42)
-#define UPDMAP_USER_OPT (YES_OUTPUT_OPTS + G_UPDMAP_USER)
-
-#define CHAR_OPTTYPE (Clp_ValFirstUser)
-
-static Clp_Option options[] = {
-
- { "script", 's', SCRIPT_OPT, Clp_ValString, 0 },
- { "feature", 'f', FEATURE_OPT, Clp_ValString, 0 },
- { "letter-feature", 0, LETTER_FEATURE_OPT, Clp_ValString, 0 },
- { "lf", 0, LETTER_FEATURE_OPT, Clp_ValString, 0 },
- { "include-substitutions", 0, INCLUDE_SUBS_OPT, Clp_ValString, 0 },
- { "exclude-substitutions", 0, EXCLUDE_SUBS_OPT, Clp_ValString, 0 },
- { "clear-substitutions", 0, CLEAR_SUBS_OPT, 0, 0 },
- { "substitution-filter", 0, SUBS_FILTER_OPT, Clp_ValString, 0 },
- { "subs-filter", 0, SUBS_FILTER_OPT, Clp_ValString, 0 },
- { "encoding", 'e', ENCODING_OPT, Clp_ValString, 0 },
- { "literal-encoding", 0, LITERAL_ENCODING_OPT, Clp_ValString, 0 },
- { "base-encodings", 0, BASE_ENCODINGS_OPT, Clp_ValString, 0 },
- { "extend", 'E', EXTEND_OPT, Clp_ValDouble, 0 },
- { "slant", 'S', SLANT_OPT, Clp_ValDouble, 0 },
- { "letterspacing", 'L', LETTERSPACE_OPT, Clp_ValInt, 0 },
- { "letterspace", 'L', LETTERSPACE_OPT, Clp_ValInt, 0 },
- { "min-kern", 'k', MINIMUM_KERN_OPT, Clp_ValDouble, 0 },
- { "minimum-kern", 'k', MINIMUM_KERN_OPT, Clp_ValDouble, 0 },
- { "kern-precision", 'k', MINIMUM_KERN_OPT, Clp_ValDouble, 0 },
- { "ligkern", 0, LIGKERN_OPT, Clp_ValString, 0 },
- { "position", 0, POSITION_OPT, Clp_ValString, 0 },
- { "warn-missing", 0, WARN_MISSING_OPT, 0, Clp_Negate },
- { "no-encoding-commands", 0, NO_ECOMMAND_OPT, 0, 0 },
- { "default-ligkern", 0, DEFAULT_LIGKERN_OPT, 0, Clp_Negate },
- { "unicoding", 0, UNICODING_OPT, Clp_ValString, 0 },
- { "coding-scheme", 0, CODINGSCHEME_OPT, Clp_ValString, 0 },
- { "boundary-char", 0, BOUNDARY_CHAR_OPT, CHAR_OPTTYPE, 0 },
- { "altselector", 0, ALTSELECTOR_CHAR_OPT, CHAR_OPTTYPE, Clp_PreferredMatch },
- { "altselector-char", 0, ALTSELECTOR_CHAR_OPT, CHAR_OPTTYPE, 0 },
- { "altselector-feature", 0, ALTSELECTOR_FEATURE_OPT, Clp_ValString, 0 },
- { "design-size", 0, DESIGN_SIZE_OPT, Clp_ValDouble, 0 },
- { "include-alternates", 0, INCLUDE_ALTERNATES_OPT, Clp_ValString, 0 },
- { "exclude-alternates", 0, EXCLUDE_ALTERNATES_OPT, Clp_ValString, 0 },
- { "clear-alternates", 0, CLEAR_ALTERNATES_OPT, 0, 0 },
- { "alternates-filter", 0, ALTERNATES_FILTER_OPT, Clp_ValString, 0 },
- { "space-factor", 0, SPACE_FACTOR_OPT, Clp_ValDouble, 0 },
- { "math-spacing", 0, MATH_SPACING_OPT, CHAR_OPTTYPE, Clp_Negate | Clp_Optional },
- { "fixed-pitch", 0, FIXED_PITCH_OPT, 0, Clp_Negate },
- { "fixed-width", 0, FIXED_PITCH_OPT, 0, Clp_Negate },
- { "proportional-width", 0, PROPORTIONAL_WIDTH_OPT, 0, Clp_Negate },
- { "italic-angle", 0, ITALIC_ANGLE_OPT, Clp_ValDouble, 0 },
- { "x-height", 0, X_HEIGHT_OPT, Clp_ValString, 0 },
-
- { "pl", 'p', PL_OPT, 0, Clp_Negate },
- { "tfm", 't', TFM_OPT, 0, Clp_Negate }, // not in documentation
- { "virtual", 0, VIRTUAL_OPT, 0, Clp_Negate },
- { "no-encoding", 0, NO_ENCODING_OPT, 0, 0 },
- { "no-type1", 0, NO_TYPE1_OPT, 0, 0 },
- { "no-dotlessj", 0, NO_DOTLESSJ_OPT, 0, 0 },
- { "no-updmap", 0, NO_UPDMAP_OPT, 0, 0 },
- { "updmap-sys", 0, UPDMAP_SYS_OPT, 0, 0 },
- { "updmap-user", 0, UPDMAP_USER_OPT, 0, 0 },
- { "truetype", 0, TRUETYPE_OPT, 0, Clp_Negate },
- { "type42", 0, TYPE42_OPT, 0, Clp_Negate },
- { "map-file", 0, MAP_FILE_OPT, Clp_ValString, Clp_Negate },
- { "output-encoding", 0, OUTPUT_ENCODING_OPT, Clp_ValString, Clp_Optional },
-
- { "automatic", 'a', AUTOMATIC_OPT, 0, Clp_Negate },
- { "name", 'n', FONT_NAME_OPT, Clp_ValString, 0 },
- { "vendor", 'v', VENDOR_OPT, Clp_ValString, 0 },
- { "typeface", 0, TYPEFACE_OPT, Clp_ValString, 0 },
-
- { "directory", 0, DIR_OPT, Clp_ValString, 0 },
- { "encoding-directory", 0, ENCODING_DIR_OPT, Clp_ValString, 0 },
- { "pl-directory", 0, PL_DIR_OPT, Clp_ValString, 0 },
- { "tfm-directory", 0, TFM_DIR_OPT, Clp_ValString, 0 },
- { "vpl-directory", 0, VPL_DIR_OPT, Clp_ValString, 0 },
- { "vf-directory", 0, VF_DIR_OPT, Clp_ValString, 0 },
- { "type1-directory", 0, TYPE1_DIR_OPT, Clp_ValString, 0 },
- { "type42-directory", 0, TYPE42_DIR_OPT, Clp_ValString, 0 },
- { "truetype-directory", 0, TRUETYPE_DIR_OPT, Clp_ValString, 0 },
-
- { "quiet", 'q', QUIET_OPT, 0, Clp_Negate },
- { "glyphlist", 0, GLYPHLIST_OPT, Clp_ValString, 0 },
- { "no-create", 0, NOCREATE_OPT, 0, 0 },
- { "force", 0, FORCE_OPT, 0, Clp_Negate },
- { "verbose", 'V', VERBOSE_OPT, 0, Clp_Negate },
- { "kpathsea-debug", 0, KPATHSEA_DEBUG_OPT, Clp_ValInt, 0 },
-
- { "help", 'h', HELP_OPT, 0, 0 },
- { "version", 0, VERSION_OPT, 0, 0 },
-
- { "query-features", 0, QUERY_FEATURES_OPT, 0, 0 },
- { "qf", 0, QUERY_FEATURES_OPT, 0, 0 },
- { "query-scripts", 0, QUERY_SCRIPTS_OPT, 0, 0 },
- { "qs", 0, QUERY_SCRIPTS_OPT, 0, 0 },
-
-};
-
-static const char * const default_ligkerns = "\
-space l =: lslash ; space L =: Lslash ; \
-question quoteleft =: questiondown ; \
-exclam quoteleft =: exclamdown ; \
-hyphen hyphen =: endash ; endash hyphen =: emdash ; \
-quoteleft quoteleft =: quotedblleft ; \
-quoteright quoteright =: quotedblright ;";
-
-struct BaseEncoding {
- String font_name;
- String secondary;
- DvipsEncoding encoding;
-};
-
-static const char *program_name;
-static String current_time;
-static StringAccum invocation;
-
-static PermString::Initializer perm_initializer;
-static PermString dot_notdef(".notdef");
-
-static Vector<Efont::OpenType::Tag> interesting_scripts;
-static Vector<Efont::OpenType::Tag> interesting_features;
-static Vector<Efont::OpenType::Tag> altselector_features;
-static GlyphFilter null_filter;
-static HashMap<Efont::OpenType::Tag, GlyphFilter*> feature_filters(0);
-static HashMap<Efont::OpenType::Tag, GlyphFilter*> altselector_feature_filters(0);
-
-static String font_name;
-static String encoding_file;
-static Vector<BaseEncoding *> base_encodings;
-static double extend;
-static double slant;
-int letterspace;
-static double design_size;
-static double minimum_kern = 2.0;
-static double space_factor = 1.0;
-static bool math_spacing = false;
-static int skew_char = -1;
-static bool override_is_fixed_pitch = false;
-static bool is_fixed_pitch;
-static bool override_italic_angle = false;
-static double italic_angle;
-static int override_x_height = FontInfo::x_height_auto;
-static double x_height;
-
-static String out_encoding_file;
-static String out_encoding_name;
-
-unsigned output_flags = G_ENCODING | G_METRICS | G_VMETRICS | G_PSFONTSMAP | G_TYPE1 | G_DOTLESSJ | G_UPDMAP | G_TRUETYPE;
-
-bool automatic = false;
-bool verbose = false;
-bool no_create = false;
-bool quiet = false;
-bool force = false;
-
-static String otf_data;
-
-
-void
-usage_error(ErrorHandler *errh, const char *error_message, ...)
-{
- va_list val;
- va_start(val, error_message);
- if (!error_message)
- errh->message("Usage: %s [OPTION]... FONT", program_name);
- else
- errh->xmessage(ErrorHandler::e_error, error_message, val);
- errh->message("Type %s --help for more information.", program_name);
- exit(1);
-}
-
-void
-usage()
-{
- FileErrorHandler uerrh(stdout);
- uerrh.message("\
-%<Otftotfm%> generates TeX font metrics files from an OpenType font (PostScript\n\
-flavor only), including ligatures, kerns, and some positionings. Supply\n\
-%<-s SCRIPT[.LANG]%> options to specify a language system, %<-f FEAT%> options to\n\
-turn on optional OpenType features, and a %<-e ENC%> option to specify a base\n\
-encoding. Output files are written to the current directory (but see\n\
-%<--automatic%> and the %<directory%> options).\n\
-\n\
-Usage: %s [-a] [OPTIONS] OTFFILE FONTNAME\n\n",
- program_name);
- uerrh.message("\
-Font feature and transformation options:\n\
- -s, --script=SCRIPT[.LANG] Use features for script SCRIPT[.LANG] [latn].\n\
- -f, --feature=FEAT Activate feature FEAT.\n\
- --lf, --letter-feature=FEAT Activate feature FEAT for letters.\n\
- --subs-filter=PAT Substitute only characters matching PAT.\n\
- --include-subs=PAT Same, but cumulative.\n\
- --exclude-subs=PAT Don%,t substitute characters matching PAT.\n\
- --clear-subs Clear included/excluded substitutions.\n\
- -E, --extend=F Widen characters by a factor of F.\n\
- -S, --slant=AMT Oblique characters by AMT, generally <<1.\n\
- -L, --letterspacing=AMT Letterspace each character by AMT units.\n\
- --math-spacing[=SKEWCH] Use letterspacing appropriate for math.\n\
- -k, --min-kern=N Omit kerns with absolute value < N [2.0].\n\
- --space-factor=F Scale wordspace by a factor of F.\n\
- --design-size=SIZE Set font design size to SIZE.\n\
- --fixed-width Set fixed width (no space stretch).\n\
- --italic-angle=ANGLE Set font italic angle (for positioning accents).\n\
- --x-height=AMT Set x-height to AMT units.\n\
-\n");
- uerrh.message("\
-Encoding options:\n\
- -e, --encoding=FILE Use DVIPS encoding FILE as a base encoding.\n\
- --boundary-char=CHAR Set the boundary character to CHAR.\n\
- --altselector-char=CHAR Set the alternate selector character to CHAR.\n\
- --altselector-feature=F Activate feature F for --altselector-char.\n\
- --alternates-filter=PAT Include only alternate characters matching PAT.\n\
- --include-alternates=PAT Same as --alternates-filter, but cumulative.\n\
- --exclude-alternates=PAT Ignore alternate characters matching PAT.\n\
- --clear-alternates Clear included/excluded alternates.\n\
- --ligkern=COMMAND Add a ligature or kern.\n\
- --position=COMMAND Add a POSITION command.\n\
- --unicoding=COMMAND Add a UNICODING command.\n\
- --no-encoding-commands Ignore encoding file%,s LIGKERN/UNICODINGs.\n\
- --no-default-ligkern Don%,t include default LIGKERNs.\n\
- --coding-scheme=SCHEME Set the output coding scheme to SCHEME.\n\
- --warn-missing Warn about characters not supported by font.\n\
- --literal-encoding=FILE Use DVIPS encoding FILE verbatim.\n\
- --base-encodings=FILE Output can refer to base fonts named in FILE.\n\
-\n");
- uerrh.message("\
-Automatic mode options:\n\
- -a, --automatic Install in a TeX Directory Structure.\n\
- -v, --vendor=NAME Set font vendor for TDS [lcdftools].\n\
- --typeface=NAME Set typeface name for TDS [<font family>].\n\
- --no-type1 Do not generate Type 1 fonts.\n\
- --no-dotlessj Do not generate dotless-j fonts.\n\
- --no-truetype Do not install TrueType-flavored input fonts.\n\
- --no-updmap Do not run updmap.\n\
- --updmap-user Run `updmap-user` instead of `updmap-sys`.\n\
-\n\
-Output options:\n\
- -n, --name=NAME Generated font name is NAME.\n\
- -p, --pl Output human-readable PL/VPLs, not TFM/VFs.\n\
- --no-virtual Do not generate VFs or VPLs.\n\
- --no-encoding Do not generate an encoding file.\n\
- --no-map Do not generate a psfonts.map line.\n\
- --output-encoding[=FILE] Only generate an encoding file.\n\
-\n");
- uerrh.message("\
-File location options:\n\
- --tfm-directory=DIR Put TFM files in DIR [.|automatic].\n\
- --pl-directory=DIR Put PL files in DIR [.|automatic].\n\
- --vf-directory=DIR Put VF files in DIR [.|automatic].\n\
- --vpl-directory=DIR Put VPL files in DIR [.|automatic].\n\
- --encoding-directory=DIR Put encoding files in DIR [.|automatic].\n\
- --type1-directory=DIR Put Type 1 fonts in DIR [automatic].\n\
- --truetype-directory=DIR Put TrueType fonts in DIR [automatic].\n\
- --directory=DIR Put output in DIR [.|automatic].\n\
- --map-file=FILE Update FILE with psfonts.map information [-].\n\
-\n\
-Other options:\n\
- --glyphlist=FILE Use FILE to map Adobe glyph names to Unicode.\n\
- -V, --verbose Print progress information to standard error.\n\
- --no-create Print messages, don't modify any files.\n\
- --force Generate files even if versions already exist.\n"
-#if HAVE_KPATHSEA
-" --kpathsea-debug=MASK Set path searching debug flags to MASK.\n"
-#endif
-" -h, --help Print this message and exit.\n\
- -q, --quiet Do not generate any error messages.\n\
- --version Print version number and exit.\n\
-\n\
-Report bugs to <ekohler@gmail.com>.\n");
-}
-
-#ifndef WIN32
-extern "C" {
-static void
-sigchld_handler(int s)
-{
-#if !HAVE_SIGACTION
- signal(s, sigchld_handler);
-#else
- (void) s;
-#endif
-}
-}
-
-static void
-handle_sigchld()
-{
-#ifdef SIGCHLD
- int sigchld = SIGCHLD;
-#else
- int sigchld = SIGCLD;
-#endif
-#if HAVE_SIGACTION
- struct sigaction sa;
- sigaction(sigchld, 0, &sa);
- sa.sa_handler = sigchld_handler;
- sa.sa_flags = 0;
- sigaction(sigchld, &sa, 0);
-#else
- signal(sigchld, sigchld_handler);
-#endif
-}
-#endif
-
-
-// MAIN
-
-String
-suffix_font_name(const String &font_name, const String &suffix)
-{
- const char *begin = font_name.begin(), *end = font_name.end();
- while (end > begin && isdigit((unsigned char) end[-1]))
- --end;
- if (end < font_name.end() && end > begin && end[-1] != '-' && end[-1] != '+')
- end = font_name.end();
- else
- while (end > begin && (end[-1] == '-' || end[-1] == '+'))
- --end;
- if (end == begin)
- end = font_name.end();
- return font_name.substring(begin, end) + suffix + font_name.substring(end, font_name.end());
-}
-
-static inline String
-make_base_font_name(const String &font_name)
-{
- return suffix_font_name(font_name, "--base");
-}
-
-static double
-get_design_size(const FontInfo &finfo)
-{
- try {
- String gpos_table = finfo.otf->table("GPOS");
- if (!gpos_table)
- throw OpenType::Error();
-
- ErrorHandler *errh = ErrorHandler::silent_handler();
- OpenType::Gpos gpos(gpos_table, errh);
-
- // extract 'size' feature(s)
- int required_fid;
- Vector<int> fids;
- for (const OpenType::Tag *t = interesting_scripts.begin(); t < interesting_scripts.end(); t += 2)
- gpos.script_list().features(t[0], t[1], required_fid, fids, 0, false);
-
- int size_fid = gpos.feature_list().find(OpenType::Tag("size"), fids);
- if (size_fid < 0)
- throw OpenType::Error();
-
- // old Adobe fonts implement an old, incorrect idea
- // of what the FeatureParams offset means.
- OpenType::Name name(finfo.otf->table("name"), errh);
- OpenType::Data size_data = gpos.feature_list().size_params(size_fid, name, errh);
- if (!size_data.length())
- throw OpenType::Error();
-
- double result = size_data.u16(0) / 10.;
- // check for insane design sizes
- if (result < 1 || result > 1000)
- throw OpenType::Error();
-
- // return a number in 'pt', not 'bp'
- return result * 72.27 / 72.;
-
- } catch (OpenType::Error) {
- return 10.0;
- }
-}
-
-static const char * const digit_names[] = {
- "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"
-};
-
-static inline const char *
-lig_context_str(int ctx)
-{
- return (ctx == 0 ? "LIG" : (ctx < 0 ? "/LIG" : "LIG/"));
-}
-
-static double max_printed_real;
-
-namespace {
-struct Printer {
- Printer(FILE* f, unsigned design_units, unsigned units_per_em)
- : f_(f), du_((double) design_units / units_per_em),
- round_(design_units == 1000) {
- }
- inline double transform(double value) const;
- void print_transformed(const char* prefix, double value) const;
- void print(const char* prefix, double value) const;
- String render(double value) const;
- FILE* f_;
- double du_;
- bool round_;
-};
-
-inline double Printer::transform(double value) const {
- value *= du_;
- if (round_)
- value = ceil(value);
- return value;
-}
-
-void Printer::print_transformed(const char* prefix, double value) const {
- if (round_ || value == 0 || (value > 0.01 && value - floor(value) < 0.01))
- fprintf(f_, "%s R %g)\n", prefix, value);
- else
- fprintf(f_, "%s R %.4f)\n", prefix, value);
- max_printed_real = std::max(max_printed_real, fabs(value));
-}
-
-void Printer::print(const char* prefix, double value) const {
- print_transformed(prefix, transform(value));
-}
-
-String Printer::render(double value) const {
- value = transform(value);
- if (round_ || value == 0 || (value > 0.01 && value - floor(value) < 0.01))
- return String(value);
- else {
- char buf[128];
- sprintf(buf, "%.4f", value);
- return String(buf);
- }
-}
-} // namespace
-
-double
-font_slant(const FontInfo &finfo)
-{
- double val = finfo.italic_angle();
- return -tan(val * M_PI / 180.0) + slant;
-}
-
-static void
-output_pl(Metrics &metrics, const String &ps_name, int boundary_char,
- const FontInfo &finfo, bool vpl,
- const String &filename, ErrorHandler *errh)
-{
- // create file
- if (no_create) {
- errh->message("would create %s", filename.c_str());
- return;
- }
-
- if (verbose)
- errh->message("creating %s", filename.c_str());
- FILE *f = fopen(filename.c_str(), "wb");
- if (!f) {
- errh->error("%s: %s", filename.c_str(), strerror(errno));
- return;
- }
-
- // XXX check DESIGNSIZE and DESIGNUNITS for correctness
-
- fprintf(f, "(COMMENT Created by '%s'%s)\n", invocation.c_str(), current_time.c_str());
-
- // calculate a TeX FAMILY name using afm2tfm's algorithm
- String family_name = String("TeX-") + ps_name;
- if (family_name.length() > 19)
- family_name = family_name.substring(0, 9) + family_name.substring(-10);
- fprintf(f, "(FAMILY %s)\n", family_name.c_str());
-
- if (metrics.coding_scheme())
- fprintf(f, "(CODINGSCHEME %.39s)\n", String(metrics.coding_scheme()).c_str());
- int design_units = metrics.design_units();
-
- if (design_size <= 0)
- design_size = get_design_size(finfo);
- max_printed_real = 0;
-
- fprintf(f, "(DESIGNSIZE R %.1f)\n"
- "(DESIGNUNITS R %d.0)\n"
- "(COMMENT DESIGNSIZE (1 em) IS IN POINTS)\n"
- "(COMMENT OTHER DIMENSIONS ARE MULTIPLES OF DESIGNSIZE/%d)\n"
- "(FONTDIMEN\n", design_size, design_units, design_units);
-
- // figure out font dimensions
- Transform font_xform;
- if (extend)
- font_xform.scale(extend, 1);
- if (slant)
- font_xform.shear(slant);
- double bounds[4], width;
- Printer pr(f, design_units, metrics.units_per_em());
-
- double actual_slant = font_slant(finfo);
- if (actual_slant)
- fprintf(f, " (SLANT R %g)\n", actual_slant);
-
- if (char_bounds(bounds, width, finfo, font_xform, ' ')) {
- // advance space width by letterspacing, scale by space_factor
- double space_width = (width + (vpl ? letterspace : 0)) * space_factor;
- pr.print(" (SPACE", space_width);
- if (finfo.is_fixed_pitch()) {
- // fixed-pitch: no space stretch or shrink
- pr.print(" (STRETCH", 0);
- pr.print(" (SHRINK", 0);
- pr.print(" (EXTRASPACE", space_width);
- } else {
- pr.print(" (STRETCH", space_width / 2.);
- pr.print(" (SHRINK", space_width / 3.);
- pr.print(" (EXTRASPACE", space_width / 6.);
- }
- }
-
- double x_height = finfo.x_height(font_xform);
- if (x_height < finfo.units_per_em())
- pr.print(" (XHEIGHT", x_height);
-
- pr.print(" (QUAD", finfo.units_per_em());
- fprintf(f, " )\n");
-
- if (boundary_char >= 0)
- fprintf(f, "(BOUNDARYCHAR D %d)\n", boundary_char);
-
- // figure out font mapping
- int mapped_font0 = 0;
- Vector<int> font_mapping;
- if (vpl) {
- int vpl_first_font = (metrics.need_base() ? 0 : 1);
- font_mapping.assign(metrics.n_mapped_fonts(), 0);
- if (vpl_first_font == 1 && font_mapping.size() == 1)
- font_mapping.push_back(0);
- // how many times is each font used?
- Vector<Setting> settings;
- for (int i = 0; i < 256; i++)
- if (metrics.setting(i, settings)) {
- int font_number = 0;
- for (const Setting *s = settings.begin(); s < settings.end(); s++)
- if (s->op == Setting::SHOW)
- font_mapping[font_number]++;
- else if (s->op == Setting::FONT)
- font_number = (int) s->x;
- }
- // make sure the most-used font is number 0
- mapped_font0 = std::max_element(font_mapping.begin(), font_mapping.end()) - font_mapping.begin();
- // prepare the mapping
- for (int i = vpl_first_font; i < font_mapping.size(); i++)
- font_mapping[i] = i - vpl_first_font;
- font_mapping[mapped_font0] = 0;
- font_mapping[vpl_first_font] = mapped_font0 - vpl_first_font;
- if (vpl_first_font != 0)
- font_mapping[0] = font_mapping.size() - 1;
- // write MAPFONT
- for (int i = 0; i < metrics.n_mapped_fonts() - vpl_first_font; i++) {
- int j = std::find(font_mapping.begin(), font_mapping.end(), i) - font_mapping.begin();
- String name = metrics.mapped_font_name(j);
- if (!name)
- name = make_base_font_name(font_name);
- fprintf(f, "(MAPFONT D %d\n (FONTNAME %s)\n (FONTDSIZE R %.1f)\n )\n", i, name.c_str(), design_size);
- }
- } else
- for (int i = 0; i < metrics.n_mapped_fonts(); i++)
- font_mapping.push_back(i);
-
- // figure out the proper names and numbers for glyphs
- Vector<String> glyph_ids;
- Vector<String> glyph_comments(257, String());
- Vector<String> glyph_base_comments(257, String());
- for (int i = 0; i < metrics.encoding_size(); i++) {
- if (metrics.glyph(i)) {
- PermString name = metrics.code_name(i), expected_name;
- if (i >= '0' && i <= '9')
- expected_name = digit_names[i - '0'];
- else if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z'))
- expected_name = PermString((char)i);
- String glyph_comment;
- if (name != expected_name)
- glyph_comment = " (COMMENT " + String(name) + ")";
-
- int base = metrics.base_code(i);
- if (base >= 0 && base < 256)
- glyph_base_comments[base] = glyph_comment;
-
- if (i >= 256)
- continue;
-
- char* expected_name_end;
- if (expected_name
- && (name == expected_name
- || (name.length() == 7
- && memcmp(name.data(), "uni00", 5) == 0
- && strtol(name.c_str() + 3, &expected_name_end, 16) == i
- && *expected_name_end == 0)))
- glyph_ids.push_back("C " + String((char)i));
- else
- glyph_ids.push_back("D " + String(i));
-
- glyph_comments[i] = glyph_base_comments[i] = glyph_comment;
-
- } else if (i < 256)
- glyph_ids.push_back("D " + String(i));
- }
- // finally, BOUNDARYCHAR
- glyph_ids.push_back("BOUNDARYCHAR");
-
- // LIGTABLE
- fprintf(f, "(LIGTABLE\n");
- Vector<int> lig_code2, lig_outcode, lig_context, kern_code2, kern_amt;
- // don't print KRN x after printing LIG x
- uint32_t used[8];
- bool any_ligs = false;
- StringAccum omitted_clig_sa;
- for (int i = 0; i <= 256; i++)
- if (metrics.glyph(i) && minimum_kern < 10000) {
- int any_lig = metrics.ligatures(i, lig_code2, lig_outcode, lig_context);
- int any_kern = metrics.kerns(i, kern_code2, kern_amt);
- if (any_lig || any_kern) {
- StringAccum kern_sa;
- memset(used, 0, sizeof(used));
- for (int j = 0; j < lig_code2.size(); j++) {
- if (lig_outcode[j] < 257) {
- kern_sa << " (" << lig_context_str(lig_context[j])
- << ' ' << glyph_ids[lig_code2[j]]
- << ' ' << glyph_ids[lig_outcode[j]]
- << ')' << glyph_comments[lig_code2[j]]
- << glyph_comments[lig_outcode[j]] << '\n';
- used[lig_code2[j] >> 5] |= (1 << (lig_code2[j] & 0x1F));
- } else {
- omitted_clig_sa << "(COMMENT omitted "
- << lig_context_str(lig_context[j])
- << ' ' << metrics.code_name(i)
- << ' ' << metrics.code_name(lig_code2[j])
- << ' ' << metrics.code_name(lig_outcode[j])
- << ")\n";
- }
- }
- for (Vector<int>::const_iterator k2 = kern_code2.begin(); k2 < kern_code2.end(); k2++)
- if (!(used[*k2 >> 5] & (1 << (*k2 & 0x1F)))) {
- double this_kern = kern_amt[k2 - kern_code2.begin()];
- if (fabs(this_kern) >= minimum_kern)
- kern_sa << " (KRN " << glyph_ids[*k2]
- << " R " << pr.render(this_kern)
- << ')' << glyph_comments[*k2] << '\n';
- }
- if (kern_sa) {
- if (any_ligs)
- fprintf(f, "\n");
- fprintf(f, " (LABEL %s)%s\n%s (STOP)\n", glyph_ids[i].c_str(), glyph_comments[i].c_str(), kern_sa.c_str());
- any_ligs = true;
- }
- }
- }
- fprintf(f, " )\n");
- if (omitted_clig_sa)
- fprintf(f, "%s\n", omitted_clig_sa.c_str());
-
- // CHARACTERs
- Vector<Setting> settings;
- StringAccum sa;
- Vector<Point> push_stack;
-
- for (int i = 0; i < 256; i++)
- if (metrics.setting(i, settings)) {
- fprintf(f, "(CHARACTER %s%s\n", glyph_ids[i].c_str(), glyph_comments[i].c_str());
-
- // unparse settings into DVI commands
- sa.clear();
- push_stack.clear();
- CharstringBounds boundser(font_xform);
- int program_number = mapped_font0;
- const CharstringProgram *program = finfo.program();
- for (const Setting *s = settings.begin(); s < settings.end(); s++)
- switch (s->op) {
-
- case Setting::SHOW:
- if (vpl || program == finfo.program())
- boundser.char_bounds(program->glyph_context(s->y));
- // 3.Aug.2004 -- reported by Marco Kuhlmann: Don't use
- // glyph_ids[] array when looking at a different font.
- if (program_number == 0)
- sa << " (SETCHAR " << glyph_ids[s->x] << ')' << glyph_base_comments[s->x] << "\n";
- else
- sa << " (SETCHAR D " << s->x << ")\n";
- break;
-
- case Setting::MOVE: {
- int x = 0, y = 0;
- while (s+1 < settings.end() && s[1].op == Setting::MOVE)
- x += s->x, y += s->y, s++;
- if (vpl)
- boundser.translate(s->x + x, s->y + y);
- if (s->x + x)
- sa << " (MOVERIGHT R " << pr.render(s->x + x) << ")\n";
- if (s->y + y)
- sa << " (MOVEUP R " << pr.render(s->y + y) << ")\n";
- break;
- }
-
- case Setting::RULE:
- if (vpl) {
- boundser.mark(Point(0, 0));
- boundser.mark(Point(s->x, s->y));
- boundser.translate(s->x, 0);
- }
- sa << " (SETRULE R " << pr.render(s->y) << " R " << pr.render(s->x) << ")\n";
- break;
-
- case Setting::FONT:
- if ((int) s->x != program_number) {
- program = metrics.mapped_font((int) s->x);
- program_number = (int) s->x;
- sa << " (SELECTFONT D " << font_mapping[program_number] << ")\n";
- }
- break;
-
- case Setting::PUSH:
- push_stack.push_back(boundser.transform(Point(0, 0)));
- sa << " (PUSH)\n";
- break;
-
- case Setting::POP: {
- assert(push_stack.size());
- Point p = push_stack.back() - boundser.transform(Point(0, 0));
- if (vpl)
- boundser.translate(p.x, p.y);
- push_stack.pop_back();
- sa << " (POP)\n";
- break;
- }
-
- case Setting::SPECIAL: {
- bool needhex = false;
- for (const char *str = s->s.begin(); str < s->s.end() && !needhex; str++)
- if (*str < ' ' || *str > '~' || *str == '(' || *str == ')')
- needhex = true;
- if (needhex) {
- sa << " (SPECIALHEX ";
- for (const char *str = s->s.begin(); str < s->s.end(); str++) {
- static const char hexdig[] = "0123456789ABCDEF";
- int val = (unsigned char) *str;
- sa << hexdig[val >> 4] << hexdig[val & 0xF];
- }
- sa << ")\n";
- } else
- sa << " (SPECIAL " << s->s << ")\n";
- break;
- }
-
- }
-
- assert(push_stack.size() == 0);
-
- // output information
- boundser.output(bounds, width);
- pr.print(" (CHARWD", width);
- if (bounds[3] > 0)
- pr.print(" (CHARHT", bounds[3]);
- if (bounds[1] < 0)
- pr.print(" (CHARDP", -bounds[1]);
- if (bounds[2] > width)
- pr.print_transformed(" (CHARIC", pr.transform(bounds[2]) - pr.transform(width));
- if (vpl && (settings.size() > 1 || settings[0].op != Setting::SHOW))
- fprintf(f, " (MAP\n%s )\n", sa.c_str());
- fprintf(f, " )\n");
- }
-
- // at last, close the file
- fclose(f);
-
- // Did we print a number too big for TeX to handle? If so, try again.
- if (max_printed_real >= 2047) {
- if (metrics.design_units() <= 1)
- errh->fatal("This font appears to be broken. It has characters so big that the PL format\ncannot represent them.");
- metrics.set_design_units(metrics.design_units() > 200 ? metrics.design_units() - 250 : 1);
- if (verbose)
- errh->message("the font%,s metrics overflow the limits of PL files\n(reducing DESIGNUNITS to %d and trying again)", metrics.design_units());
- output_pl(metrics, ps_name, boundary_char, finfo, vpl, filename, errh);
- }
-}
-
-struct Lookup {
- bool used;
- bool required;
- Vector<OpenType::Tag> features;
- GlyphFilter* filter;
- Lookup() : used(false), required(false), filter(0) { }
-};
-
-static void
-find_lookups(const OpenType::ScriptList& scripts, const OpenType::FeatureList& features, Vector<Lookup>& lookups, ErrorHandler* errh)
-{
- Vector<int> fids, lookupids;
- int required;
-
- // go over all scripts
- for (int i = 0; i < interesting_scripts.size(); i += 2) {
- OpenType::Tag script = interesting_scripts[i];
- OpenType::Tag langsys = interesting_scripts[i+1];
-
- // collect features applying to this script
- scripts.features(script, langsys, required, fids, errh);
-
- // only use the selected features
- features.filter(fids, interesting_features);
-
- // mark features as having been used
- for (int j = (required < 0 ? 0 : -1); j < fids.size(); j++) {
- int fid = (j < 0 ? required : fids[j]);
- OpenType::Tag ftag = features.tag(fid);
- if (features.lookups(fid, lookupids, errh) < 0)
- lookupids.clear();
- for (int k = 0; k < lookupids.size(); k++) {
- int l = lookupids[k];
- if (l < 0 || l >= lookups.size())
- errh->error("lookup for %<%s%> feature out of range", OpenType::Tag::langsys_text(script, langsys).c_str());
- else {
- lookups[l].used = true;
- lookups[l].features.push_back(ftag);
- if (j < 0)
- lookups[l].required = true;
- }
- }
- }
- }
-
- // now check for compatible glyph filters
- for (Lookup* l = lookups.begin(); l < lookups.end(); l++)
- if (l->used && !l->required) {
- l->filter = feature_filters[l->features[0]];
- for (OpenType::Tag* ft = l->features.begin() + 1; ft < l->features.end(); ft++)
- if (!l->filter->check_eq(*feature_filters[*ft])) {
- errh->error("%<%s%> and %<%s%> features share a lookup, but have different filters", l->features[0].text().c_str(), ft->text().c_str());
- break;
- }
- }
-}
-
-static int
-write_encoding_file(String &filename, const String &encoding_name,
- StringAccum &contents, ErrorHandler *errh)
-{
- FILE *f;
- int ok_retval = (access(filename.c_str(), R_OK) >= 0 ? 0 : 1);
-
- if (no_create) {
- errh->message((ok_retval ? "would create encoding file %s" : "would update encoding file %s"), filename.c_str());
- return ok_retval;
- } else if (verbose)
- errh->message((ok_retval ? "creating encoding file %s" : "updating encoding file %s"), filename.c_str());
-
- int fd = open(filename.c_str(), O_RDWR | O_CREAT, 0666);
- if (fd < 0)
- return errh->error("%s: %s", filename.c_str(), strerror(errno));
- f = fdopen(fd, "r+");
- // NB: also change update_autofont_map if you change this code
-
-#if defined(F_SETLKW) && defined(HAVE_FTRUNCATE)
- {
- struct flock lock;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- int result;
- while ((result = fcntl(fd, F_SETLKW, &lock)) < 0 && errno == EINTR)
- /* try again */;
- if (result < 0) {
- result = errno;
- fclose(f);
- return errh->error("locking %s: %s", filename.c_str(), strerror(result));
- }
- }
-#endif
-
- // read old data from encoding file
- StringAccum sa;
- int amt;
- do {
- if (char *x = sa.reserve(8192)) {
- amt = fread(x, 1, 8192, f);
- sa.adjust_length(amt);
- } else
- amt = 0;
- } while (amt != 0);
- if (!feof(f) || ferror(f))
- return errh->error("%s: %s", filename.c_str(), strerror(errno));
- String old_encodings = sa.take_string();
- bool created = (!old_encodings);
-
- // append old encodings
- int pos1 = old_encodings.find_left("\n%%");
- while (pos1 >= 0 && pos1 < old_encodings.length()) {
- int pos2 = old_encodings.find_left("\n%%", pos1 + 1);
- if (pos2 < 0)
- pos2 = old_encodings.length();
- if (old_encodings.substring(pos1 + 3, encoding_name.length()) == encoding_name) {
- // encoding already exists, don't change it
- fclose(f);
- if (verbose)
- errh->message("%s unchanged", filename.c_str());
- return 0;
- } else
- contents << old_encodings.substring(pos1, pos2 - pos1);
- pos1 = pos2;
- }
-
- // rewind file
-#ifdef HAVE_FTRUNCATE
- rewind(f);
- if (ftruncate(fd, 0) < 0)
-#endif
- {
- fclose(f);
- f = fopen(filename.c_str(), "wb");
- fd = fileno(f);
- }
-
- ignore_result(fwrite(contents.data(), 1, contents.length(), f));
-
- fclose(f);
-
- // inform about the new file if necessary
- if (created)
- update_odir(O_ENCODING, filename, errh);
- return 0;
-}
-
-static bool
-output_encoding(const Metrics &metrics,
- const Vector<PermString> &glyph_names,
- ErrorHandler *errh)
-{
- static const char * const hex_digits = "0123456789ABCDEF";
-
- // collect encoding data
- Vector<Metrics::Glyph> glyphs;
- if (!metrics.base_glyphs(glyphs, 256))
- return false;
-
- StringAccum sa;
- for (int i = 0; i < 256; i++) {
- if ((i & 0xF) == 0)
- sa << (i ? "\n%" : "%") << hex_digits[(i >> 4) & 0xF] << '0' << '\n' << ' ';
- else if ((i & 0x7) == 0)
- sa << '\n' << ' ';
- int g = glyphs[i];
- if (g > 0 && g < glyph_names.size())
- sa << ' ' << '/' << glyph_names[g];
- else
- sa << " /.notdef";
- }
- sa << '\n';
-
- // digest encoding data
- MD5_CONTEXT md5;
- md5_init(&md5);
- md5_update(&md5, (const unsigned char *)sa.data(), sa.length());
- char text_digest[MD5_TEXT_DIGEST_SIZE + 1];
- md5_final_text(text_digest, &md5);
-
- // name encoding using digest
- out_encoding_name = "AutoEnc_" + String(text_digest);
-
- // create encoding filename
- bool output_encoding_only = (bool) out_encoding_file;
- if (!out_encoding_file)
- out_encoding_file = getodir(O_ENCODING, errh) + String("/a_") + String(text_digest).substring(0, 6) + ".enc";
-
- // exit if we're not responsible for generating an encoding
- if (!(output_flags & G_ENCODING))
- return true;
-
- // put encoding block in a StringAccum
- // 3.Jun.2003: stick command line definition at the end of the encoding,
- // where it won't confuse idiotic ps2pk
- StringAccum contents;
- if (!output_encoding_only)
- contents << "% THIS FILE WAS AUTOMATICALLY GENERATED -- DO NOT EDIT\n\n\
-%%" << out_encoding_name << "\n";
- contents << "% Encoding created by otftotfm" << current_time << "\n\
-% Command line follows encoding\n";
-
- // the encoding itself
- contents << '/' << out_encoding_name << " [\n" << sa << "] def\n";
-
- // write banner -- unfortunately this takes some doing
- String banner = String("Command line: '") + String(invocation.data(), invocation.length()) + String("'");
- char *buf = banner.mutable_data();
- // get rid of crap characters
- for (int i = 0; i < banner.length(); i++)
- if (buf[i] < ' ' || buf[i] > 0176) {
- if (buf[i] == '\n' || buf[i] == '\r')
- buf[i] = ' ';
- else
- buf[i] = '.';
- }
- // break lines at 80 characters -- it would be nice if this were in a
- // library
- while (banner.length() > 0) {
- int pos = banner.find_left(' '), last_pos = pos;
- while (pos < 75 && pos >= 0) {
- last_pos = pos;
- pos = banner.find_left(' ', pos + 1);
- }
- if (last_pos < 0 || (pos < 0 && banner.length() < 75))
- last_pos = banner.length();
- contents << "% " << banner.substring(0, last_pos) << '\n';
- banner = banner.substring(last_pos + 1);
- }
-
- // open encoding file
- if (out_encoding_file == "-")
- ignore_result(fwrite(contents.data(), 1, contents.length(), stdout));
- else if (write_encoding_file(out_encoding_file, out_encoding_name, contents, errh) == 1)
- update_odir(O_ENCODING, out_encoding_file, errh);
- return true;
-}
-
-static void
-output_tfm(Metrics &metrics, const String &ps_name, int boundary_char,
- const FontInfo &finfo, String tfm_filename, String vf_filename,
- String pl_filename, ErrorHandler *errh)
-{
- bool had_pl_filename = !pl_filename.empty();
- bool vpl = vf_filename;
-
- if (!pl_filename) {
- if (no_create) {
- errh->message("would write %s to temporary file", (vpl ? "VPL" : "PL"));
- pl_filename = "<temporary>";
- } else {
- int pl_fd = temporary_file(pl_filename, errh);
- if (pl_fd < 0)
- return;
- output_pl(metrics, ps_name, boundary_char, finfo, vpl, pl_filename, errh);
- close(pl_fd);
- }
- }
-
- StringAccum command;
- if (vpl)
- command << "vptovf " << shell_quote(pl_filename) << ' ' << shell_quote(vf_filename) << ' ' << shell_quote(tfm_filename) << " 2>&1";
- else
- command << "pltotf " << shell_quote(pl_filename) << ' ' << shell_quote(tfm_filename) << " 2>&1";
-
- FILE* cmdfile = mypopen(command.c_str(), "r", errh);
- int status;
- if (cmdfile) {
- StringAccum results;
- while (!feof(cmdfile)) {
- char* buf = results.reserve(BUFSIZ);
- results.adjust_length(fread(buf, 1, BUFSIZ, cmdfile));
- }
- // compensate for shitty vptovf/pltotf messages
- char* by_units, *last_line;
- while (!results.empty()
- && (by_units = strstr((char*) results.c_str(), " units"))
- && (last_line = strrchr((char*) results.c_str(), '\n'))
- && results.end() - last_line > 1
- && isdigit((unsigned char) last_line[1])) {
- char* start_number = last_line + 1;
- char* end_number = last_line + 2;
- while (end_number < results.end()
- && isdigit((unsigned char) *end_number))
- ++end_number;
- if (end_number < results.end() && *end_number == '.') {
- char* dot = end_number;
- ++end_number;
- while (end_number < results.end()
- && end_number < dot + 8
- && isdigit((unsigned char) *end_number))
- ++end_number;
- }
- String number(start_number, end_number);
- char* by_units_dest = by_units + 1 + (end_number - start_number);
- memmove(by_units_dest, by_units + 1, start_number - (by_units + 1));
- memmove(by_units + 1, number.begin(), number.length());
- }
- if (!results.empty())
- fwrite(results.begin(), 1, results.length(), stderr);
- status = pclose(cmdfile);
- } else
- status = -1;
-
- if (!no_create && !had_pl_filename)
- unlink(pl_filename.c_str());
-
- if (status != 0)
- errh->fatal("%s execution failed", (vpl ? "vptovf" : "pltotf"));
- else {
- update_odir(O_TFM, tfm_filename, errh);
- if (vpl)
- update_odir(O_VF, vf_filename, errh);
- }
-}
-
-void
-output_metrics(Metrics &metrics, const String &ps_name, int boundary_char,
- const FontInfo &finfo,
- const String &encoding_name, const String &encoding_file,
- const String &font_name,
- String (*dvips_include)(const String &ps_name, const FontInfo &, ErrorHandler *),
- ErrorHandler *errh)
-{
- String base_font_name = font_name;
- bool need_virtual = metrics.need_virtual(257);
- if (need_virtual) {
- if (output_flags & G_VMETRICS)
- base_font_name = make_base_font_name(font_name);
- else if (output_flags & G_METRICS)
- errh->warning("features require virtual fonts");
- }
-
- // output virtual metrics
- if (!(output_flags & G_VMETRICS))
- /* do nothing */;
- else if (!need_virtual) {
- if (automatic) {
- // erase old virtual font
- String vf = getodir(O_VF, errh) + "/" + font_name + ".vf";
- if (no_create)
- errh->message("would remove potential VF file %<%s%>", vf.c_str());
- else {
- if (verbose)
- errh->message("removing potential VF file %<%s%>", vf.c_str());
- if (unlink(vf.c_str()) < 0 && errno != ENOENT)
- errh->error("removing %s: %s", vf.c_str(), strerror(errno));
- }
- }
- } else {
- String vplfile;
- if (output_flags & G_ASCII) {
- vplfile = getodir(O_VPL, errh) + "/" + font_name + ".vpl";
- output_pl(metrics, ps_name, boundary_char, finfo, true, vplfile, errh);
- update_odir(O_VPL, vplfile, errh);
- }
- if (output_flags & G_BINARY) {
- String tfm = getodir(O_TFM, errh) + "/" + font_name + ".tfm";
- String vf = getodir(O_VF, errh) + "/" + font_name + ".vf";
- output_tfm(metrics, ps_name, boundary_char, finfo, tfm, vf, vplfile, errh);
- }
- }
-
- // quit if no base needed
- metrics.make_base(257);
- if (!metrics.need_base())
- return;
-
- // output metrics
- double save_minimum_kern = minimum_kern;
- if (need_virtual)
- minimum_kern = 100000;
- if (output_flags & G_METRICS) {
- String plfile;
- if (output_flags & G_ASCII) {
- plfile = getodir(O_PL, errh) + "/" + base_font_name + ".pl";
- output_pl(metrics, ps_name, boundary_char, finfo, false, plfile, errh);
- update_odir(O_PL, plfile, errh);
- }
- if (output_flags & G_BINARY) {
- String tfm = getodir(O_TFM, errh) + "/" + base_font_name + ".tfm";
- output_tfm(metrics, ps_name, boundary_char, finfo, tfm, String(), plfile, errh);
- }
- }
- minimum_kern = save_minimum_kern;
-
- // print DVIPS map line
- if (errh->nerrors() == 0 && (output_flags & G_PSFONTSMAP)) {
- StringAccum sa;
- sa << base_font_name << ' ' << ps_name << " \"";
- if (extend)
- sa << extend << " ExtendFont ";
- if (slant)
- sa << slant << " SlantFont ";
- if (encoding_name)
- sa << encoding_name << " ReEncodeFont\" <[" << pathname_filename(encoding_file);
- else
- sa << "\"";
- sa << ' ' << dvips_include(ps_name, finfo, errh) << '\n';
- update_autofont_map(base_font_name, sa.take_string(), errh);
- // if virtual font, remove any map line for base font name
- if (base_font_name != font_name)
- update_autofont_map(font_name, "", errh);
- }
-}
-
-
-enum { F_GSUB_TRY = 1, F_GSUB_PART = 2, F_GSUB_ALL = 4,
- F_GPOS_TRY = 8, F_GPOS_PART = 16, F_GPOS_ALL = 32,
- X_UNUSED = 0, X_BOTH_NONE = 1, X_GSUB_NONE = 2,
- X_GSUB_PART = 3, X_GPOS_NONE = 4, X_GPOS_PART = 5, X_COUNT };
-
-static const char * const x_messages[] = {
- "% ignored, not supported by font",
- "% ignored, too complex for me",
- "complex substitutions from % ignored",
- "some complex substitutions from % ignored",
- "complex positionings from % ignored",
- "some complex positionings from % ignored",
-};
-
-static void
-report_underused_features(const HashMap<uint32_t, int> &feature_usage, ErrorHandler *errh)
-{
- Vector<String> x[X_COUNT];
- for (int i = 0; i < interesting_features.size(); i++) {
- OpenType::Tag f = interesting_features[i];
- int fu = feature_usage[f.value()];
- String ftext = errh->format("%<%s%>", f.text().c_str());
- if (fu == 0)
- x[X_UNUSED].push_back(ftext);
- else if ((fu & (F_GSUB_TRY | F_GPOS_TRY)) == fu)
- x[X_BOTH_NONE].push_back(ftext);
- else {
- if (fu & F_GSUB_TRY) {
- if ((fu & (F_GSUB_PART | F_GSUB_ALL)) == 0)
- x[X_GSUB_NONE].push_back(ftext);
- else if (fu & F_GSUB_PART)
- x[X_GSUB_PART].push_back(ftext);
- }
- if (fu & F_GPOS_TRY) {
- if ((fu & (F_GPOS_PART | F_GPOS_ALL)) == 0)
- x[X_GPOS_NONE].push_back(ftext);
- else if (fu & F_GPOS_PART)
- x[X_GPOS_PART].push_back(ftext);
- }
- }
- }
-
- for (int i = 0; i < X_COUNT; i++)
- if (x[i].size())
- goto found;
- return;
-
- found:
- for (int i = 0; i < X_COUNT; i++)
- if (x[i].size()) {
- StringAccum sa;
- const char* msg_pct = strchr(x_messages[i], '%');
- sa.append(x_messages[i], msg_pct - x_messages[i]);
- const char* sep = (x[i].size() > 2 ? ", " : " ");
- for (const String* a = x[i].begin(); a < x[i].end() - 1; a++)
- sa << *a << sep;
- sa << (x[i].size() > 1 ? "and " : "") << x[i].back()
- << (x[i].size() > 1 ? " features" : " feature") << (msg_pct+1);
- sa.append_break_lines(sa.take_string(), 58);
- errh->warning("%s", sa.c_str());
- }
-}
-
-static String otf_filename;
-
-static String
-main_dvips_map(const String &ps_name, const FontInfo &finfo, ErrorHandler *errh)
-{
- if (String fn = installed_type1(otf_filename, ps_name, (output_flags & G_TYPE1) != 0, errh))
- return "<" + pathname_filename(fn);
- if (!finfo.cff) {
- String ttf_fn, t42_fn;
- ttf_fn = installed_truetype(otf_filename, (output_flags & G_TRUETYPE) != 0, errh);
- t42_fn = installed_type42(otf_filename, ps_name, (output_flags & G_TYPE42) != 0, errh);
- if (t42_fn && (!ttf_fn || (output_flags & G_TYPE42) != 0))
- return "<" + pathname_filename(t42_fn);
- else if (ttf_fn)
- return "<" + pathname_filename(ttf_fn);
- }
- return "<" + pathname_filename(otf_filename);
-}
-
-static void
-do_gsub(Metrics& metrics, const OpenType::Font& otf,
- DvipsEncoding& dvipsenc, bool dvipsenc_literal,
- HashMap<uint32_t, int>& feature_usage,
- const Vector<PermString>& glyph_names, ErrorHandler* errh)
-{
- // find activated GSUB features
- OpenType::Gsub gsub(otf.table("GSUB"), &otf, errh);
- Vector<Lookup> lookups(gsub.nlookups(), Lookup());
- find_lookups(gsub.script_list(), gsub.feature_list(), lookups, errh);
-
- // find all characters that might result
- Vector<bool> used(glyph_names.size(), false);
- for (Metrics::Code c = 0; c < metrics.encoding_size(); ++c) {
- Metrics::Glyph g = metrics.glyph(c);
- if (g >= 0 && g < used.size())
- used[g] = true;
- }
- for (int i = 0; i < lookups.size(); ++i)
- if (lookups[i].used) {
- OpenType::GsubLookup l = gsub.lookup(i);
- l.mark_out_glyphs(gsub, used);
- }
- OpenType::Coverage used_coverage(used);
-
- // apply activated GSUB features
- Vector<OpenType::Substitution> subs;
- for (int i = 0; i < lookups.size(); i++)
- if (lookups[i].used) {
- OpenType::GsubLookup l = gsub.lookup(i);
- subs.clear();
- bool understood = l.unparse_automatics(gsub, subs, used_coverage);
-
- // check for -ffina, which should apply only at the ends of words,
- // and -finit, which should apply only at the beginnings.
- OpenType::Tag feature = (lookups[i].features.size() == 1 ? lookups[i].features[0] : OpenType::Tag());
- if (feature == OpenType::Tag("fina") || feature == OpenType::Tag("fin2") || feature == OpenType::Tag("fin3")) {
- if (dvipsenc.boundary_char() < 0)
- errh->warning("%<-ffina%> requires a boundary character\n(The input encoding didn%,t specify a boundary character, but\nI need one to implement %<-ffina%> features correctly. Try\nthe %<--boundary-char%> option.)");
- else {
- int bg = metrics.boundary_glyph();
- for (int j = 0; j < subs.size(); j++)
- subs[j].add_outer_right(bg);
- }
- } else if (feature == OpenType::Tag("init")) {
- int bg = metrics.boundary_glyph();
- for (int j = 0; j < subs.size(); j++)
- subs[j].add_outer_left(bg);
- }
-
- //for (int subno = 0; subno < subs.size(); subno++) fprintf(stderr, "%5d\t%s\n", i, subs[subno].unparse().c_str());
-
- // figure out which glyph filter to use
- int nunderstood = metrics.apply(subs, !dvipsenc_literal, i, *lookups[i].filter, glyph_names);
-
- // mark as used
- int d = (understood && nunderstood == subs.size() ? F_GSUB_ALL : (nunderstood ? F_GSUB_PART : 0)) + F_GSUB_TRY;
- for (int j = 0; j < lookups[i].features.size(); j++)
- feature_usage.find_force(lookups[i].features[j].value()) |= d;
- }
-
- // apply alternate selectors
- if (metrics.altselectors() && !dvipsenc_literal) {
- // do lookups
- altselector_features.swap(interesting_features);
- altselector_feature_filters.swap(feature_filters);
- Vector<Lookup> alt_lookups(gsub.nlookups(), Lookup());
- find_lookups(gsub.script_list(), gsub.feature_list(), alt_lookups, ErrorHandler::silent_handler());
- Vector<OpenType::Substitution> alt_subs;
- for (int i = 0; i < alt_lookups.size(); i++)
- if (alt_lookups[i].used) {
- OpenType::GsubLookup l = gsub.lookup(i);
- alt_subs.clear();
- (void) l.unparse_automatics(gsub, alt_subs, used_coverage);
- metrics.apply_alternates(alt_subs, i, *alt_lookups[i].filter, glyph_names);
- }
- altselector_features.swap(interesting_features);
- altselector_feature_filters.swap(feature_filters);
- }
-}
-
-static bool
-kern_feature_requested()
-{
- return std::find(interesting_features.begin(), interesting_features.end(),
- OpenType::Tag("kern")) != interesting_features.end();
-}
-
-static void
-do_try_ttf_kern(Metrics& metrics, const OpenType::Font& otf, HashMap<uint32_t, int>& feature_usage, ErrorHandler* errh)
-{
- // if no GPOS "kern" lookups and "kern" requested, try "kern" table
- if (!kern_feature_requested())
- return;
- try {
- OpenType::KernTable kern(otf.table("kern"), errh);
- Vector<OpenType::Positioning> poss;
- bool understood = kern.unparse_automatics(poss, errh);
- int nunderstood = metrics.apply(poss);
-
- // mark as used
- int d = (understood && nunderstood == poss.size() ? F_GPOS_ALL : (nunderstood ? F_GPOS_PART : 0)) + F_GPOS_TRY;
- feature_usage.find_force(OpenType::Tag("kern").value()) |= d;
- } catch (OpenType::BlankTable) {
- // nada
- } catch (OpenType::Error e) {
- errh->warning("kern %<%s%> error, continuing", e.description.c_str());
- }
-}
-
-static void
-do_gpos(Metrics& metrics, const OpenType::Font& otf, HashMap<uint32_t, int>& feature_usage, ErrorHandler* errh)
-{
- OpenType::Gpos gpos(otf.table("GPOS"), errh);
- Vector<Lookup> lookups(gpos.nlookups(), Lookup());
- find_lookups(gpos.script_list(), gpos.feature_list(), lookups, errh);
-
- // OpenType recommends that if GPOS exists, but the "kern" feature loads
- // no lookups, we use the TrueType "kern" table, if any.
- if (kern_feature_requested()) {
- OpenType::Tag kern_tag("kern");
- for (Lookup *l = lookups.begin(); l != lookups.end(); ++l)
- if (std::find(l->features.begin(), l->features.end(), kern_tag) != l->features.end())
- goto skip_ttf_kern;
- do_try_ttf_kern(metrics, otf, feature_usage, errh);
- skip_ttf_kern: ;
- }
-
- Vector<OpenType::Positioning> poss;
- for (int i = 0; i < lookups.size(); i++)
- if (lookups[i].used) {
- OpenType::GposLookup l = gpos.lookup(i);
- poss.clear();
- bool understood = l.unparse_automatics(poss, errh);
- int nunderstood = metrics.apply(poss);
-
- // mark as used
- int d = (understood && nunderstood == poss.size() ? F_GPOS_ALL : (nunderstood ? F_GPOS_PART : 0)) + F_GPOS_TRY;
- for (int j = 0; j < lookups[i].features.size(); j++)
- feature_usage.find_force(lookups[i].features[j].value()) |= d;
- }
-}
-
-static void
-do_math_spacing(Metrics &metrics, const FontInfo &finfo,
- const DvipsEncoding &dvipsenc)
-{
- Transform font_xform;
- if (extend)
- font_xform.scale(extend, 1);
- if (slant)
- font_xform.shear(slant);
- CharstringBounds boundser(font_xform);
-
- double x_height = finfo.x_height(font_xform);
- double actual_slant = font_slant(finfo);
- int boundary_char = dvipsenc.boundary_char();
-
- double bounds[4], width;
-
- for (int code = 0; code < metrics.encoding_size(); code++)
- if (metrics.was_base_glyph(code)
- && code != boundary_char
- && char_bounds(bounds, width, finfo, font_xform, metrics.unicode(code))) {
- int left_sb = (bounds[0] < 0 ? (int) ceil(-bounds[0]) : 0);
- metrics.add_single_positioning(code, left_sb, 0, left_sb);
-
- if (skew_char >= 0 && code < 256) {
- double sheight = std::max(bounds[3], x_height) - 0.5 * x_height;
- double right_sb = std::max(bounds[2] - width, 0.0);
- double desired = left_sb + 0.5 * width + actual_slant * sheight + 0.25 * right_sb;
- double computed = 0.5 * (left_sb + width + right_sb);
- int skew = (int) (desired - computed);
- metrics.add_kern(code, skew_char, skew);
- }
- }
-}
-
-static void
-do_file(const String &otf_filename, const OpenType::Font &otf,
- const DvipsEncoding &dvipsenc_in, bool dvipsenc_literal,
- ErrorHandler *errh)
-{
- FontInfo finfo(&otf, errh);
- if (!finfo.ok())
- return;
- if (!finfo.cff)
- errh->warning("TrueType-flavored font support is experimental");
- if (override_is_fixed_pitch)
- finfo.set_is_fixed_pitch(is_fixed_pitch);
- if (override_italic_angle)
- finfo.set_italic_angle(italic_angle);
- if (override_x_height != FontInfo::x_height_auto)
- finfo.set_x_height(override_x_height, x_height);
-
- // save glyph names
- Vector<PermString> glyph_names;
- finfo.glyph_names(glyph_names);
- OpenType::debug_glyph_names = glyph_names;
-
- // set typeface name from font family name
- {
- String typeface = finfo.family_name();
-
- // make it reasonable for the shell
- StringAccum sa;
- for (int i = 0; i < typeface.length(); i++)
- if (isalnum((unsigned char) typeface[i]) || typeface[i] == '_' || typeface[i] == '-' || typeface[i] == '.' || typeface[i] == ',' || typeface[i] == '+')
- sa << typeface[i];
-
- set_typeface(sa.length() ? sa.take_string() : font_name, false);
- }
-
- // initialize encoding
- DvipsEncoding dvipsenc(dvipsenc_in); // make copy
- Metrics metrics(finfo.program(), finfo.nglyphs());
- // encode boundary glyph at 256; pretend its Unicode value is '\n'
- metrics.encode(256, '\n', metrics.boundary_glyph());
- if (dvipsenc_literal)
- dvipsenc.make_metrics(metrics, finfo, 0, true, errh);
- else {
- T1Secondary secondary(finfo, font_name, otf_filename);
- dvipsenc.make_metrics(metrics, finfo, &secondary, false, errh);
- }
-
- // maintain statistics about features
- HashMap<uint32_t, int> feature_usage(0);
-
- // apply activated GSUB features
- try {
- do_gsub(metrics, otf, dvipsenc, dvipsenc_literal, feature_usage, glyph_names, errh);
- } catch (OpenType::BlankTable) {
- // nada
- } catch (OpenType::Error e) {
- errh->warning("GSUB %<%s%> error, continuing", e.description.c_str());
- }
-
- // apply LIGKERN ligature commands to the result
- dvipsenc.apply_ligkern_lig(metrics, errh);
-
- // test fake ligature mechanism
- //metrics.add_threeligature('T', 'h', 'e', '0');
-
- // reencode characters to fit within 8 bytes (+ 1 for the boundary)
- if (!dvipsenc_literal)
- metrics.shrink_encoding(257, dvipsenc_in, errh);
-
- // apply activated GPOS features
- try {
- do_gpos(metrics, otf, feature_usage, errh);
- } catch (OpenType::BlankTable) {
- do_try_ttf_kern(metrics, otf, feature_usage, errh);
- } catch (OpenType::Error e) {
- errh->warning("GPOS %<%s%> error, continuing", e.description.c_str());
- }
-
- // apply LIGKERN kerning and POS positioning commands to the result
- dvipsenc.apply_ligkern_kern(metrics, errh);
- dvipsenc.apply_position(metrics, errh);
-
- // use prespecified raw fonts
- for (BaseEncoding **be = base_encodings.begin(); be != base_encodings.end(); be++)
- if (!(*be)->secondary) {
- Vector<int> mapp;
- (*be)->encoding.make_base_mappings(mapp, finfo);
- metrics.apply_base_encoding((*be)->font_name, (*be)->encoding, mapp);
- }
-
- // remove extra characters
- metrics.cut_encoding(257);
- //metrics.unparse();
- int boundary_char = dvipsenc.boundary_char();
-
- // apply letterspacing, if any
- if (letterspace)
- for (int code = 0; code < metrics.encoding_size(); code++)
- if (metrics.was_base_glyph(code) && code != boundary_char) {
- metrics.add_single_positioning(code, letterspace / 2, 0, letterspace);
- if (code < 256) {
- metrics.add_kern(code, 256, -letterspace / 2);
- metrics.add_kern(256, code, -letterspace / 2);
- }
- }
-
- // apply math letterspacing, if any
- if (math_spacing)
- do_math_spacing(metrics, finfo, dvipsenc);
-
- // reencode right components of boundary_glyph as boundary_char
- if (metrics.reencode_right_ligkern(256, boundary_char) > 0
- && boundary_char < 0) {
- errh->warning("no boundary character, ignoring some ligatures and/or kerns\n");
- errh->message("(You may want to try the --boundary-char option.)");
- }
-
- // report unused and underused features if any
- report_underused_features(feature_usage, errh);
-
- // figure out our FONTNAME
- if (!font_name) {
- // derive font name from OpenType font name
- font_name = finfo.postscript_name();
- if (encoding_file) {
- int slash = encoding_file.find_right('/') + 1;
- int dot = encoding_file.find_right('.');
- if (dot < slash) // includes dot < 0 case
- dot = encoding_file.length();
- font_name += String("--") + encoding_file.substring(slash, dot - slash);
- }
- if (interesting_scripts.size() != 2 || interesting_scripts[0] != OpenType::Tag("latn") || interesting_scripts[1].valid())
- for (int i = 0; i < interesting_scripts.size(); i += 2) {
- font_name += String("--S") + interesting_scripts[i].text();
- if (interesting_scripts[i+1].valid())
- font_name += String(".") + interesting_scripts[i+1].text();
- }
- for (int i = 0; i < interesting_features.size(); i++)
- if (feature_usage[interesting_features[i].value()])
- font_name += String("--F") + interesting_features[i].text();
- }
-
- // output encoding
- if (dvipsenc_literal) {
- out_encoding_name = dvipsenc_in.name();
- out_encoding_file = dvipsenc_in.filename();
- } else
- output_encoding(metrics, glyph_names, errh);
-
- // set up coding scheme
- if (metrics.coding_scheme())
- metrics.set_design_units(1);
- else
- metrics.set_coding_scheme(out_encoding_name);
-
- // force no type 1
- if (!finfo.cff && (output_flags & G_TYPE1)) {
- errh->warning("assuming --no-type1 since this font is TrueType-flavored");
- output_flags &= ~G_TYPE1;
- }
-
- // output
- ::otf_filename = otf_filename;
- output_metrics(metrics, finfo.postscript_name(), dvipsenc.boundary_char(),
- finfo,
- out_encoding_name, out_encoding_file,
- font_name, main_dvips_map, errh);
-}
-
-
-String
-installed_metrics_font_name(const String &base_font_name, const String &secondary)
-{
- for (BaseEncoding **be = base_encodings.begin(); be != base_encodings.end(); be++)
- if ((*be)->secondary == secondary && ::font_name == base_font_name)
- return (*be)->font_name;
- return String();
-}
-
-
-extern "C" {
-static int
-clp_parse_char(Clp_Parser *clp, const char *arg, int complain, void *)
-{
- if (arg[0] && !arg[1] && !isdigit((unsigned char) arg[0])) {
- clp->val.i = (unsigned char)arg[0];
- return 1;
- } else if (arg[0] == '-' || isdigit((unsigned char) arg[0])) {
- char *end;
- clp->val.i = strtol(arg, &end, 10);
- if (clp->val.i <= 255 && !*end)
- return 1;
- }
- if (complain)
- Clp_OptionError(clp, "'%O' expects a character, not '%s'", arg);
- return 0;
-}
-}
-
-static void
-parse_base_encodings(const String &filename, ErrorHandler *errh)
-{
- String str = read_file(filename, errh, true);
- String print_filename = (filename == "-" ? "<stdin>" : filename) + ":";
- int lineno = 1;
- str.c_str();
- const char *s_end = str.end();
- for (const char *s = str.begin(); s != s_end; ) {
- while (s != s_end && isspace((unsigned char) *s) && *s != '\n' && *s != '\r')
- s++;
- // skip comments and blank lines
- if (s != s_end && *s != '%' && *s != '#' && *s != '\n' && *s != '\r') {
- const char *w1 = s;
- while (s != s_end && !isspace((unsigned char) *s))
- s++;
- BaseEncoding *be = new BaseEncoding;
- be->font_name = str.substring(w1, s);
- while (s != s_end && isspace((unsigned char) *s) && *s != '\n' && *s != '\r')
- s++;
- const char *w2 = s;
- while (s != s_end && !isspace((unsigned char) *s))
- s++;
- String efile = str.substring(w2, s);
- while (s != s_end && isspace((unsigned char) *s) && *s != '\n' && *s != '\r')
- s++;
- if (s != s_end && !isspace((unsigned char) *s) && *s != '%' && *s != '#') {
- const char *w3 = s;
- while (s != s_end && !isspace((unsigned char) *s))
- s++;
- be->secondary = str.substring(w3, s);
- }
- LandmarkErrorHandler lerrh(errh, print_filename + String(lineno));
- int before = lerrh.nerrors();
- if (be->secondary)
- /* encoding ignored */;
- else if (!efile)
- lerrh.error("missing encoding name");
- else if (String path = locate_encoding(efile, errh))
- be->encoding.parse(path, true, true, &lerrh);
- else
- lerrh.error("encoding %<%s%> not found", efile.c_str());
- if (lerrh.nerrors() == before)
- base_encodings.push_back(be);
- else
- delete be;
- }
- while (s != s_end && *s != '\n' && *s != '\r')
- s++;
- if (s != s_end && *s == '\r')
- s++;
- if (s != s_end && *s == '\n')
- s++;
- lineno++;
- }
-}
-
-int
-main(int argc, char *argv[])
-{
-#ifndef WIN32
- handle_sigchld();
-#endif
- Clp_Parser *clp =
- Clp_NewParser(argc, (const char * const *)argv, sizeof(options) / sizeof(options[0]), options);
- Clp_AddType(clp, CHAR_OPTTYPE, 0, clp_parse_char, 0);
- program_name = Clp_ProgramName(clp);
-#if HAVE_KPATHSEA
- kpsei_init(argv[0], "lcdftools");
-#endif
-#ifdef HAVE_CTIME
- {
- time_t t = time(0);
- char *c = ctime(&t);
- current_time = " on " + String(c).substring(0, -1); // get rid of \n
- }
-#endif
- for (int i = 0; i < argc; i++)
- invocation << (i ? " " : "") << argv[i];
-
- ErrorHandler *errh = ErrorHandler::static_initialize(new FileErrorHandler(stderr, String(program_name) + ": "));
- const char *input_file = 0;
- Vector<String> glyphlist_files;
- bool literal_encoding = false;
- bool have_encoding_file = false;
- Vector<String> ligkern;
- Vector<String> pos;
- Vector<String> unicoding;
- Vector<String> base_encoding_files;
- bool no_ecommand = false, default_ligkern = true;
- int warn_missing = -1;
- unsigned specified_output_flags = 0;
- String codingscheme;
- const char* odirs[NUMODIR + 1];
- for (int i = 0; i <= NUMODIR; ++i) {
- odirs[i] = 0;
- }
-
- GlyphFilter current_substitution_filter;
- GlyphFilter current_alternate_filter;
- GlyphFilter* current_filter_ptr = &null_filter;
- Vector<GlyphFilter*> allocated_filters;
-
- while (1) {
- int opt = Clp_Next(clp);
- switch (opt) {
-
- case SCRIPT_OPT: {
- String arg = clp->vstr;
- int period = arg.find_left('.');
- OpenType::Tag scr(period <= 0 ? arg : arg.substring(0, period));
- if (scr.valid() && period > 0) {
- OpenType::Tag lang(arg.substring(period + 1));
- if (lang.valid()) {
- interesting_scripts.push_back(scr);
- interesting_scripts.push_back(lang);
- } else
- usage_error(errh, "bad language tag");
- } else if (scr.valid()) {
- interesting_scripts.push_back(scr);
- interesting_scripts.push_back(OpenType::Tag());
- } else
- usage_error(errh, "bad script tag");
- break;
- }
-
- case FEATURE_OPT: {
- OpenType::Tag t(clp->vstr);
- if (!t.valid())
- usage_error(errh, "bad feature tag");
- else if (feature_filters[t])
- usage_error(errh, "feature %<%s%> included twice", t.text().c_str());
- else {
- if (!current_filter_ptr) {
- current_filter_ptr = new GlyphFilter(current_substitution_filter + current_alternate_filter);
- allocated_filters.push_back(current_filter_ptr);
- }
- interesting_features.push_back(t);
- feature_filters.insert(t, current_filter_ptr);
- }
- break;
- }
-
- case LETTER_FEATURE_OPT: {
- OpenType::Tag t(clp->vstr);
- if (!t.valid())
- usage_error(errh, "bad feature tag");
- else if (feature_filters[t])
- usage_error(errh, "feature %<%s%> included twice", t.text().c_str());
- else {
- interesting_features.push_back(t);
- GlyphFilter* gf = new GlyphFilter;
- gf->add_substitution_filter("<Letter>", false, errh);
- *gf += current_alternate_filter;
- feature_filters.insert(t, gf);
- }
- break;
- }
-
- case SUBS_FILTER_OPT:
- current_substitution_filter = null_filter;
- /* fallthru */
- case EXCLUDE_SUBS_OPT:
- case INCLUDE_SUBS_OPT:
- current_substitution_filter.add_substitution_filter(clp->vstr, opt == EXCLUDE_SUBS_OPT, errh);
- current_filter_ptr = 0;
- break;
-
- case CLEAR_SUBS_OPT:
- current_substitution_filter = null_filter;
- current_filter_ptr = 0;
- break;
-
- case ENCODING_OPT:
- if (encoding_file)
- usage_error(errh, "encoding specified twice");
- encoding_file = clp->vstr;
- have_encoding_file = true;
- break;
-
- case LITERAL_ENCODING_OPT:
- if (encoding_file)
- usage_error(errh, "encoding specified twice");
- encoding_file = clp->vstr;
- have_encoding_file = true;
- literal_encoding = true;
- break;
-
- case BASE_ENCODINGS_OPT:
- base_encoding_files.push_back(clp->vstr);
- break;
-
- case EXTEND_OPT:
- if (extend)
- usage_error(errh, "extend value specified twice");
- extend = clp->val.d;
- break;
-
- case SLANT_OPT:
- if (slant)
- usage_error(errh, "slant value specified twice");
- slant = clp->val.d;
- break;
-
- case LETTERSPACE_OPT:
- if (letterspace)
- usage_error(errh, "letterspacing value specified twice");
- letterspace = clp->val.i;
- break;
-
- case SPACE_FACTOR_OPT:
- if (space_factor != 1)
- usage_error(errh, "space factor specified twice");
- space_factor = clp->val.d;
- break;
-
- case MATH_SPACING_OPT:
- math_spacing = !clp->negated;
- if (math_spacing && clp->have_val) {
- if (clp->val.i < 0 || clp->val.i > 255)
- usage_error(errh, "--math-spacing skew character must be between 0 and 255");
- skew_char = clp->val.i;
- }
- break;
-
- case DESIGN_SIZE_OPT:
- if (design_size > 0)
- usage_error(errh, "design size value specified twice");
- else if (clp->val.d <= 0)
- usage_error(errh, "design size must be > 0");
- design_size = clp->val.d;
- break;
-
- case LIGKERN_OPT:
- ligkern.push_back(clp->vstr);
- break;
-
- case POSITION_OPT:
- pos.push_back(clp->vstr);
- break;
-
- case WARN_MISSING_OPT:
- warn_missing = !clp->negated;
- break;
-
- case NO_ECOMMAND_OPT:
- no_ecommand = true;
- break;
-
- case DEFAULT_LIGKERN_OPT:
- default_ligkern = !clp->negated;
- break;
-
- case BOUNDARY_CHAR_OPT:
- ligkern.push_back(String("|| = ") + String(clp->val.i));
- break;
-
- case ALTSELECTOR_CHAR_OPT:
- ligkern.push_back(String("^^ = ") + String(clp->val.i));
- break;
-
- case ALTSELECTOR_FEATURE_OPT: {
- OpenType::Tag t(clp->vstr);
- if (!t.valid())
- usage_error(errh, "bad feature tag");
- else if (altselector_feature_filters[t])
- usage_error(errh, "altselector feature %<%s%> included twice", t.text().c_str());
- else {
- if (!current_filter_ptr) {
- current_filter_ptr = new GlyphFilter(current_substitution_filter + current_alternate_filter);
- allocated_filters.push_back(current_filter_ptr);
- }
- altselector_features.push_back(t);
- altselector_feature_filters.insert(t, current_filter_ptr);
- }
- break;
- }
-
- case ALTERNATES_FILTER_OPT:
- current_alternate_filter = null_filter;
- /* fallthru */
- case EXCLUDE_ALTERNATES_OPT:
- case INCLUDE_ALTERNATES_OPT:
- current_alternate_filter.add_alternate_filter(clp->vstr, opt == EXCLUDE_ALTERNATES_OPT, errh);
- current_filter_ptr = 0;
- break;
-
- case CLEAR_ALTERNATES_OPT:
- current_alternate_filter = null_filter;
- current_filter_ptr = 0;
- break;
-
- case UNICODING_OPT:
- unicoding.push_back(clp->vstr);
- break;
-
- case CODINGSCHEME_OPT:
- if (codingscheme)
- usage_error(errh, "coding scheme specified twice");
- codingscheme = clp->vstr;
- if (codingscheme.length() > 39)
- errh->warning("only first 39 characters of coding scheme are significant");
- if (codingscheme.find_left('(') >= 0 || codingscheme.find_left(')') >= 0)
- usage_error(errh, "coding scheme cannot contain parentheses");
- break;
-
- case AUTOMATIC_OPT:
- automatic = !clp->negated;
- break;
-
- case VENDOR_OPT:
- if (!set_vendor(clp->vstr))
- usage_error(errh, "vendor name specified twice");
- break;
-
- case TYPEFACE_OPT:
- if (!set_typeface(clp->vstr, true))
- usage_error(errh, "typeface name specified twice");
- break;
-
- case VIRTUAL_OPT:
- if (clp->negated)
- output_flags &= ~G_VMETRICS;
- else
- output_flags |= G_VMETRICS;
- specified_output_flags |= G_VMETRICS;
- break;
-
- case NO_ENCODING_OPT:
- case NO_TYPE1_OPT:
- case NO_DOTLESSJ_OPT:
- case NO_UPDMAP_OPT:
- case UPDMAP_SYS_OPT:
- output_flags &= ~(opt - NO_OUTPUT_OPTS);
- specified_output_flags |= opt - NO_OUTPUT_OPTS;
- break;
-
- case TRUETYPE_OPT:
- case TYPE42_OPT:
- case UPDMAP_USER_OPT:
- if (!clp->negated)
- output_flags |= (opt - YES_OUTPUT_OPTS);
- else
- output_flags &= ~(opt - YES_OUTPUT_OPTS);
- specified_output_flags |= opt - YES_OUTPUT_OPTS;
- break;
-
- case OUTPUT_ENCODING_OPT:
- if (out_encoding_file)
- usage_error(errh, "encoding output file specified twice");
- out_encoding_file = (clp->have_val ? clp->vstr : "-");
- output_flags = G_ENCODING;
- specified_output_flags = -1;
- break;
-
- case MINIMUM_KERN_OPT:
- minimum_kern = clp->val.d;
- break;
-
- case MAP_FILE_OPT:
- if (clp->negated)
- output_flags &= ~G_PSFONTSMAP;
- else {
- output_flags |= G_PSFONTSMAP;
- if (!set_map_file(clp->vstr))
- usage_error(errh, "map file specified twice");
- }
- specified_output_flags |= G_PSFONTSMAP;
- break;
-
- case PL_OPT:
- if (clp->negated)
- output_flags &= ~G_ASCII;
- else
- output_flags |= G_ASCII;
- specified_output_flags |= G_ASCII;
- break;
-
- case TFM_OPT:
- if (clp->negated)
- output_flags &= ~G_BINARY;
- else
- output_flags |= G_BINARY;
- specified_output_flags |= G_BINARY;
- break;
-
- case ENCODING_DIR_OPT:
- case TFM_DIR_OPT:
- case PL_DIR_OPT:
- case VF_DIR_OPT:
- case VPL_DIR_OPT:
- case TYPE1_DIR_OPT:
- case TRUETYPE_DIR_OPT:
- case TYPE42_DIR_OPT:
- case DIR_OPT:
- if (!odirs[opt - DIR_OPTS])
- odirs[opt - DIR_OPTS] = clp->vstr;
- else
- usage_error(errh, "%s directory specified twice", odirname(opt - DIR_OPTS));
- break;
-
- case FONT_NAME_OPT:
- font_name:
- if (font_name)
- usage_error(errh, "font name specified twice");
- font_name = clp->vstr;
- break;
-
- case FIXED_PITCH_OPT:
- override_is_fixed_pitch = true;
- is_fixed_pitch = !clp->negated;
- break;
-
- case PROPORTIONAL_WIDTH_OPT:
- override_is_fixed_pitch = true;
- is_fixed_pitch = !!clp->negated;
- break;
-
- case ITALIC_ANGLE_OPT:
- override_italic_angle = true;
- italic_angle = clp->val.d;
- break;
-
- case GLYPHLIST_OPT:
- glyphlist_files.push_back(clp->vstr);
- break;
-
- case QUERY_FEATURES_OPT:
- usage_error(errh, "run %<otfinfo --query-features%> instead");
- break;
-
- case QUERY_SCRIPTS_OPT:
- usage_error(errh, "run %<otfinfo --query-scripts%> instead");
- break;
-
- case QUIET_OPT:
- if (clp->negated)
- errh = ErrorHandler::default_handler();
- else
- // 9.Nov.05 -- need a new SilentErrorHandler, because we use
- // the base SilentErrorHandler elsewhere to ignore errors
- errh = new SilentErrorHandler;
- break;
-
- case VERBOSE_OPT:
- verbose = !clp->negated;
- break;
-
- case NOCREATE_OPT:
- no_create = clp->negated;
- break;
-
- case FORCE_OPT:
- force = !clp->negated;
- break;
-
- case KPATHSEA_DEBUG_OPT:
-#if HAVE_KPATHSEA
- kpsei_set_debug_flags(clp->val.u);
-#else
- errh->warning("Not compiled with kpathsea!");
-#endif
- break;
-
- case X_HEIGHT_OPT: {
- char* ends;
- if (strcmp(clp->vstr, "auto") == 0)
- override_x_height = FontInfo::x_height_auto;
- else if (strcmp(clp->vstr, "x") == 0)
- override_x_height = FontInfo::x_height_x;
- else if (strcmp(clp->vstr, "font") == 0
- || strcmp(clp->vstr, "os/2") == 0)
- override_x_height = FontInfo::x_height_os2;
- else if ((x_height = strtod(clp->vstr, &ends)) >= 0
- && *ends == 0 && *clp->vstr != 0)
- override_x_height = FontInfo::x_height_explicit;
- else
- usage_error(errh, "bad --x-height option");
- break;
- }
-
- case VERSION_OPT:
- printf("otftotfm (LCDF typetools) %s\n", VERSION);
- printf("Copyright (C) 2002-2019 Eddie Kohler\n\
-This is free software; see the source for copying conditions.\n\
-There is NO warranty, not even for merchantability or fitness for a\n\
-particular purpose.\n");
- exit(0);
- break;
-
- case HELP_OPT:
- usage();
- exit(0);
- break;
-
- case Clp_NotOption:
- if (input_file && font_name)
- usage_error(errh, "too many arguments");
- else if (input_file)
- goto font_name;
- else
- input_file = clp->vstr;
- break;
-
- case Clp_Done:
- goto done;
-
- case Clp_BadOption:
- usage_error(errh, 0);
- break;
-
- default:
- break;
-
- }
- }
-
- done:
- // check for odd option combinations
- if (warn_missing > 0 && !(output_flags & G_VMETRICS))
- errh->warning("%<--warn-missing%> has no effect with %<--no-virtual%>");
- if (!(specified_output_flags & (G_BINARY | G_ASCII)))
- output_flags |= G_BINARY;
-
- // set up file names
- if (!input_file)
- usage_error(errh, "no font filename provided");
- if (encoding_file == "-")
- encoding_file = "";
-
- // set up output directories
- if (odirs[NUMODIR]) {
- for (int i = 0; i < NUMODIR; ++i)
- if (!odirs[i])
- odirs[i] = odirs[NUMODIR];
- }
- for (int i = 0; i < NUMODIR; ++i)
- if (odirs[i])
- setodir(i, odirs[i]);
-
- // set up feature filters
- if (!altselector_features.size()) {
- if (!current_filter_ptr) {
- current_filter_ptr = new GlyphFilter(current_substitution_filter + current_alternate_filter);
- allocated_filters.push_back(current_filter_ptr);
- }
- altselector_features.push_back(OpenType::Tag("dlig"));
- altselector_feature_filters.insert(OpenType::Tag("dlig"), current_filter_ptr);
- altselector_features.push_back(OpenType::Tag("salt"));
- altselector_feature_filters.insert(OpenType::Tag("salt"), current_filter_ptr);
- } else if (!current_filter_ptr) {
- errh->warning("some filtering options ignored");
- errh->message("(--include-*, --exclude-*, and --*-filter options must occur\nbefore the feature options to which they should apply.)");
- }
-
- try {
- // read font
- otf_data = read_file(input_file, errh);
- if (errh->nerrors())
- exit(1);
-
- LandmarkErrorHandler cerrh(errh, printable_filename(input_file));
- BailErrorHandler bail_errh(&cerrh);
-
- OpenType::Font otf(otf_data, &bail_errh);
- assert(otf.ok());
-
- // figure out scripts we care about
- if (!interesting_scripts.size()) {
- interesting_scripts.push_back(Efont::OpenType::Tag("latn"));
- interesting_scripts.push_back(Efont::OpenType::Tag());
- }
- std::sort(interesting_features.begin(), interesting_features.end());
- std::sort(altselector_features.begin(), altselector_features.end());
-
- // find glyphlist
- if (!glyphlist_files.size()) {
-#if HAVE_KPATHSEA
- if (String g = kpsei_find_file("glyphlist.txt", KPSEI_FMT_MAP)) {
- glyphlist_files.push_back(g);
- if (verbose)
- errh->message("glyphlist.txt found with kpathsea at %s", g.c_str());
- } else
-#endif
- glyphlist_files.push_back(GLYPHLISTDIR "/glyphlist.txt");
-#if HAVE_KPATHSEA
- if (String g = kpsei_find_file("texglyphlist.txt", KPSEI_FMT_MAP)) {
- glyphlist_files.push_back(g);
- if (verbose)
- errh->message("texglyphlist.txt found with kpathsea at %s", g.c_str());
- } else
-#endif
- glyphlist_files.push_back(GLYPHLISTDIR "/texglyphlist.txt");
- }
-
- // read glyphlist
- for (String *g = glyphlist_files.begin(); g < glyphlist_files.end(); g++)
- if (String s = read_file(*g, errh, true))
- DvipsEncoding::add_glyphlist(s);
-
- // read base encodings
- for (String *s = base_encoding_files.begin(); s < base_encoding_files.end(); s++)
- parse_base_encodings(*s, errh);
-
- // read encoding
- DvipsEncoding dvipsenc;
- if (encoding_file) {
- if (String path = locate_encoding(encoding_file, errh))
- dvipsenc.parse(path, no_ecommand, no_ecommand, errh);
- else
- errh->fatal("encoding %<%s%> not found", encoding_file.c_str());
- } else {
- String cff_data(otf.table("CFF"));
- if (!cff_data) {
- errh->error("explicit encoding required for TrueType fonts");
- errh->message("(Use %<-e ENCODING%> to choose an encoding. %<-e texnansx%> often works.)");
- exit(1);
- } else if (!have_encoding_file) {
- errh->warning("no encoding provided");
- errh->message("(Use %<-e ENCODING%> to choose an encoding. %<-e texnansx%> often works,\nor say %<-e -%> to turn off this warning.)");
- }
-
- // use encoding from font
- Cff cff(cff_data, otf.units_per_em(), &bail_errh);
- Cff::FontParent *font = cff.font(PermString(), &bail_errh);
- assert(cff.ok() && font->ok());
- if (Type1Encoding *t1e = font->type1_encoding()) {
- for (int i = 0; i < 256; i++)
- dvipsenc.encode(i, (*t1e)[i]);
- } else
- errh->fatal("font has no encoding, specify one explicitly");
- }
-
- // apply default ligkern commands
- if (default_ligkern)
- dvipsenc.parse_ligkern(default_ligkerns, 0, ErrorHandler::silent_handler());
-
- // apply command-line ligkern commands and coding scheme
- cerrh.set_landmark("--ligkern command");
- for (int i = 0; i < ligkern.size(); i++)
- dvipsenc.parse_ligkern(ligkern[i], 1, &cerrh);
- cerrh.set_landmark("--position command");
- for (int i = 0; i < pos.size(); i++)
- dvipsenc.parse_position(pos[i], 1, &cerrh);
- cerrh.set_landmark("--unicoding command");
- for (int i = 0; i < unicoding.size(); i++)
- dvipsenc.parse_unicoding(unicoding[i], 1, &cerrh);
- if (codingscheme)
- dvipsenc.set_coding_scheme(codingscheme);
- if (warn_missing >= 0)
- dvipsenc.set_warn_missing(warn_missing);
-
- do_file(input_file, otf, dvipsenc, literal_encoding, errh);
-
- } catch (OpenType::Error e) {
- errh->error("unhandled exception %<%s%>", e.description.c_str());
- }
-
- for (int i = 0; i < allocated_filters.size(); ++i)
- delete allocated_filters[i];
- Clp_DeleteParser(clp);
- return (errh->nerrors() == 0 ? 0 : 1);
-}