예제 #1
0
        /// <summary>
        /// Closes this instance.
        /// </summary>
        public void Close()
        {
            if (!CanModify)
            {
                throw new InvalidOperationException(PSSR.CannotModify);
            }

            if (OutStream != null)
            {
                // Get security handler if document gets encrypted
                PDFStandardSecurityHandler securityHandler = null;
                if (SecuritySettings.DocumentSecurityLevel != PDFDocumentSecurityLevel.None)
                {
                    securityHandler = SecuritySettings.SecurityHandler;
                }

                PDFWriter writer = new PDFWriter(OutStream, securityHandler);
                try
                {
                    DoSave(writer);
                }
                finally
                {
                    writer.Close();
                }
            }
        }
예제 #2
0
        public PDFWriter(Stream pdfStream, PDFStandardSecurityHandler securityHandler)
        {
            Stream          = pdfStream;
            SecurityHandler = securityHandler;
            //System.Xml.XmlTextWriter
#if DEBUG
            Layout = PDFWriterLayout.Verbose;
#endif
        }
예제 #3
0
        /// <summary>
        /// Converts a raw string into a raw hexadecimal string literal, possibly encrypted.
        /// </summary>
        public static string ToHexStringLiteral(byte[] bytes, bool unicode, PDFStandardSecurityHandler securityHandler)
        {
            if (bytes == null || bytes.Length == 0)
            {
                return("<>");
            }

            byte[] agTemp = FormatStringLiteral(bytes, unicode, true, true, securityHandler);
            return(RawEncoding.GetString(agTemp, 0, agTemp.Length));
        }
예제 #4
0
        /// <summary>
        /// Saves the document to the specified stream.
        /// </summary>
        public void Save(Stream stream, bool closeStream)
        {
            if (!CanModify)
            {
                throw new InvalidOperationException(PSSR.CannotModify);
            }

            // TODO: more diagnostic checks
            string message = "";

            if (!CanSave(ref message))
            {
                throw new PDFSharpException(message);
            }

            // Get security handler if document gets encrypted.
            PDFStandardSecurityHandler securityHandler = null;

            if (SecuritySettings.DocumentSecurityLevel != PDFDocumentSecurityLevel.None)
            {
                securityHandler = SecuritySettings.SecurityHandler;
            }

            PDFWriter writer = null;

            try
            {
                writer = new PDFWriter(stream, securityHandler);
                DoSave(writer);
            }
            finally
            {
                if (stream is Stream)
                {
                    if (closeStream)
                    {
                        stream.Close();
                    }
                    else if (stream.CanRead == true && stream.CanSeek == true)
                    {
                        stream.Position = 0;     // Reset the stream position if the stream is kept open.
                    }
                }

                writer?.Close(closeStream);
            }
        }
