예제 #1
0
        private static void ReadProcedure(Type1Tokenizer tokenizer, List <Type1Token> tokens, ref int depth)
        {
            if (depth == -1)
            {
                ReadExpected(tokenizer, Type1Token.TokenType.StartProc);
                depth = 1;
            }

            if (depth == 0)
            {
                return;
            }

            Type1Token token;

            while ((token = tokenizer.GetNext()) != null)
            {
                if (token.Type == Type1Token.TokenType.StartProc)
                {
                    depth += 1;
                    ReadProcedure(tokenizer, tokens, ref depth);
                }
                else if (token.Type == Type1Token.TokenType.EndProc)
                {
                    depth--;
                    break;
                }
                else
                {
                    tokens.Add(token);
                }
            }
        }
예제 #2
0
        private static void ReadOtherSubroutines(Type1Tokenizer tokenizer, bool isLenientParsing)
        {
            var start = tokenizer.GetNext();

            if (start.Type == Type1Token.TokenType.StartArray)
            {
                ReadArrayValues(tokenizer, x => x, true, false);
            }
            else if (start.Type == Type1Token.TokenType.Integer || start.Type == Type1Token.TokenType.Real)
            {
                var length = start.AsInt();
                ReadExpected(tokenizer, Type1Token.TokenType.Name, "array");

                for (var i = 0; i < length; i++)
                {
                    ReadExpected(tokenizer, Type1Token.TokenType.Name, "dup");
                    ReadNumeric(tokenizer);
                    ReadTillPut(tokenizer);
                }
                ReadTillDef(tokenizer);
            }
            else if (!isLenientParsing)
            {
                throw new InvalidOperationException($"Failed to read start of /OtherSubrs array. Got start token: {start}.");
            }
        }
예제 #3
0
        private static IReadOnlyList <Type1Token> ReadProcedure(Type1Tokenizer tokenizer)
        {
            var tokens = new List <Type1Token>();
            var depth  = -1;

            ReadProcedure(tokenizer, tokens, ref depth);
            return(tokens);
        }
예제 #4
0
        private static IReadOnlyList <Type1Token> ReadProcedure(Type1Tokenizer tokenizer, bool hasReadStartProc)
        {
            var tokens = new List <Type1Token>();
            var depth  = hasReadStartProc ? 1 : -1;

            ReadProcedure(tokenizer, tokens, ref depth);
            return(tokens);
        }
예제 #5
0
        private static decimal ReadNumeric(Type1Tokenizer tokenizer)
        {
            var token = tokenizer.GetNext();

            if (token == null || (token.Type != Type1Token.TokenType.Integer && token.Type != Type1Token.TokenType.Real))
            {
                throw new InvalidOperationException($"Expected to read a numeric token, instead got: {token}.");
            }

            return(token.AsDecimal());
        }
예제 #6
0
        private static bool ReadBoolean(Type1Tokenizer tokenizer)
        {
            var token = tokenizer.GetNext();

            if (token == null || (!string.Equals(token.Text, "true", StringComparison.OrdinalIgnoreCase) &&
                                  !string.Equals(token.Text, "false", StringComparison.OrdinalIgnoreCase)))
            {
                throw new InvalidOperationException($"Expected to read a boolean token, instead got: {token}.");
            }

            return(token.AsBool());
        }
