diff options
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/cff.cc')
-rw-r--r-- | Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/cff.cc | 1693 |
1 files changed, 1693 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/cff.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/cff.cc new file mode 100644 index 00000000000..c0eadaa9ae6 --- /dev/null +++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-src/libefont/cff.cc @@ -0,0 +1,1693 @@ +// -*- related-file-name: "../include/efont/cff.hh" -*- + +/* cff.{cc,hh} -- Compact Font Format fonts + * + * Copyright (c) 1998-2014 Eddie Kohler + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <efont/cff.hh> +#include <lcdf/error.hh> +#include <efont/t1item.hh> +#include <lcdf/straccum.hh> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <ctype.h> +#include <efont/t1unparser.hh> + +#ifndef static_assert +#define static_assert(c, msg) switch ((int) (c)) case 0: case (c): +#endif + +namespace Efont { + +const char * const Cff::operator_names[] = { + "version", "Notice", "FullName", "FamilyName", + "Weight", "FontBBox", "BlueValues", "OtherBlues", + "FamilyBlues", "FamilyOtherBlues", "StdHW", "StdVW", + "UNKNOWN_12", "UniqueID", "XUID", "charset", + "Encoding", "CharStrings", "Private", "Subrs", + "defaultWidthX", "nominalWidthX", "UNKNOWN_22", "UNKNOWN_23", + "UNKNOWN_24", "UNKNOWN_25", "UNKNOWN_26", "UNKNOWN_27", + "UNKNOWN_28", "UNKNOWN_29", "UNKNOWN_30", "UNKNOWN_31", + "Copyright", "isFixedPitch", "ItalicAngle", "UnderlinePosition", + "UnderlineThickness", "PaintType", "CharstringType", "FontMatrix", + "StrokeWidth", "BlueScale", "BlueShift", "BlueFuzz", + "StemSnapH", "StemSnapV", "ForceBold", "UNKNOWN_12_15", + "UNKNOWN_12_16", "LanguageGroup", "ExpansionFactor", "initialRandomSeed", + "SyntheticBase", "PostScript", "BaseFontName", "BaseFontBlend", + "UNKNOWN_12_24", "UNKNOWN_12_25", "UNKNOWN_12_26", "UNKNOWN_12_27", + "UNKNOWN_12_28", "UNKNOWN_12_29", "ROS", "CIDFontVersion", + "CIDFontRevision", "CIDFontType", "CIDCount", "UIDBase", + "FDArray", "FDSelect", "FontName" +}; + +const int Cff::operator_types[] = { + tSID, tSID, tSID, tSID, // version, Notice, FullName, FamilyName + tSID, tArray4, tP+tArray, tP+tArray, // Weight, FontBBox, BlueValues, OtherBlues + tP+tArray, tP+tArray, tP+tNumber, tP+tNumber, // FamBlues, FamOthBlues, StdHW, StdVW + tNone, tNumber, tArray, tOffset, // escape, UniqueID, XUID, charset + tOffset, tOffset, tPrivateType, tP+tLocalOffset, // Encoding, CharStrings, Private, Subrs + tP+tNumber, tP+tNumber, tNone, tNone, // defaultWX, nominalWX, 22, 23 + tNone, tNone, tNone, tNone, // 24, 25, 26, 27 + tNone, tNone, tNone, tNone, // 28, 29, 30, 31 + tSID, tBoolean, tNumber, tNumber, // Copyright, isFixedPitch, ItalicAngle, UnderlinePosition + tNumber, tNumber, tNumber, tArray6, // UnderlineThickness, PaintType, CharstringType, FontMatrix + tNumber, tP+tNumber, tP+tNumber, tP+tNumber, // StrokeWidth, BlueScale, BlueShift, BlueFuzz + tP+tArray, tP+tArray, tP+tBoolean, tNone, // StemSnapH, StemSnapV, ForceBold, 12 15 + tNone, tP+tNumber, tP+tNumber, tP+tNumber, // 12 16, LanguageGroup, ExpansionFactor, initialRandomSeed + tNumber, tSID, tSID, tArray, // SyntheticBase, PostScript, BaseFontName, BaseFontBlend + tNone, tNone, tNone, tNone, // 12 24, 12 25, 12 26, 12 27 + tNone, tNone, tArray, tNumber, // 12 28, 12 29, ROS, CIDFontVersion + tNumber, tNumber, tNumber, tNumber, // CIDFontRevision, CIDFontType, CIDCount, UIDBase + tOffset, tOffset, tSID // FDArray, FDSelect, FontName +}; + +static PermString::Initializer initializer; +static const char * const standard_strings[] = { + // Automatically generated from Appendix A of the CFF specification; do + // not edit. Size should be 391. + ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", + "percent", "ampersand", "quoteright", "parenleft", "parenright", + "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", + "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", + "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", + "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", + "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", + "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", + "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", + "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", + "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", + "florin", "section", "currency", "quotesingle", "quotedblleft", + "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash", + "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet", + "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", + "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex", + "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", + "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", + "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", + "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", + "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", + "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", + "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", + "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", + "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", + "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", + "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", + "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", + "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", + "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", + "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", + "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", + "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall", + "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", + "parenleftsuperior", "parenrightsuperior", "twodotenleader", + "onedotenleader", "zerooldstyle", "oneoldstyle", "twooldstyle", + "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", + "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior", + "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", + "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", + "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", + "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", + "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", + "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", + "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", + "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", + "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", + "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", + "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", + "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", + "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", + "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", + "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior", + "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", + "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", + "threeinferior", "fourinferior", "fiveinferior", "sixinferior", + "seveninferior", "eightinferior", "nineinferior", "centinferior", + "dollarinferior", "periodinferior", "commainferior", "Agravesmall", + "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", + "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", + "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", + "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", + "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", + "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", + "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", + "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black", + "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold" +}; +static PermString standard_permstrings[Cff::NSTANDARD_STRINGS]; +static HashMap<PermString, int> standard_permstrings_map(-1); + +static const int standard_encoding[] = { + // Automatically generated from Appendix B of the CFF specification; do + // not edit. Size should be 256. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 0, 111, 112, 113, + 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, + 0, 123, 0, 124, 125, 126, 127, 128, 129, 130, + 131, 0, 132, 133, 0, 134, 135, 136, 137, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 138, 0, 139, 0, 0, + 0, 0, 140, 141, 142, 143, 0, 0, 0, 0, + 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, + 148, 149, 0, 0, 0, 0 +}; + +static const int expert_encoding[] = { + // Automatically generated from Appendix B of the CFF specification; do + // not edit. Size should be 256. + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 229, 230, 0, 231, 232, 233, 234, + 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, + 249, 250, 251, 252, 0, 253, 254, 255, 256, 257, + 0, 0, 0, 258, 0, 0, 259, 260, 261, 262, + 0, 0, 263, 264, 265, 0, 266, 109, 110, 267, + 268, 269, 0, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 304, 305, 306, 0, 0, 307, 308, 309, 310, + 311, 0, 312, 0, 0, 313, 0, 0, 314, 315, + 0, 0, 316, 317, 318, 0, 0, 0, 158, 155, + 163, 319, 320, 321, 322, 323, 324, 325, 0, 0, + 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, + 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, + 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378 +}; + +static const int iso_adobe_charset[] = { + // Automatically generated from Appendix C of the CFF specification; do + // not edit. + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228 +}; + +static const int expert_charset[] = { + // Automatically generated from Appendix C of the CFF specification; do + // not edit. + 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, + 326, 150, 164, 169, 327, 328, 329, 330, 331, 332, + 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, + 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, + 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378 +}; + +static const int expert_subset_charset[] = { + // Automatically generated from Appendix C of the CFF specification; do + // not edit. + 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, + 15, 99, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 109, 110, 267, 268, 269, 270, 272, 300, 301, + 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, + 323, 324, 325, 326, 150, 164, 169, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346 +}; + +static const uint8_t default_dict_cff_data[] = { + // CFF header + 1, 0, // format major and minor version + 4, // header size + 4, // absolute offset size + + // Name INDEX + 0, 1, // one element in index + 1, // offset size + 1, 14, // offset array + '%', 'D', 'E', 'F', 'A', 'U', 'L', 'T', 'D', 'I', 'C', 'T', '%', + + // Top DICT INDEX + 0, 1, // one element in index + 1, // offset size + 1, 92, // offset array + // and the DICT + 139, 12, 1, // isFixedPitch false + 139, 12, 2, // ItalicAngle 0 + 39, 12, 3, // UnderlinePosition -100 + 189, 12, 4, // UnderlineThickness 50 + 139, 12, 5, // PaintType 0 + 141, 12, 6, // CharstringType 2 + 30, 0x0A, 0x00, 0x1F, 139, 139, 30, 0x0A, 0x00, 0x1F, 139, 139, 12, 7, + // FontMatrix 0.001 0 0 0.001 0 0 + 139, 139, 139, 139, 5, // FontBBox 0 0 0 0 + 139, 12, 8, // StrokeWidth 0 + 139, 15, // charset 0 + 139, 16, // Encoding 0 + 139, 12, 31, // CIDFontVersion 0 + 139, 12, 32, // CIDFontRevision 0 + 139, 12, 33, // CIDFontType 0 + 28, 34, 16, 12, 34, // CIDCount 8720 + 30, 0x0A, 0x03, 0x96, 0x25, 0xFF, 12, 9, // BlueScale 0.039625 + 146, 12, 10, // BlueShift 7 + 140, 12, 11, // BlueFuzz 1 + 139, 12, 14, // ForceBold false + 139, 12, 17, // LanguageGroup 0 + 30, 0x0A, 0x06, 0xFF, 12, 18, // ExpansionFactor 0.06 + 139, 12, 19, // initialRandomSeed 0 + 139, 20, // defaultWidthX 0 + 139, 21, // nominalWidthX 0 + + // String INDEX + 0, 0, + + // Gsubr INDEX + 0, 0 +}; + +static const Cff::Dict & +default_dict() +{ + static Cff *cff; + static Cff::Font *cfffont; + if (!cfffont) { + cff = new Cff(String::make_stable((const char *) default_dict_cff_data, sizeof(default_dict_cff_data)), + 0, ErrorHandler::default_handler()); + cfffont = (Cff::Font *) cff->font(); + } + return cfffont->top_dict(); +} + + +#define POS_GT(pos1, pos2) ((unsigned)(pos1) > (unsigned)(pos2)) + + +Cff::Cff(const String& s, unsigned units_per_em, ErrorHandler* errh) + : _data_string(s), _data(reinterpret_cast<const uint8_t *>(_data_string.data())), _len(_data_string.length()), + _strings_map(-2), _units_per_em(units_per_em) +{ + static_assert((sizeof(standard_strings) / sizeof(standard_strings[0])) == NSTANDARD_STRINGS, + "NSTANDARD_STRINGS defined incorrectly"); + static_assert((sizeof(standard_encoding) / sizeof(standard_encoding[0])) == 256, + "standard_encoding has wrong size"); + static_assert((sizeof(expert_encoding) / sizeof(expert_encoding[0])) == 256, + "expert_encoding has wrong size"); + _error = parse_header(errh ? errh : ErrorHandler::silent_handler()); +} + +Cff::~Cff() +{ + for (int i = 0; i < _gsubrs_cs.size(); i++) + delete _gsubrs_cs[i]; +} + +/* + * Parsing the file header + */ + +int +Cff::parse_header(ErrorHandler *errh) +{ + if (_gsubrs_index.error() >= 0) // already done + return 0; + + // parse header + if (_len == 0) + return errh->error("not a PostScript-flavored OpenType font"), -EFAULT; + if (_len < HEADER_SIZE) + return errh->error("CFF file corrupted (too small)"), -EFAULT; + if (_data[0] != 1) // major version number + return errh->error("bad major version number %d", _data[0]), -ERANGE; + int hdrSize = _data[2], offSize = _data[3]; + if (hdrSize < 4 || hdrSize > _len || offSize < 1 || offSize > 4) + return errh->error("corrupted file header"), -EINVAL; + int name_index_pos = hdrSize; + + // parse name INDEX + IndexIterator niter(_data, name_index_pos, _len, errh, "Name INDEX"); + if (niter.error() < 0) + return niter.error(); + _name_index.clear(); + for (; niter; niter++) { + const uint8_t *d0 = niter[0]; + const uint8_t *d1 = niter[1]; + if (d0 == d1 || d0[0] == 0) + _name_index.push_back(PermString()); + else + _name_index.push_back(PermString(reinterpret_cast<const char *>(d0), d1 - d0)); + } + int top_dict_index_pos = niter.index_end() - _data; + + // check top DICT INDEX + _top_dict_index = IndexIterator(_data, top_dict_index_pos, _len, errh, "Top DICT INDEX"); + if (_top_dict_index.error() < 0) + return _top_dict_index.error(); + else if (_top_dict_index.nitems() != nfonts()) + return errh->error("invalid font: Top DICT INDEX has %d elements, but there are %d fonts", _top_dict_index.nitems(), nfonts()), -EINVAL; + int string_index_pos = _top_dict_index.index_end() - _data; + + // check strings INDEX + _strings_index = IndexIterator(_data, string_index_pos, _len, errh, "Strings INDEX"); + if (_strings_index.error() < 0) + return _strings_index.error(); + else if (NSTANDARD_STRINGS + _strings_index.nitems() - 1 > MAX_SID) + return errh->error("too many strings defined in font"), -EINVAL; + _strings.assign(_strings_index.nitems(), PermString()); + int global_subr_index_pos = _strings_index.index_end() - _data; + + // check gsubr INDEX + _gsubrs_index = IndexIterator(_data, global_subr_index_pos, _len, errh, "Gsubrs INDEX"); + if (_gsubrs_index.error() < 0) + return _gsubrs_index.error(); + _gsubrs_cs.assign(ngsubrs(), 0); + + return 0; +} + +int +Cff::sid(PermString s) +{ + if (!s) // XXX? + return -1; + + // check standard strings + if (standard_permstrings_map["a"] < 0) + for (int i = 0; i < NSTANDARD_STRINGS; i++) { + if (!standard_permstrings[i]) + standard_permstrings[i] = PermString(standard_strings[i]); + standard_permstrings_map.insert(standard_permstrings[i], i); + } + int sid = standard_permstrings_map[s]; + if (sid >= 0) + return sid; + + // check user strings + sid = _strings_map[s]; + if (sid >= -1) + return sid; + + for (int i = 0; i < _strings.size(); i++) + if (!_strings[i] && s.length() == _strings_index[i+1] - _strings_index[i] && memcmp(s.c_str(), _strings_index[i], s.length()) == 0) { + _strings[i] = s; + _strings_map.insert(s, i + NSTANDARD_STRINGS); + return i + NSTANDARD_STRINGS; + } + + _strings_map.insert(s, -1); + return -1; +} + +String +Cff::sid_string(int sid) const +{ + if (sid < 0) + return String(); + else if (sid < NSTANDARD_STRINGS) + return String(sid_permstring(sid)); + else { + sid -= NSTANDARD_STRINGS; + if (sid >= _strings.size()) + return String(); + else if (_strings[sid]) + return String(_strings[sid]); + else + return String(reinterpret_cast<const char *>(_strings_index[sid]), _strings_index[sid + 1] - _strings_index[sid]); + } +} + +PermString +Cff::sid_permstring(int sid) const +{ + if (sid < 0) + return PermString(); + else if (sid < NSTANDARD_STRINGS) { + if (!standard_permstrings[sid]) + standard_permstrings[sid] = PermString(standard_strings[sid]); + return standard_permstrings[sid]; + } else { + sid -= NSTANDARD_STRINGS; + if (sid >= _strings.size()) + return PermString(); + else if (_strings[sid]) + return _strings[sid]; + else { + PermString s = PermString(reinterpret_cast<const char *>(_strings_index[sid]), _strings_index[sid + 1] - _strings_index[sid]); + _strings[sid] = s; + _strings_map.insert(s, sid + NSTANDARD_STRINGS); + return s; + } + } +} + +int +Cff::font_offset(int findex, int &offset, int &length) const +{ + if (findex < 0 || findex >= nfonts()) + return -ENOENT; + offset = _top_dict_index[findex] - _data; + length = _top_dict_index[findex + 1] - _top_dict_index[findex]; + return 0; +} + +int +Cff::font_offset(PermString name, int &offset, int &length) const +{ + for (int i = 0; i < _name_index.size(); i++) + if (_name_index[i] == name && name) + return font_offset(i, offset, length); + return -ENOENT; +} + +Cff::FontParent * +Cff::font(PermString font_name, ErrorHandler *errh) +{ + if (!errh) + errh = ErrorHandler::silent_handler(); + + if (!ok()) + return errh->error("invalid CFF"), (FontParent *) 0; + + // search for a font named 'font_name' + for (int i = 0; i < _name_index.size(); i++) + if (_name_index[i] && (!font_name || font_name == _name_index[i])) { + int td_offset = _top_dict_index[i] - _data; + int td_length = _top_dict_index[i + 1] - _top_dict_index[i]; + Dict top_dict(this, td_offset, td_length, errh, "Top DICT"); + if (!top_dict.ok()) + return 0; + else if (top_dict.has_first(oROS)) + return new Cff::CIDFont(this, _name_index[i], top_dict, errh); + else + return new Cff::Font(this, _name_index[i], top_dict, errh); + } + + if (!font_name) + errh->error("no fonts in CFF"); + else + errh->error("font %<%s%> not found", font_name.c_str()); + return 0; +} + +static inline int +subr_bias(int charstring_type, int nsubrs) +{ + if (charstring_type == 1) + return 0; + else if (nsubrs < 1240) + return 107; + else if (nsubrs < 33900) + return 1131; + else + return 32768; +} + +Charstring * +Cff::gsubr(int i) +{ + i += subr_bias(2, ngsubrs()); + if (i < 0 || i >= ngsubrs()) + return 0; + if (!_gsubrs_cs[i]) { + const uint8_t *s1 = _gsubrs_index[i]; + int slen = _gsubrs_index[i + 1] - s1; + String cs = data_string().substring(s1 - data(), slen); + if (slen == 0) + return 0; + else + _gsubrs_cs[i] = new Type2Charstring(cs); + } + return _gsubrs_cs[i]; +} + + +/***** + * Cff::Charset + **/ + +Cff::Charset::Charset(const Cff *cff, int pos, int nglyphs, int max_sid, ErrorHandler *errh) +{ + assign(cff, pos, nglyphs, max_sid, errh); +} + +void +Cff::Charset::assign(const Cff *cff, int pos, int nglyphs, int max_sid, ErrorHandler *errh) +{ + if (!errh) + errh = ErrorHandler::silent_handler(); + + _sids.reserve(nglyphs); + + if (pos == 0) + assign(iso_adobe_charset, sizeof(iso_adobe_charset) / sizeof(int), nglyphs); + else if (pos == 1) + assign(expert_charset, sizeof(expert_charset) / sizeof(int), nglyphs); + else if (pos == 2) + assign(expert_subset_charset, sizeof(expert_subset_charset) / sizeof(int), nglyphs); + else + _error = parse(cff, pos, nglyphs, max_sid, errh); + + if (_error >= 0) + for (int g = 0; g < _sids.size(); g++) { + if (_gids[_sids[g]] >= 0) { + errh->error("glyph %<%s%> in charset twice", cff->sid_permstring(_sids[g]).c_str()); + _error = -EEXIST; + } + _gids[_sids[g]] = g; + } +} + +void +Cff::Charset::assign(const int *data, int size, int nglyphs) +{ + if (size < nglyphs) + size = nglyphs; + _sids.resize(size); + memcpy(&_sids[0], data, sizeof(const int) * size); + _gids.resize(data[size-1] + 1, -1); + _error = 0; +} + +int +Cff::Charset::parse(const Cff *cff, int pos, int nglyphs, int max_sid, ErrorHandler *errh) +{ + const uint8_t *data = cff->data(); + int len = cff->length(); + + if (pos + 1 > len) + return errh->error("charset position out of range"), -EFAULT; + + _sids.push_back(0); + int actual_max_sid = 0; + + int format = data[pos]; + if (format == 0) { + if (pos + 1 + (nglyphs - 1) * 2 > len) + return errh->error("charset [format 0] out of range"), -EFAULT; + const uint8_t *p = data + pos + 1; + for (; _sids.size() < nglyphs; p += 2) { + int sid = (p[0] << 8) | p[1]; + if (sid > actual_max_sid) + actual_max_sid = sid; + _sids.push_back(sid); + } + + } else if (format == 1) { + const uint8_t *p = data + pos + 1; + for (; _sids.size() < nglyphs; p += 3) { + if (p + 3 > data + len) + return errh->error("charset [format 1] out of range"), -EFAULT; + int sid = (p[0] << 8) | p[1]; + int n = p[2]; + if (sid + n > actual_max_sid) + actual_max_sid = sid + n; + for (int i = 0; i <= n; i++) + _sids.push_back(sid + i); + } + + } else if (format == 2) { + const uint8_t *p = data + pos + 1; + for (; _sids.size() < nglyphs; p += 4) { + if (p + 4 > data + len) + return errh->error("charset [format 2] out of range"), -EFAULT; + int sid = (p[0] << 8) | p[1]; + int n = (p[2] << 8) | p[3]; + if (sid + n > actual_max_sid) + actual_max_sid = sid + n; + for (int i = 0; i <= n; i++) + _sids.push_back(sid + i); + } + + } else + return errh->error("unknown charset format %d", format), -EINVAL; + + if (max_sid >= 0 && actual_max_sid > max_sid) + return errh->error("charset [format %d] uses bad SID %d", format, actual_max_sid), -EINVAL; + _sids.resize(nglyphs); + _gids.resize(actual_max_sid + 1, -1); + return 0; +} + + +/***** + * Cff::FDSelect + **/ + +void +Cff::FDSelect::assign(const Cff *cff, int pos, int nglyphs, ErrorHandler *errh) +{ + if (!errh) + errh = ErrorHandler::silent_handler(); + if (_my_fds) + delete[] _fds; + _fds = 0; + _my_fds = false; + _nglyphs = nglyphs; + _error = parse(cff, pos, nglyphs, errh); +} + +Cff::FDSelect::~FDSelect() +{ + if (_my_fds) + delete[] _fds; +} + +int +Cff::FDSelect::parse(const Cff *cff, int pos, int nglyphs, ErrorHandler *errh) +{ + const uint8_t *data = cff->data(); + int len = cff->length(); + + if (pos + 1 > len) + return errh->error("FDSelect position out of range"), -EFAULT; + + int format = data[pos]; + if (format == 0) { + if (pos + 1 + nglyphs > len) + return errh->error("FDSelect [format 0] out of range"), -EFAULT; + _fds = data + pos + 1; + _my_fds = false; + return 0; + + } else if (format == 3) { + int nranges = (data[pos+1] << 8) | data[pos+2]; + if (pos + 3 + 3*nranges + 2 > len) + return errh->error("FDSelect [format 3] out of range"), -EFAULT; + + const uint8_t *p = data + pos + 3; + int last_glyph = (p[3*nranges] << 8) | p[3*nranges + 1]; + if (p[0] || p[1] || last_glyph != nglyphs) + return errh->error("FDSelect [format 3] bad values"), -EINVAL; + + _fds = new uint8_t[nglyphs]; + _my_fds = true; + int curglyph = 0; + for (; curglyph < nglyphs; p += 3) { + int nextglyph = (p[3] << 8) | p[4]; + if (nextglyph > nglyphs || nextglyph < curglyph) + return errh->error("FDSelect [format 3] sorting error"), -EINVAL; + memset(const_cast<uint8_t *>(_fds + curglyph), p[2], nextglyph - curglyph); + curglyph = nextglyph; + } + return 0; + + } else + return errh->error("unknown charset format %d", format), -EINVAL; +} + + +/***** + * Cff::IndexIterator + **/ + +Cff::IndexIterator::IndexIterator(const uint8_t *data, int pos, int len, ErrorHandler *errh, const char *index_name) + : _contents(0), _offset(0), _last_offset(0) +{ + if (!errh) + errh = ErrorHandler::silent_handler(); + + // check header + int nitems = 0; + if (POS_GT(pos + 2, len)) { + errh->error("%s: position out of range", index_name); + _offsize = -EFAULT; + } else if (data[pos] == 0 && data[pos + 1] == 0) { + _contents = data + pos + 2; + _offsize = 0; + } else if (POS_GT(pos + 3, len)) { + errh->error("%s: position out of range", index_name); + _offsize = -EFAULT; + } else if ((_offsize = data[pos + 2]), (_offsize < 1 || _offsize > 4)) { + errh->error("%s: offset size %d out of range", index_name, _offsize); + _offsize = -EINVAL; + } else { + nitems = (data[pos] << 8) | data[pos + 1]; + if (POS_GT(pos + 3 + (nitems + 1) * _offsize, len)) { + errh->error("%s: data out of range", index_name); + _offsize = -EFAULT; + } else { + _offset = data + pos + 3; + _last_offset = _offset + nitems * _offsize; + _contents = _last_offset + _offsize - 1; + } + } + + // check items in offset array + uint32_t max_doff_allowed = len - (pos + 2 + (nitems + 1) * _offsize); + uint32_t last_doff = 1; + for (const uint8_t *o = _offset; o <= _last_offset && _offsize > 0; o += _offsize) { + uint32_t doff = offset_at(o); + if (doff > max_doff_allowed) { + errh->error("%s: element out of range", index_name); + _offsize = -EFAULT; + } else if (doff < last_doff) { + errh->error("%s: garbled elements", index_name); + break; + } + last_doff = doff; + } +} + +const uint8_t * +Cff::IndexIterator::index_end() const +{ + if (_offsize <= 0) + return _contents; + else + return _contents + offset_at(_last_offset); +} + +int +Cff::IndexIterator::nitems() const +{ + if (_offsize <= 0) + return 0; + else + return (_last_offset - _offset) / _offsize; +} + + + +/***** + * Cff::Dict + **/ + +Cff::Dict::Dict() + : _cff(0), _pos(0), _error(-ENOENT) +{ +} + +Cff::Dict::Dict(Cff *cff, int pos, int dict_len, ErrorHandler *errh, const char *dict_name) +{ + assign(cff, pos, dict_len, errh, dict_name); +} + +int +Cff::Dict::assign(Cff *cff, int pos, int dict_len, ErrorHandler *errh, const char *dict_name) +{ + _cff = cff; + _pos = pos; + _operators.clear(); + _pointers.clear(); + _operands.clear(); + + if (!errh) + errh = ErrorHandler::silent_handler(); + + const uint8_t *data = cff->data() + pos; + const uint8_t *end_data = data + dict_len; + + _pointers.push_back(0); + while (data < end_data) + switch (data[0]) { + + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + case 8: case 9: case 10: case 11: case 13: case 14: case 15: + case 16: case 17: case 18: case 19: case 20: case 21: + _operators.push_back(data[0]); + _pointers.push_back(_operands.size()); + data++; + break; + + case 22: case 23: case 24: case 25: case 26: case 27: case 31: + case 255: // reserved + errh->error("%s: reserved operator %d", dict_name, data[0]); + return (_error = -ERANGE); + + case 12: + if (data + 1 >= end_data) + goto runoff; + _operators.push_back(32 + data[1]); + _pointers.push_back(_operands.size()); + data += 2; + break; + + case 28: { + if (data + 2 >= end_data) + goto runoff; + int16_t val = (data[1] << 8) | data[2]; + _operands.push_back(val); + data += 3; + break; + } + + case 29: { + if (data + 4 >= end_data) + goto runoff; + int32_t val = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]; + _operands.push_back(val); + data += 5; + break; + } + + case 30: { + char buf[1024]; + int pos = 0; + if (data + 1 >= end_data) + goto runoff; + for (data++; data < end_data && pos < 1020; data++) { + int d = *data; + for (int i = 0; i < 2; i++, d <<= 4) { + int digit = (d >> 4) & 0xF; + switch (digit) { + case 10: + buf[pos++] = '.'; + break; + case 11: + buf[pos++] = 'E'; + break; + case 12: + buf[pos++] = 'E'; + buf[pos++] = '-'; + break; + case 13: + errh->error("%s: bad digit in real number", dict_name); + goto invalid; + case 14: + buf[pos++] = '-'; + break; + case 15: + goto found; + default: + buf[pos++] = digit + '0'; + break; + } + } + } + // number not found + goto runoff; + found: + char *endptr; + buf[pos] = '\0'; + _operands.push_back(strtod(buf, &endptr)); + if (*endptr) { + errh->error("%s: real number syntax error", dict_name); + goto invalid; + } + data++; + break; + } + + case 247: case 248: case 249: case 250: { + if (data + 1 >= end_data) + goto runoff; + int val = ((data[0] - 247) << 8) + data[1] + 108; + _operands.push_back(val); + data += 2; + break; + } + + case 251: case 252: case 253: case 254: { + if (data + 1 >= end_data) + goto runoff; + int val = -((data[0] - 251) << 8) - data[1] - 108; + _operands.push_back(val); + data += 2; + break; + } + + default: + _operands.push_back(data[0] - 139); + data++; + break; + + } + + // not closed by an operator? + if (_pointers.back() != _operands.size()) { + errh->error("%s: not closed by an operator", dict_name); + goto invalid; + } + + return (_error = 0); + + runoff: + errh->error("%s: runoff end of DICT", dict_name); + return (_error = -EFAULT); + + invalid: + return (_error = -EINVAL); +} + +int +Cff::Dict::check(bool is_private, ErrorHandler *errh, const char *dict_name) const +{ + if (!errh) + errh = ErrorHandler::silent_handler(); + int before_nerrors = errh->nerrors(); + + // keep track of operator reuse + Vector<int> operators_used; + + for (int i = 0; i < _operators.size(); i++) { + int arity = _pointers[i+1] - _pointers[i]; + double num = (arity == 0 ? 0 : _operands[_pointers[i]]); + double truncnum = floor(num); + int op = _operators[i]; + int type = (op > oLastOperator ? tNone : operator_types[op]); + + // check reuse + if (op >= operators_used.size()) + operators_used.resize(op + 1, 0); + if (operators_used[op] && (type & tTypeMask) != tNone) + errh->error("%s: operator %<%s%> specified twice", dict_name, operator_names[op]); + operators_used[op]++; + + // check data + switch (type & tTypeMask) { + + case tNone: + if (op >= 32) + errh->warning("%s: unknown operator %<12 %d%>", dict_name, op - 32); + else + errh->warning("%s: unknown operator %<%d%>", dict_name, op); + continue; + + case tSID: + if (arity != 1 || num != truncnum || num < 0 || num > _cff->max_sid()) + goto bad_data; + break; + + case tFontNumber: + if (arity != 1 || num != truncnum || num < 0 || num >= _cff->nfonts()) + goto bad_data; + break; + + case tBoolean: + if (arity != 1) + goto bad_data; + else if (num != 0 && num != 1) + errh->warning("%s: data for Boolean operator %<%s%> not 0 or 1", dict_name, operator_names[op]); + break; + + case tNumber: + if (arity != 1) + goto bad_data; + break; + + case tOffset: + if (arity != 1 || num != truncnum || num < 0 || num >= _cff->length()) + goto bad_data; + break; + + case tLocalOffset: + if (arity != 1 || num != truncnum || _pos + num < 0 || _pos + num >= _cff->length()) + goto bad_data; + break; + + case tPrivateType: { + if (arity != 2 || num != truncnum || num < 0) + goto bad_data; + double off = _operands[_pointers[i] + 1]; + if (off < 0 || off + num > _cff->length()) + goto bad_data; + break; + } + + case tArray2: case tArray3: case tArray4: + case tArray5: case tArray6: + if (arity != (type & tTypeMask) - tArray2 + 2) + goto bad_data; + break; + + case tArray: + break; + + } + + // check dict location + if (((type & tPrivate) != 0) != is_private) + errh->warning("%s: operator %<%s%> in wrong DICT", dict_name, operator_names[op]); + + continue; + + bad_data: + errh->error("%s: bad data for operator %<%s%>", dict_name, operator_names[op]); + } + + return (errh->nerrors() != before_nerrors ? -1 : 0); +} + +bool +Cff::Dict::has(DictOperator op) const +{ + for (int i = 0; i < _operators.size(); i++) + if (_operators[i] == op) + return true; + return false; +} + +bool +Cff::Dict::xvalue(DictOperator op, Vector<double> &out) const +{ + out.clear(); + for (int i = 0; i < _operators.size(); i++) + if (_operators[i] == op) { + for (int j = _pointers[i]; j < _pointers[i+1]; j++) + out.push_back(_operands[j]); + return true; + } + return false; +} + +bool +Cff::Dict::xvalue(DictOperator op, int *val) const +{ + for (int i = 0; i < _operators.size(); i++) + if (_operators[i] == op && _pointers[i] + 1 == _pointers[i+1]) { + *val = (int) _operands[_pointers[i]]; + return true; + } + return false; +} + +bool +Cff::Dict::xvalue(DictOperator op, double *val) const +{ + for (int i = 0; i < _operators.size(); i++) + if (_operators[i] == op && _pointers[i] + 1 == _pointers[i+1]) { + *val = _operands[_pointers[i]]; + return true; + } + return false; +} + +bool +Cff::Dict::value(DictOperator op, Vector<double> &out) const +{ + return xvalue(op, out) || default_dict().xvalue(op, out); +} + +bool +Cff::Dict::value(DictOperator op, int *val) const +{ + return xvalue(op, val) || default_dict().xvalue(op, val); +} + +bool +Cff::Dict::value(DictOperator op, double *val) const +{ + return xvalue(op, val) || default_dict().xvalue(op, val); +} + +void +Cff::Dict::unparse(ErrorHandler *errh, const char *dict_name) const +{ + StringAccum sa; + for (int i = 0; i < _operators.size(); i++) { + sa.clear(); + if (_pointers[i] + 1 == _pointers[i+1]) + sa << _operands[_pointers[i]]; + else { + sa << "["; + for (int j = _pointers[i]; j < _pointers[i+1]; j++) + sa << _operands[j] << ' '; + sa.pop_back(); + sa << "]"; + } + errh->message("%s: %s %s", dict_name, operator_names[_operators[i]], sa.c_str()); + } +} + + +/***** + * CffFontParent + **/ + +static int +handle_private(Cff *cff, const Cff::Dict &top_dict, Cff::Dict &private_dict, + double &default_width_x, double &nominal_width_x, + Cff::IndexIterator &subrs_index, Vector<Charstring *> &subrs_cs, + ErrorHandler *errh) +{ + Vector<double> private_info; + top_dict.value(Cff::oPrivate, private_info); + int private_offset = (int) private_info[1]; + private_dict.assign(cff, private_offset, (int) private_info[0], errh, "Private DICT"); + if (private_dict.error() < 0) + return private_dict.error(); + else if (private_dict.check(true, errh, "Private DICT") < 0) + return -EINVAL; + //private_dict.unparse(errh, "Private DICT"); + + private_dict.value(Cff::oDefaultWidthX, &default_width_x); + private_dict.value(Cff::oNominalWidthX, &nominal_width_x); + if (private_dict.has(Cff::oSubrs)) { + int subrs_offset = 0; + private_dict.value(Cff::oSubrs, &subrs_offset); + subrs_index = Cff::IndexIterator(cff->data(), private_offset + subrs_offset, cff->length(), errh, "Subrs INDEX"); + if (subrs_index.error() < 0) + return subrs_index.error(); + } + subrs_cs.assign(subrs_index.nitems(), 0); + return 0; +} + + +Cff::FontParent::FontParent(Cff* cff) + : CharstringProgram(cff->units_per_em()), _cff(cff), _error(-1) +{ +} + +Charstring * +Cff::FontParent::charstring(const IndexIterator &iiter, int which) const +{ + const uint8_t *s1 = iiter[which]; + int slen = iiter[which + 1] - s1; + String cs = _cff->data_string().substring(s1 - _cff->data(), slen); + if (slen == 0) + return 0; + else if (_charstring_type == 1) + return new Type1Charstring(cs); + else + return new Type2Charstring(cs); +} + +Charstring * +Cff::FontParent::gsubr(int i) const +{ + return _cff->gsubr(i); +} + +int +Cff::FontParent::gsubr_bias() const +{ + return Efont::subr_bias(2, ngsubrs_x()); +} + + +/***** + * CffFont + **/ + +Cff::Font::Font(Cff *cff, PermString font_name, const Dict &top_dict, ErrorHandler *errh) + : ChildFont(cff, 0, 2, top_dict, errh), _font_name(font_name), + _t1encoding(0) +{ + assert(!_top_dict.has_first(oROS)); + if (_error < 0) + return; + + // extract CharStrings + // must use xvalue because we could be creating the default dict! + int charstrings_offset = 0; + _top_dict.xvalue(oCharStrings, &charstrings_offset); + _charstrings_index = Cff::IndexIterator(cff->data(), charstrings_offset, cff->length(), errh, "CharStrings INDEX"); + if (_charstrings_index.error() < 0) { + _error = _charstrings_index.error(); + return; + } + _charstrings_cs.assign(_charstrings_index.nitems(), 0); + + int charset = 0; + _top_dict.xvalue(oCharset, &charset); + _charset.assign(cff, charset, _charstrings_index.nitems(), cff->max_sid(), errh); + if (_charset.error() < 0) { + _error = _charset.error(); + return; + } + + int Encoding = 0; + _top_dict.xvalue(oEncoding, &Encoding); + if (parse_encoding(Encoding, errh) < 0) + return; + + // success! + _error = 0; +} + +Cff::Font::~Font() +{ + for (int i = 0; i < _charstrings_cs.size(); i++) + delete _charstrings_cs[i]; + delete _t1encoding; +} + +int +Cff::Font::parse_encoding(int pos, ErrorHandler *errh) +{ + _encoding_pos = pos; + for (int i = 0; i < 256; i++) + _encoding[i] = 0; + + // check for standard encodings + if (pos == 0) + return assign_standard_encoding(standard_encoding); + else if (pos == 1) + return assign_standard_encoding(expert_encoding); + + // otherwise, a custom encoding + const uint8_t *data = _cff->data(); + int len = _cff->length(); + if (pos + 1 > len) + return errh->error("Encoding position out of range"), -EFAULT; + bool supplemented = (data[pos] & 0x80) != 0; + int format = (data[pos] & 0x7F); + + int retval = 0; + int endpos, g = 1; + if (format == 0) { + endpos = pos + 2 + data[pos + 1]; + if (endpos > len) + return errh->error("Encoding[0] out of range"), -EFAULT; + const uint8_t *p = data + pos + 2; + int n = data[pos + 1]; + for (; g <= n; g++, p++) { + int e = p[0]; + if (_encoding[e]) + retval = 1; + _encoding[e] = g; + } + + } else if (format == 1) { + endpos = pos + 2 + data[pos + 1] * 2; + if (endpos > len) + return errh->error("Encoding[1] out of range"), -EFAULT; + const uint8_t *p = data + pos + 2; + int n = data[pos + 1]; + for (int i = 0; i < n; i++, p += 2) { + int first = p[0]; + int nLeft = p[1]; + for (int e = first; e <= first + nLeft; e++) { + if (_encoding[e]) + retval = 1; + _encoding[e] = g++; + } + } + + } else + return errh->error("unknown Encoding format %d", format), -EINVAL; + + if (g > _charset.nglyphs()) + return errh->error("Encoding glyph %d out of range", g), -EINVAL; + + // check supplements + if (supplemented) { + if (endpos + data[endpos] * 3 > len) + return -EINVAL; + const uint8_t *p = data + endpos + 1; + int n = data[endpos]; + for (int i = 0; i < n; i++, p += 3) { + int e = p[0]; + int s = (p[1] << 8) | p[2]; + int g = _charset.sid_to_gid(s); + if (_encoding[e]) + retval = 1; + if (g < 0 || g >= _charset.nglyphs()) + return errh->error("Encoding glyph %d out of range", g), -EINVAL; + _encoding[e] = g; + } + } + + // successfully done + return retval; +} + +int +Cff::Font::assign_standard_encoding(const int *standard_encoding) +{ + for (int i = 0; i < 256; i++) + _encoding[i] = _charset.sid_to_gid(standard_encoding[i]); + return 0; +} + +void +Cff::Font::font_matrix(double matrix[6]) const +{ + Vector<double> t1d_matrix; + if (dict_value(oFontMatrix, t1d_matrix) && t1d_matrix.size() == 6) + memcpy(&matrix[0], &t1d_matrix[0], sizeof(double) * 6); + else { + matrix[0] = matrix[3] = 0.001; + matrix[1] = matrix[2] = matrix[4] = matrix[5] = 0; + } +} + +PermString +Cff::Font::glyph_name(int gid) const +{ + if (gid >= 0 && gid < nglyphs()) + return _cff->sid_permstring(_charset.gid_to_sid(gid)); + else + return PermString(); +} + +void +Cff::Font::glyph_names(Vector<PermString> &gnames) const +{ + gnames.resize(nglyphs()); + for (int i = 0; i < nglyphs(); i++) + gnames[i] = _cff->sid_permstring(_charset.gid_to_sid(i)); +} + +Charstring * +Cff::Font::glyph(int gid) const +{ + if (gid < 0 || gid >= nglyphs()) + return 0; + if (!_charstrings_cs[gid]) + _charstrings_cs[gid] = charstring(_charstrings_index, gid); + return _charstrings_cs[gid]; +} + +Charstring * +Cff::Font::glyph(PermString name) const +{ + int gid = _charset.sid_to_gid(_cff->sid(name)); + if (gid < 0) + return 0; + if (!_charstrings_cs[gid]) + _charstrings_cs[gid] = charstring(_charstrings_index, gid); + return _charstrings_cs[gid]; +} + +int +Cff::Font::glyphid(PermString name) const +{ + return _charset.sid_to_gid(_cff->sid(name)); +} + +Type1Encoding * +Cff::Font::type1_encoding() const +{ + if (_encoding_pos == 0) + return Type1Encoding::standard_encoding(); + if (!_t1encoding) + _t1encoding = type1_encoding_copy(); + return _t1encoding; +} + +Type1Encoding * +Cff::Font::type1_encoding_copy() const +{ + if (_encoding_pos == 0) + return Type1Encoding::standard_encoding(); + Type1Encoding *e = new Type1Encoding; + for (int i = 0; i < 256; i++) + if (_encoding[i]) + e->put(i, _cff->sid_permstring(_charset.gid_to_sid(_encoding[i]))); + return e; +} + +bool +Cff::Font::dict_has(DictOperator op) const +{ + return dict_of(op).has(op); +} + +String +Cff::Font::dict_string(DictOperator op) const +{ + Vector<double> vec; + dict_of(op).value(op, vec); + if (vec.size() == 1 && vec[0] >= 0 && vec[0] <= _cff->max_sid()) + return _cff->sid_string((int) vec[0]); + else + return String(); +} + + +/***** + * Cff::CIDFont + **/ + +Cff::CIDFont::CIDFont(Cff *cff, PermString font_name, const Dict &top_dict, ErrorHandler *errh) + : FontParent(cff), _font_name(font_name), _top_dict(top_dict) +{ + assert(_top_dict.has_first(oROS)); + + // parse top DICT + _error = -EINVAL; + if (_top_dict.check(false, errh, "Top DICT") < 0) + return; + else if (!_top_dict.has(oCharStrings)) { + errh->error("font has no CharStrings dictionary"); + return; + } + //_top_dict.unparse(errh, "Top DICT"); + + // extract offsets and information from TOP DICT + _top_dict.value(oCharstringType, &_charstring_type); + if (_charstring_type != 1 && _charstring_type != 2) { + errh->error("unknown CharString type %d", _charstring_type); + return; + } + + int charstrings_offset = 0; + _top_dict.value(oCharStrings, &charstrings_offset); + _charstrings_index = Cff::IndexIterator(cff->data(), charstrings_offset, cff->length(), errh, "CharStrings INDEX"); + if (_charstrings_index.error() < 0) { + _error = _charstrings_index.error(); + return; + } + _charstrings_cs.assign(_charstrings_index.nitems(), 0); + + int charset = 0; + _top_dict.value(oCharset, &charset); + _charset.assign(cff, charset, _charstrings_index.nitems(), -1, errh); + if (_charset.error() < 0) { + _error = _charset.error(); + return; + } + + // extract information about child fonts + int fdarray_offset = 0; + if (!_top_dict.value(oFDArray, &fdarray_offset)) { + errh->error("CID-keyed font missing FDArray"); + return; + } + IndexIterator fdarray_index(cff->data(), fdarray_offset, cff->length(), errh, "FDArray INDEX"); + for (; fdarray_index; fdarray_index++) { + Dict d(cff, fdarray_index[0] - cff->data(), fdarray_index[1] - fdarray_index[0], errh, "Top DICT"); + if (!d.ok() || d.check(false, errh, "Top DICT") < 0) { + _error = d.error(); + return; + } + _child_fonts.push_back(new ChildFont(cff, this, _charstring_type, d, errh)); + if (!_child_fonts.back()->ok()) + return; + } + + int fdselect_offset = 0; + if (!_top_dict.value(oFDSelect, &fdselect_offset)) { + errh->error("CID-keyed font missing FDSelect"); + return; + } + _fdselect.assign(cff, fdselect_offset, _charstrings_cs.size(), errh); + if (_fdselect.error() < 0) + return; + + // success! + _error = 0; + set_parent_program(true); +} + +Cff::CIDFont::~CIDFont() +{ + for (int i = 0; i < _charstrings_cs.size(); i++) + delete _charstrings_cs[i]; + for (int i = 0; i < _child_fonts.size(); i++) + delete _child_fonts[i]; +} + +void +Cff::CIDFont::font_matrix(double matrix[6]) const +{ + // XXX + matrix[0] = matrix[3] = 0.001; + matrix[1] = matrix[2] = matrix[4] = matrix[5] = 0; +} + +const CharstringProgram * +Cff::CIDFont::child_program(int gid) const +{ + int fd = _fdselect.gid_to_fd(gid); + if (fd >= 0 && fd < _child_fonts.size()) + return _child_fonts.at_u(fd); + else + return 0; +} + +PermString +Cff::CIDFont::glyph_name(int gid) const +{ + if (gid >= 0 && gid < nglyphs()) + return permprintf("#%d", _charset.gid_to_sid(gid)); + else + return PermString(); +} + +void +Cff::CIDFont::glyph_names(Vector<PermString> &gnames) const +{ + gnames.resize(nglyphs()); + for (int i = 0; i < nglyphs(); i++) + gnames[i] = permprintf("#%d", _charset.gid_to_sid(i)); +} + +Charstring * +Cff::CIDFont::glyph(int gid) const +{ + if (gid < 0 || gid >= nglyphs()) + return 0; + if (!_charstrings_cs[gid]) + _charstrings_cs[gid] = charstring(_charstrings_index, gid); + return _charstrings_cs[gid]; +} + +int +Cff::CIDFont::glyphid(PermString name) const +{ + if (name.length() <= 1 || name[0] != '#' || !isdigit((unsigned char) name[1])) + return -1; + char *endptr; + long cid = strtol(name.c_str() + 1, &endptr, 10); + if (*endptr != 0) + return -1; + return _charset.sid_to_gid(cid); +} + +Charstring * +Cff::CIDFont::glyph(PermString name) const +{ + return CIDFont::glyph(CIDFont::glyphid(name)); +} + + +/***** + * ChildFont + **/ + +Cff::ChildFont::ChildFont(Cff *cff, Cff::CIDFont *parent, int charstring_type, const Dict &top_dict, ErrorHandler *errh) + : FontParent(cff), _parent(parent), _top_dict(top_dict) +{ + if (!errh) + errh = ErrorHandler::silent_handler(); + + if (!cff->ok() || !_top_dict.ok()) { + errh->error("invalid CFF"); + _error = -EINVAL; + return; + } + + // extract offsets and information from TOP DICT + _charstring_type = charstring_type; + _top_dict.value(oCharstringType, &_charstring_type); + if (_charstring_type != 1 && _charstring_type != 2) { + errh->error("unknown CharString type %d", _charstring_type); + return; + } + + // extract information from Private DICT + if (_top_dict.has(oPrivate) + && (_error = handle_private(cff, _top_dict, _private_dict, _default_width_x, _nominal_width_x, _subrs_index, _subrs_cs, errh)) < 0) + return; + + // success! + _error = 0; +} + +Cff::ChildFont::~ChildFont() +{ + for (int i = 0; i < _subrs_cs.size(); i++) + delete _subrs_cs[i]; +} + +Charstring * +Cff::ChildFont::charstring(const IndexIterator &iiter, int which) const +{ + const uint8_t *s1 = iiter[which]; + int slen = iiter[which + 1] - s1; + String cs = _cff->data_string().substring(s1 - _cff->data(), slen); + if (slen == 0) + return 0; + else if (_charstring_type == 1) + return new Type1Charstring(cs); + else + return new Type2Charstring(cs); +} + +Charstring * +Cff::ChildFont::subr(int i) const +{ + i += Efont::subr_bias(_charstring_type, nsubrs_x()); + if (i < 0 || i >= nsubrs_x()) + return 0; + if (!_subrs_cs[i]) + _subrs_cs[i] = charstring(_subrs_index, i); + return _subrs_cs[i]; +} + +int +Cff::ChildFont::subr_bias() const +{ + return Efont::subr_bias(_charstring_type, nsubrs_x()); +} + +double +Cff::ChildFont::global_width_x(bool is_nominal) const +{ + return (is_nominal ? _nominal_width_x : _default_width_x); +} + +} |