예제 #5
0
        /// <summary>
        /// Opens an existing PDF document.
        /// </summary>
        public static PDFDocument Open(Stream stream, string password, PDFDocumentOpenMode openmode, PDFPasswordProvider passwordProvider)
        {
            PDFDocument document;

            try
            {
                Lexer lexer = new Lexer(stream);
                document          = new PDFDocument(lexer);
                document.State   |= DocumentState.Imported;
                document.OpenMode = openmode;
                document.FileSize = stream.Length;

                // Get file version.
                byte[] header = new byte[1024];
                stream.Position = 0;
                stream.Read(header, 0, 1024);
                document._version = GetPDFFileVersion(header);
                if (document._version == 0)
                {
                    throw new InvalidOperationException(PSSR.InvalidPDF);
                }

                document.IrefTable.IsUnderConstruction = true;
                Parser parser = new Parser(document);
                // Read all trailers or cross-reference streams, but no objects.
                document.Trailer = parser.ReadTrailer();
                if (document.Trailer == null)
                {
                    ParserDiagnostics.ThrowParserException("Invalid PDF file: no trailer found."); // TODO L10N using PSSR.
                }
                Debug.Assert(document.IrefTable.IsUnderConstruction);
                document.IrefTable.IsUnderConstruction = false;

                // Is document encrypted?
                PDFReference xrefEncrypt = document.Trailer.Elements[PDFTrailer.Keys.Encrypt] as PDFReference;
                if (xrefEncrypt != null)
                {
                    //xrefEncrypt.Value = parser.ReadObject(null, xrefEncrypt.ObjectID, false);
                    PDFObject encrypt = parser.ReadObject(null, xrefEncrypt.ObjectID, false, false);

                    encrypt.Reference = xrefEncrypt;
                    xrefEncrypt.Value = encrypt;
                    PDFStandardSecurityHandler securityHandler = document.SecurityHandler;
TryAgain:
                    PasswordValidity validity = securityHandler.ValidatePassword(password);
                    if (validity == PasswordValidity.Invalid)
                    {
                        if (passwordProvider != null)
                        {
                            PDFPasswordProviderArgs args = new PDFPasswordProviderArgs();
                            passwordProvider(args);
                            if (args.Abort)
                            {
                                return(null);
                            }
                            password = args.Password;
                            goto TryAgain;
                        }
                        else
                        {
                            if (password == null)
                            {
                                throw new PDFReaderException(PSSR.PasswordRequired);
                            }
                            else
                            {
                                throw new PDFReaderException(PSSR.InvalidPassword);
                            }
                        }
                    }
                    else if (validity == PasswordValidity.UserPassword && openmode == PDFDocumentOpenMode.Modify)
                    {
                        if (passwordProvider != null)
                        {
                            PDFPasswordProviderArgs args = new PDFPasswordProviderArgs();
                            passwordProvider(args);
                            if (args.Abort)
                            {
                                return(null);
                            }
                            password = args.Password;
                            goto TryAgain;
                        }
                        else
                        {
                            throw new PDFReaderException(PSSR.OwnerPasswordRequired);
                        }
                    }
                }
                else
                {
                    if (password != null)
                    {
                        // Password specified but document is not encrypted.
                        // ignore
                    }
                }

                PDFReference[] irefs2 = document.IrefTable.AllReferences;
                int            count2 = irefs2.Length;

                // 3rd: Create iRefs for all compressed objects.
                Dictionary <int, object> objectStreams = new Dictionary <int, object>();
                for (int idx = 0; idx < count2; idx++)
                {
                    PDFReference iref = irefs2[idx];
                    if (iref.Value is PDFCrossReferenceStream xrefStream)
                    {
                        for (int idx2 = 0; idx2 < xrefStream.Entries.Count; idx2++)
                        {
                            PDFCrossReferenceStream.CrossReferenceStreamEntry item = xrefStream.Entries[idx2];
                            // Is type xref to compressed object?
                            if (item.Type == 2)
                            {
                                //PDFReference irefNew = parser.ReadCompressedObject(new PDFObjectID((int)item.Field2), (int)item.Field3);
                                //document._irefTable.Add(irefNew);
                                int objectNumber = (int)item.Field2;
                                if (!objectStreams.ContainsKey(objectNumber))
                                {
                                    objectStreams.Add(objectNumber, null);
                                    PDFObjectID objectID = new PDFObjectID((int)item.Field2);
                                    parser.ReadIRefsFromCompressedObject(objectID);
                                }
                            }
                        }
                    }
                }

                // 4th: Read compressed objects.
                for (int idx = 0; idx < count2; idx++)
                {
                    PDFReference iref = irefs2[idx];
                    if (iref.Value is PDFCrossReferenceStream xrefStream)
                    {
                        for (int idx2 = 0; idx2 < xrefStream.Entries.Count; idx2++)
                        {
                            PDFCrossReferenceStream.CrossReferenceStreamEntry item = xrefStream.Entries[idx2];
                            // Is type xref to compressed object?
                            if (item.Type == 2)
                            {
                                PDFReference irefNew = parser.ReadCompressedObject(new PDFObjectID((int)item.Field2),
                                                                                   (int)item.Field3);
                                Debug.Assert(document.IrefTable.Contains(iref.ObjectID));
                                //document._irefTable.Add(irefNew);
                            }
                        }
                    }
                }


                PDFReference[] irefs = document.IrefTable.AllReferences;
                int            count = irefs.Length;

                // Read all indirect objects.
                for (int idx = 0; idx < count; idx++)
                {
                    PDFReference iref = irefs[idx];
                    if (iref.Value == null)
                    {
#if DEBUG_
                        if (iref.ObjectNumber == 1074)
                        {
                            iref.GetType();
                        }
#endif
                        try
                        {
                            Debug.Assert(document.IrefTable.Contains(iref.ObjectID));
                            PDFObject pdfObject = parser.ReadObject(null, iref.ObjectID, false, false);
                            Debug.Assert(pdfObject.Reference == iref);
                            pdfObject.Reference = iref;
                            Debug.Assert(pdfObject.Reference.Value != null, "Something went wrong.");
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.Message);
                            // 4STLA rethrow exception to notify caller.
                            throw;
                        }
                    }
                    else
                    {
                        Debug.Assert(document.IrefTable.Contains(iref.ObjectID));
                        //iref.GetType();
                    }
                    // Set maximum object number.
                    document.IrefTable._maxObjectNumber = Math.Max(document.IrefTable._maxObjectNumber,
                                                                   iref.ObjectNumber);
                }

                // Encrypt all objects.
                if (xrefEncrypt != null)
                {
                    document.SecurityHandler.EncryptDocument();
                }

                // Fix references of trailer values and then objects and irefs are consistent.
                document.Trailer.Finish();

#if DEBUG_
                // Some tests...
                PDFReference[] reachables = document.xrefTable.TransitiveClosure(document.trailer);
                reachables.GetType();
                reachables = document.xrefTable.AllXRefs;
                document.xrefTable.CheckConsistence();
#endif

                if (openmode == PDFDocumentOpenMode.Modify)
                {
                    // Create new or change existing document IDs.
                    if (document.Internals.SecondDocumentID == "")
                    {
                        document.Trailer.CreateNewDocumentIDs();
                    }
                    else
                    {
                        byte[] agTemp = Guid.NewGuid().ToByteArray();
                        document.Internals.SecondDocumentID = PDFEncoders.RawEncoding.GetString(agTemp, 0, agTemp.Length);
                    }

                    // Change modification date
                    document.Info.ModificationDate = DateTime.Now;

                    // Remove all unreachable objects
                    int removed = document.IrefTable.Compact();
                    if (removed != 0)
                    {
                        Debug.WriteLine("Number of deleted unreachable objects: " + removed);
                    }

                    // Force flattening of page tree
                    PDFPages pages = document.Pages;
                    Debug.Assert(pages != null);

                    //bool b = document.irefTable.Contains(new PDFObjectID(1108));
                    //b.GetType();

                    document.IrefTable.CheckConsistence();
                    document.IrefTable.Renumber();
                    document.IrefTable.CheckConsistence();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                throw;
            }
            return(document);
        }
