Appendix.cs 6.18 KB
Newer Older
1
2
3
4
5
6
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Xobject;
using iText.Layout;
using iText.Layout.Borders;
using iText.Layout.Element;
7
using PDFGenerator.Models.EvaluationReport;
8
9
10
11
12
13
using PDFGenerator.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using static PDFGenerator.Utilities.Globals;
14
using Styles = PDFGenerator.Utilities.Globals.Constants.EvaluationReport.Styles;
15

16
17

namespace PDFGenerator.DocumentStructures.EvaluationReport
18
{
19
    internal class Appendix : EvaluationReportBaseSection
20
21
22
23
24
25
26
27
28
29
30
31
    {
        private PDFAppendix PDFAppendix { get; }
        private bool FirstFile { get; set; }

        public Appendix(PDFAppendix appendix, Document document, Bookmarks bookmarks, TableOfContents tableOfContents)
        : base(document, bookmarks, tableOfContents)
        {
            PDFAppendix = appendix;
            FirstFile = true;
        }

        /// <summary>
Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
32
        /// Includes custom uploads to attachments available.
33
        /// </summary>
Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
34
        /// <param name="customUploads">Custom attachments to be added.</param>
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
        public void UpdateAppendix(Dictionary<string, byte[]> customUploads)
        {
            foreach (KeyValuePair<string, byte[]> item in customUploads)
            {
                if (!PDFAppendix.Attachments.ContainsKey(item.Key))
                {
                    PDFAppendix.Attachments.Add(item.Key, item.Value);
                }
            }
        }

        /// <summary>
        /// Renders the attachments for the appendix section.
        /// </summary>
        public override void Render()
        {
51
52
53
54
55
            if (PDFAppendix.Attachments.Count == 0)
            {
                return;
            }

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
            Bookmarks.AddRootOutline(PDFAppendix.Heading, RootOutline);

            TocItem tocItem = new TocItem
            {
                Title = PDFAppendix.Heading,
                Indent = 0,
                Page = GetCurrentPage()
            };
            TableOfContents.AddToTOC(tocItem);

            foreach (KeyValuePair<string, byte[]> attachment in PDFAppendix.Attachments)
            {
                AddAttachment(attachment);
            }

            SetPageOrientation(Orientation.Portrait);
        }

        /// <summary>
        /// Adds a single attachment to document.
        /// </summary>
Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
77
        /// <param name="item">The item composed of an attachment key and content.</param>
78
79
80
81
82
83
84
85
86
        private void AddAttachment(KeyValuePair<string, byte[]> item)
        {
            PdfReader attachment = new PdfReader(new MemoryStream(item.Value));
            PdfDocument source = new PdfDocument(attachment);
            PdfDocument dest = Document.GetPdfDocument();

            // avoiding the exception 'PdfReader not opened with owner password' from third party encrypted PDF uploads involves
            // manipulating both 'unethicalReading' and 'encrypted' flags from PdfReader

Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
87
88
            // In case the original author of the document defined permissions these permissions are ignored by setting 'unethicalReading' to true.
            // This is not a problem, as setting such permissions has become obsolete, since PDF became an ISO standard and
89
90
91
92
            // there is no longer a penalty for removing those permissions 
            // (refer to https://stackoverflow.com/questions/48064902/itext-7-pdfreader-is-not-opened-with-owner-password-error)
            attachment.SetUnethicalReading(true);

Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
93
            // Mislead iText into thinking that the original PDF file was not encrypted
94
95
96
97
98
99
100
101
102
103
104
105
106
107
            attachment.GetType()
                .GetField("encrypted", BindingFlags.NonPublic | BindingFlags.Instance)
                .SetValue(attachment, false);

            // detect page orientation
            Rectangle attachmentPageSize = source.GetPage(1).GetPageSizeWithRotation();
            PageSize pageSize = GetPageSize();

            // if attachment orientation is landscape, but destination pdf is not
            if ((attachmentPageSize.GetHeight() < attachmentPageSize.GetWidth()) && ((int)Math.Round(attachmentPageSize.GetHeight()) < pageSize.GetHeight()))
            {
                SetPageOrientation(Orientation.Landscape);
            }

Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
108
            // if attachment orientation is portrait but destination pdf is not
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
            else if ((attachmentPageSize.GetHeight() > attachmentPageSize.GetWidth()) && ((int)Math.Round(attachmentPageSize.GetHeight()) > pageSize.GetHeight()))
            {
                SetPageOrientation(Orientation.Portrait);
            }

            if (FirstFile)
            {
                FirstFile = false;
                Paragraph p = new Paragraph(new Text(PDFAppendix.Heading).AddStyle(Styles.H1)).SetDestination(PDFAppendix.Heading);
                Document.Add(p);
            }
            else
            {
                AddPageBreak();
            }

Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
125
126
            // table for inserting imported pages
            // true flag: Avoid memory overhead problems from big imported files
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
            Table table = new Table(1, true)
            .UseAllAvailableWidth()
            .SetDestination(item.Key);

            Document.Add(table);

            TocItem tocItem = new TocItem
            {
                Title = item.Key,
                Indent = 1,
                Page = GetCurrentPage()
            };
            TableOfContents.AddToTOC(tocItem);

            for (int i = 1; i <= source.GetNumberOfPages(); i++)
            {
                PdfPage page = source.GetPage(i);
                PdfFormXObject pageCopy = page.CopyAsFormXObject(dest);
                Image image = new Image(pageCopy);


                Cell cell = new Cell()
                .Add(GetScaledImage(image))
                .SetBorder(Border.NO_BORDER)
                .SetPadding(0)
                .SetKeepTogether(true);

                table.AddCell(cell);

Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
156
157
                // flush content of a single page to render part of the table:
                // memory used be rendered cell objects is made available to the garbage collector for release
158
159
160
                table.Flush();
            }

Sebastian Drenckberg's avatar
Sebastian Drenckberg committed
161
            // once all the cells are added write the remainder of the table that was not rendered yet
162
163
164
165
166
167
            table.Complete();

            source.Close();
        }
    }
}