├── PdfFileAnalyzer ├── TestPdfFileAnalyzer │ ├── Work │ │ └── TempFile │ ├── Program.cs │ ├── TestPdfFileAnalyzer.csproj │ ├── TestPdfFileAnalyzer.csproj.user │ ├── Password.cs │ ├── RecentFilesSelector.cs │ ├── RecentFilesSelector.Designer.cs │ ├── TestPdfFileAnalyzer.Designer.cs │ ├── AnalysisScreen.designer.cs │ ├── Password.designer.cs │ ├── TestPdfFileAnalyzer.cs │ ├── DisplayText.designer.cs │ ├── DisplayText.cs │ └── AnalysisScreen.cs ├── PdfFileAnalyzer │ ├── PdfFileAnalyzer.csproj │ ├── DecryptCtrl.cs │ ├── PdfNull.cs │ ├── PdfKeyword.cs │ ├── PdfName.cs │ ├── PdfRawData.cs │ ├── PdfReference.cs │ ├── NumFormatInfo.cs │ ├── PdfReal.cs │ ├── PdfBoolean.cs │ ├── PdfArray.cs │ ├── PdfKeyValue.cs │ ├── Adler32.cs │ ├── PdfFileParser.cs │ ├── PdfOp.cs │ ├── PdfInteger.cs │ ├── PdfByteArrayParser.cs │ ├── PdfObject.cs │ ├── PdfString.cs │ ├── OutputCtrl.cs │ ├── LZWDecode.cs │ ├── Reports.cs │ ├── PdfDictionary.cs │ ├── PdfBase.cs │ ├── CryptoEngine.cs │ ├── OpCtrl.cs │ └── PdfParser.cs └── PdfFileAnalyzer.sln └── README.md /PdfFileAnalyzer/TestPdfFileAnalyzer/Work/TempFile: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfFileAnalyzer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-windows 5 | disable 6 | true 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/Program.cs: -------------------------------------------------------------------------------- 1 | namespace TestPdfFileAnalyzer 2 | { 3 | internal static class Program 4 | { 5 | /// 6 | /// The main entry point for the application. 7 | /// 8 | [STAThread] 9 | static void Main() 10 | { 11 | ApplicationConfiguration.Initialize(); 12 | Application.Run(new TestPdfFileAnalyzer()); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/TestPdfFileAnalyzer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net6.0-windows 6 | disable 7 | true 8 | enable 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/TestPdfFileAnalyzer.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Form 7 | 8 | 9 | Form 10 | 11 | 12 | Form 13 | 14 | 15 | Form 16 | 17 | 18 | Form 19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PdfFileAnalyzer 2 | PDF File Analyzer With C# Parsing Classes (Version 3.0 for VS 2022 .NET 6.0) 3 | 4 | The PDF File Analyzer is designed to read, parse, and display the internal structure of PDF files. Version 2.1 supports encrypted files. 5 | 6 | For further information and documentation visit CodeProject article PDF File Analyzer With C# Parsing Classes 7 | 8 | This project allows you to read and parse PDF filse and display their internal structure. The PDF file specification document is available from Adobe. This project is based on “PDF Reference, Sixth Edition, Adobe Portable Document Format Version 1.7 November 2006”. It is an intimidating 1310 pages document. This article provides a concise overview of the specifications. The associated project defines C# classes for reading and parsing a PDF file. To test these classes the attached test program PdfFileAnalyzer allows you to read a PDF file analyzes it and display and save the result. The program breaks the PDF file into individual page descriptions, fonts, images and other objects. 9 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/DecryptCtrl.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Decrypt Control 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | // Decrypt control 31 | internal class DecryptCtrl 32 | { 33 | internal CryptoEngine Encryption; 34 | internal int ObjectNumber; 35 | internal PdfDictionary EncryptionDict; 36 | 37 | internal DecryptCtrl 38 | ( 39 | CryptoEngine Encryption, 40 | int ObjectNumber, 41 | PdfDictionary EncryptionDict 42 | ) 43 | { 44 | this.Encryption = Encryption; 45 | this.ObjectNumber = ObjectNumber; 46 | this.EncryptionDict = EncryptionDict; 47 | return; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfNull.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF Null Object 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF null object class 32 | /// 33 | public class PdfNull : PdfBase 34 | { 35 | /// 36 | /// Default contructor 37 | /// 38 | public PdfNull() {} 39 | 40 | /// 41 | /// Convert PdfNull to string 42 | /// 43 | /// string 44 | public override string ToString() 45 | { 46 | return "null"; 47 | } 48 | 49 | /// 50 | /// PdfNull type to string 51 | /// 52 | /// Null 53 | public override string TypeToString() 54 | { 55 | return "Null"; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31919.166 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfFileAnalyzer", "PdfFileAnalyzer\PdfFileAnalyzer.csproj", "{70E1650C-17D7-4D60-8E57-AD4CCCBDB5AB}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPdfFileAnalyzer", "TestPdfFileAnalyzer\TestPdfFileAnalyzer.csproj", "{479D919F-23C2-406A-8567-A3D1B9A85615}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {70E1650C-17D7-4D60-8E57-AD4CCCBDB5AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {70E1650C-17D7-4D60-8E57-AD4CCCBDB5AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {70E1650C-17D7-4D60-8E57-AD4CCCBDB5AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {70E1650C-17D7-4D60-8E57-AD4CCCBDB5AB}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {479D919F-23C2-406A-8567-A3D1B9A85615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {479D919F-23C2-406A-8567-A3D1B9A85615}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {479D919F-23C2-406A-8567-A3D1B9A85615}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {479D919F-23C2-406A-8567-A3D1B9A85615}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {844E3C61-8323-4BF0-82DA-B6D3C867B878} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/Password.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Get user password 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace TestPdfFileAnalyzer 29 | { 30 | /// 31 | /// Password class 32 | /// 33 | public partial class Password : Form 34 | { 35 | /// 36 | /// Password text string 37 | /// 38 | public string PasswordStr; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | public Password() 44 | { 45 | InitializeComponent(); 46 | } 47 | 48 | private void OnLoad(object sender, EventArgs e) 49 | { 50 | OK_Button.Enabled = false; 51 | return; 52 | } 53 | 54 | private void OnTextChanged(object sender, EventArgs e) 55 | { 56 | OK_Button.Enabled = !string.IsNullOrEmpty(PasswordTextBox.Text); 57 | return; 58 | } 59 | 60 | private void OnClosing(object sender, FormClosingEventArgs e) 61 | { 62 | if(DialogResult == DialogResult.OK) PasswordStr = PasswordTextBox.Text; 63 | return; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfKeyword.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF key word object 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF key word object 32 | /// 33 | public class PdfKeyword : PdfBase 34 | { 35 | /// 36 | /// Keyword enumeration 37 | /// 38 | public KeyWord KeywordValue; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | /// 44 | public PdfKeyword(KeyWord KeywordValue) 45 | { 46 | this.KeywordValue = KeywordValue; 47 | return; 48 | } 49 | 50 | /// 51 | /// Keyword to string 52 | /// 53 | /// string 54 | public override string ToString() 55 | { 56 | return KeywordValue.ToString(); 57 | } 58 | 59 | /// 60 | /// Object type to string 61 | /// 62 | /// string 63 | public override string TypeToString() 64 | { 65 | return "Keyword"; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfName.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF Name object 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF Name class 32 | /// 33 | public class PdfName : PdfBase 34 | { 35 | /// 36 | /// Gets name 37 | /// 38 | public string NameValue; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | /// 44 | /// First character must be / 45 | /// 46 | /// Name 47 | public PdfName(string NameValue) 48 | { 49 | this.NameValue = NameValue; 50 | return; 51 | } 52 | 53 | /// 54 | /// Convert name to string 55 | /// 56 | /// Name 57 | public override string ToString() 58 | { 59 | return NameValue; 60 | } 61 | 62 | /// 63 | /// Object type to string 64 | /// 65 | /// Name 66 | public override string TypeToString() 67 | { 68 | return "Name"; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfRawData.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF raw string class (for encoding) 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF raw string class (for encoding) 32 | /// 33 | public class PdfRawData : PdfBase 34 | { 35 | /// 36 | /// Gets raw data 37 | /// 38 | public string RawDataValue; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | /// Raw data string 44 | public PdfRawData(string RawDataValue) 45 | { 46 | this.RawDataValue = RawDataValue; 47 | return; 48 | } 49 | 50 | /// 51 | /// return string 52 | /// 53 | /// string representation of real number 54 | public override string ToString() 55 | { 56 | return RawDataValue; 57 | } 58 | 59 | /// 60 | /// Object type to string 61 | /// 62 | /// Raw data 63 | public override string TypeToString() 64 | { 65 | return "RawData"; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfReference.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF indirect object number 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF indirect object number 32 | /// 33 | public class PdfReference : PdfBase 34 | { 35 | /// 36 | /// Gets object number 37 | /// 38 | public int ObjectNumber; 39 | 40 | /// 41 | /// Contructor 42 | /// 43 | /// Object number 44 | public PdfReference(int ObjectNumber) 45 | { 46 | this.ObjectNumber = ObjectNumber; 47 | return; 48 | } 49 | 50 | /// 51 | /// Convert object number to indirect reference notation (n 0 R) 52 | /// 53 | /// Indirect reference 54 | public override string ToString() 55 | { 56 | return ObjectNumber.ToString() + " 0 R"; 57 | } 58 | 59 | /// 60 | /// Object type to string 61 | /// 62 | /// Reference 63 | public override string TypeToString() 64 | { 65 | return "Reference"; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/NumFormatInfo.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // NumFormatInfo Number Format Information static class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | using System.Globalization; 29 | 30 | namespace PdfFileAnalyzer 31 | { 32 | /// 33 | /// Number Format Information static class 34 | /// 35 | /// 36 | /// Adobe readers expect decimal separator to be a period. 37 | /// Some countries define decimal separator as a comma. 38 | /// The project uses NFI.DecSep to force period for all regions. 39 | /// 40 | public static class NumFormatInfo 41 | { 42 | /// 43 | /// Define period as number decimal separator. 44 | /// 45 | /// 46 | /// NumberFormatInfo is used with string formatting to set the 47 | /// decimal separator to a period regardless of region. 48 | /// 49 | public static NumberFormatInfo PeriodDecSep { get; private set; } 50 | 51 | // static constructor 52 | static NumFormatInfo() 53 | { 54 | // number format (decimal separator is period) 55 | PeriodDecSep = new NumberFormatInfo 56 | { 57 | NumberDecimalSeparator = "." 58 | }; 59 | return; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfReal.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF real number class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF real number class 32 | /// 33 | public class PdfReal : PdfBase 34 | { 35 | /// 36 | /// Gets real value 37 | /// 38 | public double RealValue; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | /// Real value 44 | public PdfReal(double RealValue) 45 | { 46 | this.RealValue = RealValue; 47 | return; 48 | } 49 | 50 | /// 51 | /// Convert real number to string 52 | /// 53 | /// string representation of real number 54 | public override string ToString() 55 | { 56 | // protect against engineering notation 57 | if(Math.Abs(RealValue) < 0.0001) return "0"; 58 | return ((float) RealValue).ToString("G", NumFormatInfo.PeriodDecSep); 59 | } 60 | 61 | /// 62 | /// Object type to string 63 | /// 64 | /// Real 65 | public override string TypeToString() 66 | { 67 | return "Real"; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfBoolean.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PdfBoolean object 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF boolean object 32 | /// 33 | public class PdfBoolean : PdfBase 34 | { 35 | /// 36 | /// Gets object value 37 | /// 38 | public bool BooleanValue; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | /// Object value 44 | public PdfBoolean(bool BooleanValue) 45 | { 46 | this.BooleanValue = BooleanValue; 47 | return; 48 | } 49 | 50 | /// 51 | /// Override ToString methos 52 | /// 53 | /// Either true or false 54 | public override string ToString() 55 | { 56 | return BooleanValue ? "true" : "false"; 57 | } 58 | 59 | /// 60 | /// Object type to string 61 | /// 62 | /// bool 63 | public override string TypeToString() 64 | { 65 | return "bool"; 66 | } 67 | 68 | /// 69 | /// Static false object 70 | /// 71 | public static readonly PdfBoolean False = new(false); 72 | 73 | /// 74 | /// Static true object 75 | /// 76 | public static readonly PdfBoolean True = new(true); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfArray.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PdfArray object 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF array object 32 | /// 33 | public class PdfArray : PdfBase 34 | { 35 | /// 36 | /// PdfArray items list 37 | /// 38 | public List Items; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | /// Array initial values 44 | public PdfArray 45 | ( 46 | params PdfBase[] ArrayItems 47 | ) 48 | { 49 | this.Items = new List(ArrayItems); 50 | return; 51 | } 52 | 53 | /// 54 | /// Add one object to the array 55 | /// 56 | /// Added value 57 | public void Add 58 | ( 59 | PdfBase Obj 60 | ) 61 | { 62 | Items.Add(Obj); 63 | return; 64 | } 65 | 66 | /// 67 | /// Return array as PdfBase[] array 68 | /// 69 | public PdfBase[] ArrayItems 70 | { 71 | get 72 | { 73 | return Items.ToArray(); 74 | } 75 | } 76 | 77 | /// 78 | /// Derived class type to string 79 | /// 80 | /// Array 81 | public override string TypeToString() 82 | { 83 | return "Array"; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfKeyValue.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF Dictionary key and value class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// Dictionary key and value class 32 | /// 33 | public class PdfKeyValue : PdfBase, IComparable 34 | { 35 | /// 36 | /// Gets key 37 | /// 38 | public string Key; 39 | 40 | /// 41 | /// Gets value 42 | /// 43 | public PdfBase Value; 44 | 45 | /// 46 | /// Contructor 47 | /// 48 | /// Dictionary key 49 | /// Dictionary value 50 | public PdfKeyValue 51 | ( 52 | string Key, 53 | PdfBase Value 54 | ) 55 | { 56 | // DEBUG 57 | if(Key[0] != '/') throw new ApplicationException("Key must start with /"); 58 | this.Key = Key; 59 | this.Value = Value; 60 | return; 61 | } 62 | 63 | /// 64 | /// Constructor for compare 65 | /// 66 | /// Dictionary key 67 | public PdfKeyValue 68 | ( 69 | string Key 70 | ) 71 | { 72 | if(Key[0] != '/') throw new ApplicationException("Key must start with /"); 73 | this.Key = Key; 74 | return; 75 | } 76 | 77 | /// 78 | /// Compare two key value pairs 79 | /// 80 | /// Other key value 81 | /// Result 82 | public int CompareTo 83 | ( 84 | PdfKeyValue Other 85 | ) 86 | { 87 | return string.Compare(this.Key, Other.Key); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/Adler32.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Adler32 checksum 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// Adler32 Checksum 32 | /// 33 | public static class Adler32 34 | { 35 | /// 36 | /// Accumulate Adler Checksum 37 | /// 38 | /// Input buffer 39 | /// Adler32 Checksum 40 | public static uint Checksum 41 | ( 42 | byte[] Buffer 43 | ) 44 | { 45 | const uint Adler32Base = 65521; 46 | 47 | // split current Adler checksum into two 48 | uint AdlerLow = 1; // AdlerValue & 0xFFFF; 49 | uint AdlerHigh = 0; // AdlerValue >> 16; 50 | 51 | int Len = Buffer.Length; 52 | int Pos = 0; 53 | while (Len > 0) 54 | { 55 | // We can defer the modulo operation: 56 | // Under worst case the starting value of the two halves is 65520 = (AdlerBase - 1) 57 | // each new byte is maximum 255 58 | // The low half grows AdlerLow(n) = AdlerBase - 1 + n * 255 59 | // The high half grows AdlerHigh(n) = (n + 1)*(AdlerBase - 1) + n * (n + 1) * 255 / 2 60 | // The maximum n before overflow of 32 bit unsigned integer is 5552 61 | // it is the solution of the following quadratic equation 62 | // 255 * n * n + (2 * (AdlerBase - 1) + 255) * n + 2 * (AdlerBase - 1 - uint.MaxValue) = 0 63 | int n = Len < 5552 ? Len : 5552; 64 | Len -= n; 65 | while (--n >= 0) 66 | { 67 | AdlerLow += (uint)Buffer[Pos++]; 68 | AdlerHigh += AdlerLow; 69 | } 70 | AdlerLow %= Adler32Base; 71 | AdlerHigh %= Adler32Base; 72 | } 73 | return (AdlerHigh << 16) | AdlerLow; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/RecentFilesSelector.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Display recent files for selection 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace TestPdfFileAnalyzer 29 | { 30 | /// 31 | /// Recent files selector class 32 | /// 33 | public partial class RecentFilesSelector : Form 34 | { 35 | /// 36 | /// Selected file name 37 | /// 38 | public string FileName; 39 | 40 | private readonly List RecentFiles; 41 | 42 | /// 43 | /// Recent files selector constructor 44 | /// 45 | /// 46 | public RecentFilesSelector 47 | ( 48 | List RecentFiles 49 | ) 50 | { 51 | this.RecentFiles = RecentFiles; 52 | InitializeComponent(); 53 | return; 54 | } 55 | 56 | private void OnLoad(object sender, EventArgs e) 57 | { 58 | foreach(string OneFile in RecentFiles) FilesListBox.Items.Add(OneFile); 59 | FilesListBox.SelectedIndex = 0; 60 | return; 61 | } 62 | 63 | private void ONOK_Button(object sender, EventArgs e) 64 | { 65 | FileName = (string) FilesListBox.SelectedItem; 66 | DialogResult = DialogResult.OK; 67 | Close(); 68 | return; 69 | } 70 | 71 | private void OnMouseDoubleClick(object sender, MouseEventArgs e) 72 | { 73 | if(sender != FilesListBox) return; 74 | 75 | int Index = FilesListBox.IndexFromPoint(e.Location); 76 | if(Index >= 0 && Index < FilesListBox.Items.Count) 77 | { 78 | FileName = (string) FilesListBox.Items[Index]; 79 | DialogResult = DialogResult.OK; 80 | Close(); 81 | } 82 | return; 83 | } 84 | 85 | private void OnCancel_Button(object sender, EventArgs e) 86 | { 87 | DialogResult = DialogResult.Cancel; 88 | return; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfFileParser.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF file parser 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF file parser 32 | /// 33 | public class PdfFileParser : PdfParser 34 | { 35 | internal BinaryReader PdfBinaryReader; 36 | 37 | /// 38 | /// Constructor 39 | /// 40 | /// PdfReader 41 | public PdfFileParser 42 | ( 43 | PdfReader Reader 44 | ) : base(Reader, false) 45 | { 46 | PdfBinaryReader = Reader.PdfBinaryReader; 47 | return; 48 | } 49 | 50 | /// 51 | /// Read one byte from input stream 52 | /// 53 | /// One byte 54 | public override int ReadChar() 55 | { 56 | try 57 | { 58 | return PdfBinaryReader.ReadByte(); 59 | } 60 | catch 61 | { 62 | throw new ApplicationException("Unexpected end of file"); 63 | } 64 | } 65 | 66 | /// 67 | /// Step back one byte 68 | /// 69 | public override void StepBack() 70 | { 71 | PdfBinaryReader.BaseStream.Position--; 72 | return; 73 | } 74 | 75 | /// 76 | /// Get current file position 77 | /// 78 | /// File position 79 | public override int GetPos() 80 | { 81 | return (int) PdfBinaryReader.BaseStream.Position; 82 | } 83 | 84 | /// 85 | /// Set file posion 86 | /// 87 | /// File position 88 | public override void SetPos(int Pos) 89 | { 90 | PdfBinaryReader.BaseStream.Position = Pos; 91 | return; 92 | } 93 | 94 | /// 95 | /// Set relative position 96 | /// 97 | /// Step size 98 | public override void SkipPos(int Pos) 99 | { 100 | PdfBinaryReader.BaseStream.Position += Pos; 101 | return; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfOp.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF contents operator class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF contents operator class 32 | /// 33 | public class PdfOp : PdfBase 34 | { 35 | /// 36 | /// Gets operator enumeration 37 | /// 38 | public Operator OpValue; 39 | 40 | /// 41 | /// Gets argument array 42 | /// 43 | public PdfBase[] ArgumentArray; 44 | 45 | /// 46 | /// Constructor 47 | /// 48 | /// Operator enumerator 49 | public PdfOp(Operator OpValue) 50 | { 51 | this.OpValue = OpValue; 52 | return; 53 | } 54 | 55 | /// 56 | /// Convert output control to byte array 57 | /// 58 | /// Ouput Control 59 | public override void ToByteArray 60 | ( 61 | OutputCtrl Ctrl 62 | ) 63 | { 64 | if(OpValue != Operator.BeginInlineImage) 65 | { 66 | // add arguments 67 | if(ArgumentArray != null) foreach(PdfBase Arg in ArgumentArray) 68 | { 69 | Arg.ToByteArray(Ctrl); 70 | Ctrl.Add(' '); 71 | } 72 | // add code 73 | Ctrl.AppendText(OpCtrl.OperatorCode(OpValue)); 74 | Ctrl.Add(' '); 75 | Ctrl.Add('%'); 76 | Ctrl.Add(' '); 77 | Ctrl.AppendText(OpValue.ToString()); 78 | Ctrl.AddEol(); 79 | return; 80 | } 81 | 82 | Ctrl.Add('B'); 83 | Ctrl.Add('I'); 84 | Ctrl.Add(' '); 85 | foreach(PdfKeyValue KeyValue in ((PdfDictionary) ArgumentArray[0]).KeyValueArray) 86 | { 87 | Ctrl.AppendText(KeyValue.Key); 88 | KeyValue.Value.ToByteArray(Ctrl); 89 | } 90 | 91 | Ctrl.Add(' '); 92 | Ctrl.Add('I'); 93 | Ctrl.Add('D'); 94 | 95 | Ctrl.AppendText("INLINE IMAGE DATA EI % InlineImage"); 96 | Ctrl.AddEol(); 97 | return; 98 | } 99 | 100 | /// 101 | /// Object type to string 102 | /// 103 | /// Operator 104 | public override string TypeToString() 105 | { 106 | return "Operator"; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfInteger.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PdfInteger object 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF integer class 32 | /// 33 | public class PdfInteger : PdfBase 34 | { 35 | /// 36 | /// Gets stored integer value 37 | /// 38 | public int IntValue; 39 | 40 | /// 41 | /// constructor 42 | /// 43 | /// Integer value 44 | public PdfInteger(int IntValue) 45 | { 46 | this.IntValue = IntValue; 47 | return; 48 | } 49 | 50 | /// 51 | /// Convert PdfInteger to string 52 | /// 53 | /// 54 | public override string ToString() 55 | { 56 | return IntValue.ToString(); 57 | } 58 | 59 | /// 60 | /// Object type to string 61 | /// 62 | /// Integer 63 | public override string TypeToString() 64 | { 65 | return "Integer"; 66 | } 67 | 68 | /// 69 | /// PdfInteger constant = 0 70 | /// 71 | public static readonly PdfInteger Zero = new(0); 72 | 73 | /// 74 | /// PdfInteger constant = 1 75 | /// 76 | public static readonly PdfInteger One = new(1); 77 | 78 | /// 79 | /// PdfInteger constant = 2 80 | /// 81 | public static readonly PdfInteger Two = new(2); 82 | 83 | /// 84 | /// PdfInteger constant = 3 85 | /// 86 | public static readonly PdfInteger Three = new(3); 87 | 88 | /// 89 | /// PdfInteger constant = 4 90 | /// 91 | public static readonly PdfInteger Four = new(4); 92 | 93 | /// 94 | /// PdfInteger constant = 8 95 | /// 96 | public static readonly PdfInteger Eight = new(8); 97 | 98 | /// 99 | /// PdfInteger constant = 16 100 | /// 101 | public static readonly PdfInteger Sixteen = new(16); 102 | 103 | /// 104 | /// PdfInteger constant = 128 105 | /// 106 | public static readonly PdfInteger OneTwoEight = new(128); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfByteArrayParser.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF byte array parser for graphics contents 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// byte array parser for graphics contents 32 | /// 33 | public class PdfByteArrayParser : PdfParser 34 | { 35 | internal byte[] Contents; 36 | internal int Position; 37 | 38 | //////////////////////////////////////////////////////////////////// 39 | // constructor 40 | //////////////////////////////////////////////////////////////////// 41 | /// 42 | /// Parser constructor for byte array 43 | /// 44 | /// PdfReader 45 | /// Byte array contents 46 | /// Stream mode 47 | public PdfByteArrayParser 48 | ( 49 | PdfReader Reader, 50 | byte[] Contents, 51 | bool StreamMode 52 | ) : base(Reader, StreamMode) 53 | { 54 | this.Contents = Contents; 55 | return; 56 | } 57 | 58 | /// 59 | /// Read one byte from contents byte array 60 | /// 61 | /// One byte within integer 62 | public override int ReadChar() 63 | { 64 | return Position == Contents.Length ? EOF : Contents[Position++]; 65 | } 66 | 67 | /// 68 | /// Step back one character 69 | /// 70 | public override void StepBack() 71 | { 72 | Position--; 73 | return; 74 | } 75 | 76 | /// 77 | /// Get current read position 78 | /// 79 | /// Position 80 | public override int GetPos() 81 | { 82 | return Position; 83 | } 84 | 85 | /// 86 | /// Set current read position 87 | /// 88 | /// Position 89 | public override void SetPos(int Pos) 90 | { 91 | Position = Pos; 92 | return; 93 | } 94 | 95 | /// 96 | /// Relative set position 97 | /// 98 | /// Step size 99 | public override void SkipPos(int Pos) 100 | { 101 | Position += Pos; 102 | return; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfObject.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF indirect object class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF indirect object type 32 | /// 33 | public enum ObjectType 34 | { 35 | /// 36 | /// Object is not in use 37 | /// 38 | Free, 39 | 40 | /// 41 | /// Object is other than a dictionary or a stream 42 | /// 43 | Other, 44 | 45 | /// 46 | /// Object is a dictionary 47 | /// 48 | Dictionary, 49 | 50 | /// 51 | /// Object is a stream 52 | /// 53 | Stream, 54 | } 55 | 56 | /// 57 | /// PDF indirect object base class 58 | /// 59 | public class PdfObject 60 | { 61 | /// 62 | /// Gets indirect object number 63 | /// 64 | public int ObjectNumber { get; internal set; } 65 | 66 | /// 67 | /// Gets object's file position 68 | /// 69 | public int FilePosition { get; internal set; } 70 | 71 | /// 72 | /// Gets parent object number (for object stream) 73 | /// 74 | public int ParentObjectNo { get; internal set; } 75 | 76 | /// 77 | /// Gets parent object index (for object stream) 78 | /// 79 | public int ParentObjectIndex { get; internal set; } 80 | 81 | /// 82 | /// Gets object type 83 | /// 84 | public ObjectType ObjectType { get; internal set; } 85 | 86 | /// 87 | /// Gets object type 88 | /// 89 | public string PdfObjectType { get; internal set; } 90 | 91 | /// 92 | /// Object dictionary 93 | /// 94 | public PdfDictionary Dictionary { get; internal set; } 95 | 96 | /// 97 | /// Object value if ObjectType = Other 98 | /// 99 | public PdfBase Value { get; internal set; } 100 | 101 | /// 102 | /// PDF Object description 103 | /// 104 | /// string 105 | public string ObjectDescription() 106 | { 107 | return ObjectType switch 108 | { 109 | ObjectType.Free => "Free", 110 | ObjectType.Other => Value.TypeToString(), 111 | ObjectType.Dictionary => "Dictionary", 112 | ObjectType.Stream => "Stream", 113 | _ => "Error", 114 | }; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/RecentFilesSelector.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestPdfFileAnalyzer 2 | { 3 | partial class RecentFilesSelector 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.FilesListBox = new System.Windows.Forms.ListBox(); 32 | this.button1 = new System.Windows.Forms.Button(); 33 | this.button2 = new System.Windows.Forms.Button(); 34 | this.SuspendLayout(); 35 | // 36 | // FilesListBox 37 | // 38 | this.FilesListBox.FormattingEnabled = true; 39 | this.FilesListBox.ItemHeight = 16; 40 | this.FilesListBox.Location = new System.Drawing.Point(0, 0); 41 | this.FilesListBox.Name = "FilesListBox"; 42 | this.FilesListBox.Size = new System.Drawing.Size(461, 276); 43 | this.FilesListBox.TabIndex = 0; 44 | this.FilesListBox.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.OnMouseDoubleClick); 45 | // 46 | // button1 47 | // 48 | this.button1.Location = new System.Drawing.Point(99, 298); 49 | this.button1.Name = "button1"; 50 | this.button1.Size = new System.Drawing.Size(107, 38); 51 | this.button1.TabIndex = 1; 52 | this.button1.Text = "OK"; 53 | this.button1.UseVisualStyleBackColor = true; 54 | this.button1.Click += new System.EventHandler(this.ONOK_Button); 55 | // 56 | // button2 57 | // 58 | this.button2.Location = new System.Drawing.Point(230, 298); 59 | this.button2.Name = "button2"; 60 | this.button2.Size = new System.Drawing.Size(107, 38); 61 | this.button2.TabIndex = 2; 62 | this.button2.Text = "Cancel"; 63 | this.button2.UseVisualStyleBackColor = true; 64 | this.button2.Click += new System.EventHandler(this.OnCancel_Button); 65 | // 66 | // RecentFilesSelector 67 | // 68 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); 69 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 70 | this.ClientSize = new System.Drawing.Size(461, 350); 71 | this.ControlBox = false; 72 | this.Controls.Add(this.button2); 73 | this.Controls.Add(this.button1); 74 | this.Controls.Add(this.FilesListBox); 75 | this.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 76 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 77 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 78 | this.Name = "RecentFilesSelector"; 79 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 80 | this.Text = "Recent Files"; 81 | this.Load += new System.EventHandler(this.OnLoad); 82 | this.ResumeLayout(false); 83 | 84 | } 85 | 86 | #endregion 87 | 88 | private System.Windows.Forms.ListBox FilesListBox; 89 | private System.Windows.Forms.Button button1; 90 | private System.Windows.Forms.Button button2; 91 | } 92 | } -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfString.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF string class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF string class 32 | /// 33 | public class PdfString : PdfBase 34 | { 35 | /// 36 | /// string value 37 | /// 38 | public byte[] StrValue; 39 | 40 | /// 41 | /// Constructor for PDF reader 42 | /// 43 | /// byte array 44 | public PdfString(byte[] StrValue) 45 | { 46 | this.StrValue = StrValue; 47 | return; 48 | } 49 | 50 | /// 51 | /// Constructor for PDF Writer 52 | /// 53 | /// C Sharp type string 54 | public PdfString(string CSharpStr) 55 | { 56 | // scan input text for Unicode characters 57 | bool Unicode = false; 58 | foreach(char TestChar in CSharpStr) if(TestChar > 255) 59 | { 60 | Unicode = true; 61 | break; 62 | } 63 | 64 | // all characters are one byte long 65 | if(!Unicode) 66 | { 67 | // save each imput character in one byte 68 | StrValue = new byte[CSharpStr.Length]; 69 | int Index1 = 0; 70 | foreach(char TestChar in CSharpStr) StrValue[Index1++] = (byte) TestChar; 71 | return; 72 | } 73 | 74 | // Unicode case. we have some two bytes characters 75 | // allocate output byte array 76 | StrValue = new byte[2 * CSharpStr.Length + 2]; 77 | 78 | // add Unicode marker at the start of the string 79 | StrValue[0] = 0xfe; 80 | StrValue[1] = 0xff; 81 | 82 | // save each character as two bytes 83 | int Index2 = 2; 84 | foreach(char TestChar in CSharpStr) 85 | { 86 | StrValue[Index2++] = (byte) (TestChar >> 8); 87 | StrValue[Index2++] = (byte) TestChar; 88 | } 89 | return; 90 | } 91 | 92 | /// 93 | /// Convert PdfString to unicode string 94 | /// 95 | /// Text string 96 | public string ToUnicode() 97 | { 98 | if(StrValue == null) return string.Empty; 99 | 100 | // unicode 101 | if(StrValue.Length >= 2 && StrValue[0] == 0xfe && StrValue[1] == 0xff) 102 | { 103 | char[] UniArray = new char[StrValue.Length / 2]; 104 | for(int Index = 0; Index < UniArray.Length; Index++) UniArray[Index] = (char) (StrValue[2 * Index] << 8 | StrValue[2 * Index + 1]); 105 | return new string(UniArray); 106 | } 107 | 108 | // ascii 109 | char[] ChrArray = new char[StrValue.Length]; 110 | for(int Index = 0; Index < StrValue.Length; Index++) ChrArray[Index] = (char) StrValue[Index]; 111 | return new string(ChrArray); 112 | } 113 | 114 | /// 115 | /// Object type to string 116 | /// 117 | /// PDFString 118 | public override string TypeToString() 119 | { 120 | return "PDFString"; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/TestPdfFileAnalyzer.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestPdfFileAnalyzer 2 | { 3 | partial class TestPdfFileAnalyzer 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.AnalyzeButton = new System.Windows.Forms.Button(); 32 | this.CopyrightTextBox = new System.Windows.Forms.RichTextBox(); 33 | this.RecentFilesButton = new System.Windows.Forms.Button(); 34 | this.SuspendLayout(); 35 | // 36 | // AnalyzeButton 37 | // 38 | this.AnalyzeButton.Location = new System.Drawing.Point(152, 375); 39 | this.AnalyzeButton.Name = "AnalyzeButton"; 40 | this.AnalyzeButton.Size = new System.Drawing.Size(149, 45); 41 | this.AnalyzeButton.TabIndex = 1; 42 | this.AnalyzeButton.Text = "Open PDF File"; 43 | this.AnalyzeButton.UseVisualStyleBackColor = true; 44 | this.AnalyzeButton.Click += new System.EventHandler(this.OnOpenPdfFile); 45 | // 46 | // CopyrightTextBox 47 | // 48 | this.CopyrightTextBox.BackColor = System.Drawing.SystemColors.GradientInactiveCaption; 49 | this.CopyrightTextBox.Location = new System.Drawing.Point(37, 47); 50 | this.CopyrightTextBox.MaxLength = 2048; 51 | this.CopyrightTextBox.Name = "CopyrightTextBox"; 52 | this.CopyrightTextBox.ReadOnly = true; 53 | this.CopyrightTextBox.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None; 54 | this.CopyrightTextBox.Size = new System.Drawing.Size(548, 276); 55 | this.CopyrightTextBox.TabIndex = 0; 56 | this.CopyrightTextBox.Text = ""; 57 | // 58 | // RecentFilesButton 59 | // 60 | this.RecentFilesButton.Location = new System.Drawing.Point(321, 375); 61 | this.RecentFilesButton.Name = "RecentFilesButton"; 62 | this.RecentFilesButton.Size = new System.Drawing.Size(149, 45); 63 | this.RecentFilesButton.TabIndex = 2; 64 | this.RecentFilesButton.Text = "Recent Files"; 65 | this.RecentFilesButton.UseVisualStyleBackColor = true; 66 | this.RecentFilesButton.Click += new System.EventHandler(this.OnRecentFiles); 67 | // 68 | // TestPdfFileAnalyzer 69 | // 70 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); 71 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 72 | this.ClientSize = new System.Drawing.Size(622, 442); 73 | this.Controls.Add(this.RecentFilesButton); 74 | this.Controls.Add(this.AnalyzeButton); 75 | this.Controls.Add(this.CopyrightTextBox); 76 | this.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 77 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 78 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 79 | this.MaximizeBox = false; 80 | this.Name = "TestPdfFileAnalyzer"; 81 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 82 | this.Text = "PDF File Analyzer"; 83 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnClosing); 84 | this.Load += new System.EventHandler(this.OnLoad); 85 | this.ResumeLayout(false); 86 | 87 | } 88 | 89 | #endregion 90 | 91 | private System.Windows.Forms.Button AnalyzeButton; 92 | private System.Windows.Forms.RichTextBox CopyrightTextBox; 93 | private System.Windows.Forms.Button RecentFilesButton; 94 | } 95 | } 96 | 97 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/OutputCtrl.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Output control 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// Output control class 32 | /// 33 | public class OutputCtrl 34 | { 35 | private readonly List ByteArray; 36 | private int EolMarker; 37 | 38 | /// 39 | /// Output control constructor 40 | /// 41 | public OutputCtrl() 42 | { 43 | ByteArray = new List(); 44 | EolMarker = 100; 45 | return; 46 | } 47 | 48 | /// 49 | /// Add one byte 50 | /// 51 | /// 52 | public void Add 53 | ( 54 | byte Chr 55 | ) 56 | { 57 | ByteArray.Add(Chr); 58 | return; 59 | } 60 | 61 | /// 62 | /// add one character 63 | /// 64 | /// 65 | public void Add 66 | ( 67 | char Chr 68 | ) 69 | { 70 | ByteArray.Add((byte) Chr); 71 | return; 72 | } 73 | 74 | /// 75 | /// Append text string 76 | /// 77 | /// 78 | public void AppendText 79 | ( 80 | string Text 81 | ) 82 | { 83 | // remove double delimeters 84 | if(ByteArray.Count > 0 && !PdfBase.IsDelimiter(ByteArray[^1]) && !PdfBase.IsDelimiter(Text[0])) 85 | ByteArray.Add((byte) ' '); 86 | 87 | // move charaters to bytes 88 | foreach(char Chr in Text) ByteArray.Add((byte) Chr); 89 | return; 90 | } 91 | 92 | /// 93 | /// Append text message and add end of linw 94 | /// 95 | /// 96 | public void AppendMessage 97 | ( 98 | string Text 99 | ) 100 | { 101 | foreach(char Chr in Text) ByteArray.Add((byte) Chr); 102 | AddEol(); 103 | return; 104 | } 105 | 106 | /// 107 | /// Add end of line 108 | /// 109 | public void AddEol() 110 | { 111 | ByteArray.Add((byte) '\n'); 112 | EolMarker = ByteArray.Count + 100; 113 | return; 114 | } 115 | 116 | /// 117 | /// test for line too long 118 | /// 119 | public void TestEol() 120 | { 121 | // add new line to cut down very long lines (just appearance) 122 | if(ByteArray.Count > EolMarker) 123 | { 124 | ByteArray.Add((byte) '\n'); 125 | EolMarker = ByteArray.Count + 100; 126 | } 127 | return; 128 | } 129 | 130 | /// 131 | /// Test for end of line 132 | /// 133 | public void TestEscEol() 134 | { 135 | // add new line to cut down very long lines (just appearance) 136 | if(ByteArray.Count > EolMarker) 137 | { 138 | ByteArray.Add((byte) '\\'); 139 | ByteArray.Add((byte) '\n'); 140 | EolMarker = ByteArray.Count + 100; 141 | } 142 | return; 143 | } 144 | /// 145 | /// Convert list to array 146 | /// 147 | /// 148 | public byte[] ToArray() 149 | { 150 | return ByteArray.ToArray(); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/AnalysisScreen.designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestPdfFileAnalyzer 2 | { 3 | partial class AnalysisScreen 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.ButtonsGroupBox = new System.Windows.Forms.GroupBox(); 32 | this.ViewButton = new System.Windows.Forms.Button(); 33 | this.SummaryButton = new System.Windows.Forms.Button(); 34 | this.ExitButton = new System.Windows.Forms.Button(); 35 | this.ButtonsGroupBox.SuspendLayout(); 36 | this.SuspendLayout(); 37 | // 38 | // ButtonsGroupBox 39 | // 40 | this.ButtonsGroupBox.Controls.Add(this.ViewButton); 41 | this.ButtonsGroupBox.Controls.Add(this.SummaryButton); 42 | this.ButtonsGroupBox.Controls.Add(this.ExitButton); 43 | this.ButtonsGroupBox.Location = new System.Drawing.Point(140, 410); 44 | this.ButtonsGroupBox.Name = "ButtonsGroupBox"; 45 | this.ButtonsGroupBox.Size = new System.Drawing.Size(375, 74); 46 | this.ButtonsGroupBox.TabIndex = 0; 47 | this.ButtonsGroupBox.TabStop = false; 48 | // 49 | // ViewButton 50 | // 51 | this.ViewButton.Location = new System.Drawing.Point(26, 22); 52 | this.ViewButton.Name = "ViewButton"; 53 | this.ViewButton.Size = new System.Drawing.Size(88, 36); 54 | this.ViewButton.TabIndex = 0; 55 | this.ViewButton.Text = "View"; 56 | this.ViewButton.UseVisualStyleBackColor = true; 57 | this.ViewButton.Click += new System.EventHandler(this.OnView); 58 | // 59 | // SummaryButton 60 | // 61 | this.SummaryButton.Location = new System.Drawing.Point(143, 22); 62 | this.SummaryButton.Name = "SummaryButton"; 63 | this.SummaryButton.Size = new System.Drawing.Size(88, 36); 64 | this.SummaryButton.TabIndex = 1; 65 | this.SummaryButton.Text = "Summary"; 66 | this.SummaryButton.UseVisualStyleBackColor = true; 67 | this.SummaryButton.Click += new System.EventHandler(this.OnSummary); 68 | // 69 | // ExitButton 70 | // 71 | this.ExitButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; 72 | this.ExitButton.Location = new System.Drawing.Point(260, 22); 73 | this.ExitButton.Name = "ExitButton"; 74 | this.ExitButton.Size = new System.Drawing.Size(88, 36); 75 | this.ExitButton.TabIndex = 2; 76 | this.ExitButton.Text = "Close"; 77 | this.ExitButton.UseVisualStyleBackColor = true; 78 | this.ExitButton.Click += new System.EventHandler(this.OnExit); 79 | // 80 | // PdfFileAnalyzer 81 | // 82 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); 83 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 84 | this.ClientSize = new System.Drawing.Size(655, 508); 85 | this.Controls.Add(this.ButtonsGroupBox); 86 | this.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 87 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 88 | this.Name = "PdfFileAnalyzer"; 89 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 90 | this.Text = "PDF File Analayzer"; 91 | this.Load += new System.EventHandler(this.OnLoad); 92 | this.Resize += new System.EventHandler(this.OnResize); 93 | this.ButtonsGroupBox.ResumeLayout(false); 94 | this.ResumeLayout(false); 95 | 96 | } 97 | 98 | #endregion 99 | 100 | private System.Windows.Forms.GroupBox ButtonsGroupBox; 101 | private System.Windows.Forms.Button ViewButton; 102 | private System.Windows.Forms.Button SummaryButton; 103 | private System.Windows.Forms.Button ExitButton; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/Password.designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestPdfFileAnalyzer 2 | { 3 | partial class Password 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.label1 = new System.Windows.Forms.Label(); 32 | this.MessageLabel = new System.Windows.Forms.Label(); 33 | this.PasswordTextBox = new System.Windows.Forms.TextBox(); 34 | this.OK_Button = new System.Windows.Forms.Button(); 35 | this.Cancel_Button = new System.Windows.Forms.Button(); 36 | this.SuspendLayout(); 37 | // 38 | // label1 39 | // 40 | this.label1.AutoSize = true; 41 | this.label1.Location = new System.Drawing.Point(26, 26); 42 | this.label1.Name = "label1"; 43 | this.label1.Size = new System.Drawing.Size(202, 16); 44 | this.label1.TabIndex = 0; 45 | this.label1.Text = "PDF File is protected (Encrypted)"; 46 | // 47 | // MessageLabel 48 | // 49 | this.MessageLabel.AutoSize = true; 50 | this.MessageLabel.Location = new System.Drawing.Point(26, 58); 51 | this.MessageLabel.Name = "MessageLabel"; 52 | this.MessageLabel.Size = new System.Drawing.Size(216, 16); 53 | this.MessageLabel.TabIndex = 1; 54 | this.MessageLabel.Text = "Enter either user or owner password"; 55 | // 56 | // PasswordTextBox 57 | // 58 | this.PasswordTextBox.Location = new System.Drawing.Point(29, 90); 59 | this.PasswordTextBox.Name = "PasswordTextBox"; 60 | this.PasswordTextBox.Size = new System.Drawing.Size(282, 22); 61 | this.PasswordTextBox.TabIndex = 3; 62 | this.PasswordTextBox.TextChanged += new System.EventHandler(this.OnTextChanged); 63 | // 64 | // OK_Button 65 | // 66 | this.OK_Button.DialogResult = System.Windows.Forms.DialogResult.OK; 67 | this.OK_Button.Location = new System.Drawing.Point(64, 135); 68 | this.OK_Button.Name = "OK_Button"; 69 | this.OK_Button.Size = new System.Drawing.Size(89, 35); 70 | this.OK_Button.TabIndex = 4; 71 | this.OK_Button.Text = "OK"; 72 | this.OK_Button.UseVisualStyleBackColor = true; 73 | // 74 | // Cancel_Button 75 | // 76 | this.Cancel_Button.DialogResult = System.Windows.Forms.DialogResult.Cancel; 77 | this.Cancel_Button.Location = new System.Drawing.Point(184, 135); 78 | this.Cancel_Button.Name = "Cancel_Button"; 79 | this.Cancel_Button.Size = new System.Drawing.Size(89, 35); 80 | this.Cancel_Button.TabIndex = 5; 81 | this.Cancel_Button.Text = "Cancel"; 82 | this.Cancel_Button.UseVisualStyleBackColor = true; 83 | // 84 | // Password 85 | // 86 | this.AcceptButton = this.OK_Button; 87 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); 88 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 89 | this.CancelButton = this.Cancel_Button; 90 | this.ClientSize = new System.Drawing.Size(337, 189); 91 | this.Controls.Add(this.Cancel_Button); 92 | this.Controls.Add(this.OK_Button); 93 | this.Controls.Add(this.PasswordTextBox); 94 | this.Controls.Add(this.MessageLabel); 95 | this.Controls.Add(this.label1); 96 | this.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 97 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 98 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 99 | this.MaximizeBox = false; 100 | this.MinimizeBox = false; 101 | this.Name = "Password"; 102 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 103 | this.Text = "Encryption Password"; 104 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnClosing); 105 | this.Load += new System.EventHandler(this.OnLoad); 106 | this.ResumeLayout(false); 107 | this.PerformLayout(); 108 | 109 | } 110 | 111 | #endregion 112 | 113 | private System.Windows.Forms.Label label1; 114 | private System.Windows.Forms.Label MessageLabel; 115 | private System.Windows.Forms.TextBox PasswordTextBox; 116 | private System.Windows.Forms.Button OK_Button; 117 | private System.Windows.Forms.Button Cancel_Button; 118 | } 119 | } -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/LZWDecode.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // LZWDecode Decode object compressed by LZW 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// LZW Decompression class 32 | /// 33 | public static class LZW 34 | { 35 | /// 36 | /// Decompress LZW buffer 37 | /// 38 | /// Read buffer byte array 39 | /// Decoded byte array 40 | public static byte[] Decode 41 | ( 42 | byte[] ReadBuffer 43 | ) 44 | { 45 | // define two special codes 46 | const int ResetDictionary = 256; 47 | const int EndOfStream = 257; 48 | 49 | // create new dictionary 50 | byte[][] Dictionary = new byte[4096][]; 51 | 52 | // initialize first 256 entries 53 | for(int Index = 0; Index < 256; Index++) Dictionary[Index] = new byte[] {(byte) Index}; 54 | 55 | // output buffer 56 | List WriteBuffer = new(); 57 | 58 | // Initialize variables 59 | int ReadPtr = 0; 60 | int BitBuffer = 0; 61 | int BitCount = 0; 62 | int DictionaryPtr = 258; 63 | int CodeLength = 9; 64 | int CodeMask = 511; 65 | int OldCode = -1; 66 | 67 | // loop for all codes in the buffer 68 | for(;;) 69 | { 70 | // fill the buffer such that it will contain 17 to 24 bits 71 | for(; BitCount <= 16 && ReadPtr < ReadBuffer.Length; BitCount += 8) BitBuffer = (BitBuffer << 8) | ReadBuffer[ReadPtr++]; 72 | 73 | // for LZW blocks with missing end of block mark 74 | if(BitCount < CodeLength) break; 75 | 76 | // get next code 77 | int Code = (BitBuffer >> (BitCount - CodeLength)) & CodeMask; 78 | BitCount -= CodeLength; 79 | 80 | // end of encoded area 81 | if(Code == EndOfStream) break; 82 | 83 | // reset dictionary 84 | if(Code == ResetDictionary) 85 | { 86 | DictionaryPtr = 258; 87 | CodeLength = 9; 88 | CodeMask = 511; 89 | OldCode = -1; 90 | continue; 91 | } 92 | 93 | // text to be added to output buffer 94 | byte[] AddToOutput; 95 | 96 | // code is available in the dictionary 97 | if(Code < DictionaryPtr) 98 | { 99 | // text to be added to output buffer 100 | AddToOutput = Dictionary[Code]; 101 | 102 | // first time after dictionary reset 103 | if(OldCode < 0) 104 | { 105 | WriteBuffer.AddRange(AddToOutput); 106 | OldCode = Code; 107 | continue; 108 | } 109 | 110 | // add new entry to dictionary 111 | // the previous match and the new first byte 112 | Dictionary[DictionaryPtr++] = BuildString(Dictionary[OldCode], AddToOutput[0]); 113 | } 114 | 115 | // special case repeating the same squence with first and last byte being the same 116 | else if(Code == DictionaryPtr) 117 | { 118 | // text to be added to output buffer 119 | AddToOutput = Dictionary[OldCode]; 120 | AddToOutput = BuildString(AddToOutput, AddToOutput[0]); 121 | 122 | // add new entry to the dictionary 123 | Dictionary[DictionaryPtr++] = AddToOutput; 124 | } 125 | 126 | // code should not be greater than dictionary size 127 | else throw new ApplicationException("LZWDecode: Code error"); 128 | 129 | // add to output buffer 130 | WriteBuffer.AddRange(AddToOutput); 131 | 132 | // save code 133 | OldCode = Code; 134 | 135 | // switch code length from 9 to 10, 11 and 12 136 | if(DictionaryPtr == 511 || DictionaryPtr == 1023 || DictionaryPtr == 2047) 137 | { 138 | CodeLength++; 139 | CodeMask = (CodeMask << 1) + 1; 140 | } 141 | } 142 | 143 | // return decoded byte array 144 | return WriteBuffer.ToArray(); 145 | } 146 | 147 | ///////////////////////////////////////////////////////////////// 148 | // Build new dictionary string 149 | ///////////////////////////////////////////////////////////////// 150 | private static byte[] BuildString 151 | ( 152 | byte[] OldString, 153 | byte AddedByte 154 | ) 155 | { 156 | int Length = OldString.Length; 157 | byte[] NewString = new byte[Length + 1]; 158 | Array.Copy(OldString, 0, NewString, 0, Length); 159 | NewString[Length] = AddedByte; 160 | return NewString; 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/TestPdfFileAnalyzer.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PdfFileAnalyser Test/Demo application 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | using PdfFileAnalyzer; 29 | using System.Text; 30 | 31 | namespace TestPdfFileAnalyzer 32 | { 33 | /// 34 | /// Test PDF file analyzer class 35 | /// 36 | public partial class TestPdfFileAnalyzer : Form 37 | { 38 | private List RecentFiles; 39 | private static readonly string RecentFilesName = "PdfFileAnalyzerFiles.txt"; 40 | 41 | /// 42 | /// Test PDF file analyzer constructor 43 | /// 44 | public TestPdfFileAnalyzer() 45 | { 46 | InitializeComponent(); 47 | return; 48 | } 49 | 50 | private void OnLoad(object sender, EventArgs e) 51 | { 52 | #if DEBUG 53 | // current directory 54 | string CurDir = Environment.CurrentDirectory; 55 | int Index = CurDir.IndexOf("bin\\Debug"); 56 | if (Index > 0) 57 | { 58 | string WorkDir = string.Concat(CurDir.AsSpan(0, Index), "Work"); 59 | if (Directory.Exists(WorkDir)) Environment.CurrentDirectory = WorkDir; 60 | } 61 | #endif 62 | 63 | // program title 64 | Text = "PdfFileAnalyzer-Version " + PdfReader.VersionNumber + " " + PdfReader.VersionDate + "-\u00a9 2012-2022 Uzi Granot"; 65 | 66 | // copyright box 67 | CopyrightTextBox.Rtf = 68 | "{\\rtf1\\ansi\\deff0\\deftab720{\\fonttbl{\\f0\\fswiss\\fprq2 Verdana;}}" + 69 | "\\par\\plain\\fs24\\b PdfFileAnalyzer\\plain \\fs20 \\par\\par \n" + 70 | "PDF File Analyzer is designed to read, parse and display\\par the internal structure of PDF files.\\par\\par \n" + 71 | "Version Number: " + PdfReader.VersionNumber + "\\par \n" + 72 | "Version Date: " + PdfReader.VersionDate + "\\par \n" + 73 | "Author: Uzi Granot\\par\\par \n" + 74 | "Copyright \u00a9 2012-2022 Uzi Granot. All rights reserved.\\par\\par \n" + 75 | "Free software distributed under the Code Project Open License (CPOL) 1.02.\\par \n" + 76 | "As per PdfFileAnalyzerReadmeAndLicense.pdf file attached to this distribution.\\par \n" + 77 | "You must read and agree with the terms specified to use this program.}"; 78 | 79 | // recent files 80 | RecentFiles = new List(); 81 | if (File.Exists(RecentFilesName)) 82 | { 83 | using StreamReader Reader = new(RecentFilesName, Encoding.ASCII); 84 | for (;;) 85 | { 86 | string OneFile = Reader.ReadLine(); 87 | if (OneFile == null) break; 88 | if (File.Exists(OneFile)) RecentFiles.Add(OneFile); 89 | } 90 | } 91 | if (RecentFiles.Count == 0) RecentFilesButton.Enabled = false; 92 | 93 | // exit 94 | return; 95 | } 96 | 97 | private void OnOpenPdfFile(object sender, EventArgs e) 98 | { 99 | // get file name 100 | OpenFileDialog OFD = new(); 101 | OFD.InitialDirectory = "."; 102 | OFD.Filter = "PDF File (*.pdf)|*.PDF"; 103 | OFD.RestoreDirectory = true; 104 | if (OFD.ShowDialog() != DialogResult.OK) return; 105 | 106 | // open the file 107 | OpenPdfFile(OFD.FileName); 108 | return; 109 | } 110 | 111 | private void OnRecentFiles(object sender, EventArgs e) 112 | { 113 | RecentFilesSelector Selector = new(RecentFiles); 114 | if (Selector.ShowDialog(this) == DialogResult.OK) 115 | { 116 | OpenPdfFile(Selector.FileName); 117 | } 118 | return; 119 | } 120 | 121 | private void OpenPdfFile 122 | ( 123 | string FileName 124 | ) 125 | { 126 | // load document 127 | PdfReader Reader = new(); 128 | 129 | // try to open the file 130 | if (!Reader.OpenPdfFile(FileName)) 131 | { 132 | if (Reader.DecryptionStatus == DecryptionStatus.Unsupported) 133 | { 134 | MessageBox.Show("PDF document is encrypted. Encryption method is not supported."); 135 | return; 136 | } 137 | 138 | // get password from user 139 | for (; ; ) 140 | { 141 | Password PasswordDialog = new(); 142 | DialogResult DResult = PasswordDialog.ShowDialog(); 143 | if (DResult != DialogResult.OK) return; 144 | if (Reader.TestPassword(PasswordDialog.PasswordStr)) break; 145 | } 146 | } 147 | 148 | // open message 149 | string OpenMsg = string.Format("PDF document was successfuly loaded\r\n\r\nNumber of pages: {0}\r\n\r\nNumber of objects: {1:#,##0}", 150 | Reader.PageCount, Reader.ObjectArray.Length); 151 | if (Reader.DecryptionStatus == DecryptionStatus.OwnerPassword) OpenMsg += "\r\n\r\nDocument was encrypted with owner password"; 152 | else if (Reader.DecryptionStatus == DecryptionStatus.UserPassword) OpenMsg += "\r\n\r\nPDF document was encrypted with user password"; 153 | if (Reader.InvalidPdfFile) OpenMsg += "\r\n\r\nInvalid PDF document. Does not meet standard"; 154 | MessageBox.Show(OpenMsg); 155 | 156 | // display analysis screen 157 | AnalysisScreen AnalyzerForm = new(Reader); 158 | AnalyzerForm.Text = FileName; 159 | AnalyzerForm.ShowDialog(); 160 | Reader.Dispose(); 161 | 162 | // recent files 163 | RecentFiles.Insert(0, FileName); 164 | for (int Index = 1; Index < RecentFiles.Count; Index++) 165 | { 166 | if (string.Compare(RecentFiles[Index], FileName, true) != 0) continue; 167 | RecentFiles.RemoveAt(Index); 168 | break; 169 | } 170 | if (RecentFiles.Count > 20) RecentFiles.RemoveRange(20, RecentFiles.Count - 20); 171 | RecentFilesButton.Enabled = true; 172 | return; 173 | } 174 | 175 | private void OnClosing(object sender, FormClosingEventArgs e) 176 | { 177 | // recent files 178 | using StreamWriter Writer = new(RecentFilesName, false, Encoding.UTF8); 179 | foreach (string FileName in RecentFiles) Writer.WriteLine(FileName); 180 | return; 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/Reports.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Reports 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | using System.Text; 29 | 30 | namespace PdfFileAnalyzer 31 | { 32 | /// 33 | /// Static report utilities 34 | /// 35 | public static class Reports 36 | { 37 | /// 38 | /// Byte array to string 39 | /// 40 | /// Byte array 41 | /// Text string 42 | public static string ByteArrayToString 43 | ( 44 | byte[] ByteArray 45 | ) 46 | { 47 | StringBuilder Str = new(); 48 | byte LastByte = 0; 49 | foreach(byte OneByte in ByteArray) 50 | { 51 | if(OneByte == (byte) '\n') 52 | { 53 | if(LastByte != (byte) '\r') Str.Append("\r\n"); 54 | } 55 | else if(OneByte == (byte) '\r') 56 | { 57 | Str.Append("\r\n"); 58 | } 59 | else if(OneByte < 32 || OneByte > 126 && OneByte < 160) 60 | { 61 | Str.Append('.'); 62 | } 63 | else 64 | { 65 | Str.Append((char) OneByte); 66 | } 67 | LastByte = OneByte; 68 | } 69 | if(Str.Length != 0 && Str[^1] != '\n') Str.Append("\r\n"); 70 | return Str.ToString(); 71 | } 72 | 73 | /// 74 | /// Byte array to hex string 75 | /// 76 | /// Byte array 77 | /// Text string 78 | public static string ByteArrayToHex 79 | ( 80 | byte[] ByteArray 81 | ) 82 | { 83 | byte[] HexLine = new byte[16]; 84 | 85 | StringBuilder Str = new(); 86 | 87 | // loop for multiple of 16 bytes 88 | int Length = ByteArray.Length & ~15; 89 | for(int Pos = 0; Pos < Length; Pos += 16) 90 | { 91 | Array.Copy(ByteArray, Pos, HexLine, 0, 16); 92 | FormatHexLine(Pos, HexLine, Str); 93 | } 94 | 95 | // last partial line 96 | int Extra = ByteArray.Length - Length; 97 | if(Extra > 0) 98 | { 99 | // The start of the formatted line is correct. The end is left over from previous line 100 | int Ptr = Str.Length; 101 | Array.Copy(ByteArray, Length, HexLine, 0, Extra); 102 | FormatHexLine(Length, HexLine, Str); 103 | 104 | // erase the portion after the end of the file 105 | Ptr += 10 + 3 * Extra; 106 | int Len = 3 * (16 - Extra) + 1; 107 | if(Extra > 7) 108 | { 109 | Ptr++; 110 | Len--; 111 | } 112 | while(Len-- > 0) Str[Ptr++] = ' '; 113 | Ptr += 1 + Extra; 114 | Len = 16 - Extra; 115 | while(Len-- > 0) Str[Ptr++] = ' '; 116 | } 117 | return Str.ToString(); 118 | } 119 | 120 | private static void FormatHexLine 121 | ( 122 | int Pos, 123 | byte[] Hex, 124 | StringBuilder Text 125 | ) 126 | { 127 | Text.Append(string.Format("{0:X8} {1:X2} {2:X2} {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} " + 128 | "{9:X2} {10:X2} {11:X2} {12:X2} {13:X2} {14:X2} {15:X2} {16:X2} " + 129 | "{17}{18}{19}{20}{21}{22}{23}{24}{25}{26}{27}{28}{29}{30}{31}{32}\r\n", 130 | Pos, Hex[0], Hex[1], Hex[2], Hex[3], Hex[4], Hex[5], Hex[6], Hex[7], Hex[8], Hex[9], Hex[10], 131 | Hex[11], Hex[12], Hex[13], Hex[14], Hex[15], Prt(Hex[0]), Prt(Hex[1]), Prt(Hex[2]), Prt(Hex[3]), 132 | Prt(Hex[4]), Prt(Hex[5]), Prt(Hex[6]), Prt(Hex[7]), Prt(Hex[8]), Prt(Hex[9]), Prt(Hex[10]), 133 | Prt(Hex[11]), Prt(Hex[12]), Prt(Hex[13]), Prt(Hex[14]), Prt(Hex[15]))); 134 | return; 135 | } 136 | 137 | /// 138 | /// Format one character 139 | /// 140 | /// Input byte 141 | /// Output character 142 | private static char Prt 143 | ( 144 | byte C 145 | ) 146 | { 147 | return C >= ' ' && C <= '~' ? (char) C : '.'; 148 | } 149 | 150 | /// 151 | /// Write PDF document object summary to a file 152 | /// 153 | /// PdfReader 154 | /// File name 155 | public static void PdfFileSummary 156 | ( 157 | PdfReader Reader, 158 | string OutputFileName 159 | ) 160 | { 161 | using BinaryWriter BinWriter = new(File.Open(OutputFileName, FileMode.Create)); 162 | BinWriter.Write(PdfFileSummary(Reader)); 163 | return; 164 | } 165 | 166 | /// 167 | /// Write PDF document object summary to byte array 168 | /// 169 | /// Text string 170 | public static string PdfFileSummary 171 | ( 172 | PdfReader Reader 173 | ) 174 | { 175 | OutputCtrl Ctrl = new(); 176 | 177 | Ctrl.AppendMessage(string.Format("PDF file name: {0}", Reader.SafeFileName)); 178 | Ctrl.AddEol(); 179 | Ctrl.AppendMessage("Trailer Dictionary"); 180 | Ctrl.AppendMessage("------------------"); 181 | Reader.TrailerDict.ToByteArray(Ctrl); 182 | Ctrl.AddEol(); 183 | 184 | Ctrl.AddEol(); 185 | Ctrl.AppendMessage("Indirect Objects"); 186 | Ctrl.AppendMessage("----------------"); 187 | 188 | // output one object at a time 189 | foreach(PdfIndirectObject Obj in Reader.ObjectArray) if(Obj != null) Obj.ObjectSummary(Ctrl); 190 | 191 | // successful exit 192 | return ByteArrayToString(Ctrl.ToArray()); 193 | } 194 | 195 | /// 196 | /// Create PDF file's object summary 197 | /// 198 | /// ASCII byte array 199 | public static string ObjectSummary 200 | ( 201 | PdfIndirectObject ReaderObject 202 | ) 203 | { 204 | OutputCtrl Ctrl = new(); 205 | ReaderObject.ObjectSummary(Ctrl); 206 | return ByteArrayToString(Ctrl.ToArray()); 207 | } 208 | 209 | /// 210 | /// Page contents to text 211 | /// 212 | /// Op array 213 | /// Byte array 214 | public static byte[] ContentsToText 215 | ( 216 | PdfOp[] OpArray 217 | ) 218 | { 219 | OutputCtrl Ctrl = new(); 220 | 221 | // output one operator at a time 222 | foreach(PdfOp Op in OpArray) Op.ToByteArray(Ctrl); 223 | 224 | // successful exit 225 | return Ctrl.ToArray(); 226 | } 227 | 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfDictionary.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF Dictionary class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// PDF dictionary class 32 | /// 33 | public class PdfDictionary : PdfBase 34 | { 35 | /// 36 | /// List of key value pairs 37 | /// 38 | public List KeyValueArray; 39 | 40 | /// 41 | /// Default constructor 42 | /// 43 | public PdfDictionary() 44 | { 45 | KeyValueArray = new List(); 46 | return; 47 | } 48 | 49 | /// 50 | /// Add array to dictionary 51 | /// 52 | /// Dictionary key 53 | /// Array of objects 54 | public void AddArray 55 | ( 56 | string Key, // key (first character must be forward slash /) 57 | params PdfBase[] Items 58 | ) 59 | { 60 | AddKeyValue(Key, new PdfArray(Items)); 61 | return; 62 | } 63 | 64 | /// 65 | /// Add boolean value to dictionary 66 | /// 67 | /// Dictionary key 68 | /// bool value 69 | public void AddBoolean 70 | ( 71 | string Key, // key (first character must be forward slash /) 72 | bool Bool 73 | ) 74 | { 75 | AddKeyValue(Key, Bool ? PdfBoolean.True : PdfBoolean.False); 76 | return; 77 | } 78 | 79 | //////////////////////////////////////////////////////////////////// 80 | // Add dictionary 81 | //////////////////////////////////////////////////////////////////// 82 | /// 83 | /// Add dictionary to dictionary 84 | /// 85 | /// Dictionary key 86 | /// Dictionary value 87 | public void AddDictionary 88 | ( 89 | string Key, // key (first character must be forward slash /) 90 | PdfDictionary Value // value 91 | ) 92 | { 93 | AddKeyValue(Key, Value); 94 | return; 95 | } 96 | 97 | /// 98 | /// Add integer to dictionary 99 | /// 100 | /// Dictionary key 101 | /// Integer 102 | public void AddInteger 103 | ( 104 | string Key, // key (first character must be forward slash /) 105 | int Integer 106 | ) 107 | { 108 | AddKeyValue(Key, new PdfInteger(Integer)); 109 | return; 110 | } 111 | 112 | /// 113 | /// Add PdfName to dictionark 114 | /// 115 | /// Dictionary key 116 | /// Name string (must start with /) 117 | public void AddName 118 | ( 119 | string Key, // key (first character must be forward slash /) 120 | string NameStr // name (first character must be forward slash /) 121 | ) 122 | { 123 | // DEBUG 124 | if(NameStr[0] != '/') throw new ApplicationException("DEBUG Name must start with /"); 125 | AddKeyValue(Key, new PdfName(NameStr)); 126 | return; 127 | } 128 | 129 | /// 130 | /// Add PDF string 131 | /// 132 | /// Dictionary key 133 | /// Text string to be converted to PDF string 134 | public void AddPdfString 135 | ( 136 | string Key, // key (first character must be forward slash /) 137 | string Str 138 | ) 139 | { 140 | AddKeyValue(Key, new PdfString(Str)); 141 | return; 142 | } 143 | 144 | /// 145 | /// Add PDF string 146 | /// 147 | /// Dictionary key 148 | /// byte array 149 | public void AddPdfString 150 | ( 151 | string Key, // key (first character must be forward slash /) 152 | byte[] Str 153 | ) 154 | { 155 | AddKeyValue(Key, new PdfString(Str)); 156 | return; 157 | } 158 | 159 | /// 160 | /// Add real number to dictionary. 161 | /// 162 | /// Dictionary key 163 | /// 164 | public void AddReal 165 | ( 166 | string Key, // key (first character must be forward slash /) 167 | double Real 168 | ) 169 | { 170 | AddKeyValue(Key, new PdfReal(Real)); 171 | return; 172 | } 173 | 174 | /// 175 | /// Add any object derived from PdfBase to dictionary 176 | /// 177 | /// Dictionary key 178 | /// Derived object of PdfBase 179 | public void AddKeyValue 180 | ( 181 | string Key, 182 | PdfBase Value 183 | ) 184 | { 185 | // create pair 186 | PdfKeyValue KeyValue = new(Key, Value); 187 | 188 | // keep dictionary sorted 189 | int Index = KeyValueArray.BinarySearch(KeyValue); 190 | 191 | // replace existing duplicate entry 192 | if(Index >= 0) KeyValueArray[Index] = KeyValue; 193 | 194 | // add to result dictionary 195 | else KeyValueArray.Insert(~Index, KeyValue); 196 | 197 | // exit 198 | return; 199 | } 200 | 201 | /// 202 | /// Search dictionary for key and return the associated value 203 | /// 204 | /// Dictionary key 205 | /// Derived object of PdfBase 206 | public PdfBase FindValue 207 | ( 208 | string Key 209 | ) 210 | { 211 | int Index = KeyValueArray.BinarySearch(new PdfKeyValue(Key)); 212 | return Index < 0 ? PdfBase.Empty : KeyValueArray[Index].Value; 213 | } 214 | 215 | /// 216 | /// Search dictionary for key and return the associated value 217 | /// 218 | /// Dictionary key 219 | /// Derived object of PdfBase 220 | public bool Exists 221 | ( 222 | string Key 223 | ) 224 | { 225 | int Index = KeyValueArray.BinarySearch(new PdfKeyValue(Key)); 226 | return Index >= 0; 227 | } 228 | 229 | /// 230 | /// Gets number of items in the dictionary 231 | /// 232 | public int Count 233 | { 234 | get 235 | { 236 | return KeyValueArray.Count; 237 | } 238 | } 239 | 240 | /// 241 | /// Remove key value pair from dictionary 242 | /// 243 | /// Dictionary key 244 | public void Remove 245 | ( 246 | string Key // key (first character must be forward slash /) 247 | ) 248 | { 249 | int Index = KeyValueArray.BinarySearch(new PdfKeyValue(Key)); 250 | if(Index >= 0) KeyValueArray.RemoveAt(Index); 251 | return; 252 | } 253 | 254 | /// 255 | /// Dictionary type string 256 | /// 257 | /// Dictionary 258 | public override string TypeToString() 259 | { 260 | return "Dictionary"; 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/DisplayText.designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestPdfFileAnalyzer 2 | { 3 | partial class DisplayText 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.DisplayBox = new System.Windows.Forms.TextBox(); 32 | this.Close_Button = new System.Windows.Forms.Button(); 33 | this.SaveToFileButton = new System.Windows.Forms.Button(); 34 | this.ButtonsBox = new System.Windows.Forms.GroupBox(); 35 | this.ViewStreamButton = new System.Windows.Forms.Button(); 36 | this.RotateLeftButton = new System.Windows.Forms.Button(); 37 | this.RotateRightButton = new System.Windows.Forms.Button(); 38 | this.ViewContentsButton = new System.Windows.Forms.Button(); 39 | this.CopyToClipboardButton = new System.Windows.Forms.Button(); 40 | this.HexDisplayButton = new System.Windows.Forms.Button(); 41 | this.TextDisplayButton = new System.Windows.Forms.Button(); 42 | this.ButtonsBox.SuspendLayout(); 43 | this.SuspendLayout(); 44 | // 45 | // DisplayBox 46 | // 47 | this.DisplayBox.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 48 | this.DisplayBox.Location = new System.Drawing.Point(5, 2); 49 | this.DisplayBox.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 50 | this.DisplayBox.Multiline = true; 51 | this.DisplayBox.Name = "DisplayBox"; 52 | this.DisplayBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; 53 | this.DisplayBox.Size = new System.Drawing.Size(1024, 460); 54 | this.DisplayBox.TabIndex = 0; 55 | this.DisplayBox.WordWrap = false; 56 | // 57 | // Close_Button 58 | // 59 | this.Close_Button.DialogResult = System.Windows.Forms.DialogResult.Cancel; 60 | this.Close_Button.Location = new System.Drawing.Point(772, 18); 61 | this.Close_Button.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 62 | this.Close_Button.Name = "Close_Button"; 63 | this.Close_Button.Size = new System.Drawing.Size(75, 44); 64 | this.Close_Button.TabIndex = 8; 65 | this.Close_Button.Text = "Close"; 66 | this.Close_Button.UseVisualStyleBackColor = true; 67 | // 68 | // SaveToFileButton 69 | // 70 | this.SaveToFileButton.Location = new System.Drawing.Point(514, 18); 71 | this.SaveToFileButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 72 | this.SaveToFileButton.Name = "SaveToFileButton"; 73 | this.SaveToFileButton.Size = new System.Drawing.Size(110, 44); 74 | this.SaveToFileButton.TabIndex = 6; 75 | this.SaveToFileButton.Text = "Save to File"; 76 | this.SaveToFileButton.UseVisualStyleBackColor = true; 77 | this.SaveToFileButton.Click += new System.EventHandler(this.OnSaveToFile); 78 | // 79 | // ButtonsBox 80 | // 81 | this.ButtonsBox.Controls.Add(this.HexDisplayButton); 82 | this.ButtonsBox.Controls.Add(this.TextDisplayButton); 83 | this.ButtonsBox.Controls.Add(this.ViewStreamButton); 84 | this.ButtonsBox.Controls.Add(this.RotateLeftButton); 85 | this.ButtonsBox.Controls.Add(this.RotateRightButton); 86 | this.ButtonsBox.Controls.Add(this.ViewContentsButton); 87 | this.ButtonsBox.Controls.Add(this.CopyToClipboardButton); 88 | this.ButtonsBox.Controls.Add(this.SaveToFileButton); 89 | this.ButtonsBox.Controls.Add(this.Close_Button); 90 | this.ButtonsBox.Location = new System.Drawing.Point(98, 469); 91 | this.ButtonsBox.Name = "ButtonsBox"; 92 | this.ButtonsBox.Size = new System.Drawing.Size(864, 71); 93 | this.ButtonsBox.TabIndex = 1; 94 | this.ButtonsBox.TabStop = false; 95 | // 96 | // ViewStreamButton 97 | // 98 | this.ViewStreamButton.Location = new System.Drawing.Point(18, 18); 99 | this.ViewStreamButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 100 | this.ViewStreamButton.Name = "ViewStreamButton"; 101 | this.ViewStreamButton.Size = new System.Drawing.Size(106, 44); 102 | this.ViewStreamButton.TabIndex = 0; 103 | this.ViewStreamButton.Text = "View Stream"; 104 | this.ViewStreamButton.UseVisualStyleBackColor = true; 105 | this.ViewStreamButton.Click += new System.EventHandler(this.OnViewStream); 106 | // 107 | // RotateLeftButton 108 | // 109 | this.RotateLeftButton.Font = new System.Drawing.Font("Wingdings 3", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2))); 110 | this.RotateLeftButton.Location = new System.Drawing.Point(450, 18); 111 | this.RotateLeftButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 112 | this.RotateLeftButton.Name = "RotateLeftButton"; 113 | this.RotateLeftButton.Size = new System.Drawing.Size(52, 44); 114 | this.RotateLeftButton.TabIndex = 5; 115 | this.RotateLeftButton.Text = "Q"; 116 | this.RotateLeftButton.UseVisualStyleBackColor = true; 117 | this.RotateLeftButton.Click += new System.EventHandler(this.OnRotateLeft); 118 | // 119 | // RotateRightButton 120 | // 121 | this.RotateRightButton.Font = new System.Drawing.Font("Wingdings 3", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2))); 122 | this.RotateRightButton.Location = new System.Drawing.Point(386, 18); 123 | this.RotateRightButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 124 | this.RotateRightButton.Name = "RotateRightButton"; 125 | this.RotateRightButton.Size = new System.Drawing.Size(52, 44); 126 | this.RotateRightButton.TabIndex = 4; 127 | this.RotateRightButton.Text = "P"; 128 | this.RotateRightButton.UseVisualStyleBackColor = true; 129 | this.RotateRightButton.Click += new System.EventHandler(this.OnRotateRight); 130 | // 131 | // ViewContentsButton 132 | // 133 | this.ViewContentsButton.Location = new System.Drawing.Point(136, 18); 134 | this.ViewContentsButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 135 | this.ViewContentsButton.Name = "ViewContentsButton"; 136 | this.ViewContentsButton.Size = new System.Drawing.Size(110, 44); 137 | this.ViewContentsButton.TabIndex = 1; 138 | this.ViewContentsButton.Text = "View Contents"; 139 | this.ViewContentsButton.UseVisualStyleBackColor = true; 140 | this.ViewContentsButton.Click += new System.EventHandler(this.OnViewContents); 141 | // 142 | // CopyToClipboardButton 143 | // 144 | this.CopyToClipboardButton.Location = new System.Drawing.Point(636, 18); 145 | this.CopyToClipboardButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 146 | this.CopyToClipboardButton.Name = "CopyToClipboardButton"; 147 | this.CopyToClipboardButton.Size = new System.Drawing.Size(124, 44); 148 | this.CopyToClipboardButton.TabIndex = 7; 149 | this.CopyToClipboardButton.Text = "Copy to Clipboard"; 150 | this.CopyToClipboardButton.UseVisualStyleBackColor = true; 151 | this.CopyToClipboardButton.Click += new System.EventHandler(this.OnCopyToClipboard); 152 | // 153 | // HexDisplayButton 154 | // 155 | this.HexDisplayButton.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 156 | this.HexDisplayButton.Location = new System.Drawing.Point(322, 18); 157 | this.HexDisplayButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 158 | this.HexDisplayButton.Name = "HexDisplayButton"; 159 | this.HexDisplayButton.Size = new System.Drawing.Size(52, 44); 160 | this.HexDisplayButton.TabIndex = 3; 161 | this.HexDisplayButton.Text = "Hex"; 162 | this.HexDisplayButton.UseVisualStyleBackColor = true; 163 | this.HexDisplayButton.Click += new System.EventHandler(this.OnHexDisplay); 164 | // 165 | // TextDisplayButton 166 | // 167 | this.TextDisplayButton.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 168 | this.TextDisplayButton.Location = new System.Drawing.Point(258, 18); 169 | this.TextDisplayButton.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 170 | this.TextDisplayButton.Name = "TextDisplayButton"; 171 | this.TextDisplayButton.Size = new System.Drawing.Size(52, 44); 172 | this.TextDisplayButton.TabIndex = 2; 173 | this.TextDisplayButton.Text = "Text"; 174 | this.TextDisplayButton.UseVisualStyleBackColor = true; 175 | this.TextDisplayButton.Click += new System.EventHandler(this.OnTextDisplay); 176 | // 177 | // DisplayText 178 | // 179 | this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F); 180 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 181 | this.CancelButton = this.Close_Button; 182 | this.ClientSize = new System.Drawing.Size(1031, 552); 183 | this.Controls.Add(this.ButtonsBox); 184 | this.Controls.Add(this.DisplayBox); 185 | this.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 186 | this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); 187 | this.Name = "DisplayText"; 188 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 189 | this.Text = "Display Text"; 190 | this.Load += new System.EventHandler(this.OnLoad); 191 | this.ResizeBegin += new System.EventHandler(this.OnResizeBegin); 192 | this.ResizeEnd += new System.EventHandler(this.OnResizeEnd); 193 | this.Paint += new System.Windows.Forms.PaintEventHandler(this.OnPaint); 194 | this.Resize += new System.EventHandler(this.OnResize); 195 | this.ButtonsBox.ResumeLayout(false); 196 | this.ResumeLayout(false); 197 | this.PerformLayout(); 198 | 199 | } 200 | 201 | #endregion 202 | 203 | private System.Windows.Forms.TextBox DisplayBox; 204 | private System.Windows.Forms.Button Close_Button; 205 | private System.Windows.Forms.Button SaveToFileButton; 206 | private System.Windows.Forms.GroupBox ButtonsBox; 207 | private System.Windows.Forms.Button CopyToClipboardButton; 208 | private System.Windows.Forms.Button ViewContentsButton; 209 | private System.Windows.Forms.Button RotateLeftButton; 210 | private System.Windows.Forms.Button RotateRightButton; 211 | private System.Windows.Forms.Button ViewStreamButton; 212 | private System.Windows.Forms.Button HexDisplayButton; 213 | private System.Windows.Forms.Button TextDisplayButton; 214 | } 215 | } -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/DisplayText.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Display Text 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | using PdfFileAnalyzer; 29 | using System; 30 | 31 | namespace TestPdfFileAnalyzer 32 | { 33 | internal enum DisplayMode 34 | { 35 | PdfSummary, 36 | ObjectSummary, 37 | Stream, 38 | Contents, 39 | Image 40 | } 41 | 42 | internal partial class DisplayText : Form 43 | { 44 | private readonly DisplayMode Mode; 45 | private readonly PdfReader Reader; 46 | private readonly PdfIndirectObject ReaderObject; 47 | private byte[] StreamByteArray; 48 | private readonly byte[] ByteArray; 49 | private string DisplayString; 50 | private readonly string SafeFileName; 51 | private Rectangle DisplayRect = new(); 52 | private Bitmap Image; 53 | private Rectangle ImageRect = new(); 54 | private bool ImageMode; 55 | private bool FormResizeActive; 56 | 57 | // Constructor 58 | internal DisplayText 59 | ( 60 | DisplayMode Mode, 61 | PdfReader Reader, 62 | PdfIndirectObject ReaderObject, 63 | byte[] ByteArray 64 | ) 65 | { 66 | this.Mode = Mode; 67 | this.Reader = Reader; 68 | this.ReaderObject = ReaderObject; 69 | this.ByteArray = ByteArray; 70 | SafeFileName = Reader.SafeFileName; 71 | InitializeComponent(); 72 | return; 73 | } 74 | 75 | // initialization 76 | private void OnLoad(object sender, EventArgs e) 77 | { 78 | ViewStreamButton.Enabled = false; 79 | ViewContentsButton.Enabled = false; 80 | RotateRightButton.Enabled = false; 81 | RotateLeftButton.Enabled = false; 82 | TextDisplayButton.Enabled = false; 83 | HexDisplayButton.Enabled = false; 84 | switch(Mode) 85 | { 86 | case DisplayMode.PdfSummary: 87 | Text = string.Format("PDF File {0} Summary", SafeFileName); 88 | DisplayString = Reports.PdfFileSummary(Reader); 89 | DisplayBox.Text = DisplayString; 90 | HexDisplayButton.Enabled = ByteArray != null; 91 | break; 92 | 93 | case DisplayMode.ObjectSummary: 94 | Text = string.Format("Object No {0} Summary", ReaderObject.ObjectNumber); 95 | DisplayString = Reports.ObjectSummary(ReaderObject); 96 | DisplayBox.Text = DisplayString; 97 | HexDisplayButton.Enabled = ByteArray != null; 98 | if(ReaderObject.ObjectType == ObjectType.Stream || ReaderObject.PdfObjectType == "/Page") 99 | { 100 | ViewStreamButton.Enabled = true; 101 | ViewContentsButton.Enabled = true; 102 | } 103 | break; 104 | 105 | case DisplayMode.Stream: 106 | Text = string.Format("Object No. {0} Stream", ReaderObject.ObjectNumber); 107 | DisplayString = Reports.ByteArrayToString(ByteArray); 108 | DisplayBox.Text = DisplayString; 109 | HexDisplayButton.Enabled = true; 110 | break; 111 | 112 | case DisplayMode.Contents: 113 | Text = string.Format("Object No. {0} Contents", ReaderObject.ObjectNumber); 114 | DisplayString = Reports.ByteArrayToString(ByteArray); 115 | DisplayBox.Text = DisplayString; 116 | HexDisplayButton.Enabled = true; 117 | break; 118 | 119 | case DisplayMode.Image: 120 | Image = new Bitmap(new MemoryStream(ByteArray)); 121 | DisplayBox.Visible = false; 122 | ImageMode = true; 123 | OnResize(this, null); 124 | RotateRightButton.Enabled = true; 125 | RotateLeftButton.Enabled = true; 126 | HexDisplayButton.Enabled = true; 127 | TextDisplayButton.Text = "Image"; 128 | break; 129 | } 130 | 131 | // load to display box 132 | DisplayBox.Select(0, 0); 133 | DisplayBox.BackColor = Color.White; 134 | DisplayBox.ForeColor = Color.Black; 135 | DisplayBox.ReadOnly = true; 136 | OnResize(this, null); 137 | if(Mode == DisplayMode.Stream) 138 | { 139 | Rectangle Rect = Bounds; 140 | Rect.X += 16; 141 | Rect.Y += 32; 142 | Bounds = Rect; 143 | } 144 | return; 145 | } 146 | 147 | // view stream 148 | private void OnViewStream(object sender, EventArgs e) 149 | { 150 | // load the stream 151 | if(!LoadStream()) 152 | return; 153 | 154 | // display text dialog 155 | DisplayText Dialog; 156 | if(ReaderObject.PdfObjectType == "/JpegImage") 157 | { 158 | // stream is a jpeg image 159 | Dialog = new DisplayText(DisplayMode.Image, Reader, ReaderObject, StreamByteArray); 160 | ViewContentsButton.Enabled = false; 161 | } 162 | else 163 | { 164 | Dialog = new DisplayText(DisplayMode.Stream, Reader, ReaderObject, StreamByteArray); 165 | } 166 | Dialog.ShowDialog(); 167 | return; 168 | } 169 | 170 | // view contents 171 | private void OnViewContents(object sender, EventArgs e) 172 | { 173 | // load the stream 174 | if(!LoadStream()) 175 | return; 176 | 177 | PdfOp[] OpArray; 178 | try 179 | { 180 | // parse contents 181 | OpArray = Reader.ParseContents(StreamByteArray); 182 | } 183 | catch 184 | { 185 | ViewContentsButton.Enabled = false; 186 | return; 187 | } 188 | 189 | byte[] TempByteArray = Reports.ContentsToText(OpArray); 190 | DisplayText Dialog = new(DisplayMode.Contents, Reader, ReaderObject, TempByteArray); 191 | Dialog.ShowDialog(); 192 | return; 193 | } 194 | 195 | // Display text or image after hex display 196 | private void OnTextDisplay(object sender, EventArgs e) 197 | { 198 | if(Mode == DisplayMode.Image) 199 | { 200 | DisplayBox.Visible = false; 201 | ImageMode = true; 202 | OnResize(this, null); 203 | RotateRightButton.Enabled = true; 204 | RotateLeftButton.Enabled = true; 205 | } 206 | else 207 | { 208 | DisplayString = Reports.ByteArrayToString(ByteArray); 209 | DisplayBox.Text = DisplayString; 210 | } 211 | TextDisplayButton.Enabled = false; 212 | HexDisplayButton.Enabled = true; 213 | return; 214 | } 215 | 216 | // display stream in hex 217 | private void OnHexDisplay(object sender, EventArgs e) 218 | { 219 | if(Mode == DisplayMode.Image) 220 | { 221 | Invalidate(); 222 | DisplayBox.Visible = true; 223 | ImageMode = false; 224 | RotateRightButton.Enabled = false; 225 | RotateLeftButton.Enabled = false; 226 | } 227 | DisplayString = Reports.ByteArrayToHex(ByteArray); 228 | DisplayBox.Text = DisplayString; 229 | TextDisplayButton.Enabled = true; 230 | HexDisplayButton.Enabled = false; 231 | return; 232 | } 233 | 234 | // load stream 235 | private bool LoadStream() 236 | { 237 | if(StreamByteArray != null) 238 | return true; 239 | 240 | // page object has no stream 241 | if(ReaderObject.PdfObjectType == "/Page") 242 | { 243 | StreamByteArray = ReaderObject.PageContents(); 244 | } 245 | else 246 | { 247 | // read stream 248 | StreamByteArray = ReaderObject.ReadStream(); 249 | 250 | // decompress stream 251 | byte[] TempByteArray = ReaderObject.DecompressStream(StreamByteArray); 252 | 253 | // this file is using unsupported filter 254 | if(TempByteArray == null) 255 | MessageBox.Show("Stream is compressed by unsuported filter"); 256 | 257 | // replace compressed stream with uncompressed stream 258 | else 259 | StreamByteArray = TempByteArray; 260 | } 261 | 262 | if(StreamByteArray == null || StreamByteArray.Length == 0) 263 | { 264 | MessageBox.Show("Stream is empty"); 265 | ViewStreamButton.Enabled = false; 266 | ViewContentsButton.Enabled = false; 267 | StreamByteArray = null; 268 | return false; 269 | } 270 | return true; 271 | } 272 | 273 | // test if stream is mainly binary 274 | private bool IsBinary() 275 | { 276 | int NonPrint = 0; 277 | int Len = ByteArray.Length; 278 | for(int Index = 0; Index < Len; Index++) 279 | { 280 | byte OneByte = ByteArray[Index]; 281 | if(OneByte < 32 && OneByte != (byte) '\r' && OneByte != (byte) '\n' || OneByte > 126 && OneByte < 160) 282 | NonPrint++; 283 | } 284 | return NonPrint * 10 > Len; 285 | } 286 | 287 | 288 | //////////////////////////////////////////////////////////////////// 289 | // 290 | //////////////////////////////////////////////////////////////////// 291 | 292 | private void OnPaint(object sender, PaintEventArgs e) 293 | { 294 | // ignore if not in image mode 295 | if(!ImageMode) 296 | return; 297 | 298 | // clear display area 299 | e.Graphics.FillRectangle(new SolidBrush(Color.Beige), DisplayRect); 300 | 301 | // image frame 302 | e.Graphics.DrawRectangle(new Pen(Color.Black), ImageRect.X - 1, ImageRect.Y - 1, ImageRect.Width + 1, ImageRect.Height + 1); 303 | 304 | // draw image 305 | if(!FormResizeActive) 306 | e.Graphics.DrawImage(Image, ImageRect); 307 | 308 | // exit 309 | return; 310 | } 311 | 312 | // save to file 313 | private void OnSaveToFile(object sender, EventArgs e) 314 | { 315 | // get file name 316 | SaveFileDialog SFD = new(); 317 | SFD.InitialDirectory = "."; 318 | SFD.RestoreDirectory = true; 319 | 320 | switch(Mode) 321 | { 322 | case DisplayMode.PdfSummary: 323 | SFD.Filter = "Text File (*.txt)|*.TXT"; 324 | SFD.FileName = string.Concat(SafeFileName.AsSpan(0, SafeFileName.Length - 4), "Analysis.txt"); 325 | if(SFD.ShowDialog() != DialogResult.OK) 326 | return; 327 | using(StreamWriter SaveFile = new(SFD.FileName)) 328 | SaveFile.Write(DisplayString); 329 | break; 330 | 331 | case DisplayMode.ObjectSummary: 332 | SFD.Filter = "Text File (*.txt)|*.TXT"; 333 | SFD.FileName = string.Format("ObjectNo{0}Summary.txt", ReaderObject.ObjectNumber); 334 | if(SFD.ShowDialog() != DialogResult.OK) 335 | return; 336 | using(StreamWriter SaveFile = new(SFD.FileName)) 337 | SaveFile.Write(DisplayString); 338 | break; 339 | 340 | case DisplayMode.Stream: 341 | if(IsBinary()) 342 | { 343 | SFD.Filter = "Binary File (*.bin)|*.BIN"; 344 | SFD.FileName = string.Format("ObjectNo{0}Stream.bin", ReaderObject.ObjectNumber); 345 | } 346 | else 347 | { 348 | SFD.Filter = "Text File (*.txt)|*.TXT"; 349 | SFD.FileName = string.Format("ObjectNo{0}Stream.txt", ReaderObject.ObjectNumber); 350 | } 351 | if(SFD.ShowDialog() != DialogResult.OK) return; 352 | using(FileStream OutStream = new(SFD.FileName, FileMode.Create, FileAccess.Write, FileShare.None)) 353 | { 354 | using BinaryWriter OutFile = new(OutStream); 355 | OutFile.Write(ByteArray); 356 | } 357 | break; 358 | 359 | case DisplayMode.Contents: 360 | break; 361 | 362 | case DisplayMode.Image: 363 | SFD.Filter = "Image File (*.jpg)|*.JPG"; 364 | SFD.FileName = string.Format("ObjectNo{0}Image.jpg", ReaderObject.ObjectNumber); 365 | break; 366 | } 367 | return; 368 | } 369 | 370 | // copy text to clipboard 371 | private void OnCopyToClipboard(object sender, EventArgs e) 372 | { 373 | Clipboard.SetText(DisplayBox.Text); 374 | return; 375 | } 376 | 377 | ///////////////////////////////////////////////////////////////// 378 | // On rotate right 379 | ///////////////////////////////////////////////////////////////// 380 | private void OnRotateRight 381 | ( 382 | object sender, 383 | EventArgs e 384 | ) 385 | { 386 | Image.RotateFlip(RotateFlipType.Rotate90FlipNone); 387 | OnResize(null, null); 388 | Invalidate(DisplayRect); 389 | return; 390 | } 391 | 392 | ///////////////////////////////////////////////////////////////// 393 | // On rotate left 394 | ///////////////////////////////////////////////////////////////// 395 | private void OnRotateLeft 396 | ( 397 | object sender, 398 | EventArgs e 399 | ) 400 | { 401 | Image.RotateFlip(RotateFlipType.Rotate270FlipNone); 402 | OnResize(null, null); 403 | Invalidate(DisplayRect); 404 | return; 405 | } 406 | 407 | ///////////////////////////////////////////////////////////////// 408 | // On Resize Begin 409 | ///////////////////////////////////////////////////////////////// 410 | private void OnResizeBegin 411 | ( 412 | object sender, 413 | EventArgs e 414 | ) 415 | { 416 | FormResizeActive = true; 417 | return; 418 | } 419 | 420 | ///////////////////////////////////////////////////////////////// 421 | // On Resize End 422 | ///////////////////////////////////////////////////////////////// 423 | private void OnResizeEnd 424 | ( 425 | object sender, 426 | EventArgs e 427 | ) 428 | { 429 | FormResizeActive = false; 430 | Invalidate(); 431 | return; 432 | } 433 | 434 | ///////////////////////////////////////////////////////////////// 435 | // On Resize 436 | ///////////////////////////////////////////////////////////////// 437 | private void OnResize(object sender, EventArgs e) 438 | { 439 | // protect against minimize button 440 | if(ClientSize.Width == 0) 441 | return; 442 | 443 | // buttons 444 | ButtonsBox.Left = (ClientSize.Width - ButtonsBox.Width) / 2; 445 | ButtonsBox.Top = ClientSize.Height - ButtonsBox.Height - 4; 446 | 447 | // file display area 448 | DisplayRect.X = 2; 449 | DisplayRect.Y = 2; 450 | DisplayRect.Width = ClientSize.Width - 4; 451 | DisplayRect.Height = ButtonsBox.Top - 4; 452 | 453 | // display box area is the same as display area 454 | DisplayBox.Bounds = DisplayRect; 455 | 456 | // make sure picture is defined 457 | if(ImageMode) 458 | { 459 | // image maximum size 460 | // change either width or height to keep picture aspect ratio 461 | int CalcHeight = Image.Height * (DisplayRect.Width - 4) / Image.Width; 462 | if(CalcHeight <= DisplayRect.Height - 4) 463 | { 464 | ImageRect.Width = DisplayRect.Width - 4; 465 | ImageRect.Height = CalcHeight; 466 | } 467 | else 468 | { 469 | ImageRect.Height = DisplayRect.Height - 4; 470 | ImageRect.Width = Image.Width * ImageRect.Height / Image.Height; 471 | } 472 | } 473 | return; 474 | } 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/TestPdfFileAnalyzer/AnalysisScreen.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Analysis screen 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | using PdfFileAnalyzer; 29 | 30 | namespace TestPdfFileAnalyzer 31 | { 32 | /// 33 | /// Analysis screen windows form class 34 | /// 35 | public partial class AnalysisScreen : Form 36 | { 37 | //////////////////////////////////////////////////////////////////// 38 | // Data grid view columns 39 | //////////////////////////////////////////////////////////////////// 40 | private enum HeaderColumn 41 | { 42 | ObjectNo, 43 | Object, 44 | Type, 45 | Subtype, 46 | ParentObjectNo, 47 | ParentObjectIndex, 48 | FilePos, 49 | StreamPos, 50 | StreamLen, 51 | Columns, 52 | } 53 | 54 | //////////////////////////////////////////////////////////////////// 55 | // members 56 | //////////////////////////////////////////////////////////////////// 57 | 58 | private readonly PdfReader Reader; 59 | private DataGridView DataGrid; 60 | 61 | /// 62 | /// Analysis screen constructor 63 | /// 64 | /// 65 | public AnalysisScreen(PdfReader Reader) 66 | { 67 | this.Reader = Reader; 68 | InitializeComponent(); 69 | } 70 | 71 | //////////////////////////////////////////////////////////////////// 72 | // form initialization 73 | //////////////////////////////////////////////////////////////////// 74 | 75 | private void OnLoad 76 | ( 77 | object sender, 78 | EventArgs e 79 | ) 80 | { 81 | // build page array 82 | if (Reader.Active && Reader.PageCount > 0) 83 | { 84 | Reader.BuildPageArray(); 85 | Reader.BuildContentsArray(); 86 | } 87 | 88 | // add empty data grid 89 | AddDataGrid(); 90 | 91 | // load data grid 92 | LoadDataGrid(); 93 | 94 | // resize screen 95 | OnResize(this, null); 96 | 97 | // exit 98 | return; 99 | } 100 | 101 | //////////////////////////////////////////////////////////////////// 102 | // Add Data Grid View control 103 | //////////////////////////////////////////////////////////////////// 104 | 105 | private void AddDataGrid() 106 | { 107 | // add data grid 108 | DataGrid = new DataGridView 109 | { 110 | Name = "DataGrid", 111 | AllowUserToAddRows = false, 112 | AllowUserToDeleteRows = false, 113 | AllowUserToOrderColumns = true, 114 | AllowUserToResizeRows = false, 115 | RowHeadersVisible = false, 116 | MultiSelect = true, 117 | SelectionMode = DataGridViewSelectionMode.FullRowSelect, 118 | BackgroundColor = SystemColors.GradientInactiveCaption, 119 | BorderStyle = BorderStyle.None, 120 | ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText, 121 | ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize, 122 | EditMode = DataGridViewEditMode.EditProgrammatically, 123 | AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill, 124 | TabStop = true 125 | }; 126 | DataGrid.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft; 127 | DataGrid.CellFormatting += new DataGridViewCellFormattingEventHandler(OnCellFormatting); 128 | DataGrid.CellMouseDoubleClick += new DataGridViewCellMouseEventHandler(OnMouseDoubleClick); 129 | 130 | // add columns 131 | DataGrid.Columns.Add("ObjectNo", "Object\nNo."); 132 | DataGrid.Columns.Add("Object", "Object"); 133 | DataGrid.Columns.Add("Type", "Type"); 134 | DataGrid.Columns.Add("Subtype", "Subtype"); 135 | DataGrid.Columns.Add("ParentNo", "ObjStm\nObjNo"); 136 | DataGrid.Columns.Add("ParentIndex", "ObjStm\nIndex"); 137 | DataGrid.Columns.Add("ObjectPos", "Object Pos"); 138 | DataGrid.Columns.Add("StreamPos", "Stream Pos"); 139 | DataGrid.Columns.Add("StreamLen", "Stream Len"); 140 | 141 | DataGridViewCellStyle ObjNoCellStyle = new(); 142 | ObjNoCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; 143 | ObjNoCellStyle.WrapMode = DataGridViewTriState.False; 144 | DataGrid.Columns[(int)HeaderColumn.ObjectNo].DefaultCellStyle = ObjNoCellStyle; 145 | 146 | DataGridViewCellStyle ParentCellStyle = new(); 147 | ParentCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; 148 | ParentCellStyle.WrapMode = DataGridViewTriState.False; 149 | DataGrid.Columns[(int)HeaderColumn.ParentObjectNo].DefaultCellStyle = ParentCellStyle; 150 | DataGrid.Columns[(int)HeaderColumn.ParentObjectIndex].DefaultCellStyle = ParentCellStyle; 151 | 152 | // add the data grid to the list of controls of the parent form 153 | Controls.Add(DataGrid); 154 | 155 | // force resize 156 | OnResize(this, null); 157 | return; 158 | } 159 | 160 | //////////////////////////////////////////////////////////////////// 161 | // On cell formatting 162 | //////////////////////////////////////////////////////////////////// 163 | 164 | private void OnCellFormatting 165 | ( 166 | object sender, 167 | DataGridViewCellFormattingEventArgs e 168 | ) 169 | { 170 | // format position and length as integer annd hex 171 | if (e.Value != null && 172 | (e.ColumnIndex == (int)HeaderColumn.FilePos || 173 | e.ColumnIndex == (int)HeaderColumn.StreamPos || 174 | e.ColumnIndex == (int)HeaderColumn.StreamLen)) 175 | { 176 | e.Value = string.Format("{0:#,###} (0x{0:X})", (int)e.Value); 177 | e.FormattingApplied = true; 178 | } 179 | return; 180 | } 181 | 182 | //////////////////////////////////////////////////////////////////// 183 | // Load Data Grid 184 | //////////////////////////////////////////////////////////////////// 185 | 186 | private void LoadDataGrid() 187 | { 188 | // clear data grid view 189 | DataGrid.Rows.Clear(); 190 | 191 | // load one row at a time to data grid 192 | for (int Index = 0; Index < Reader.ObjectArray.Length; Index++) 193 | if (Reader.ObjectArray[Index] != null) LoadDataGridRow(Reader.ObjectArray[Index]); 194 | 195 | // select first row 196 | DataGrid.Rows[0].Selected = true; 197 | 198 | // adjust parent width and height 199 | AdjustParent(20, 680, 0, 472); 200 | 201 | // move all controls to their right place 202 | OnResize(null, null); 203 | 204 | // exit 205 | return; 206 | } 207 | 208 | //////////////////////////////////////////////////////////////////// 209 | // Load one row of data grid view 210 | //////////////////////////////////////////////////////////////////// 211 | 212 | private void LoadDataGridRow 213 | ( 214 | PdfIndirectObject ReaderObject 215 | ) 216 | { 217 | // add all rows 218 | int Row = DataGrid.Rows.Add(); 219 | 220 | // data grid row 221 | DataGridViewRow ViewRow = DataGrid.Rows[Row]; 222 | 223 | // save object pointer 224 | ViewRow.Tag = ReaderObject; 225 | 226 | // set value of each column 227 | ViewRow.Cells[(int)HeaderColumn.ObjectNo].Value = ReaderObject.ObjectNumber; 228 | 229 | ViewRow.Cells[(int)HeaderColumn.Object].Value = ReaderObject.ObjectDescription(); 230 | 231 | if (ReaderObject.PdfObjectType != null) ViewRow.Cells[(int)HeaderColumn.Type].Value = ReaderObject.PdfObjectType; 232 | 233 | string ObjectSubtypeStr = ReaderObject.ObjectSubtypeToString(); 234 | if (ObjectSubtypeStr != null) ViewRow.Cells[(int)HeaderColumn.Subtype].Value = ObjectSubtypeStr; 235 | 236 | if (ReaderObject.ParentObjectNo != 0) 237 | { 238 | ViewRow.Cells[(int)HeaderColumn.ParentObjectNo].Value = ReaderObject.ParentObjectNo; 239 | if (ReaderObject.PdfObjectType != "/ObjStm") ViewRow.Cells[(int)HeaderColumn.ParentObjectIndex].Value = ReaderObject.ParentObjectIndex; 240 | } 241 | 242 | ViewRow.Cells[(int)HeaderColumn.FilePos].Value = ReaderObject.FilePosition; 243 | 244 | if (ReaderObject.ObjectType == ObjectType.Stream) 245 | { 246 | ViewRow.Cells[(int)HeaderColumn.StreamPos].Value = ReaderObject.StreamFilePosition; 247 | ViewRow.Cells[(int)HeaderColumn.StreamLen].Value = ReaderObject.StreamLength; 248 | } 249 | 250 | // exit 251 | return; 252 | } 253 | 254 | //////////////////////////////////////////////////////////////////// 255 | // Adjust parent size to fit grid 256 | //////////////////////////////////////////////////////////////////// 257 | 258 | private void AdjustParent 259 | ( 260 | int ExtraWidth, 261 | int MinWidth, 262 | int ExtraHeight, 263 | int MinHeight 264 | ) 265 | { 266 | // calculate columns width plus a little extra 267 | int ReqWidth = ColumnsWidth() + ExtraWidth; 268 | 269 | // make sure it is not less than the minimum width requirement 270 | if (ReqWidth < MinWidth) ReqWidth = MinWidth; 271 | 272 | // required height 273 | int ReqHeight = DataGrid.ColumnHeadersHeight + ExtraHeight; 274 | if (DataGrid.Rows.Count == 0) ReqHeight += 2 * DataGrid.ColumnHeadersHeight; 275 | else ReqHeight += (DataGrid.Rows.Count < 4 ? 4 : DataGrid.Rows.Count) * (DataGrid.Rows[0].Height + DataGrid.Rows[0].DividerHeight); 276 | 277 | // make sure it is not less than minimum 278 | if (ReqHeight < MinHeight) ReqHeight = MinHeight; 279 | 280 | // find the form under the grid 281 | Form ParentForm = FindForm(); 282 | 283 | // add non client area to requirement 284 | ReqWidth += ParentForm.Bounds.Width - ParentForm.ClientRectangle.Width; 285 | ReqHeight += ParentForm.Bounds.Height - ParentForm.ClientRectangle.Height; 286 | 287 | // get screen area 288 | Rectangle ScreenWorkingArea = Screen.FromControl(ParentForm).WorkingArea; 289 | 290 | // make sure required width is less than screen width 291 | if (ReqWidth > ScreenWorkingArea.Width) ReqWidth = ScreenWorkingArea.Width; 292 | 293 | // make sure required height is less than screen height 294 | if (ReqHeight > ScreenWorkingArea.Height) ReqHeight = ScreenWorkingArea.Height; 295 | 296 | // set bounds of parent form 297 | ParentForm.SetBounds(ScreenWorkingArea.Left + (ScreenWorkingArea.Width - ReqWidth) / 2, 298 | ScreenWorkingArea.Top + (ScreenWorkingArea.Height - ReqHeight) / 2, ReqWidth, ReqHeight); 299 | return; 300 | } 301 | 302 | //////////////////////////////////////////////////////////////////// 303 | // Calculate Columns Width 304 | //////////////////////////////////////////////////////////////////// 305 | 306 | private int ColumnsWidth() 307 | { 308 | // get graphics object 309 | Graphics GR = CreateGraphics(); 310 | 311 | // get font 312 | Font GridFont = Font; 313 | 314 | // define extra width 315 | int ExtraWidth = (int)Math.Ceiling(GR.MeasureString("0", GridFont).Width); 316 | 317 | // add up total width 318 | int TotalWidth = 0; 319 | 320 | // loop for columns 321 | for (int ColNo = 0; ColNo < (int)HeaderColumn.Columns; ColNo++) 322 | { 323 | // short cut 324 | DataGridViewTextBoxColumn Col = (DataGridViewTextBoxColumn)DataGrid.Columns[ColNo]; 325 | 326 | // header width 327 | int ColWidth = (int)Math.Ceiling(GR.MeasureString(Col.HeaderText, GridFont).Width); 328 | 329 | // loop for all rows of one column 330 | for (int Row = 0; Row < DataGrid.Rows.Count; Row++) 331 | { 332 | // cell width 333 | int CellWidth = (int)Math.Ceiling(GR.MeasureString((string)DataGrid[ColNo, Row].FormattedValue, GridFont).Width); 334 | if (CellWidth > ColWidth) ColWidth = CellWidth; 335 | } 336 | 337 | // set column width 338 | ColWidth += ExtraWidth; 339 | Col.Width = ColWidth; 340 | Col.FillWeight = ColWidth; 341 | Col.MinimumWidth = ColWidth / 2; 342 | 343 | // add up total width 344 | TotalWidth += ColWidth; 345 | } 346 | 347 | // exit 348 | return TotalWidth + SystemInformation.VerticalScrollBarWidth + 1; 349 | } 350 | 351 | //////////////////////////////////////////////////////////////////// 352 | // user double click a line: display object 353 | //////////////////////////////////////////////////////////////////// 354 | 355 | private void OnMouseDoubleClick 356 | ( 357 | object sender, 358 | DataGridViewCellMouseEventArgs e 359 | ) 360 | { 361 | if (e.Button == MouseButtons.Left && e.RowIndex >= 0) 362 | { 363 | DisplayText Dialog = new(DisplayMode.ObjectSummary, Reader, (PdfIndirectObject)DataGrid.Rows[e.RowIndex].Tag, null); 364 | Dialog.ShowDialog(); 365 | } 366 | return; 367 | } 368 | 369 | private void OnView(object sender, EventArgs e) 370 | { 371 | DataGridViewSelectedRowCollection Rows = DataGrid.SelectedRows; 372 | if (Rows == null || Rows.Count == 0) return; 373 | DisplayText Dialog = new(DisplayMode.ObjectSummary, Reader, (PdfIndirectObject)Rows[0].Tag, null); 374 | Dialog.ShowDialog(); 375 | return; 376 | } 377 | 378 | private void OnSummary(object sender, EventArgs e) 379 | { 380 | DisplayText Dialog = new(DisplayMode.PdfSummary, Reader, null, null); 381 | Dialog.ShowDialog(); 382 | return; 383 | } 384 | 385 | //////////////////////////////////////////////////////////////////// 386 | // resize form 387 | //////////////////////////////////////////////////////////////////// 388 | 389 | private void OnResize 390 | ( 391 | object sender, 392 | EventArgs e 393 | ) 394 | { 395 | // protect against minimize button 396 | if (ClientSize.Width == 0) return; 397 | 398 | // buttons 399 | ButtonsGroupBox.Left = (ClientSize.Width - ButtonsGroupBox.Width) / 2; 400 | ButtonsGroupBox.Top = ClientSize.Height - ButtonsGroupBox.Height - 4; 401 | 402 | // position datagrid 403 | if (DataGrid != null) 404 | { 405 | DataGrid.Left = 2; 406 | DataGrid.Top = 2; 407 | DataGrid.Width = ClientSize.Width - 4; 408 | DataGrid.Height = ButtonsGroupBox.Top - 4; 409 | } 410 | 411 | // exit 412 | return; 413 | } 414 | 415 | //////////////////////////////////////////////////////////////////// 416 | // exit program 417 | //////////////////////////////////////////////////////////////////// 418 | 419 | private void OnExit 420 | ( 421 | object sender, 422 | EventArgs e 423 | ) 424 | { 425 | Close(); 426 | return; 427 | } 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfBase.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF Object base class 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// Enumeration of key words for PDF key word object 32 | /// 33 | public enum KeyWord 34 | { 35 | /// 36 | /// Undefined 37 | /// 38 | Undefined, 39 | 40 | /// 41 | /// Stream 42 | /// 43 | Stream, 44 | 45 | /// 46 | /// EndStream 47 | /// 48 | EndStream, 49 | 50 | /// 51 | /// EndObj 52 | /// 53 | EndObj, 54 | 55 | /// 56 | /// XRef 57 | /// 58 | XRef, 59 | 60 | /// 61 | /// Trailer 62 | /// 63 | Trailer, 64 | 65 | /// 66 | /// N 67 | /// 68 | N, 69 | 70 | /// 71 | /// F 72 | /// 73 | F, 74 | } 75 | 76 | /// 77 | /// PDF object base class 78 | /// 79 | public class PdfBase 80 | { 81 | // translation table for IsDelimiter and IsWhiteSpace methods 82 | // white space is: null, tab, line feed, form feed, carriage return and space 83 | // delimiter is: white space, (, ), <, >, [, ], {, }, /, and % 84 | internal static byte[] Delimiter = new byte[256] 85 | { 86 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 87 | /* 000 */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 3, 3, 0, 0, 88 | /* 016 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89 | /* 032 */ 3, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 90 | /* 048 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 91 | /* 064 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92 | /* 080 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 93 | /* 096 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94 | /* 112 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 95 | /* 128 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96 | /* 144 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97 | /* 160 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98 | /* 176 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99 | /* 192 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100 | /* 208 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101 | /* 224 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102 | /* 240 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103 | }; 104 | 105 | /// 106 | /// Character ia a delimiter 107 | /// 108 | /// Text character 109 | /// Result 110 | public static bool IsDelimiter(char Ch) {return Delimiter[(int) Ch] != 0;} 111 | 112 | /// 113 | /// byte is a delimiter 114 | /// 115 | /// byte 116 | /// Result 117 | public static bool IsDelimiter(byte Ch) {return Delimiter[(int) Ch] != 0;} 118 | 119 | /// 120 | /// Integer is a delimiter 121 | /// 122 | /// Integer 123 | /// Result 124 | public static bool IsDelimiter(int Ch) {return Delimiter[Ch] != 0;} 125 | 126 | /// 127 | /// Character is a white space 128 | /// 129 | /// Text character 130 | /// Result 131 | public static bool IsWhiteSpace(char Ch) {return (Delimiter[(int) Ch] & 2) != 0;} 132 | 133 | /// 134 | /// byte is a white space 135 | /// 136 | /// byte 137 | /// Result 138 | public static bool IsWhiteSpace(byte Ch) {return (Delimiter[(int) Ch] & 2) != 0;} 139 | 140 | /// 141 | /// Integer is a white space 142 | /// 143 | /// Integer 144 | /// Result 145 | public static bool IsWhiteSpace(int Ch) {return (Delimiter[Ch] & 2) != 0;} 146 | 147 | /// 148 | /// Derived class is a PdfArray class 149 | /// 150 | public bool IsArray 151 | { 152 | get 153 | { 154 | return GetType() == typeof(PdfArray); 155 | } 156 | } 157 | 158 | /// 159 | /// Derived class is a PdfDictionary class 160 | /// 161 | public bool IsDictionary 162 | { 163 | get 164 | { 165 | return GetType() == typeof(PdfDictionary); 166 | } 167 | } 168 | 169 | /// 170 | /// Object is end of file or not found 171 | /// 172 | public bool IsEmpty 173 | { 174 | get 175 | { 176 | return this == Empty; 177 | } 178 | } 179 | 180 | /// 181 | /// Derived class is a PdfInteger class 182 | /// 183 | public bool IsInteger 184 | { 185 | get 186 | { 187 | return GetType() == typeof(PdfInteger); 188 | } 189 | } 190 | 191 | /// 192 | /// Derived class is a PdfKeyValue class 193 | /// 194 | public bool IsKeyValue 195 | { 196 | get 197 | { 198 | return GetType() == typeof(PdfKeyValue); 199 | } 200 | } 201 | 202 | /// 203 | /// Derived class is a PdfName Class 204 | /// 205 | public bool IsName 206 | { 207 | get 208 | { 209 | return GetType() == typeof(PdfName); 210 | } 211 | } 212 | 213 | /// 214 | /// Derived class is either a PdfInteger or a PdfReal class 215 | /// 216 | public bool IsNumber 217 | { 218 | get 219 | { 220 | return GetType() == typeof(PdfInteger) || GetType() == typeof(PdfReal); 221 | } 222 | } 223 | 224 | /// 225 | /// Derived class is PdfOp class 226 | /// 227 | public bool IsOperator 228 | { 229 | get 230 | { 231 | return GetType() == typeof(PdfOp); 232 | } 233 | } 234 | 235 | /// 236 | /// Derived class is PdfString class 237 | /// 238 | public bool IsPdfString 239 | { 240 | get 241 | { 242 | return GetType() == typeof(PdfString); 243 | } 244 | } 245 | 246 | /// 247 | /// Derived class is PdfReference class 248 | /// 249 | public bool IsReference 250 | { 251 | get 252 | { 253 | return GetType() == typeof(PdfReference); 254 | } 255 | } 256 | 257 | /// 258 | /// If derived class is PdfArray, return array of objects 259 | /// 260 | public PdfBase[] ToArrayItems 261 | { 262 | get 263 | { 264 | return GetType() == typeof(PdfArray) ? ((PdfArray) this).Items.ToArray() : null; 265 | } 266 | } 267 | 268 | /// 269 | /// If derived class is PdfDictionary, return a PdfDictionary object 270 | /// 271 | public PdfDictionary ToDictionary 272 | { 273 | get 274 | { 275 | return GetType() == typeof(PdfDictionary) ? (PdfDictionary) this : null; 276 | } 277 | } 278 | 279 | /// 280 | /// If derived class is PdfKeyword, return a PdfKeyword object 281 | /// 282 | public KeyWord ToKeyWord 283 | { 284 | get 285 | { 286 | return GetType() == typeof(PdfKeyword) ? ((PdfKeyword) this).KeywordValue : KeyWord.Undefined; 287 | } 288 | } 289 | 290 | /// 291 | /// If derived class is PdfName, return the PdfName as a string 292 | /// 293 | public string ToName 294 | { 295 | get 296 | { 297 | return GetType() == typeof(PdfName) ? ((PdfName) this).NameValue : null; 298 | } 299 | } 300 | 301 | /// 302 | /// If derived class is PdfInteger or PdfReal return Double 303 | /// 304 | public double ToNumber 305 | { 306 | get 307 | { 308 | if(GetType() == typeof(PdfInteger)) 309 | { 310 | return (double) ((PdfInteger) this).IntValue; 311 | } 312 | else if(GetType() == typeof(PdfReal)) 313 | { 314 | return ((PdfReal) this).RealValue; 315 | } 316 | else 317 | { 318 | return double.NaN; 319 | } 320 | } 321 | } 322 | 323 | /// 324 | /// If derived class is PdfReference, return the object number as an integer 325 | /// 326 | public int ToObjectRefNo 327 | { 328 | get 329 | { 330 | return GetType() == typeof(PdfReference) ? ((PdfReference) this).ObjectNumber : 0; 331 | } 332 | } 333 | 334 | /// 335 | /// If derived class is a PdfInteger return true and set result to value. Otherwise return false and set result to 0 336 | /// 337 | public bool GetInteger 338 | ( 339 | out int Result 340 | ) 341 | { 342 | if(GetType() == typeof(PdfInteger)) 343 | { 344 | Result = ((PdfInteger) this).IntValue; 345 | return true; 346 | } 347 | Result = 0; 348 | return false; 349 | } 350 | 351 | /// 352 | /// If derived class is a PdfBoolean return true and set result to value. Otherwise return false and set result to false 353 | /// 354 | public bool GetBoolean 355 | ( 356 | out bool Result 357 | ) 358 | { 359 | if(GetType() == typeof(PdfBoolean)) 360 | { 361 | Result = ((PdfBoolean) this).BooleanValue; 362 | return true; 363 | } 364 | Result = false; 365 | return false; 366 | } 367 | 368 | /// 369 | /// append object to byte array 370 | /// 371 | /// Output Control 372 | public virtual void ToByteArray 373 | ( 374 | OutputCtrl Ctrl 375 | ) 376 | { 377 | // test for long line 378 | Ctrl.TestEol(); 379 | 380 | // dictionary 381 | if(GetType() == typeof(PdfDictionary)) 382 | { 383 | Ctrl.Add('<'); 384 | Ctrl.Add('<'); 385 | foreach(PdfKeyValue KeyValue in ((PdfDictionary) this).KeyValueArray) 386 | { 387 | Ctrl.AppendText(KeyValue.Key); 388 | KeyValue.Value.ToByteArray(Ctrl); 389 | } 390 | Ctrl.Add('>'); 391 | Ctrl.Add('>'); 392 | } 393 | 394 | // array 395 | else if(GetType() == typeof(PdfArray)) 396 | { 397 | Ctrl.Add('['); 398 | foreach(PdfBase ArrayItem in ((PdfArray) this).Items) ArrayItem.ToByteArray(Ctrl); 399 | Ctrl.Add(']'); 400 | } 401 | 402 | // PDF string 403 | else if(GetType() == typeof(PdfString)) 404 | { 405 | // convert PDF string to visual display format 406 | PdfStringToDisplay(Ctrl, ((PdfString) this).StrValue); 407 | } 408 | 409 | // get text from simple objects 410 | else 411 | { 412 | Ctrl.AppendText(ToString()); 413 | } 414 | return; 415 | } 416 | 417 | /// 418 | /// convert PDF string to PDF file format 419 | /// 420 | /// Output Control 421 | /// PDF string 422 | public static void PdfStringToPdfFile 423 | ( 424 | OutputCtrl Ctrl, 425 | byte[] StrByteArray 426 | ) 427 | { 428 | // create output string with open and closing parenthesis 429 | Ctrl.Add('('); 430 | 431 | // move string to output 432 | if(StrByteArray != null) foreach(byte TestByte in StrByteArray) 433 | { 434 | Ctrl.TestEscEol(); 435 | 436 | // CR and NL must be replaced by \r and \n 437 | // Otherwise PDF readers will convert CR or NL or CR-NL to NL 438 | if(TestByte == '\r') 439 | { 440 | Ctrl.Add('\\'); 441 | Ctrl.Add('r'); 442 | } 443 | else if(TestByte == '\n') 444 | { 445 | Ctrl.Add('\\'); 446 | Ctrl.Add('n'); 447 | } 448 | else 449 | { 450 | // the three characters \ ( ) must be preceded by \ 451 | if(TestByte == (byte) '\\' || TestByte == (byte) '(' || TestByte == (byte) ')') Ctrl.Add('\\'); 452 | Ctrl.Add(TestByte); 453 | } 454 | } 455 | 456 | // final closing parentesis 457 | Ctrl.Add(')'); 458 | return; 459 | } 460 | 461 | /// 462 | /// append object to byte array 463 | /// 464 | /// Output control 465 | /// PDF string 466 | public static void PdfStringToDisplay 467 | ( 468 | OutputCtrl Ctrl, 469 | byte[] StrByteArray 470 | ) 471 | { 472 | // test for printable characters 473 | int Printable = 0; 474 | foreach(byte TestByte in StrByteArray) if(TestByte >= ' ' && TestByte <= '~') Printable++; 475 | 476 | // mostly printable 477 | if(10 * Printable >= 9 * StrByteArray.Length) 478 | { 479 | // create output string with open and closing parenthesis 480 | Ctrl.Add('('); 481 | 482 | // move string to output 483 | foreach(byte TestByte in StrByteArray) 484 | { 485 | Ctrl.TestEscEol(); 486 | 487 | // CR and NL must be replaced by \r and \n 488 | // Otherwise PDF readers will convert CR or NL or CR-NL to NL 489 | if(TestByte == '\r') 490 | { 491 | Ctrl.Add('\\'); 492 | Ctrl.Add('r'); 493 | } 494 | else if(TestByte == '\n') 495 | { 496 | Ctrl.Add('\\'); 497 | Ctrl.Add('n'); 498 | } 499 | else if(TestByte < ' ' || TestByte > '~') 500 | { 501 | Ctrl.Add('\\'); 502 | Ctrl.Add('x'); 503 | string Hex = string.Format("{0:x2}", TestByte); 504 | Ctrl.Add(Hex[0]); 505 | Ctrl.Add(Hex[1]); 506 | } 507 | else 508 | { 509 | // the three characters \ ( ) must be preceded by \ 510 | if(TestByte == (byte) '\\' || TestByte == (byte) '(' || TestByte == (byte) ')') Ctrl.Add('\\'); 511 | Ctrl.Add(TestByte); 512 | } 513 | } 514 | 515 | // final closing parentesis 516 | Ctrl.Add(')'); 517 | return; 518 | } 519 | 520 | // mostly unprintable 521 | Ctrl.Add('<'); 522 | 523 | // move string to output 524 | foreach(byte TestByte in StrByteArray) 525 | { 526 | Ctrl.TestEol(); 527 | string Hex = string.Format("{0:x2}", TestByte); 528 | Ctrl.Add(Hex[0]); 529 | Ctrl.Add(Hex[1]); 530 | } 531 | 532 | // final closing parentesis 533 | Ctrl.Add('>'); 534 | return; 535 | } 536 | 537 | /// 538 | /// Derived classes must override this method 539 | /// 540 | /// No return 541 | public override string ToString() 542 | { 543 | throw new ApplicationException("ToString error"); 544 | } 545 | 546 | /// 547 | /// Derived classes must override this method 548 | /// 549 | /// No return 550 | public virtual string TypeToString() 551 | { 552 | throw new ApplicationException("TypeToString error"); 553 | } 554 | 555 | // recursive decrypt string method 556 | internal void DecryptStrings 557 | ( 558 | DecryptCtrl Ctrl 559 | ) 560 | { 561 | if(GetType() == typeof(PdfDictionary)) 562 | { 563 | if((PdfDictionary) this != Ctrl.EncryptionDict) 564 | { 565 | foreach(PdfKeyValue KeyValue in ((PdfDictionary) this).KeyValueArray) KeyValue.Value.DecryptStrings(Ctrl); 566 | } 567 | } 568 | else if(GetType() == typeof(PdfArray)) 569 | { 570 | foreach(PdfBase ArrayItem in ((PdfArray) this).Items) ArrayItem.DecryptStrings(Ctrl); 571 | } 572 | else if(GetType() == typeof(PdfString)) 573 | { 574 | // NOTE: some PDF file have unused objects that are not encrypted 575 | try 576 | { 577 | ((PdfString) this).StrValue = Ctrl.Encryption.DecryptByteArray(Ctrl.ObjectNumber, ((PdfString) this).StrValue); 578 | } 579 | catch {} 580 | } 581 | return; 582 | } 583 | 584 | 585 | /// 586 | /// Empty PDF base class for end of file and not found 587 | /// 588 | internal static PdfBase Empty = new(); 589 | } 590 | } 591 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/CryptoEngine.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // CryptoEngine Software to decrypt PDF document 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | using System.Security.Cryptography; 29 | 30 | namespace PdfFileAnalyzer 31 | { 32 | /// 33 | /// Encryption type enumeration 34 | /// 35 | public enum EncryptionType 36 | { 37 | /// 38 | /// AES 128 bits 39 | /// 40 | Aes128, 41 | 42 | /// 43 | /// Standard 128 bits 44 | /// 45 | Standard128, 46 | 47 | /// 48 | /// No support for encryption method 49 | /// 50 | Unsupported, 51 | } 52 | 53 | /// 54 | /// PDF reader permission flags enumeration 55 | /// 56 | /// 57 | /// PDF reference manual version 1.7 Table 3.20 58 | /// 59 | public enum Permission 60 | { 61 | /// 62 | /// No permission flags 63 | /// 64 | None = 0, 65 | 66 | /// 67 | /// Low quality print (bit 3) 68 | /// 69 | LowQalityPrint = 4, // bit 3 70 | 71 | /// 72 | /// Modify contents (bit 4) 73 | /// 74 | ModifyContents = 8, // bit 4 75 | 76 | /// 77 | /// Extract contents (bit 5) 78 | /// 79 | ExtractContents = 0x10, // bit 5 80 | 81 | /// 82 | /// Annotation (bit 6) 83 | /// 84 | Annotation = 0x20, // bit 6 85 | 86 | /// 87 | /// Interactive (bit 9) 88 | /// 89 | Interactive = 0x100, // bit 9 90 | 91 | /// 92 | /// Accessibility (bit 10) 93 | /// 94 | Accessibility = 0x200, // bit 10 95 | 96 | /// 97 | /// Assemble document (bit 11) 98 | /// 99 | AssembleDoc = 0x400, // bit 11 100 | 101 | /// 102 | /// Print (bit 12 plus bit 3) 103 | /// 104 | Print = 0x804, // bit 12 + bit 3 105 | 106 | /// 107 | /// All permission bits 108 | /// 109 | All = 0xf3c, // bits 3, 4, 5, 6, 9, 10, 11, 12 110 | } 111 | 112 | /// 113 | /// PDF encryption class 114 | /// 115 | public class CryptoEngine : IDisposable 116 | { 117 | internal const int PermissionBase = unchecked((int)0xfffff0c0); 118 | 119 | internal DecryptionStatus DecryptionStatus; 120 | internal byte[] DocumentID; 121 | internal EncryptionType EncryptionType; 122 | internal int Permissions; 123 | internal byte[] UserKey; 124 | internal byte[] OwnerKey; 125 | internal byte[] MasterKey; 126 | internal MD5 MD5 = MD5.Create(); 127 | internal Aes AES = Aes.Create(); 128 | 129 | internal static readonly byte[] PasswordPad = 130 | { 131 | (byte) 0x28, (byte) 0xBF, (byte) 0x4E, (byte) 0x5E, (byte) 0x4E, (byte) 0x75, (byte) 0x8A, (byte) 0x41, 132 | (byte) 0x64, (byte) 0x00, (byte) 0x4E, (byte) 0x56, (byte) 0xFF, (byte) 0xFA, (byte) 0x01, (byte) 0x08, 133 | (byte) 0x2E, (byte) 0x2E, (byte) 0x00, (byte) 0xB6, (byte) 0xD0, (byte) 0x68, (byte) 0x3E, (byte) 0x80, 134 | (byte) 0x2F, (byte) 0x0C, (byte) 0xA9, (byte) 0xFE, (byte) 0x64, (byte) 0x53, (byte) 0x69, (byte) 0x7A 135 | }; 136 | 137 | internal static readonly byte[] Salt = { (byte)0x73, (byte)0x41, (byte)0x6c, (byte)0x54 }; 138 | 139 | //////////////////////////////////////////////////////////////////// 140 | // Encryption Constructor 141 | //////////////////////////////////////////////////////////////////// 142 | 143 | internal CryptoEngine 144 | ( 145 | EncryptionType EncryptionType, 146 | byte[] DocumentID, 147 | int Permissions, 148 | byte[] UserKey = null, 149 | byte[] OwnerKey = null 150 | ) 151 | { 152 | this.DocumentID = DocumentID; 153 | this.Permissions = Permissions; 154 | this.UserKey = UserKey; 155 | this.OwnerKey = OwnerKey; 156 | this.EncryptionType = EncryptionType; 157 | return; 158 | } 159 | 160 | //////////////////////////////////////////////////////////////////// 161 | // Encrypt byte array 162 | //////////////////////////////////////////////////////////////////// 163 | 164 | internal byte[] EncryptByteArray 165 | ( 166 | int ObjectNumber, 167 | byte[] PlainText 168 | ) 169 | { 170 | // create encryption key 171 | byte[] EncryptionKey = CreateEncryptionKey(ObjectNumber); 172 | byte[] CipherText; 173 | 174 | if (EncryptionType == EncryptionType.Aes128) 175 | { 176 | // generate new initialization vector IV 177 | AES.GenerateIV(); 178 | 179 | // create cipher text buffer including initialization vector 180 | int CipherTextLen = (PlainText.Length & 0x7ffffff0) + 16; 181 | CipherText = new byte[CipherTextLen + 16]; 182 | Array.Copy(AES.IV, 0, CipherText, 0, 16); 183 | 184 | // set encryption key and key length 185 | AES.Key = EncryptionKey; 186 | 187 | // Create the streams used for encryption. 188 | MemoryStream OutputStream = new(); 189 | CryptoStream CryptoStream = new(OutputStream, AES.CreateEncryptor(), CryptoStreamMode.Write); 190 | 191 | // write plain text byte array 192 | CryptoStream.Write(PlainText, 0, PlainText.Length); 193 | 194 | // encrypt plain text to cipher text 195 | CryptoStream.FlushFinalBlock(); 196 | 197 | // get the result 198 | OutputStream.Seek(0, SeekOrigin.Begin); 199 | OutputStream.Read(CipherText, 16, CipherTextLen); 200 | 201 | // release resources 202 | CryptoStream.Clear(); 203 | OutputStream.Close(); 204 | } 205 | else 206 | { 207 | CipherText = (byte[])PlainText.Clone(); 208 | EncryptRC4(EncryptionKey, CipherText); 209 | } 210 | // return result 211 | return CipherText; 212 | } 213 | 214 | //////////////////////////////////////////////////////////////////// 215 | // decrypt byte array 216 | //////////////////////////////////////////////////////////////////// 217 | 218 | internal byte[] DecryptByteArray 219 | ( 220 | int ObjectNumber, 221 | byte[] CipherText 222 | ) 223 | { 224 | // create encryption key 225 | byte[] EncryptionKey = CreateEncryptionKey(ObjectNumber); 226 | byte[] PlainText; 227 | 228 | if (EncryptionType == EncryptionType.Aes128) 229 | { 230 | // set encryption key and key length 231 | AES.Key = EncryptionKey; 232 | 233 | // set IV 234 | byte[] IVArray = new byte[16]; 235 | Array.Copy(CipherText, 0, IVArray, 0, 16); 236 | AES.IV = IVArray; 237 | 238 | // Create a decrytor to perform the stream transform. 239 | ICryptoTransform Decryptor = AES.CreateDecryptor(AES.Key, AES.IV); 240 | 241 | // Create the streams used for decryption. 242 | MemoryStream CipherStream = new(CipherText, 16, CipherText.Length - 16); 243 | CryptoStream CryptoStream = new(CipherStream, Decryptor, CryptoStreamMode.Read); 244 | 245 | // plain text length should be less than cipher text 246 | PlainText = new byte[CipherText.Length + 32]; 247 | int Index = 0; 248 | int ReadCount; 249 | while ((ReadCount = CryptoStream.Read(PlainText, Index, PlainText.Length - Index)) > 0) 250 | { 251 | Index += ReadCount; 252 | if (Index > PlainText.Length) throw new ApplicationException("Decrypt error"); 253 | } 254 | 255 | // resize array 256 | Array.Resize(ref PlainText, Index); 257 | 258 | // release resources 259 | CryptoStream.Close(); 260 | CipherStream.Close(); 261 | } 262 | else 263 | { 264 | PlainText = (byte[])CipherText.Clone(); 265 | EncryptRC4(EncryptionKey, PlainText); 266 | } 267 | 268 | // return result 269 | return PlainText; 270 | } 271 | 272 | //////////////////////////////////////////////////////////////////// 273 | // Create encryption key 274 | //////////////////////////////////////////////////////////////////// 275 | internal byte[] CreateEncryptionKey 276 | ( 277 | int ObjectNumber 278 | ) 279 | { 280 | byte[] HashInput = new byte[MasterKey.Length + 5 + (EncryptionType == EncryptionType.Aes128 ? Salt.Length : 0)]; 281 | int Ptr = 0; 282 | Array.Copy(MasterKey, 0, HashInput, Ptr, MasterKey.Length); 283 | Ptr += MasterKey.Length; 284 | HashInput[Ptr++] = (byte)ObjectNumber; 285 | HashInput[Ptr++] = (byte)(ObjectNumber >> 8); 286 | HashInput[Ptr++] = (byte)(ObjectNumber >> 16); 287 | HashInput[Ptr++] = 0; // Generation is always zero for this library 288 | HashInput[Ptr++] = 0; // Generation is always zero for this library 289 | if (EncryptionType == EncryptionType.Aes128) Array.Copy(Salt, 0, HashInput, Ptr, Salt.Length); 290 | byte[] EncryptionKey = MD5.ComputeHash(HashInput); 291 | if (EncryptionKey.Length > 16) Array.Resize(ref EncryptionKey, 16); 292 | return EncryptionKey; 293 | } 294 | 295 | //////////////////////////////////////////////////////////////////// 296 | // Process Permissions 297 | //////////////////////////////////////////////////////////////////// 298 | internal static int ProcessPermissions 299 | ( 300 | Permission UserPermissions 301 | ) 302 | { 303 | return ((int)UserPermissions & (int)Permission.All) | PermissionBase; 304 | } 305 | 306 | //////////////////////////////////////////////////////////////////// 307 | // Process Password 308 | //////////////////////////////////////////////////////////////////// 309 | internal static byte[] ProcessPassword 310 | ( 311 | string StringPassword 312 | ) 313 | { 314 | // no user password 315 | if (string.IsNullOrEmpty(StringPassword)) return (byte[])PasswordPad.Clone(); 316 | 317 | // convert password to byte array 318 | byte[] BinaryPassword = new byte[32]; 319 | int IndexEnd = Math.Min(StringPassword.Length, 32); 320 | for (int Index = 0; Index < IndexEnd; Index++) 321 | { 322 | char PWChar = StringPassword[Index]; 323 | if (PWChar > 255) throw new ApplicationException("Owner or user Password has invalid character (allowed 0-255)"); 324 | BinaryPassword[Index] = (byte)PWChar; 325 | } 326 | 327 | // if user password is shorter than 32 bytes, add padding 328 | if (IndexEnd < 32) Array.Copy(PasswordPad, 0, BinaryPassword, IndexEnd, 32 - IndexEnd); 329 | 330 | // return password 331 | return BinaryPassword; 332 | } 333 | 334 | //////////////////////////////////////////////////////////////////// 335 | // Create owner key 336 | //////////////////////////////////////////////////////////////////// 337 | internal byte[] CreateOwnerKey 338 | ( 339 | byte[] UserBinaryPassword, 340 | byte[] OwnerBinaryPassword 341 | ) 342 | { 343 | // create hash array for owner password 344 | byte[] OwnerHash = MD5.ComputeHash(OwnerBinaryPassword); 345 | 346 | // loop 50 times creating hash of a hash 347 | for (int Index = 0; Index < 50; Index++) OwnerHash = MD5.ComputeHash(OwnerHash); 348 | 349 | byte[] ownerKey = (byte[])UserBinaryPassword.Clone(); 350 | byte[] TempKey = new byte[16]; 351 | for (int Index = 0; Index < 20; Index++) 352 | { 353 | for (int Tindex = 0; Tindex < 16; Tindex++) TempKey[Tindex] = (byte)(OwnerHash[Tindex] ^ Index); 354 | EncryptRC4(TempKey, ownerKey); 355 | } 356 | 357 | // return encryption key 358 | return ownerKey; 359 | } 360 | 361 | //////////////////////////////////////////////////////////////////// 362 | // Create master key 363 | //////////////////////////////////////////////////////////////////// 364 | internal void CreateMasterKey 365 | ( 366 | byte[] UserBinaryPassword, 367 | byte[] OwnerKey, 368 | bool EncryptMetadata = true 369 | ) 370 | { 371 | // input byte array for MD5 hash function 372 | byte[] HashInput = new byte[UserBinaryPassword.Length + OwnerKey.Length + DocumentID.Length + (EncryptMetadata ? 4 : 8)]; 373 | int Ptr = 0; 374 | Array.Copy(UserBinaryPassword, 0, HashInput, Ptr, UserBinaryPassword.Length); 375 | Ptr += UserBinaryPassword.Length; 376 | Array.Copy(OwnerKey, 0, HashInput, Ptr, OwnerKey.Length); 377 | Ptr += OwnerKey.Length; 378 | HashInput[Ptr++] = (byte)Permissions; 379 | HashInput[Ptr++] = (byte)(Permissions >> 8); 380 | HashInput[Ptr++] = (byte)(Permissions >> 16); 381 | HashInput[Ptr++] = (byte)(Permissions >> 24); 382 | Array.Copy(DocumentID, 0, HashInput, Ptr, DocumentID.Length); 383 | if (!EncryptMetadata) 384 | { 385 | HashInput[Ptr++] = (byte)255; 386 | HashInput[Ptr++] = (byte)255; 387 | HashInput[Ptr++] = (byte)255; 388 | HashInput[Ptr++] = (byte)255; 389 | } 390 | MasterKey = MD5.ComputeHash(HashInput); 391 | 392 | // loop 50 times creating hash of a hash 393 | for (int Index = 0; Index < 50; Index++) MasterKey = MD5.ComputeHash(MasterKey); 394 | 395 | // exit 396 | return; 397 | } 398 | 399 | //////////////////////////////////////////////////////////////////// 400 | // Create user key 401 | //////////////////////////////////////////////////////////////////// 402 | internal byte[] CreateUserKey() 403 | { 404 | // input byte array for MD5 hash function 405 | byte[] HashInput = new byte[PasswordPad.Length + DocumentID.Length]; 406 | Array.Copy(PasswordPad, 0, HashInput, 0, PasswordPad.Length); 407 | Array.Copy(DocumentID, 0, HashInput, PasswordPad.Length, DocumentID.Length); 408 | byte[] UserKey = MD5.ComputeHash(HashInput); 409 | byte[] TempKey = new byte[16]; 410 | 411 | for (int Index = 0; Index < 20; Index++) 412 | { 413 | for (int Tindex = 0; Tindex < 16; Tindex++) TempKey[Tindex] = (byte)(MasterKey[Tindex] ^ Index); 414 | EncryptRC4(TempKey, UserKey); 415 | } 416 | Array.Resize(ref UserKey, 32); 417 | return UserKey; 418 | } 419 | 420 | //////////////////////////////////////////////////////////////////// 421 | // Test Password 422 | //////////////////////////////////////////////////////////////////// 423 | internal bool TestPassword 424 | ( 425 | string Password 426 | ) 427 | { 428 | // convert password from null or string to byte array 429 | byte[] BinaryPassword = ProcessPassword(Password); 430 | 431 | // assume that the password is owner password 432 | // calculate owner binary password 433 | byte[] OwnerBinaryPassword = CreateOwnerKey(OwnerKey, BinaryPassword); 434 | 435 | // create master key 436 | CreateMasterKey(OwnerBinaryPassword, OwnerKey); 437 | 438 | // create user key 439 | byte[] UserKey1 = CreateUserKey(); 440 | 441 | // compare calculated key to dictionary user key 442 | if (ArrayCompare(UserKey1, UserKey)) 443 | { 444 | DecryptionStatus = DecryptionStatus.OwnerPassword; 445 | return true; 446 | } 447 | 448 | // assume the password is user password 449 | // create master key 450 | CreateMasterKey(BinaryPassword, OwnerKey); 451 | 452 | // create user key 453 | UserKey1 = CreateUserKey(); 454 | 455 | // compare calculated key to dictionary user key 456 | if (ArrayCompare(UserKey1, UserKey)) 457 | { 458 | DecryptionStatus = DecryptionStatus.UserPassword; 459 | return true; 460 | } 461 | 462 | // password not valid 463 | DecryptionStatus = DecryptionStatus.InvalidPassword; 464 | return false; 465 | } 466 | 467 | //////////////////////////////////////////////////////////////////// 468 | // Compare two byte arrays 469 | //////////////////////////////////////////////////////////////////// 470 | internal static bool ArrayCompare 471 | ( 472 | byte[] Array1, 473 | byte[] Array2 474 | ) 475 | { 476 | for (int Index = 0; Index < Array1.Length; Index++) if (Array1[Index] != Array2[Index]) return false; 477 | return true; 478 | } 479 | 480 | //////////////////////////////////////////////////////////////////// 481 | // RC4 Encryption 482 | //////////////////////////////////////////////////////////////////// 483 | internal static void EncryptRC4 484 | ( 485 | byte[] Key, 486 | byte[] Data 487 | ) 488 | { 489 | byte[] State = new byte[256]; 490 | for (int Index = 0; Index < 256; Index++) State[Index] = (byte)Index; 491 | 492 | int Index1 = 0; 493 | int Index2 = 0; 494 | for (int Index = 0; Index < 256; Index++) 495 | { 496 | Index2 = (Key[Index1] + State[Index] + Index2) & 255; 497 | byte tmp = State[Index]; 498 | State[Index] = State[Index2]; 499 | State[Index2] = tmp; 500 | Index1 = (Index1 + 1) % Key.Length; 501 | } 502 | 503 | int x = 0; 504 | int y = 0; 505 | for (int Index = 0; Index < Data.Length; Index++) 506 | { 507 | x = (x + 1) & 255; 508 | y = (State[x] + y) & 255; 509 | byte tmp = State[x]; 510 | State[x] = State[y]; 511 | State[y] = tmp; 512 | Data[Index] = (byte)(Data[Index] ^ State[(State[x] + State[y]) & 255]); 513 | } 514 | return; 515 | } 516 | 517 | /// 518 | /// Dispose unmanaged resources 519 | /// 520 | public void Dispose() 521 | { 522 | if (AES != null) 523 | { 524 | AES.Clear(); 525 | // NOTE: AES.Dispose() is valid for .NET 4.0 and later. 526 | // In other words visual studio 2010 and later. 527 | // If you compile this source with older versions of VS 528 | // remove this call at your risk. 529 | AES.Dispose(); 530 | AES = null; 531 | } 532 | 533 | if (MD5 != null) 534 | { 535 | MD5.Clear(); 536 | MD5 = null; 537 | } 538 | GC.SuppressFinalize(this); 539 | return; 540 | } 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/OpCtrl.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // Graphics page contents operators 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | namespace PdfFileAnalyzer 29 | { 30 | /// 31 | /// Graphics page contents operators 32 | /// 33 | public enum Operator 34 | { 35 | /// 36 | /// (B) Fill and stroke path using nonzero winding number rule 37 | /// 38 | FillStrokeNonZeroRule, // B Fill and stroke path using nonzero winding number rule 4.10 230 39 | 40 | /// 41 | /// (B*) Fill and stroke path using even-odd rule 42 | /// 43 | FillStrokeEvenOddRule, // B* Fill and stroke path using even-odd rule 4.10 230 44 | 45 | /// 46 | /// (b) Close, fill, and stroke path using nonzero winding number rule 47 | /// 48 | CloseFillStrokeNonZeroRule, // b Close, fill, and stroke path using nonzero winding number rule 4.10 230 49 | 50 | /// 51 | /// (b*) Close, fill, and stroke path using even-odd rule 52 | /// 53 | CloseFillStrokeEvenOddRule, // b* Close, fill, and stroke path using even-odd rule 4.10 230 54 | 55 | /// 56 | /// (BDC) Begin marked-content sequence with property list 57 | /// 58 | BeginMarkedContentPropList, // BDC Begin marked-content sequence with property list 10.7 851 59 | 60 | /// 61 | /// (BI) Begin inline image object 62 | /// 63 | BeginInlineImage, // BI Begin inline image object 4.42 352 64 | 65 | /// 66 | /// (BMC) Begin marked-content sequence 67 | /// 68 | BeginMarkedContent, // BMC Begin marked-content sequence 10.7 851 69 | 70 | /// 71 | /// (BT) Begin text object 72 | /// 73 | BeginText, // BT Begin text object 5.4 405 74 | 75 | /// 76 | /// (BX) Begin compatibility section 77 | /// 78 | BeginCompatibility, // BX Begin compatibility section 3.29 152 79 | 80 | /// 81 | /// (c) Append Bezier segment to path (three control points) 82 | /// 83 | Bezier, // c Append Bezier segment to path (three control points) 4.9 226 84 | 85 | /// 86 | /// (cm) Concatenate matrix to current transformation matrix 87 | /// 88 | TransMatrix, // cm Concatenate matrix to current transformation matrix 4.7 219 89 | 90 | /// 91 | /// (CS) Set color space for stroking operations 92 | /// 93 | ColorSpaceForStroking, // CS Set color space for stroking operations 4.24 287 94 | 95 | /// 96 | /// (cs) Set color space for nonstroking operations 97 | /// 98 | ColorSpaceForNonStroking, // cs Set color space for nonstroking operations 4.24 287 99 | 100 | /// 101 | /// (d) Set line dash pattern 102 | /// 103 | LineDashPattern, // d Set line dash pattern 4.7 219 104 | 105 | /// 106 | /// (d0) Set glyph width in Type 3 font 107 | /// 108 | GlyphWidthType3, // d0 Set glyph width in Type 3 font 5.10 423 109 | 110 | /// 111 | /// (d1) Set glyph width and bounding box in Type 3 font 112 | /// 113 | GlyphWidthBBoxType3, // d1 Set glyph width and bounding box in Type 3 font 5.10 423 114 | 115 | /// 116 | /// (Do) Invoke named XObject 117 | /// 118 | XObject, // Do Invoke named XObject 4.37 332 119 | 120 | /// 121 | /// (DP) Define marked-content point with property list 122 | /// 123 | DefineMarkedContentPropList, // DP Define marked-content point with property list 10.7 851 124 | 125 | /// 126 | /// (EI) End inline image object 127 | /// 128 | EndInlineImage, // EI End inline image object 4.42 352 129 | 130 | /// 131 | /// (EMC) End marked-content sequence 132 | /// 133 | EndMarkedContent, // EMC End marked-content sequence 10.7 851 134 | 135 | /// 136 | /// (ET) End text object 137 | /// 138 | EndTextObject, // ET End text object 5.4 405 139 | 140 | /// 141 | /// (EX) End compatibility section 142 | /// 143 | EndCompatibility, // EX End compatibility section 3.29 152 144 | 145 | /// 146 | /// (f) Fill path using nonzero winding number rule 147 | /// 148 | FillNonZeroRule, // f Fill path using nonzero winding number rule 4.10 230 149 | 150 | /// 151 | /// (f*) Fill path using even-odd rule 152 | /// 153 | FillEvenOddRule, // f* Fill path using even-odd rule 4.10 230 154 | 155 | /// 156 | /// (G) Set gray level for stroking operations 157 | /// 158 | GrayLevelForStroking, // G Set gray level for stroking operations 4.24 288 159 | 160 | /// 161 | /// (g) Set gray level for nonstroking operations 162 | /// 163 | GrayLevelForNonStroking, // g Set gray level for nonstroking operations 4.24 288 164 | 165 | /// 166 | /// (gs) Set parameters from graphics state parameter dictionary 167 | /// 168 | ParamFromGraphicsStateDict, // gs Set parameters from graphics state parameter dictionary 4.7 219 169 | 170 | /// 171 | /// (h) Close subpath 172 | /// 173 | ClosePath, // h Close subpath 4.9 227 174 | 175 | /// 176 | /// (i) Set flatness tolerance 177 | /// 178 | FlatnessTolerance, // i Set flatness tolerance 4.7 219 179 | 180 | /// 181 | /// (ID) Begin inline image data 182 | /// 183 | BeginInlineImageData, // ID Begin inline image data 4.42 352 184 | 185 | /// 186 | /// (j) Set line join style 187 | /// 188 | LineJoinStyle, // j Set line join style 4.7 219 189 | 190 | /// 191 | /// (J) Set line cap style 192 | /// 193 | LineCapStyle, // J Set line cap style 4.7 219 194 | 195 | /// 196 | /// (K) Set CMYK color for stroking operations 197 | /// 198 | CmykColorForStroking, // K Set CMYK color for stroking operations 4.24 288 199 | 200 | /// 201 | /// (k) Set CMYK color for nonstroking operations 202 | /// 203 | CmykColorForNonStroking, // k Set CMYK color for nonstroking operations 4.24 288 204 | 205 | /// 206 | /// (l) Append straight line segment to path 207 | /// 208 | LineTo, // l Append straight line segment to path 4.9 226 209 | 210 | /// 211 | /// (m) Begin new subpath 212 | /// 213 | MoveTo, // m Begin new subpath 4.9 226 214 | 215 | /// 216 | /// (M) Set miter limit 217 | /// 218 | MiterLimit, // M Set miter limit 4.7 219 219 | 220 | /// 221 | /// (MP) Define marked-content point 222 | /// 223 | DefineMarkedContent, // MP Define marked-content point 10.7 851 224 | 225 | /// 226 | /// (n) End path without filling or stroking 227 | /// 228 | NoPaint, // n End path without filling or stroking 4.10 230 229 | 230 | /// 231 | /// (q) Save graphics state 232 | /// 233 | SaveGraphicsState, // q Save graphics state 4.7 219 234 | 235 | /// 236 | /// (Q) Restore graphics state 237 | /// 238 | RestoreGraphicsState, // Q Restore graphics state 4.7 219 239 | 240 | /// 241 | /// (re) Append rectangle to path 242 | /// 243 | Rectangle, // re Append rectangle to path 4.9 227 244 | 245 | /// 246 | /// (RG) Set RGB color for stroking operations 247 | /// 248 | RgbColorForStroking, // RG Set RGB color for stroking operations 4.24 288 249 | 250 | /// 251 | /// (rg) Set RGB color for nonstroking operations 252 | /// 253 | RgbColorForNonStroking, // rg Set RGB color for nonstroking operations 4.24 288 254 | 255 | /// 256 | /// (ri) Set color rendering intent 257 | /// 258 | ColorRenderingIntent, // ri Set color rendering intent 4.7 219 259 | 260 | /// 261 | /// (S) Stroke path 262 | /// 263 | Stroke, // S Stroke path 4.10 230 264 | 265 | /// 266 | /// (s) Close and stroke path 267 | /// 268 | CloseStroke, // s Close and stroke path 4.10 230 269 | 270 | /// 271 | /// (SC) Set color for stroking operations 272 | /// 273 | ColorForStroking, // SC Set color for stroking operations 4.24 287 274 | 275 | /// 276 | /// (sc) Set color for nonstroking operations 277 | /// 278 | ColorForNonStroking, // sc Set color for nonstroking operations 4.24 288 279 | 280 | /// 281 | /// (SCN) Set color for stroking operations (ICCBased special color) 282 | /// 283 | ColorForStrokingSpecial, // SCN Set color for stroking operations (ICCBased special color) 4.24 288 284 | 285 | /// 286 | /// (scn) Set color for nonstroking operations (ICCBased special color) 287 | /// 288 | ColorForNonStrokingSpecial, // scn Set color for nonstroking operations (ICCBased & special color) 4.24 288 289 | 290 | /// 291 | /// (sh) Paint area defined by shading pattern 292 | /// 293 | PaintAreaShadingPattern, // sh Paint area defined by shading pattern 4.27 303 294 | 295 | /// 296 | /// (T*) Move to start of next text line 297 | /// 298 | MoveToStartOfNextLine, // T* Move to start of next text line 5.5 406 299 | 300 | /// 301 | /// (Tc) Set character spacing 302 | /// 303 | SetCharSpacing, // Tc Set character spacing 5.2 398 304 | 305 | /// 306 | /// (Td) Move text position 307 | /// 308 | MoveTextPos, // Td Move text position 5.5 406 309 | 310 | /// 311 | /// (TD) Move text position and set leading 312 | /// 313 | MoveTextPosSetLeading, // TD Move text position and set leading 5.5 406 314 | 315 | /// 316 | /// (Tf) Set text font and size 317 | /// 318 | SelectFontAndSize, // Tf Set text font and size 5.2 398 319 | 320 | /// 321 | /// (Tj) Show text 322 | /// 323 | ShowText, // Tj Show text 5.6 407 324 | 325 | /// 326 | /// (TJ) Show text, allowing individual glyph positioning 327 | /// 328 | ShowTextWithGlyphPos, // TJ Show text, allowing individual glyph positioning 5.6 408 329 | 330 | /// 331 | /// (TL) Set text leading 332 | /// 333 | TextLeading, // TL Set text leading 5.2 398 334 | 335 | /// 336 | /// (Tm) Set text matrix and text line matrix 337 | /// 338 | TextMatrix, // Tm Set text matrix and text line matrix 5.5 406 339 | 340 | /// 341 | /// (Tr) Set text rendering mode 342 | /// 343 | TextRenderingMode, // Tr Set text rendering mode 5.2 398 344 | 345 | /// 346 | /// (Ts) Set text rise 347 | /// 348 | TextRize, // Ts Set text rise 5.2 398 349 | 350 | /// 351 | /// (Tw) Set word spacing 352 | /// 353 | TextWorkSpacing, // Tw Set word spacing 5.2 398 354 | 355 | /// 356 | /// (Tz) Set horizontal text scaling 357 | /// 358 | TextHorizontalScaling, // Tz Set horizontal text scaling 5.2 398 359 | 360 | /// 361 | /// (v) Append Bezier segment to path (initial point replicated) 362 | /// 363 | BezierNoP1, // v Append Bezier segment to path (initial point replicated) 4.9 226 364 | 365 | /// 366 | /// (W) Set clipping path using nonzero winding number rule 367 | /// 368 | ClippingPathNonZeroRule, // W Set clipping path using nonzero winding number rule 4.11 235 369 | 370 | /// 371 | /// (W*) Set clipping path using even-odd rule 372 | /// 373 | ClippingPathEvenOddRule, // W* Set clipping path using even-odd rule 4.11 235 374 | 375 | /// 376 | /// (w) Set line width 377 | /// 378 | LineWidth, // w Set line width 4.7 219 379 | 380 | /// 381 | /// (y) Append bEZIER segment to path (final point replicated) 382 | /// 383 | BezierNoP2, // y Append bEZIER segment to path (final point replicated) 4.9 226 384 | 385 | /// 386 | /// (') Move to next line and show text 387 | /// 388 | MoveToNextLineAndShow, // ' Move to next line and show text 5.6 407 389 | 390 | /// 391 | /// (") Set word and character spacing, move to next line show text 392 | /// 393 | WordCharSpacingShowText, // " Set word and character spacing, move to next line show text 5.6 407 394 | 395 | /// 396 | /// Enumeration count 397 | /// 398 | Count, 399 | } 400 | 401 | /// 402 | /// Operator control table 403 | /// 404 | public class OpCtrl : IComparable 405 | { 406 | /// 407 | /// Graphics operator text string 408 | /// 409 | public string OpText; 410 | 411 | /// 412 | /// Graphics operator enumeration 413 | /// 414 | public Operator OpCode; 415 | 416 | /// 417 | /// Translate operator enumeration to text string 418 | /// 419 | /// Enumeration code 420 | /// Operator text string code 421 | public static string OperatorCode 422 | ( 423 | Operator Op 424 | ) 425 | { 426 | return OpStr[(int) Op]; 427 | } 428 | 429 | /// 430 | /// OpCtrl Constructor 431 | /// 432 | /// Operator text 433 | /// Operator code 434 | public OpCtrl 435 | ( 436 | string OpText, 437 | Operator OpCode 438 | ) 439 | { 440 | this.OpText = OpText; 441 | this.OpCode = OpCode; 442 | return; 443 | } 444 | 445 | /// 446 | /// OpCtrl constructor for binary search 447 | /// 448 | /// Operator text 449 | public OpCtrl 450 | ( 451 | string OpText 452 | ) 453 | { 454 | this.OpText = OpText; 455 | return; 456 | } 457 | 458 | /// 459 | /// Compare graphics operators 460 | /// 461 | /// Other operator 462 | /// Result 463 | public int CompareTo 464 | ( 465 | OpCtrl Other 466 | ) 467 | { 468 | return string.Compare(this.OpText, Other.OpText); 469 | } 470 | 471 | /// 472 | /// page contents operators array 473 | /// this array is sorted by the operator code string order 474 | /// during program initialization in Parse class static constructor 475 | /// 476 | internal static OpCtrl[] OpCtrlArray = new OpCtrl[] 477 | { 478 | new OpCtrl("b", Operator.CloseFillStrokeNonZeroRule), 479 | new OpCtrl("B", Operator.FillStrokeNonZeroRule), 480 | new OpCtrl("b*", Operator.CloseFillStrokeEvenOddRule), 481 | new OpCtrl("B*", Operator.FillStrokeEvenOddRule), 482 | new OpCtrl("BDC", Operator.BeginMarkedContentPropList), 483 | new OpCtrl("BI", Operator.BeginInlineImage), 484 | new OpCtrl("BMC", Operator.BeginMarkedContent), 485 | new OpCtrl("BT", Operator.BeginText), 486 | new OpCtrl("BX", Operator.BeginCompatibility), 487 | new OpCtrl("c", Operator.Bezier), 488 | new OpCtrl("cm", Operator.TransMatrix), 489 | new OpCtrl("CS", Operator.ColorSpaceForStroking), 490 | new OpCtrl("cs", Operator.ColorSpaceForNonStroking), 491 | new OpCtrl("d", Operator.LineDashPattern), 492 | new OpCtrl("d0", Operator.GlyphWidthType3), 493 | new OpCtrl("d1", Operator.GlyphWidthBBoxType3), 494 | new OpCtrl("Do", Operator.XObject), 495 | new OpCtrl("DP", Operator.DefineMarkedContentPropList), 496 | new OpCtrl("EI", Operator.EndInlineImage), 497 | new OpCtrl("EMC", Operator.EndMarkedContent), 498 | new OpCtrl("ET", Operator.EndTextObject), 499 | new OpCtrl("EX", Operator.EndCompatibility), 500 | new OpCtrl("f", Operator.FillNonZeroRule), 501 | new OpCtrl("F", Operator.FillNonZeroRule), 502 | new OpCtrl("f*", Operator.FillEvenOddRule), 503 | new OpCtrl("G", Operator.GrayLevelForStroking), 504 | new OpCtrl("g", Operator.GrayLevelForNonStroking), 505 | new OpCtrl("gs", Operator.ParamFromGraphicsStateDict), 506 | new OpCtrl("h", Operator.ClosePath), 507 | new OpCtrl("i", Operator.FlatnessTolerance), 508 | new OpCtrl("ID", Operator.BeginInlineImageData), 509 | new OpCtrl("j", Operator.LineJoinStyle), 510 | new OpCtrl("J", Operator.LineCapStyle), 511 | new OpCtrl("K", Operator.CmykColorForStroking), 512 | new OpCtrl("k", Operator.CmykColorForNonStroking), 513 | new OpCtrl("l", Operator.LineTo), 514 | new OpCtrl("m", Operator.MoveTo), 515 | new OpCtrl("M", Operator.MiterLimit), 516 | new OpCtrl("MP", Operator.DefineMarkedContent), 517 | new OpCtrl("n", Operator.NoPaint), 518 | new OpCtrl("q", Operator.SaveGraphicsState), 519 | new OpCtrl("Q", Operator.RestoreGraphicsState), 520 | new OpCtrl("re", Operator.Rectangle), 521 | new OpCtrl("RG", Operator.RgbColorForStroking), 522 | new OpCtrl("rg", Operator.RgbColorForNonStroking), 523 | new OpCtrl("ri", Operator.ColorRenderingIntent), 524 | new OpCtrl("s", Operator.CloseStroke), 525 | new OpCtrl("S", Operator.Stroke), 526 | new OpCtrl("SC", Operator.ColorForStroking), 527 | new OpCtrl("sc", Operator.ColorForNonStroking), 528 | new OpCtrl("SCN", Operator.ColorForStrokingSpecial), 529 | new OpCtrl("scn", Operator.ColorForNonStrokingSpecial), 530 | new OpCtrl("sh", Operator.PaintAreaShadingPattern), 531 | new OpCtrl("T*", Operator.MoveToStartOfNextLine), 532 | new OpCtrl("Tc", Operator.SetCharSpacing), 533 | new OpCtrl("Td", Operator.MoveTextPos), 534 | new OpCtrl("TD", Operator.MoveTextPosSetLeading), 535 | new OpCtrl("Tf", Operator.SelectFontAndSize), 536 | new OpCtrl("Tj", Operator.ShowText), 537 | new OpCtrl("TJ", Operator.ShowTextWithGlyphPos), 538 | new OpCtrl("TL", Operator.TextLeading), 539 | new OpCtrl("Tm", Operator.TextMatrix), 540 | new OpCtrl("Tr", Operator.TextRenderingMode), 541 | new OpCtrl("Ts", Operator.TextRize), 542 | new OpCtrl("Tw", Operator.TextWorkSpacing), 543 | new OpCtrl("Tz", Operator.TextHorizontalScaling), 544 | new OpCtrl("v", Operator.BezierNoP1), 545 | new OpCtrl("w", Operator.LineWidth), 546 | new OpCtrl("W", Operator.ClippingPathNonZeroRule), 547 | new OpCtrl("W*", Operator.ClippingPathEvenOddRule), 548 | new OpCtrl("y", Operator.BezierNoP2), 549 | new OpCtrl("'", Operator.MoveToNextLineAndShow), 550 | new OpCtrl("\"", Operator.WordCharSpacingShowText), 551 | }; 552 | 553 | //////////////////////////////////////////////////////////////////// 554 | // static constructor 555 | //////////////////////////////////////////////////////////////////// 556 | static OpCtrl() 557 | { 558 | // sort the operator control array 559 | Array.Sort(OpCtrlArray); 560 | 561 | // create the operator string array 562 | foreach(OpCtrl Op in OpCtrlArray) if(Op.OpText != "F") OpStr[(int) Op.OpCode] = Op.OpText; 563 | return; 564 | } 565 | 566 | // array of strings of page contents operator 567 | // this array is created during program initialization in Parse class static constructor. 568 | // the string are sorted by the Operator enumeration value. 569 | // the array allows a direct translation from Operator code to string value 570 | private static readonly string[] OpStr = new string[(int) Operator.Count]; 571 | } 572 | } 573 | -------------------------------------------------------------------------------- /PdfFileAnalyzer/PdfFileAnalyzer/PdfParser.cs: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////// 2 | // 3 | // PdfFileAnalyzer 4 | // PDF file analysis program 5 | // 6 | // PDF base parser 7 | // 8 | // Author: Uzi Granot 9 | // Original Version: 1.0 10 | // Original Date: September 1, 2012 11 | // Copyright (C) 2012-2022 Uzi Granot. All Rights Reserved. 12 | // 13 | // PdfFileAnalyzer application is a free software. 14 | // It is distributed under the Code Project Open License (CPOL). 15 | // The document PdfFileAnalyzerReadmeAndLicense.pdf contained within 16 | // the distribution specify the license agreement and other 17 | // conditions and notes. You must read this document and agree 18 | // with the conditions specified in order to use this software. 19 | // 20 | // Version History: 21 | // 22 | // Version 1.0 2012/09/01 23 | // Original revision 24 | // 25 | // PdfReader.cs has the full version history 26 | ///////////////////////////////////////////////////////////////////// 27 | 28 | using System.Globalization; 29 | using System.Text; 30 | 31 | namespace PdfFileAnalyzer 32 | { 33 | /// 34 | /// PDF base parser 35 | /// 36 | public class PdfParser 37 | { 38 | internal PdfReader Reader; 39 | internal bool ContentsStream; 40 | internal int NextChar; 41 | 42 | internal const int EOF = -1; 43 | 44 | /// 45 | /// Constructor 46 | /// 47 | /// PdfReader 48 | /// The parsed stream is graphics contents 49 | internal PdfParser 50 | ( 51 | PdfReader Reader, 52 | bool ContentsStream 53 | ) 54 | { 55 | this.Reader = Reader; 56 | this.ContentsStream = ContentsStream; 57 | return; 58 | } 59 | 60 | /// 61 | /// Read first character 62 | /// 63 | public void ReadFirstChar() 64 | { 65 | NextChar = ReadChar(); 66 | return; 67 | } 68 | 69 | /// 70 | /// Parse object reference number n 0 R obj 71 | /// 72 | /// Object number 73 | public int ParseObjectRefNo() 74 | { 75 | // loop in case of one or more comments 76 | SkipComments(); 77 | 78 | // must be a digit 79 | if(NextChar < '0' || NextChar > '9') return 0; 80 | 81 | // next content element 82 | StringBuilder NextItem = new(((char) NextChar).ToString()); 83 | 84 | // add more characters until next delimiter 85 | while((NextChar = ReadChar()) != EOF && !PdfBase.IsDelimiter(NextChar)) NextItem.Append((char) NextChar); 86 | 87 | // integer 88 | if(!int.TryParse(NextItem.ToString(), out int ObjNo) || ObjNo <= 0) return 0; 89 | 90 | // next character must be space 91 | if(!PdfBase.IsWhiteSpace(NextChar)) return 0; 92 | 93 | // skip additional white space 94 | while((NextChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(NextChar)); 95 | 96 | // next character must be zero 97 | if(NextChar != '0') return 0; 98 | 99 | // next character must be white space 100 | NextChar = ReadChar(); 101 | if(!PdfBase.IsWhiteSpace(NextChar)) return 0; 102 | 103 | // skip additional white space 104 | while((NextChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(NextChar)); 105 | 106 | // next 3 characters must be obj 107 | if(NextChar != 'o' || ReadChar() != 'b' || ReadChar() != 'j') return 0; 108 | 109 | // next character must be a delimiter 110 | NextChar = ReadChar(); 111 | if(!PdfBase.IsDelimiter(NextChar)) return 0; 112 | 113 | // return object number 114 | return ObjNo; 115 | } 116 | 117 | /// 118 | /// Parse next item 119 | /// 120 | /// Derived class from PdfBase 121 | public PdfBase ParseNextItem() 122 | { 123 | // loop in case of one or more comments 124 | SkipComments(); 125 | 126 | // end of file 127 | if(NextChar == EOF) return PdfBase.Empty; 128 | 129 | // string 130 | if(NextChar == '(') return ParseString(); 131 | 132 | // array 133 | if(NextChar == '[') return ParseArray(); 134 | 135 | // hex string or dictionary 136 | if(NextChar == '<') 137 | { 138 | // test for dictionary 139 | if(ReadChar() == '<') return ParseDictionary(false); 140 | 141 | // move pointer back 142 | StepBack(); 143 | 144 | // hex string 145 | return ParseHexString(); 146 | } 147 | 148 | // next content element 149 | StringBuilder NextItem = new(); 150 | NextItem.Append((char) NextChar); 151 | 152 | // add more characters until next delimiter 153 | while((NextChar = ReadChar()) != EOF && !PdfBase.IsDelimiter(NextChar)) NextItem.Append((char) NextChar); 154 | 155 | // convert next item to string token 156 | string Token = NextItem.ToString(); 157 | 158 | // name 159 | if(Token[0] == '/') 160 | { 161 | // empty name 162 | if(Token.Length == 1) throw new ApplicationException("Empty name token"); 163 | 164 | // exit 165 | return new PdfName(Token); 166 | } 167 | 168 | // integer 169 | if(int.TryParse(Token, out int IntVal)) 170 | { 171 | // if parsing non contents streams, an integer can be the start of indirect reference number 172 | if(!ContentsStream && IntVal > 0 && TestReference()) return new PdfReference(IntVal); 173 | 174 | // integer 175 | return new PdfInteger(IntVal); 176 | } 177 | 178 | // real number with period as decimal separator regardless of region 179 | if(float.TryParse(Token, 180 | NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, NumFormatInfo.PeriodDecSep, out float RealVal)) 181 | { 182 | // if real number is an integer return PdfInt object 183 | int TestInt = (int) Math.Truncate(RealVal); 184 | if(RealVal == (double) TestInt) return new PdfInteger(TestInt); 185 | return new PdfReal(RealVal); 186 | } 187 | 188 | // false 189 | if(Token == "false") return new PdfBoolean(false); 190 | 191 | // true 192 | if(Token == "true") return new PdfBoolean(true); 193 | 194 | // null 195 | if(Token == "null") return new PdfNull(); 196 | 197 | // parse all but contents stream 198 | if(!ContentsStream) 199 | { 200 | // stream special case 201 | if(Token == "stream") 202 | { 203 | // stream must be foloowed by NL or CR and NL 204 | // if(NextChar == '\n' || NextChar == '\r' && ReadChar() == '\n') return new PdfKeyword(KeyWord.Stream); 205 | if(NextChar == '\n') return new PdfKeyword(KeyWord.Stream); 206 | if(NextChar == '\r') 207 | { 208 | // the PDF spec is very clear that stream must be foloowed by NL or CR and NL 209 | // CR by itself is not acceptable 210 | if(ReadChar() != '\n') 211 | { 212 | // HP Scanners Scanned PDF does not conform to PDF standards 213 | // https://www.google.com/search?client=firefox-b-d&q=hp+officejet+PDF+scan+files+not+standard 214 | // step back to allow re-parsing of the last character 215 | StepBack(); 216 | Reader.InvalidPdfFile = true; 217 | } 218 | return new PdfKeyword(KeyWord.Stream); 219 | } 220 | 221 | // error 222 | throw new ApplicationException("Stream word must be followed by EOL"); 223 | } 224 | 225 | // endstream 226 | if(Token == "endstream") return new PdfKeyword(KeyWord.EndStream); 227 | 228 | // endobj 229 | if(Token == "endobj") return new PdfKeyword(KeyWord.EndObj); 230 | 231 | // xref 232 | if(Token == "xref") return new PdfKeyword(KeyWord.XRef); 233 | 234 | // xref n 235 | if(Token == "n") return new PdfKeyword(KeyWord.N); 236 | 237 | // xref f 238 | if(Token == "f") return new PdfKeyword(KeyWord.F); 239 | 240 | // trailer 241 | if(Token == "trailer") return new PdfKeyword(KeyWord.Trailer); 242 | } 243 | 244 | // parse contents stream 245 | else 246 | { 247 | // search for contents operator 248 | int OpIndex = Array.BinarySearch(OpCtrl.OpCtrlArray, new OpCtrl(Token)); 249 | 250 | // not found 251 | if(OpIndex < 0) throw new ApplicationException("Parsing failed: Unknown contents operator"); 252 | 253 | // operator enumeration 254 | Operator OpCode = OpCtrl.OpCtrlArray[OpIndex].OpCode; 255 | 256 | // inline image 257 | if(OpCode == Operator.BeginInlineImage) return ParseInlineImage(); 258 | 259 | // PDF operator object 260 | if(OpCode != Operator.BeginInlineImageData && OpCode != Operator.EndInlineImage) return new PdfOp(OpCode); 261 | } 262 | 263 | // error 264 | throw new ApplicationException("Parsing failed: Unknown token: " + Token); 265 | } 266 | 267 | /// 268 | /// Skip comments 269 | /// 270 | public void SkipComments() 271 | { 272 | // loop in case of one or more comments 273 | for(;;) 274 | { 275 | // skip white space 276 | SkipWhiteSpace(); 277 | 278 | // not a comment 279 | if(NextChar != '%') return; 280 | 281 | // read characters until next end of line 282 | for(;;) 283 | { 284 | NextChar = ReadChar(); 285 | if(NextChar == EOF) return; 286 | if(NextChar == '\n' || NextChar == '\r') break; 287 | } 288 | } 289 | } 290 | 291 | /// 292 | /// Skip white space 293 | /// 294 | public void SkipWhiteSpace() 295 | { 296 | // skip white space 297 | if(PdfBase.IsWhiteSpace(NextChar)) while((NextChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(NextChar)); 298 | return; 299 | } 300 | 301 | //////////////////////////////////////////////////////////////////// 302 | // Test for reference 303 | // We have positive integer already. Test for zero and R 304 | //////////////////////////////////////////////////////////////////// 305 | 306 | internal bool TestReference() 307 | { 308 | // save current file position 309 | int Pos = GetPos(); 310 | 311 | // save next character 312 | int TempChar = NextChar; 313 | 314 | for(;;) 315 | { 316 | // next character must be space 317 | if(!PdfBase.IsWhiteSpace(TempChar)) break; 318 | 319 | // skip additional white space 320 | while((TempChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(TempChar)); 321 | 322 | // generation is not supported 323 | // next character must be zero 324 | if(TempChar != '0') break; 325 | 326 | // next character must be white space 327 | TempChar = ReadChar(); 328 | if(!PdfBase.IsWhiteSpace(TempChar)) break; 329 | 330 | // skip additional white space 331 | while((TempChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(TempChar)); 332 | 333 | // next character must be R 334 | if(TempChar != 'R') break; 335 | 336 | // next character must be a delimiter 337 | TempChar = ReadChar(); 338 | if(!PdfBase.IsDelimiter(TempChar)) break; 339 | 340 | // found 341 | NextChar = TempChar; 342 | return true; 343 | } 344 | 345 | // restore position 346 | SetPos(Pos); 347 | return false; 348 | } 349 | 350 | //////////////////////////////////////////////////////////////////// 351 | // Read PDF string and return PdfString 352 | //////////////////////////////////////////////////////////////////// 353 | 354 | internal PdfBase ParseString() 355 | { 356 | // create value string 357 | List StrArr = new(); 358 | 359 | // parenthesis protection logic 360 | bool Esc = false; 361 | int Level = 0; 362 | 363 | // read string to the end 364 | for(;;) 365 | { 366 | // read next character 367 | NextChar = ReadChar(); 368 | if(NextChar == EOF) throw new ApplicationException("Invalid string (End of contents)"); 369 | 370 | // backslash state 371 | if(Esc) 372 | { 373 | switch(NextChar) 374 | { 375 | case 'n': NextChar = '\n'; break; 376 | case 'r': NextChar = '\r'; break; 377 | case 't': NextChar = '\t'; break; 378 | case 'b': NextChar = '\b'; break; 379 | case 'f': NextChar = '\f'; break; 380 | 381 | // end of line 382 | case '\n': 383 | Esc = false; 384 | continue; 385 | 386 | case '\r': 387 | NextChar = ReadChar(); 388 | if(NextChar != '\n') 389 | { 390 | if(NextChar == EOF) throw new ApplicationException("Invalid string (End of contents)"); 391 | StepBack(); 392 | } 393 | Esc = false; 394 | continue; 395 | 396 | // octal sequence \nnn 397 | default: 398 | if(NextChar < '0' || NextChar > '7') break; 399 | int Octal = NextChar - '0'; 400 | NextChar = ReadChar(); 401 | if(NextChar < '0' || NextChar > '7') 402 | { 403 | if(NextChar == EOF) throw new ApplicationException("Invalid string (End of contents)"); 404 | NextChar = Octal; 405 | StepBack(); 406 | break; 407 | } 408 | Octal = (Octal << 3) + (NextChar - '0'); 409 | NextChar = ReadChar(); 410 | if(NextChar < '0' || NextChar > '7') 411 | { 412 | if(NextChar == EOF) throw new ApplicationException("Invalid string (End of contents)"); 413 | NextChar = Octal; 414 | StepBack(); 415 | break; 416 | } 417 | NextChar = ((Octal << 3) + (NextChar - '0')) & 0xff; 418 | break; 419 | } 420 | 421 | // reset backslash escape and accept current character without testing 422 | Esc = false; 423 | } 424 | 425 | // not backslash state 426 | else 427 | { 428 | // set escape logic for next character 429 | if(NextChar == '\\') 430 | { 431 | Esc = true; 432 | continue; 433 | } 434 | 435 | // left parenthesis 436 | else if(NextChar == '(') 437 | { 438 | Level++; 439 | } 440 | 441 | // right parenthesis 442 | else if(NextChar == ')') 443 | { 444 | if(Level == 0) break; 445 | Level--; 446 | } 447 | } 448 | 449 | // append it in value 450 | StrArr.Add((byte) NextChar); 451 | } 452 | 453 | // read next character after closing ) 454 | NextChar = ReadChar(); 455 | 456 | // exit 457 | return new PdfString(StrArr.ToArray()); 458 | } 459 | 460 | //////////////////////////////////////////////////////////////////// 461 | // Parse hex string item and return PdfString 462 | //////////////////////////////////////////////////////////////////// 463 | 464 | internal PdfBase ParseHexString() 465 | { 466 | // create value string 467 | List StrArr = new(); 468 | 469 | // add more hexadecimal numbers until next closing > 470 | bool First = true; 471 | int OneChar; 472 | int OneByte = 0; 473 | for(;;) 474 | { 475 | // read next character 476 | NextChar = ReadChar(); 477 | if(NextChar == EOF) throw new ApplicationException("Invalid hex string (End of contents)"); 478 | 479 | // end of string 480 | if(NextChar == '>') break; 481 | 482 | // ignore white space within the string 483 | if(PdfBase.IsWhiteSpace(NextChar)) continue; 484 | 485 | // test for hex digits 486 | if(NextChar >= '0' && NextChar <= '9') OneChar = NextChar - '0'; 487 | else if(NextChar >= 'A' && NextChar <= 'F') OneChar = NextChar - ('A' - 10); 488 | else if(NextChar >= 'a' && NextChar <= 'f') OneChar = NextChar - ('a' - 10); 489 | else throw new ApplicationException("Invalid hex string"); 490 | 491 | if(First) 492 | { 493 | OneByte = OneChar; 494 | First = false; 495 | } 496 | else 497 | { 498 | StrArr.Add((byte) ((OneByte << 4) | OneChar)); 499 | First = true; 500 | } 501 | } 502 | 503 | if(!First) StrArr.Add((byte) (OneByte << 4)); 504 | 505 | // read next character after closing > 506 | NextChar = ReadChar(); 507 | 508 | // exit 509 | return new PdfString(StrArr.ToArray()); 510 | } 511 | 512 | //////////////////////////////////////////////////////////////////// 513 | // Parse Array 514 | //////////////////////////////////////////////////////////////////// 515 | 516 | internal PdfArray ParseArray() 517 | { 518 | // create empty array 519 | List ResultArray = new(); 520 | 521 | // read first character after [ 522 | NextChar = ReadChar(); 523 | 524 | // loop until closing ] or EOF 525 | for(;;) 526 | { 527 | // skip white space 528 | SkipWhiteSpace(); 529 | 530 | // end of file 531 | if(NextChar == EOF) throw new ApplicationException("Invalid array (end of contents)"); 532 | 533 | // end of array 534 | if(NextChar == ']') break; 535 | 536 | // parse next item 537 | PdfBase NextItem = ParseNextItem(); 538 | 539 | // end of file 540 | if(NextItem.IsEmpty) throw new ApplicationException("Invalid array (end of contents)"); 541 | 542 | // add to result array 543 | ResultArray.Add(NextItem); 544 | } 545 | 546 | // read next character after closing ] 547 | NextChar = ReadChar(); 548 | 549 | // exit 550 | return new PdfArray(ResultArray.ToArray()); 551 | } 552 | 553 | //////////////////////////////////////////////////////////////////// 554 | // Parse Dictionary 555 | //////////////////////////////////////////////////////////////////// 556 | 557 | internal PdfDictionary ParseDictionary 558 | ( 559 | bool InlineImage 560 | ) 561 | { 562 | // create empty dictionary 563 | PdfDictionary Dictionary = new(); 564 | 565 | // read first character after << 566 | NextChar = ReadChar(); 567 | 568 | // loop until closing >> or EOF 569 | for(;;) 570 | { 571 | // skip white space 572 | SkipWhiteSpace(); 573 | 574 | // end of file 575 | if(NextChar == EOF) throw new ApplicationException("Invalid dictionary (end of contents)"); 576 | 577 | // next character must be / for name 578 | if(NextChar != '/') 579 | { 580 | // end of dictionary 581 | if(!InlineImage) 582 | { 583 | if(NextChar == '>' && ReadChar() == '>') break; 584 | } 585 | // inline image 586 | else 587 | { 588 | if(NextChar == 'I' && ReadChar() == 'D') break; 589 | } 590 | throw new ApplicationException("Invalid dictionary (name entry must have /)"); 591 | } 592 | 593 | // read name 594 | StringBuilder Name = new(); 595 | Name.Append((char) NextChar); 596 | 597 | // add more characters until next delimiter 598 | while((NextChar = ReadChar()) != EOF && !PdfBase.IsDelimiter(NextChar)) Name.Append((char) NextChar); 599 | 600 | // read next item 601 | PdfBase Value = ParseNextItem(); 602 | 603 | // end of file 604 | if(Value.IsEmpty) throw new ApplicationException("Invalid dictionary (end of contents)"); 605 | 606 | // add to result dictionary 607 | Dictionary.AddKeyValue(Name.ToString(), Value); 608 | } 609 | 610 | // read next character after >> or ID 611 | NextChar = ReadChar(); 612 | 613 | // exit 614 | return Dictionary; 615 | } 616 | 617 | //////////////////////////////////////////////////////////////////// 618 | // Parse inline image 619 | //////////////////////////////////////////////////////////////////// 620 | 621 | internal PdfOp ParseInlineImage() 622 | { 623 | // create empty dictionary 624 | PdfDictionary ImageDict = ParseDictionary(true); 625 | 626 | // get image width 627 | if(!ImageDict.FindValue("/W").GetInteger(out int Width) || Width <= 0) throw new ApplicationException("Parse inline image: Width error"); 628 | 629 | // get image height 630 | if(!ImageDict.FindValue("/H").GetInteger(out int Height) || Height <= 0) throw new ApplicationException("Parse inline image: Height error"); 631 | 632 | // get image bits per component 633 | if(!ImageDict.FindValue("/BPC").GetInteger(out int BitPerComp) || 634 | BitPerComp != 1 && BitPerComp != 2 && BitPerComp != 4 && BitPerComp != 8) throw new ApplicationException("Parse inline image: BPC error"); 635 | 636 | int Components = 0; 637 | 638 | // get color space 639 | string ColorSpace = ImageDict.FindValue("/CS").ToName; 640 | if(ColorSpace != null) 641 | { 642 | // number of components 643 | if(ColorSpace == "/G") Components = 1; 644 | else if(ColorSpace == "/RGB") Components = 3; 645 | else if(ColorSpace == "/CMYK") Components = 4; 646 | else throw new ApplicationException("Parse inline image: ColorSpace error"); 647 | } 648 | 649 | ImageDict.FindValue("/IM").GetBoolean(out bool IM); 650 | if(IM) Components = 1; 651 | 652 | PdfBase Filter = ImageDict.FindValue("/F"); 653 | if(!Filter.IsEmpty) throw new ApplicationException("Parse inline image: No filter support"); 654 | 655 | // no ASCIIHexDecode AHx or ASCII85Decode A85 656 | if(!PdfBase.IsWhiteSpace(NextChar)) throw new ApplicationException("Parse inline image: ID must be followed by white space"); 657 | 658 | // image width in bytes 659 | int WidthBytes = 0; 660 | switch(BitPerComp) 661 | { 662 | case 1: 663 | WidthBytes = (Width + 7) / 8; 664 | break; 665 | 666 | case 2: 667 | WidthBytes = (Width + 3) / 4; 668 | break; 669 | 670 | case 4: 671 | WidthBytes = (Width + 1) / 2; 672 | break; 673 | 674 | case 8: 675 | WidthBytes = Width; 676 | break; 677 | 678 | } 679 | 680 | // image size 681 | int Size = WidthBytes * Height * Components; 682 | 683 | // image stream 684 | byte[] ImageStream = new byte[Size]; 685 | 686 | for(int Index = 0; Index < Size; Index++) 687 | { 688 | // read next character 689 | NextChar = ReadChar(); 690 | 691 | // end of file error 692 | if(NextChar == EOF) throw new ApplicationException("Invalid inline image (end of contents)"); 693 | 694 | // save it in bitmap 695 | ImageStream[Index] = (byte) NextChar; 696 | } 697 | 698 | // get termination 699 | NextChar = ReadChar(); 700 | SkipWhiteSpace(); 701 | if(NextChar != 'E' || ReadChar() != 'I') throw new ApplicationException("Parse inline image: EI is missing"); 702 | NextChar = ReadChar(); 703 | 704 | PdfOp InlineImage = new(Operator.BeginInlineImage); 705 | InlineImage.ArgumentArray = new PdfBase[] {ImageDict, new PdfString(ImageStream)}; 706 | 707 | // exit 708 | return InlineImage; 709 | } 710 | 711 | /// 712 | /// Virtual read character 713 | /// 714 | /// Character 715 | public virtual int ReadChar() 716 | { 717 | return EOF; 718 | } 719 | 720 | /// 721 | /// Virtual step one position back 722 | /// 723 | public virtual void StepBack() 724 | { 725 | return; 726 | } 727 | 728 | /// 729 | /// Virtual get current position 730 | /// 731 | /// Position 732 | public virtual int GetPos() 733 | { 734 | return 0; 735 | } 736 | 737 | /// 738 | /// Virtual set position 739 | /// 740 | /// Position 741 | public virtual void SetPos(int Pos) 742 | { 743 | return; 744 | } 745 | 746 | /// 747 | /// Virtual set relative position 748 | /// 749 | /// Step size 750 | public virtual void SkipPos(int Pos) 751 | { 752 | return; 753 | } 754 | } 755 | } 756 | --------------------------------------------------------------------------------