/** * Parses the ASCII portion of a Type 1 font. */ private void ParseASCII(byte[] bytes) { if (bytes.Length == 0) { throw new ArgumentException("byte[] is empty"); } // %!FontType1-1.0 // %!PS-AdobeFont-1.0 if (bytes.Length < 2 || (bytes[0] != '%' && bytes[1] != '!')) { throw new IOException("Invalid start of ASCII segment"); } lexer = new Type1Lexer(bytes); // (corrupt?) synthetic font if (lexer.PeekToken().Text.Equals("FontDirectory", StringComparison.Ordinal)) { Read(TokenKind.NAME, "FontDirectory"); Read(TokenKind.LITERAL); // font name Read(TokenKind.NAME, "known"); Read(TokenKind.START_PROC); ReadProc(); Read(TokenKind.START_PROC); ReadProc(); Read(TokenKind.NAME, "ifelse"); } // font dict int Length = Read(TokenKind.INTEGER).IntValue; Read(TokenKind.NAME, "dict"); // found in some TeX fonts ReadMaybe(TokenKind.NAME, "dup"); // if present, the "currentdict" is not required Read(TokenKind.NAME, "begin"); for (int i = 0; i < Length; i++) { // premature end Token token = lexer.PeekToken(); if (token == null) { break; } if (token.Kind == TokenKind.NAME && ("currentdict".Equals(token.Text, StringComparison.Ordinal) || "end".Equals(token.Text, StringComparison.Ordinal))) { break; } // key/value string key = Read(TokenKind.LITERAL).Text; switch (key) { case "FontInfo": case "Fontinfo": ReadFontInfo(ReadSimpleDict()); break; case "Metrics": ReadSimpleDict(); break; case "Encoding": ReadEncoding(); break; default: ReadSimpleValue(key); break; } } ReadMaybe(TokenKind.NAME, "currentdict"); Read(TokenKind.NAME, "end"); Read(TokenKind.NAME, "currentfile"); Read(TokenKind.NAME, "eexec"); }
/** * Parses the binary portion of a Type 1 font. */ private void ParseBinary(byte[] bytes) { byte[] decrypted; // Sometimes, fonts use the hex format, so this needs to be converted before decryption if (IsBinary(bytes)) { decrypted = Decrypt(bytes, EEXEC_KEY, 4); } else { decrypted = Decrypt(hexToBinary(bytes), EEXEC_KEY, 4); } lexer = new Type1Lexer(decrypted); // find /Private dict Token peekToken = lexer.PeekToken(); while (peekToken != null && !peekToken.Text.Equals("Private", StringComparison.Ordinal)) { // for a more thorough validation, the presence of "begin" before Private // determines how code before and following charstrings should look // it is not currently checked anyway lexer.NextToken(); peekToken = lexer.PeekToken(); } if (peekToken == null) { throw new IOException("/Private token not found"); } // Private dict Read(TokenKind.LITERAL, "Private"); int Length = Read(TokenKind.INTEGER).IntValue; Read(TokenKind.NAME, "dict"); // actually could also be "/Private 10 dict def Private begin" // instead of the "dup" ReadMaybe(TokenKind.NAME, "dup"); Read(TokenKind.NAME, "begin"); int lenIV = 4; // number of random bytes at start of charstring for (int i = 0; i < Length; i++) { // premature end if (lexer.PeekToken() == null || lexer.PeekToken().Kind != TokenKind.LITERAL) { break; } // key/value string key = Read(TokenKind.LITERAL).Text; switch (key) { case "Subrs": ReadSubrs(lenIV); break; case "OtherSubrs": ReadOtherSubrs(); break; case "lenIV": lenIV = ReadDictValue()[0].IntValue; break; case "ND": Read(TokenKind.START_PROC); // the access restrictions are not mandatory ReadMaybe(TokenKind.NAME, "noaccess"); Read(TokenKind.NAME, "def"); Read(TokenKind.END_PROC); ReadMaybe(TokenKind.NAME, "executeonly"); Read(TokenKind.NAME, "def"); break; case "NP": Read(TokenKind.START_PROC); ReadMaybe(TokenKind.NAME, "noaccess"); Read(TokenKind.NAME); Read(TokenKind.END_PROC); ReadMaybe(TokenKind.NAME, "executeonly"); Read(TokenKind.NAME, "def"); break; case "RD": // /RD {string currentfile exch readstring pop} bind executeonly def Read(TokenKind.START_PROC); ReadProc(); ReadMaybe(TokenKind.NAME, "bind"); ReadMaybe(TokenKind.NAME, "executeonly"); Read(TokenKind.NAME, "def"); break; default: ReadPrivate(key, ReadDictValue()); break; } } // some fonts have "2 index" here, others have "end noaccess put" // sometimes followed by "put". Either way, we just skip until // the /CharStrings dict is found while (!(lexer.PeekToken().Kind == TokenKind.LITERAL && lexer.PeekToken().Text.Equals("CharStrings", StringComparison.Ordinal))) { lexer.NextToken(); } // CharStrings dict Read(TokenKind.LITERAL, "CharStrings"); ReadCharStrings(lenIV); }