예제 #6
0
        /// <summary>
        /// Converts the specified byte array into a byte array representing a string literal.
        /// </summary>
        /// <param name="bytes">The bytes of the string.</param>
        /// <param name="unicode">Indicates whether one or two bytes are one character.</param>
        /// <param name="prefix">Indicates whether to use Unicode prefix.</param>
        /// <param name="hex">Indicates whether to create a hexadecimal string literal.</param>
        /// <param name="securityHandler">Encrypts the bytes if specified.</param>
        /// <returns>The PDF bytes.</returns>
        public static byte[] FormatStringLiteral(byte[] bytes, bool unicode, bool prefix, bool hex, PDFStandardSecurityHandler securityHandler)
        {
            if (bytes == null || bytes.Length == 0)
            {
                return hex ? new byte[] { (byte)'<', (byte)'>' }
            }
            : new byte[] { (byte)'(', (byte)')' };

            Debug.Assert(!unicode || bytes.Length % 2 == 0, "Odd number of bytes in Unicode string.");

            byte[] originalBytes = null;

            bool encrypted = false;

            if (securityHandler != null && !hex)
            {
                originalBytes = bytes;

                bytes     = (byte[])bytes.Clone();
                bytes     = securityHandler.EncryptBytes(bytes);
                encrypted = true;
            }

            int           count = bytes.Length;
            StringBuilder pdf   = new StringBuilder();

            if (!unicode)
            {
                if (!hex)
                {
                    pdf.Append("(");
                    for (int idx = 0; idx < count; idx++)
                    {
                        char ch = (char)bytes[idx];
                        if (ch < 32)
                        {
                            switch (ch)
                            {
                            case '\n':
                                pdf.Append("\\n");
                                break;

                            case '\r':
                                pdf.Append("\\r");
                                break;

                            case '\t':
                                pdf.Append("\\t");
                                break;

                            case '\b':
                                pdf.Append("\\b");
                                break;

                            // Corrupts encrypted text.
                            //case '\f':
                            //  pdf.Append("\\f");
                            //  break;

                            default:
                                // Don't escape characters less than 32 if the string is encrypted, because it is
                                // unreadable anyway.
                                encrypted = true;
                                if (!encrypted)
                                {
                                    pdf.Append("\\0");
                                    pdf.Append((char)(ch % 8 + '0'));
                                    pdf.Append((char)(ch / 8 + '0'));
                                }
                                else
                                {
                                    pdf.Append(ch);
                                }
                                break;
                            }
                        }
                        else
                        {
                            switch (ch)
                            {
                            case '(':
                                pdf.Append("\\(");
                                break;

                            case ')':
                                pdf.Append("\\)");
                                break;

                            case '\\':
                                pdf.Append("\\\\");
                                break;

                            default:
                                pdf.Append(ch);
                                break;
                            }
                        }
                    }
                    pdf.Append(')');
                }
                else
                {
                    pdf.Append('<');
                    for (int idx = 0; idx < count; idx++)
                    {
                        pdf.AppendFormat("{0:X2}", bytes[idx]);
                    }
                    pdf.Append('>');
                }
            }
            else
            {
                //Hex:
                if (hex)
                {
                    if (securityHandler != null && prefix)
                    {
                        // TODO Reduce redundancy.
                        // Encrypt data after padding BOM.
                        byte[] bytes2 = new byte[bytes.Length + 2];
                        // Add BOM.
                        bytes2[0] = 0xfe;
                        bytes2[1] = 0xff;
                        // Copy bytes.
                        Array.Copy(bytes, 0, bytes2, 2, bytes.Length);
                        // Encyption.
                        bytes2    = securityHandler.EncryptBytes(bytes2);
                        encrypted = true;
                        pdf.Append("<");
                        int count2 = bytes2.Length;
                        for (int idx = 0; idx < count2; idx += 2)
                        {
                            pdf.AppendFormat("{0:X2}{1:X2}", bytes2[idx], bytes2[idx + 1]);
                            if (idx != 0 && idx % 48 == 0)
                            {
                                pdf.Append("\n");
                            }
                        }
                        pdf.Append(">");
                    }
                    else
                    {
                        // No prefix or no encryption.
                        pdf.Append(prefix ? "<FEFF" : "<");
                        for (int idx = 0; idx < count; idx += 2)
                        {
                            pdf.AppendFormat("{0:X2}{1:X2}", bytes[idx], bytes[idx + 1]);
                            if (idx != 0 && idx % 48 == 0)
                            {
                                pdf.Append("\n");
                            }
                        }
                        pdf.Append(">");
                    }
                }
                else
                {
                    // TODO non hex literals... not sure how to treat linefeeds, '(', '\' etc.
                    if (encrypted)
                    {
                        // Hack: Call self with hex := true.
                        return(FormatStringLiteral(originalBytes, unicode, prefix, true, securityHandler));
                    }
                    else
                    {
                        // Hack: Call self with hex := true.
                        return(FormatStringLiteral(bytes, true, prefix, true, null));
                    }
                }
            }
            return(RawEncoding.GetBytes(pdf.ToString()));
        }
