Пример #1
0
 /// <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));
 }
Пример #2
0
 /// <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));
 }
Пример #3
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);
        }