/// <summary> /// Create a new <see cref="Type1Font"/>. /// </summary> internal Type1Font(string name, IReadOnlyDictionary <int, string> encoding, ArrayToken fontMatrix, PdfRectangle boundingBox, Type1PrivateDictionary privateDictionary, Type1CharStrings charStrings) { Name = name; Encoding = encoding; FontMatrix = GetFontTransformationMatrix(fontMatrix); BoundingBox = boundingBox; PrivateDictionary = privateDictionary ?? throw new ArgumentNullException(nameof(privateDictionary)); CharStrings = charStrings ?? throw new ArgumentNullException(nameof(charStrings)); }
/// <summary> /// Create a new <see cref="Type1FontProgram"/> from the information retrieved from the PDF document. /// </summary> /// <param name="name">The name of the font.</param> /// <param name="encoding"></param> /// <param name="fontMatrix"></param> /// <param name="boundingBox"></param> /// <param name="privateDictionary"></param> /// <param name="charStrings"></param> public Type1FontProgram(string name, IReadOnlyDictionary <int, string> encoding, ArrayToken fontMatrix, PdfRectangle boundingBox, Type1PrivateDictionary privateDictionary, Type1CharStrings charStrings) { Name = name; Encoding = encoding; FontMatrix = fontMatrix; BoundingBox = boundingBox; PrivateDictionary = privateDictionary ?? throw new ArgumentNullException(nameof(privateDictionary)); CharStrings = charStrings ?? throw new ArgumentNullException(nameof(charStrings)); }
public (Type1PrivateDictionary, Type1CharStrings) Parse(IReadOnlyList <byte> bytes, bool isLenientParsing) { if (!IsBinary(bytes)) { bytes = ConvertHexToBinary(bytes); } var decrypted = Decrypt(bytes, EexecEncryptionKey, EexecRandomBytes); if (decrypted.Count == 0) { var defaultPrivateDictionary = new Type1PrivateDictionary(new Type1PrivateDictionary.Builder()); var defaultCharstrings = new Type1CharStrings(new Dictionary <string, Type1CharStrings.CommandSequence>(), new Dictionary <int, string>(), new Dictionary <int, Type1CharStrings.CommandSequence>()); return(defaultPrivateDictionary, defaultCharstrings); } var tokenizer = new Type1Tokenizer(new ByteArrayInputBytes(decrypted)); /* * After 4 random characters follows the /Private dictionary and the /CharString dictionary. * The first defines a number of technical terms involving character construction, and contains also an array of subroutines used in character paths. * The second contains the character descriptions themselves. * Both the subroutines and the character descriptions are yet again encrypted in a fashion similar to the entire binary segment, but now with an initial value of R = 4330 instead of 55665. */ while (!tokenizer.CurrentToken.IsPrivateDictionary) { tokenizer.GetNext(); if (tokenizer.CurrentToken == null) { throw new InvalidOperationException("Did not find the private dictionary start token."); } } var next = tokenizer.GetNext(); if (next?.Type != Type1Token.TokenType.Integer) { throw new InvalidOperationException($"No length token was present in the stream following the private dictionary start, instead got {next}."); } var length = next.AsInt(); ReadExpected(tokenizer, Type1Token.TokenType.Name, "dict"); // Could also be "/Private 10 dict def Private begin" instead of the "dup" ReadExpectedAfterOptional(tokenizer, Type1Token.TokenType.Name, "def", Type1Token.TokenType.Name, "dup"); ReadExpected(tokenizer, Type1Token.TokenType.Name, "begin"); /* * The lenIV entry is an integer specifying the number of random bytes at the beginning of charstrings for charstring encryption. * The default value of lenIV is 4. */ var lenIv = Len4Bytes; var builder = new Type1PrivateDictionary.Builder(); for (var i = 0; i < length; i++) { var token = tokenizer.GetNext(); // premature end if (token == null || token.Type != Type1Token.TokenType.Literal) { break; } var key = token.Text; switch (key) { case Type1Symbols.RdProcedure: case Type1Symbols.RdProcedureAlt: { var procedureTokens = ReadProcedure(tokenizer); builder.Rd = procedureTokens; ReadTillDef(tokenizer); break; } case Type1Symbols.NoAccessDef: case Type1Symbols.NoAccessDefAlt: { var procedureTokens = ReadProcedure(tokenizer); builder.NoAccessDef = procedureTokens; ReadTillDef(tokenizer); break; } case Type1Symbols.NoAccessPut: case Type1Symbols.NoAccessPutAlt: { var procedureTokens = ReadProcedure(tokenizer); builder.NoAccessPut = procedureTokens; ReadTillDef(tokenizer); break; } case Type1Symbols.BlueValues: { var blueValues = ReadArrayValues(tokenizer, x => x.AsInt()); builder.BlueValues = blueValues; break; } case Type1Symbols.OtherBlues: { var otherBlues = ReadArrayValues(tokenizer, x => x.AsInt()); builder.OtherBlues = otherBlues; break; } case Type1Symbols.StdHorizontalStemWidth: { var widths = ReadArrayValues(tokenizer, x => x.AsDecimal()); var width = widths[0]; builder.StandardHorizontalWidth = width; break; } case Type1Symbols.StdVerticalStemWidth: { var widths = ReadArrayValues(tokenizer, x => x.AsDecimal()); var width = widths[0]; builder.StandardVerticalWidth = width; break; } case Type1Symbols.StemSnapHorizontalWidths: { var widths = ReadArrayValues(tokenizer, x => x.AsDecimal()); builder.StemSnapHorizontalWidths = widths; break; } case Type1Symbols.StemSnapVerticalWidths: { var widths = ReadArrayValues(tokenizer, x => x.AsDecimal()); builder.StemSnapVerticalWidths = widths; break; } case Type1Symbols.BlueScale: { builder.BlueScale = ReadNumeric(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.ForceBold: { builder.ForceBold = ReadBoolean(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.MinFeature: { var procedureTokens = ReadProcedure(tokenizer); builder.MinFeature = new MinFeature(procedureTokens[0].AsInt(), procedureTokens[1].AsInt()); ReadTillDef(tokenizer); break; } case Type1Symbols.Password: { var password = (int)ReadNumeric(tokenizer); if (password != Password && !isLenientParsing) { throw new InvalidOperationException($"Type 1 font had the wrong password: {password}"); } builder.Password = password; ReadTillDef(tokenizer); break; } case Type1Symbols.UniqueId: { var id = (int)ReadNumeric(tokenizer); builder.UniqueId = id; ReadTillDef(tokenizer); break; } case Type1Symbols.Len4: { lenIv = (int)ReadNumeric(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.BlueShift: { builder.BlueShift = (int)ReadNumeric(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.BlueFuzz: { builder.BlueFuzz = (int)ReadNumeric(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.FamilyBlues: { builder.FamilyBlues = ReadArrayValues(tokenizer, x => x.AsInt()); break; } case Type1Symbols.FamilyOtherBlues: { builder.FamilyOtherBlues = ReadArrayValues(tokenizer, x => x.AsInt()); break; } case Type1Symbols.LanguageGroup: { builder.LanguageGroup = (int)ReadNumeric(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.RndStemUp: { builder.RoundStemUp = ReadBoolean(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.Subroutines: { builder.Subroutines = ReadSubroutines(tokenizer, lenIv, isLenientParsing); break; } case Type1Symbols.OtherSubroutines: { ReadOtherSubroutines(tokenizer, isLenientParsing); ReadTillDef(tokenizer); break; } case Type1Symbols.ExpansionFactor: { builder.ExpansionFactor = ReadNumeric(tokenizer); ReadTillDef(tokenizer); break; } case Type1Symbols.Erode: { ReadTillDef(tokenizer, true); break; } default: { ReadTillDef(tokenizer, true); break; } } } var currentToken = tokenizer.CurrentToken; IReadOnlyList <Type1CharstringDecryptedBytes> charStrings; if (currentToken != null) { while (currentToken != null && currentToken.Type != Type1Token.TokenType.Literal && !string.Equals(currentToken.Text, "CharStrings", StringComparison.OrdinalIgnoreCase)) { currentToken = tokenizer.GetNext(); } if (currentToken != null) { charStrings = ReadCharStrings(tokenizer, lenIv, isLenientParsing); } else { charStrings = new Type1CharstringDecryptedBytes[0]; } } else { charStrings = new Type1CharstringDecryptedBytes[0]; } var privateDictionary = builder.Build(); var instructions = Type1CharStringParser.Parse(charStrings, builder.Subroutines ?? new Type1CharstringDecryptedBytes[0]); return(privateDictionary, instructions); }