예제 #7
0
        /// <summary>
        /// Converts a raw string into a raw hexadecimal string literal, possibly encrypted.
        /// </summary>
        public static string ToHexStringLiteral(string text, PDFStringEncoding encoding, PDFStandardSecurityHandler securityHandler)
        {
            if (String.IsNullOrEmpty(text))
            {
                return("<>");
            }

            byte[] bytes;
            switch (encoding)
            {
            case PDFStringEncoding.RawEncoding:
                bytes = RawEncoding.GetBytes(text);
                break;

            case PDFStringEncoding.WinAnsiEncoding:
                bytes = WinAnsiEncoding.GetBytes(text);
                break;

            case PDFStringEncoding.PDFDocEncoding:
                bytes = DocEncoding.GetBytes(text);
                break;

            case PDFStringEncoding.Unicode:
                //bytes = UnicodeEncoding.GetBytes(text);
                bytes = RawUnicodeEncoding.GetBytes(text);
                break;

            default:
                throw new NotImplementedException(encoding.ToString());
            }

            byte[] agTemp = FormatStringLiteral(bytes, encoding == PDFStringEncoding.Unicode, true, true, securityHandler);
            return(RawEncoding.GetString(agTemp, 0, agTemp.Length));
        }
예제 #8
0
        /// <summary>
        /// Implements saving a PDF file.
        /// </summary>
        void DoSave(PDFWriter writer)
        {
            if (_pages == null || _pages.Count == 0)
            {
                if (OutStream != null)
                {
                    // Give feedback if the wrong constructor was used.
                    throw new InvalidOperationException("Cannot save a PDF document with no pages. Do not use \"public PDFDocument(string filename)\" or \"public PDFDocument(Stream outputStream)\" if you want to open an existing PDF document from a file or stream; use PDFReader.Open() for that purpose.");
                }
                throw new InvalidOperationException("Cannot save a PDF document with no pages.");
            }

            try
            {
                // HACK: Remove XRefTrailer
                if (Trailer is PDFCrossReferenceStream)
                {
                    // HACK^2: Preserve the SecurityHandler.
                    PDFStandardSecurityHandler securityHandler = _securitySettings.SecurityHandler;
                    Trailer = new PDFTrailer((PDFCrossReferenceStream)Trailer)
                    {
                        _securityHandler = securityHandler
                    };
                }

                bool encrypt = _securitySettings.DocumentSecurityLevel != PDFDocumentSecurityLevel.None;
                if (encrypt)
                {
                    PDFStandardSecurityHandler securityHandler = _securitySettings.SecurityHandler;
                    if (securityHandler.Reference == null)
                    {
                        IrefTable.Add(securityHandler);
                    }
                    else
                    {
                        Debug.Assert(IrefTable.Contains(securityHandler.ObjectID));
                    }
                    Trailer.Elements[PDFTrailer.Keys.Encrypt] = _securitySettings.SecurityHandler.Reference;
                }
                else
                {
                    Trailer.Elements.Remove(PDFTrailer.Keys.Encrypt);
                }

                PrepareForSave();

                if (encrypt)
                {
                    _securitySettings.SecurityHandler.PrepareEncryption();
                }

                writer.WriteFileHeader(this);
                PDFReference[] irefs = IrefTable.AllReferences;
                int            count = irefs.Length;
                for (int idx = 0; idx < count; idx++)
                {
                    PDFReference iref = irefs[idx];
                    iref.Position = writer.Position;
                    iref.Value.WriteObject(writer);
                }
                int startxref = writer.Position;
                IrefTable.WriteObject(writer);
                writer.WriteRaw("trailer\n");
                Trailer.Elements.SetInteger("/Size", count + 1);
                Trailer.WriteObject(writer);
                writer.WriteEof(this, startxref);
            }
            finally
            {
                if (writer != null)
                {
                    writer.Stream.Flush();
                }
            }
        }