예제 #7
0
        private static IReadOnlyList <Type1CharstringDecryptedBytes> ReadCharStrings(Type1Tokenizer tokenizer, int lenIv, bool isLenientParsing)
        {
            var length = (int)ReadNumeric(tokenizer);

            ReadExpected(tokenizer, Type1Token.TokenType.Name, "dict");

            // dup begin or CharStrings begin.
            ReadExpected(tokenizer, Type1Token.TokenType.Name);
            ReadExpected(tokenizer, Type1Token.TokenType.Name, "begin");

            var results = new List <Type1CharstringDecryptedBytes>();

            for (var i = 0; i < length; i++)
            {
                var token = tokenizer.GetNext();

                if (token.Type == Type1Token.TokenType.Name &&
                    string.Equals(token.Text, "end", StringComparison.OrdinalIgnoreCase))
                {
                    break;
                }

                if (token.Type != Type1Token.TokenType.Literal)
                {
                    throw new InvalidOperationException($"Type 1 font error. Expected literal for charstring name, instead got: {token}.");
                }

                var name = token.Text;

                var charstringLength = ReadNumeric(tokenizer);
                var charstring       = tokenizer.GetNext();
                if (!(charstring is Type1DataToken charstringToken))
                {
                    throw new InvalidOperationException($"Got wrong type of token, expected charstring, instead got: {charstring}.");
                }

                if (!isLenientParsing && charstringToken.Data.Count != charstringLength)
                {
                    throw new InvalidOperationException($"The charstring {charstringToken} did not have the expected length of {charstringLength}.");
                }

                var data = Decrypt(charstringToken.Data, CharstringEncryptionKey, lenIv);

                results.Add(new Type1CharstringDecryptedBytes(name, data, i));

                ReadTillDef(tokenizer);
            }

            ReadExpected(tokenizer, Type1Token.TokenType.Name, "end");

            return(results);
        }
예제 #8
0
        private static void ReadExpected(Type1Tokenizer tokenizer, Type1Token.TokenType type, string text = null)
        {
            var token = tokenizer.GetNext();

            if (token == null)
            {
                throw new InvalidOperationException($"Type 1 Encrypted portion ended when a token with text '{text}' was expected.");
            }

            if (token.Type != type || (text != null && !string.Equals(token.Text, text, StringComparison.OrdinalIgnoreCase)))
            {
                throw new InvalidOperationException($"Found invalid token {token} when type {type} with text {text} was expected.");
            }
        }
