// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Collections.Generic; namespace SixLabors.Fonts.Tables.Cff { /// /// Represents a CFF DICT operator with its name and operand kind. /// Operators are registered in a static dictionary keyed by their byte encoding /// and looked up during DICT parsing. /// /// internal sealed class CFFOperator { private static readonly Lazy> RegisteredOperators = new(CreateDictionary, true); /// /// Initializes a new instance of the class. /// /// The operator name. /// The expected operand format. private CFFOperator(string name, OperatorOperandKind operandKind) { this.Name = name; this.OperandKind = operandKind; } /// /// Gets the name of the operator (e.g. "CharStrings", "FontMatrix", "Private"). /// public string Name { get; } /// /// Gets the expected operand format for this operator. /// public OperatorOperandKind OperandKind { get; } /// /// Looks up a registered CFF operator by its one- or two-byte encoding. /// /// The first byte of the operator. /// The second byte (0 for single-byte operators, or 12 prefix byte value). /// The matching , or if not found. public static CFFOperator GetOperatorByKey(byte b0, byte b1) { RegisteredOperators.Value.TryGetValue((b1 << 8) | b0, out CFFOperator? found); return found!; } /// /// Creates the dictionary of all registered CFF DICT operators, keyed by their byte encoding. /// /// The dictionary of registered operators. private static Dictionary CreateDictionary() { Dictionary dictionary = []; // Table 9: Top DICT Operator Entries Register(dictionary, 0, "version", OperatorOperandKind.SID); Register(dictionary, 1, "Notice", OperatorOperandKind.SID); Register(dictionary, 12, 0, "Copyright", OperatorOperandKind.SID); Register(dictionary, 2, "FullName", OperatorOperandKind.SID); Register(dictionary, 3, "FamilyName", OperatorOperandKind.SID); Register(dictionary, 4, "Weight", OperatorOperandKind.SID); Register(dictionary, 12, 1, "isFixedPitch", OperatorOperandKind.Boolean); Register(dictionary, 12, 2, "ItalicAngle", OperatorOperandKind.Number); Register(dictionary, 12, 3, "UnderlinePosition", OperatorOperandKind.Number); Register(dictionary, 12, 4, "UnderlineThickness", OperatorOperandKind.Number); Register(dictionary, 12, 5, "PaintType", OperatorOperandKind.Number); Register(dictionary, 12, 6, "CharstringType", OperatorOperandKind.Number); // default value 2 Register(dictionary, 12, 7, "FontMatrix", OperatorOperandKind.Array); Register(dictionary, 13, "UniqueID", OperatorOperandKind.Number); Register(dictionary, 5, "FontBBox", OperatorOperandKind.Array); Register(dictionary, 12, 8, "StrokeWidth", OperatorOperandKind.Number); Register(dictionary, 14, "XUID", OperatorOperandKind.Array); Register(dictionary, 15, "charset", OperatorOperandKind.Number); Register(dictionary, 16, "Encoding", OperatorOperandKind.Number); Register(dictionary, 17, "CharStrings", OperatorOperandKind.Number); Register(dictionary, 18, "Private", OperatorOperandKind.NumberNumber); Register(dictionary, 12, 20, "SyntheticBase", OperatorOperandKind.Number); Register(dictionary, 12, 21, "PostScript", OperatorOperandKind.SID); Register(dictionary, 12, 22, "BaseFontName", OperatorOperandKind.SID); Register(dictionary, 12, 23, "BaseFontBlend", OperatorOperandKind.SID); // Table 10: CIDFont Operator Extensions Register(dictionary, 12, 30, "ROS", OperatorOperandKind.SID_SID_Number); Register(dictionary, 12, 31, "CIDFontVersion", OperatorOperandKind.Number); Register(dictionary, 12, 32, "CIDFontRevision", OperatorOperandKind.Number); Register(dictionary, 12, 33, "CIDFontType", OperatorOperandKind.Number); Register(dictionary, 12, 34, "CIDCount", OperatorOperandKind.Number); Register(dictionary, 12, 35, "UIDBase", OperatorOperandKind.Number); Register(dictionary, 12, 36, "FDArray", OperatorOperandKind.Number); Register(dictionary, 12, 37, "FDSelect", OperatorOperandKind.Number); Register(dictionary, 12, 38, "FontName", OperatorOperandKind.SID); // Table 23: Private DICT Operators Register(dictionary, 6, "BlueValues", OperatorOperandKind.Delta); Register(dictionary, 7, "OtherBlues", OperatorOperandKind.Delta); Register(dictionary, 8, "FamilyBlues", OperatorOperandKind.Delta); Register(dictionary, 9, "FamilyOtherBlues", OperatorOperandKind.Delta); Register(dictionary, 12, 9, "BlueScale", OperatorOperandKind.Number); Register(dictionary, 12, 10, "BlueShift", OperatorOperandKind.Number); Register(dictionary, 12, 11, "BlueFuzz", OperatorOperandKind.Number); Register(dictionary, 10, "StdHW", OperatorOperandKind.Number); Register(dictionary, 11, "StdVW", OperatorOperandKind.Number); Register(dictionary, 12, 12, "StemSnapH", OperatorOperandKind.Delta); Register(dictionary, 12, 13, "StemSnapV", OperatorOperandKind.Delta); Register(dictionary, 12, 14, "ForceBold", OperatorOperandKind.Boolean); // reserved 12 15 // reserved 12 16 Register(dictionary, 12, 17, "LanguageGroup", OperatorOperandKind.Number); Register(dictionary, 12, 18, "ExpansionFactor", OperatorOperandKind.Number); Register(dictionary, 12, 19, "initialRandomSeed", OperatorOperandKind.Number); Register(dictionary, 19, "Subrs", OperatorOperandKind.Number); Register(dictionary, 20, "defaultWidthX", OperatorOperandKind.Number); Register(dictionary, 21, "nominalWidthX", OperatorOperandKind.Number); // CFF2 operators Register(dictionary, 22, "vsindex", OperatorOperandKind.Number); Register(dictionary, 23, "blend", OperatorOperandKind.Number); Register(dictionary, 24, "vstore", OperatorOperandKind.Number); return dictionary; } /// /// Registers a two-byte CFF DICT operator. /// /// The operator dictionary. /// The first byte of the operator (always 12 for two-byte operators). /// The second byte of the operator. /// The operator name. /// The expected operand format. private static void Register(Dictionary dictionary, byte b0, byte b1, string name, OperatorOperandKind operandKind) => dictionary.Add((b1 << 8) | b0, new CFFOperator(name, operandKind)); /// /// Registers a single-byte CFF DICT operator. /// /// The operator dictionary. /// The operator byte value. /// The operator name. /// The expected operand format. private static void Register(Dictionary dictionary, byte b0, string name, OperatorOperandKind operandKind) => dictionary.Add(b0, new CFFOperator(name, operandKind)); #if DEBUG /// public override string ToString() => this.Name; #endif } }