├── 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 |
--------------------------------------------------------------------------------