예제 #9
0
        private static void ReadTillDef(Type1Tokenizer tokenizer, bool skip = false)
        {
            if (string.Equals(tokenizer.CurrentToken.Text, "def", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            Type1Token token;

            while ((token = tokenizer.GetNext()) != null)
            {
                if (token.Type == Type1Token.TokenType.Name)
                {
                    if (string.Equals(token.Text, "ND", StringComparison.OrdinalIgnoreCase) || string.Equals(token.Text, "|-", StringComparison.OrdinalIgnoreCase))
                    {
                        return;
                    }

                    if (string.Equals(token.Text, "def", StringComparison.OrdinalIgnoreCase))
                    {
                        break;
                    }

                    // PostScript wrapper (ignored for now)
                    if (string.Equals(token.Text, "systemdict"))
                    {
                        ReadExpected(tokenizer, Type1Token.TokenType.Literal, "internaldict");
                        ReadExpected(tokenizer, Type1Token.TokenType.Name, "known");
                        ReadExpected(tokenizer, Type1Token.TokenType.StartProc);

                        // TODO: read values from the wrapper, see line 396 of Type1Parser.java
                        // Skips the entire contents
                        while ((token = tokenizer.GetNext()) != null)
                        {
                            if (token.Type == Type1Token.TokenType.Name && (token.Text == "ND" || token.Text == "def"))
                            {
                                return;
                            }
                        }
                    }
                }
                else if (!skip)
                {
                    throw new InvalidOperationException($"Encountered unexpected non-name token while reading till 'def' token: {token}");
                }
            }
        }
예제 #10
0
        private static IReadOnlyList <T> ReadArrayValues <T>(Type1Tokenizer tokenizer, Func <Type1Token, T> converter, bool hasReadStart = false,
                                                             bool includeDef = true)
        {
            if (!hasReadStart)
            {
                ReadExpected(tokenizer, Type1Token.TokenType.StartArray);
            }

            var results = new List <T>();

            Type1Token token;

            while ((token = tokenizer.GetNext()) != null)
            {
                if (token.Type == Type1Token.TokenType.EndArray)
                {
                    break;
                }

                if (token.Type == Type1Token.TokenType.StartArray)
                {
                    var array = ReadArrayValues <T>(tokenizer, converter, true, false);

                    results.AddRange(array);
                }

                try
                {
                    var result = converter(token);

                    results.Add(result);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException($"Conversion of token '{token}' to value of type {typeof(T).Name} failed.", ex);
                }
            }

            if (includeDef)
            {
                ReadTillDef(tokenizer);
            }

            return(results);
        }
예제 #11
0
        private static void ReadTillPut(Type1Tokenizer tokenizer)
        {
            Type1Token token;

            while ((token = tokenizer.GetNext()) != null)
            {
                if (string.Equals(token.Text, "put", StringComparison.OrdinalIgnoreCase))
                {
                    break;
                }

                switch (token.Text)
                {
                case "NP":
                case "|":
                    return;
                }
            }
        }
예제 #12
0
        private static IReadOnlyList <Type1CharstringDecryptedBytes> ReadSubroutines(Type1Tokenizer tokenizer, int lenIv, bool isLenientParsing)
        {
            var length      = (int)ReadNumeric(tokenizer);
            var subroutines = new List <Type1CharstringDecryptedBytes>(length);

            ReadExpected(tokenizer, Type1Token.TokenType.Name, "array");

            for (var i = 0; i < length; i++)
            {
                var current = tokenizer.GetNext();
                if (current.Type != Type1Token.TokenType.Name || !string.Equals(current.Text, "dup"))
                {
                    break;
                }

                var index = (int)ReadNumeric(tokenizer);

                var byteLength = (int)ReadNumeric(tokenizer);

                var charstring = tokenizer.GetNext();

                if (!(charstring is Type1DataToken charstringToken))
                {
                    throw new InvalidOperationException($"Found an unexpected token instead of subroutine charstring: {charstring}.");
                }

                if (!isLenientParsing && charstringToken.Data.Count != byteLength)
                {
                    throw new InvalidOperationException($"The subroutine charstring {charstringToken} did not have the expected length of {byteLength}.");
                }

                var subroutine = Decrypt(charstringToken.Data, CharstringEncryptionKey, lenIv);
                subroutines.Add(new Type1CharstringDecryptedBytes(subroutine, index));
                ReadTillPut(tokenizer);
            }

            ReadTillDef(tokenizer);

            return(subroutines);
        }
예제 #13
0
        private static void ReadExpectedAfterOptional(Type1Tokenizer tokenizer, Type1Token.TokenType optionalType, string optionalText,
                                                      Type1Token.TokenType type, string text)
        {
            var token = tokenizer.GetNext();

            if (token == null)
            {
                throw new InvalidOperationException($"Type 1 Encrypted portion ended when a token with text '{optionalText}' or '{text}' was expected.");
            }

            if (token.Type == type && string.Equals(token.Text, text, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            if (token.Type == optionalType && string.Equals(token.Text, optionalText, StringComparison.OrdinalIgnoreCase))
            {
                ReadExpected(tokenizer, type, text);
                return;
            }

            throw new InvalidOperationException($"Found invalid token {token} when type {type} with text {text} was expected.");
        }
예제 #14
0
        private static void ReadTillDef(Type1Tokenizer tokenizer, bool skip = false)
        {
            Type1Token token;

            while ((token = tokenizer.GetNext()) != null)
            {
                if (token.Type == Type1Token.TokenType.Name)
                {
                    if (string.Equals(token.Text, "ND", StringComparison.OrdinalIgnoreCase) || string.Equals(token.Text, "|-", StringComparison.OrdinalIgnoreCase))
                    {
                        return;
                    }

                    if (string.Equals(token.Text, "def", StringComparison.OrdinalIgnoreCase))
                    {
                        break;
                    }
                }
                else if (!skip)
                {
                    throw new InvalidOperationException($"Encountered unexpected non-name token while reading till 'def' token: {token}");
                }
            }
        }
예제 #15
0
        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);
        }