323 lines
14 KiB
C#

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<MergedPdfProc> _mergedPdfs = null; // This list has all procedure pdfs that will be included in the merged file.
public List<MergedPdfProc> 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<string, List<int>> 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 <page> token (for <page> of <of>)
int totalPages = 0; // this is the number that will be used for the <of> token (for <page> of <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<int> 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 <page> and <of> numbers
{
bool landscape = false;
if (LandscapePages != null && LandscapePages.Contains(currentPageNumber - 1)) landscape = true;
string outputpageof = _pageof.Replace("<page>", mergedPageNumber.ToString()).Replace("<of>", 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 <page> of <of> string, note that <page> and <of> are
// resolved when adding pages to the merge document. The only other 'token' that is supported is '<set>'
if (tmp == "Other") tmp = dvc.Print_MergedPdfsPageNumFormatOther;
_pageof = tmp.Replace("<set>", _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;
}
}
}