using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Windows.Forms; using VEPROMS.CSLA.Library; using iTextSharp.text; using iTextSharp.text.pdf; using DescriptiveEnum; using JR.Utils.GUI.Forms; namespace Volian.Print.Library { public class MergedPdfProc { private string _Title; public string Title { get { return _Title; } // used for pdf outline set { _Title = value; } } private int _PageCount; // number of pages in the particular pdf, to aid in determining total page count for merged pdf public int PageCount { get { return _PageCount; } set { _PageCount = value; } } private string _PdfFileName; // used for finding the procedure's pdf file to merge public string PdfFileName { get { return _PdfFileName; } set { _PdfFileName = value; } } private bool _FirstPageNoPageNum = false; public bool FirstPageNoPageNum { get { return _FirstPageNoPageNum; } set { _FirstPageNoPageNum = value; } } public MergedPdfProc(string title, string pfname) { _Title = title; _PdfFileName = pfname; } } // this class will manage the data & do subsequent merging of pdf files. It is used if the user // selects the 'Merge' button off of the print dialog when doing a Print All. (C2019-012) public class MergedPdf { private static readonly log4net.ILog _MyLog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private List _mergedPdfs = null; // This list has all procedure pdfs that will be included in the merged file. public List MergedPdfs { get { return _mergedPdfs; } set { _mergedPdfs = value; } } string _mergedFileName = null; // this is the name to be given the merged file public string MergedFileName { get { return _mergedFileName; } set { _mergedFileName = value; } } private string _folder; // this is folder to create the merged file in public string Folder { get { return _folder; } set { _folder = value; } } private DocVersionInfo _docVersionInfo; private string _pageof = null; private iTextSharp.text.Font _itextFont = null; private MergedPdfsPageNumCorner _corner; private float _xloc = 0; private float _yloc = 0; // C2021-047: Allow for setting of Merged Pdf Landscape Page Number Location private MergedPdfsPageNumCorner _landcorner; private float _landxloc = 0; private float _landyloc = 0; // the following constructs the class that contains data for merging a group of pdfs. public MergedPdf(string folder, DocVersionInfo dvi) { _folder = folder; _docVersionInfo = dvi; } // the following merges the procedure pdfs into a single pdf public bool DoTheMerge(Dictionary> MergedLandscapPages, bool generatePointListFile) { if (MergedPdfs == null) { FlexibleMessageBox.Show("There are no PDFs to merge.", "Merging Pdfs Error", MessageBoxButtons.OK); return false; } iTextSharp.text.Document doc = new iTextSharp.text.Document(); string slashReplace = _docVersionInfo.ActiveFormat.PlantFormat.FormatData.PrintData.SlashReplace ?? "_"; MergedFileName = _folder + @"\" + _docVersionInfo.MyFolder.Name.Replace("/", slashReplace).Replace("\\", slashReplace) + ".pdf"; MergedFileName = ItemInfo.StripRtfFormatting(MergedFileName); // C2021-063 file name of Alarm Point List file containing Serial Number, Title, and merged Page Number StreamWriter sw = null; string PointListPageNums = MergedFileName.Replace(".pdf", "_PointList.txt"); if (generatePointListFile) { FileInfo fi = new FileInfo(PointListPageNums); sw = fi.CreateText(); } PdfWriter writer = null; // see if it can be created and if not, see if it is open and tell user: try { writer = PdfWriter.GetInstance(doc, new FileStream(MergedFileName, FileMode.Create)); } catch (Exception ex) { StringBuilder sb = new StringBuilder(); sb.AppendLine("Could not create"); sb.AppendLine(); sb.AppendLine(MergedFileName + "."); sb.AppendLine(); sb.AppendLine("If it is open, close and retry."); FlexibleMessageBox.Show(sb.ToString(), "Error on CreatePdf", MessageBoxButtons.OK, MessageBoxIcon.Warning); } if (writer == null) return false; doc.Open(); PdfContentByte canvas = writer.DirectContent; int mergedPageNumber = 0; // this is the number that will be used for the token (for of ) int totalPages = 0; // this is the number that will be used for the token (for of ) foreach (MergedPdfProc mpp in MergedPdfs) totalPages += mpp.PageCount; bool doPageNum = GetPageOfInfo(); // get the format and location of page number - this can be reused for each procedure // the MergedPdfs contains a list of all of the procedures' pdfs that will be merged into the final pdf int idxSerialNum = 0; // used to generate the Alarm serial number for Point List file foreach (MergedPdfProc mpp in MergedPdfs) { // C2021-063 write Alarm Point List information to the text file if (generatePointListFile) { // write Serial Number, Title, and Page Number in the Point List file sw.WriteLine("{0}\t{1}\t{2}", idxSerialNum++, mpp.Title, mergedPageNumber + 1); } string pdffilename = _folder + @"\" + mpp.PdfFileName; if (!File.Exists(pdffilename)) { FlexibleMessageBox.Show("Error in finding pdf files to merge. Cannot print with date/time as part of pdf name. Or check that individual pdfs were generated.", "Error on CreatePdf", MessageBoxButtons.OK, MessageBoxIcon.Warning); return false; } // B2019-152: The MergedLandscapePages Dictionary contains page numbers for pdfs that need to be landscaped. These are // added on 'endpages' when printing is done. List LandscapePages = null; // List of landscaped pages for a pdf if (PromsPrinter.MergedLandscapePages != null) { string fname = mpp.PdfFileName.Substring(0, mpp.PdfFileName.IndexOf(".pdf")); if (PromsPrinter.MergedLandscapePages.ContainsKey(fname)) LandscapePages = PromsPrinter.MergedLandscapePages[fname]; } PdfReader reader = null; try // B2019-041: added a try/catch for when a corrupt pdf was created for a procedure, just skip & add message to error log. { reader = new PdfReader(_folder + @"\" + mpp.PdfFileName); int numPages = reader.NumberOfPages; int currentPageNumber = 0; PdfOutline outline = null; Rectangle paperSize = PDFPageSize.UsePaperSize(_docVersionInfo.ActiveFormat.PlantFormat.FormatData.PDFPageSize.PaperSize); // C2020-002 paper size is now set in the format files do // merging pages into complete... { currentPageNumber += 1; mergedPageNumber += 1; doc.SetPageSize(paperSize); doc.NewPage(); PdfImportedPage page = writer.GetImportedPage(reader, currentPageNumber); // gets a page that is 'ready' to be written to combined pdf // F2021-046: flag if cover page section doesn't print page number on first page of merged pdf: if (doPageNum && (!(mpp.FirstPageNoPageNum && currentPageNumber == 1))) // get the string & fill in with and numbers { bool landscape = false; if (LandscapePages != null && LandscapePages.Contains(currentPageNumber - 1)) landscape = true; string outputpageof = _pageof.Replace("", mergedPageNumber.ToString()).Replace("", totalPages.ToString()); AddPageNumberToPage(page, canvas, outputpageof, landscape); } PdfDestination dest = new PdfDestination(PdfDestination.FIT); // if on the first page, add the pdfname & title as part of outline. If on remaining pages, just put out page number. // Later this may need expanded to put sections/steps/etc. if (currentPageNumber == 1) outline = new PdfOutline(canvas.RootOutline, dest, mpp.PdfFileName.Replace(".pdf", "") + " - " + mpp.Title, false); else new PdfOutline(outline, dest, "Page " + currentPageNumber.ToString(), false); canvas.AddTemplate(page, 0, 0); // adds the page to the combined pdf } while (currentPageNumber < numPages); } catch (Exception ex) { string tmp = string.Format("Error merging pdf {0}", mpp.PdfFileName); _MyLog.Error(tmp, ex); } } // C2021-063 display the generated Alarm Point List text in NotePad if (generatePointListFile) { sw.Close(); System.Diagnostics.Process.Start(PointListPageNums); } doc.Close(); return true; } // this method adds the page number to the page before it is merged in. It determines the position from the properties // that were set on the doc version properties dialog (these values were set as local class variables from GetPageOfInfo) private void AddPageNumberToPage(PdfImportedPage page, PdfContentByte cb, string outputpage, bool landscape) { float pgright = page.BoundingBox.Right; float pgleft = page.BoundingBox.Left; float pgtop = page.BoundingBox.Top; float pgbot = page.BoundingBox.Bottom; MergedPdfsPageNumCorner cnr = _corner; // C2021-047: Allow for setting of Merged Pdf Landscape Page Number Location // B2019-152: Landscape page numbers when merging if (landscape) { pgright = page.BoundingBox.Top; pgleft = page.BoundingBox.Bottom; pgtop = page.BoundingBox.Right; pgbot = page.BoundingBox.Left; cnr = _landcorner; // C2021-047: Allow for setting of Merged Pdf Landscape Page Number Location } Phrase ph = new Phrase(); Chunk chk = new Chunk(outputpage, _itextFont); ph.Add(chk); Paragraph pg = new Paragraph(ph); ColumnText columnText = new ColumnText(cb); cb.SaveState(); switch (cnr) { case MergedPdfsPageNumCorner.TopRight: columnText.Alignment = Element.ALIGN_RIGHT; if (landscape) // B2019-152: landscape page numbers { System.Drawing.Drawing2D.Matrix myMatrix1 = new System.Drawing.Drawing2D.Matrix(0, 1, -1, 0, cb.PdfDocument.PageSize.Height, 0); cb.Transform(myMatrix1); columnText.SetSimpleColumn(0, pgright - _landyloc, pgright - _landxloc, 0); // C2021-047: use landscape x & y } else columnText.SetSimpleColumn(0, pgtop - _yloc, pgright - _xloc, 0); break; case MergedPdfsPageNumCorner.BottomRight: columnText.Alignment = Element.ALIGN_RIGHT; if (landscape) // B2019-152: landscape page numbers { System.Drawing.Drawing2D.Matrix myMatrix2 = new System.Drawing.Drawing2D.Matrix(0, 1, -1, 0, cb.PdfDocument.PageSize.Width, 0); cb.Transform(myMatrix2); columnText.SetSimpleColumn(0, _landyloc, pgright - _landxloc, 0); // C2021-047: use landscape x & y } else columnText.SetSimpleColumn(0, _yloc, pgright - _xloc, 0); break; case MergedPdfsPageNumCorner.TopLeft: if (landscape) // B2019-152: landscape page numbers { System.Drawing.Drawing2D.Matrix myMatrix3 = new System.Drawing.Drawing2D.Matrix(0, 1, -1, 0, cb.PdfDocument.PageSize.Height, 0); cb.Transform(myMatrix3); columnText.SetSimpleColumn(_xloc, pgright - _landyloc, pgright - _landxloc, 0); // C2021-047: use landscape x & y } else { columnText.SetSimpleColumn(_xloc, pgtop - _yloc, pgright, 0); // page alignment defaults to ALIGN_LEFT } break; case MergedPdfsPageNumCorner.BottomLeft: if (landscape) // B2019-152: landscape page numbers { System.Drawing.Drawing2D.Matrix myMatrix4 = new System.Drawing.Drawing2D.Matrix(0, 1, -1, 0, cb.PdfDocument.PageSize.Width, 0); cb.Transform(myMatrix4); columnText.SetSimpleColumn(_xloc, _landyloc, pgright - _landxloc, 0); // C2021-047: use landscape x & y } else { columnText.SetSimpleColumn(_xloc, _yloc, pgright, 0); // page alignment defaults to ALIGN_LEFT } break; } columnText.AddText(pg); columnText.Go(); cb.RestoreState(); } // this method gets the format, font & location data from the doc version properties that are stored on the doc version's // config field. It saves them as local class variables to be used in other places in this class. private bool GetPageOfInfo() { DocVersionConfig dvc = (DocVersionConfig) _docVersionInfo.MyConfig; MergedPdfsPageOf po = dvc.Print_MergedPdfsPageOf; string tmp = EnumDescConverter.GetEnumDescription((Enum)dvc.Print_MergedPdfsPageOf); if (tmp == "None") return false; // no page number, return false so no page number is put out. // If 'Other' for the format, use text in textbox to format the of string, note that and are // resolved when adding pages to the merge document. The only other 'token' that is supported is '' if (tmp == "Other") tmp = dvc.Print_MergedPdfsPageNumFormatOther; _pageof = tmp.Replace("", _docVersionInfo.MyFolder.Name); // If font data is not supplied, use the default from the format for this working draft string famtmp = dvc.Print_MergedPdfsPageNumFont; if (famtmp == null || famtmp == "") { famtmp = _docVersionInfo.ActiveFormat.PlantFormat.FormatData.Font.Family; } tmp = dvc.Print_MergedPdfsPageNumFontSize; float? tmpi=0; if (tmp == null || tmp == "") { // use the default from the format from this working draft tmpi = _docVersionInfo.ActiveFormat.PlantFormat.FormatData.Font.Size; } else tmpi = int.Parse(tmp); // set local values of variables to be used in various other methods in this class. _itextFont = Volian.Svg.Library.Svg.GetFont(famtmp, (float)tmpi, 0, System.Drawing.Color.Black); _xloc = (float)dvc.Print_MergedPdfsPageNumLocX * 72; _yloc = (float)dvc.Print_MergedPdfsPageNumLocY * 72; _corner = dvc.Print_MergedPdfsPageNumCorner; // C2021-047: Allow for setting of Merged Pdf Landscape Page Number Location _landxloc = (float)dvc.Print_MergedPdfsLandPageNumLocX * 72; _landyloc = (float)dvc.Print_MergedPdfsLandPageNumLocY * 72; _landcorner = dvc.Print_MergedPdfsLandPageNumCorner; return true; } } }