using System; using System.Collections.Generic; using System.Text; using VEPROMS.CSLA.Library; using Volian.Svg.Library; using iTextSharp.text.factories; using iTextSharp.text; using iTextSharp.text.pdf; using System.Text.RegularExpressions; using System.IO; using System.Xml; using System.Windows.Forms; using LBWordLibrary; using System.Drawing; using System.Drawing.Imaging; using Volian.Controls.Library; using Volian.Base.Library; using JR.Utils.GUI.Forms; namespace Volian.Print.Library { public delegate void PromsPrinterStatusEvent(object sender, PromsPrintStatusArgs args); public class PromsPrintStatusArgs { private string _MyStatus; public string MyStatus { get { return _MyStatus; } } private DateTime _When = DateTime.Now; public DateTime When { get { return _When; } } private PromsPrinterStatusType _Type; public PromsPrinterStatusType Type { get { return _Type; } set { _Type = value; } } private int _Progress = 0; public int Progress { get { return _Progress; } set { _Progress = value; } } public PromsPrintStatusArgs(string myStatus, PromsPrinterStatusType type) { _MyStatus = myStatus; _Type = type; } public PromsPrintStatusArgs(string myStatus, PromsPrinterStatusType type, int progress) { _MyStatus = myStatus; _Type = type; _Progress = progress; } } public enum PromsPrinterStatusType { Start, General, MSWordToPDF, PageList, Watermark, Read16, Merge16, Open16, ReadMSWord, MergeMSWord, OpenMSWord, OpenPDF, Merge, Total, CloseDocument, NewPage, BuildSVG, SetSVG, SetPageEvent, GetSection, Before, BuildStep, Progress, ProgressSetup, LoadVlnParagraph } public class PromsPrinter { public bool NeedSupInfoBreak = false; public E_SupInfoPrintType SupInfoPrintType = E_SupInfoPrintType.None; // used to link supinfo steps in pdf public int SupInfoPdfPageCount = -1; private Dictionary _SupInfoPdfPage; public Dictionary SupInfoPdfPage { get { return _SupInfoPdfPage; } set { _SupInfoPdfPage = value; } } private bool _DoingFacingPage = false; // used to flag actual pdf generation of facing page within final print (so no section titles get printed) public bool DoingFacingPage { get { return _DoingFacingPage; } set { _DoingFacingPage = value; } } // used to save word sections with resolved ROs (for export file generated from approve) private Dictionary _DocReplace; public Dictionary DocReplace { get { return _DocReplace; } set { _DocReplace = value; } } private bool _SaveLinks = false; public bool SaveLinks { get { return _SaveLinks; } set { _SaveLinks = value; } } // C2021-010: Remove trailing returns/spaces & manual page breaks & allow save. // Use these 2 lists to track the items that will need to be saved after removing trailing newlines/spaces or // manual page breaks, if the user selects to do that. private List _RemoveTrailingHardReturnsAndSpaces = null; public List RemoveTrailingHardReturnsAndSpaces { get { return _RemoveTrailingHardReturnsAndSpaces; } set { _RemoveTrailingHardReturnsAndSpaces = value; } } private List _RemoveManualPageBreaks = null; public List RemoveManualPageBreaks { get { return _RemoveManualPageBreaks; } set { _RemoveManualPageBreaks = value; } } private string _Prefix = ""; // RHM20150506 Multiline ItemID TextBox public string Prefix { get { return _Prefix; } set { _Prefix = value; } } // B2024-062 Set to true when the procedure being printed has no content. // When set to true, will prevent the "Try Again" dialog from appearing // and simply exit the print function private bool _EmptyProcedure = false; public bool EmptyProcedure { get { return _EmptyProcedure; } } private static readonly log4net.ILog _MyLog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public event PromsPrinterStatusEvent StatusChanged; internal void OnStatusChanged(object sender, PromsPrintStatusArgs args) { if (StatusChanged != null) StatusChanged(sender, args); } internal void OnStatusChanged(string myStatus, PromsPrinterStatusType type) { OnStatusChanged(this, new PromsPrintStatusArgs(myStatus, type)); } public void OnStatusChanged(string msg) { OnStatusChanged(this, new PromsPrintStatusArgs(msg, PromsPrinterStatusType.General)); } internal void OnStatusChanged(string myStatus, PromsPrinterStatusType type, int progress) { OnStatusChanged(this, new PromsPrintStatusArgs(myStatus, type, progress)); } private string _Rev; private ItemInfo _MyItem; public ItemInfo MyItem { get { return _MyItem; } set { _MyItem = value; } } private string _Watermark; private string _WatermarkColor; //C2018-009 print PROMS version private string _PromsVersion = ""; public string PromsVersion { get { return _PromsVersion; } set { _PromsVersion = value; } } private string _PDFFile; public string PDFFile { get { return _PDFFile; } set { _PDFFile = value; } } private int _prtSectID = -1; public int PrtSectID { get { return _prtSectID; } set { _prtSectID = value; } } private bool _DebugOutput; public bool DebugOutput { get { return _DebugOutput; } set { _DebugOutput = value; } } private string _BackgroundFolder; public string BackgroundFolder { get { return _BackgroundFolder; } set { _BackgroundFolder = value; } } private bool _OpenPDF; public bool OpenPDF { get { return _OpenPDF; } set { _OpenPDF = value; } } private bool _OverWrite; public bool OverWrite { get { return _OverWrite; } set { _OverWrite = value; } } private ChangeBarDefinition _MyChangeBarDefinition; public ChangeBarDefinition MyChangeBarDefinition { get { return _MyChangeBarDefinition; } set { _MyChangeBarDefinition = value; } } private bool _OriginalPageBreak; // use 16bit page breaks. public bool OriginalPageBreak { get { return _OriginalPageBreak; } set { _OriginalPageBreak = value; } } private bool _InsertBlankPages; public bool InsertBlankPages { get { return _InsertBlankPages; } set { _InsertBlankPages = value; } } private bool _BatchPrint = true; // flags that a batch-type print is occurring, i.e. AllProcedures or Automatic testing public bool BatchPrint { get { return _BatchPrint; } set { _BatchPrint = value; } } // B2020-159: When printing, if forced pagination errors occurred (see Pagination.cs), put up a dialog with // the places they occurred. Do not do this is running baseline/automatic print testing. private static List _ForcedPaginations = null; public static List ForcedPaginations { get { return PromsPrinter._ForcedPaginations; } set { PromsPrinter._ForcedPaginations = value; } } public static DialogResult ReportForcedPaginations() { if (ForcedPaginations != null && ForcedPaginations.Count > 0) { string pnProbl = null; foreach (string pstr in ForcedPaginations) pnProbl = pnProbl + "\r\n" + pstr; Clipboard.SetText(pnProbl); return FlexibleMessageBox.Show( "PROMS has identified location(s) in the PDF file that was created that may not have paginated correctly." + "\r\nOn or around the following pages should be reviewed to ensure that the procedure is correct." + "\r\nVerify that there is no missing text on the page(s) designated." + "\r\nIf not correct, the procedure writer will have to modify the procedure content or add hard returns." + "\r\nContact Volian if assistance is needed." + "\r\nThe following list was placed on the Windows Clipboard." + "\r\n\r\nPDF File\t\tSection/Step\t\t\tPDF Page No.\r\n" + pnProbl, "Forced Pagination", MessageBoxButtons.OK, MessageBoxIcon.Warning); } return DialogResult.OK; } private static List _TransPageNumProblems = null; public static List TransPageNumProblems { get { return PromsPrinter._TransPageNumProblems; } set { PromsPrinter._TransPageNumProblems = value; } } public static void ClearTransPageNumProblems() { _TransPageNumProblems = null; } public static DialogResult ReportTransPageNumProblems() { if (TransPageNumProblems != null && TransPageNumProblems.Count > 0) { string pnProbl = null; foreach (string pstr in TransPageNumProblems) pnProbl = pnProbl + "\r\n" + pstr; Clipboard.SetText(pnProbl); return FlexibleMessageBox.Show("The following list was placed on the Windows Clipboard." + "\r\nIt shows the page numbers of the locations that are referenced by transitions." + "\r\nThis list, along with the PDF, can help you to determine the cause of the inconsistent page numbers." + "\r\n\r\nDo you want to also review the PDF output prior to the second pass printing?" // C2017-018 Output Column Headers for Inconsistent Page Transitions + "\r\n\r\nItemID\tTransition Destination\tPage Pass 1\t Page Pass 2\r\n\r\n" + pnProbl, "Inconsistent Transition Page Numbers", MessageBoxButtons.YesNo, MessageBoxIcon.Question); } return DialogResult.No; } private ReaderHelper _MyReaderHelper; public ReaderHelper MyReaderHelper { get { return _MyReaderHelper; } set { _MyReaderHelper = value; } } private string _BlankPageText; // C2019-004: Allow user to define duplex blank page text public string BlankPageText { get { return _BlankPageText; } set { _BlankPageText = value; } } // use DidAll & MergeNotIncuded to know that printing all (not single) was done. Used w/ merge code to know when to return, i.e. not include in merged pdf private bool _DidAll; public bool DidAll // value passed in from frmPdfStatusForm { get { return _DidAll; } set { _DidAll = value; } } private bool _MergeNotIncluded; public bool MergeNotIncluded { get { return _MergeNotIncluded; } set { _MergeNotIncluded = value; } } private MergedPdf _MergedPdf; public MergedPdf MergedPdf { get { return _MergedPdf; } set { _MergedPdf = value; } } public PromsPrinter(ItemInfo myItem, string rev, string watermark, bool debugOutput, bool origPgBrk, string backgroundFolder, bool openPDF, bool overWrite, ChangeBarDefinition cbd, String pdfFile, bool insertBlankPages, bool batchPrint, string prefix, bool saveLinks, int removeTrailngHardReturnsAndManualPageBreaks, string blankPageText, bool didAll, MergedPdf mergedPdf, string watermarkColor, int PrtSectID = -1) { Prefix = prefix; // RHM20150506 Multiline ItemID TextBox _MyItem = myItem; _Rev = rev; _Watermark = watermark; _WatermarkColor = watermarkColor; _DebugOutput = debugOutput; _BackgroundFolder = backgroundFolder; _OpenPDF = openPDF; _OverWrite = overWrite; _MyChangeBarDefinition = cbd; _PDFFile = pdfFile; _OriginalPageBreak = origPgBrk; _InsertBlankPages = insertBlankPages; _BatchPrint = batchPrint; _MyReaderHelper = new ReaderHelper(this); _SaveLinks = saveLinks; // C2021-010: Remove trailing returns/spaces & manual page breaks & allow save. if (removeTrailngHardReturnsAndManualPageBreaks == 1 || removeTrailngHardReturnsAndManualPageBreaks == 3) RemoveTrailingHardReturnsAndSpaces = new List(); if (removeTrailngHardReturnsAndManualPageBreaks == 2 || removeTrailngHardReturnsAndManualPageBreaks == 3) RemoveManualPageBreaks = new List(); _BlankPageText = blankPageText; _DidAll = didAll; _MergeNotIncluded = false; _MergedPdf = mergedPdf; _prtSectID = PrtSectID; //_MyReaderHelper.LoadTree(myItem); } // Pass 1 PDF Name private string _BeforePageNumberPdf = null; public string BeforePageNumberPdf { get { return _BeforePageNumberPdf; } set { _BeforePageNumberPdf = value; } } // Pass 2 PDF Name private string _BeforePageNumber2Pdf = null; public string BeforePageNumber2Pdf { get { return _BeforePageNumber2Pdf; } set { _BeforePageNumber2Pdf = value; } } // B2019-152: MergedLandscapePages is a dictionary whose key is the pdf file name & values are all of the page numbers // in that pdf that should have landscaped page numbers. These are added when the page is finished (onEndPage) public static Dictionary> MergedLandscapePages = null; public string Print(string pdfFolder, bool makePlacekeeper, bool makeContinuousActionSummary, bool makeTimeCriticalActionSummary, int PrtSectID = -1) { if (_MyItem is ProcedureInfo) { _prtSectID = PrtSectID; ProcedureConfig pcfg = (ProcedureConfig)(_MyItem as ProcedureInfo).MyConfig; if (DidAll && pcfg.Print_NotInMergeAll) // will be merging a pdf, so don't print this procedure if user set that on procedure propertures { _MergeNotIncluded = true; return null; } // B2017-186 First do Procedures that don't have page number transitions if (!(_MyItem.ActiveFormat.PlantFormat.FormatData.TransData.UseTransitionModifier || _MyItem.ActiveFormat.PlantFormat.FormatData.TransData.UseSpecificTransitionModifier)) { // B2017-186 Do Facing Page SAMGs if ((_MyItem as ProcedureInfo).ProcHasSupInfoData) { // Use two passes to print facing page supplemental information for SAMGs // First pass gets pagination for the step sections and stores the associated supplemental information ids in // the list StepSectPageBreaksForSupInfo off of the section object // Second pass prints the supplemental information pdfs using the list generated in the first pass to know where to do the // supplemental information page breaks & then merges in those pages when printing the step sections. SectionInfo.ResetLookupStepSectPageBreaks(); // B2017-192: Reset lists for tracking page breaks SectionInfo.ResetLookupStepSectPageBreaksForSupInfo(); SupInfoPrintType = E_SupInfoPrintType.DoPageBreaks; string retstr = Print(_MyItem as ProcedureInfo, pdfFolder, false, false, false); if (retstr == null) return null; SupInfoPrintType = E_SupInfoPrintType.Merge; string tmps = Print(_MyItem as ProcedureInfo, pdfFolder, false, false, false); if (!BatchPrint && ForcedPaginations != null && ForcedPaginations.Count > 0) // B2020-159: Forced Pagination Reporting { ReportForcedPaginations(); ForcedPaginations.Clear(); } return tmps; } else // B2017-186 Neither Facing Pages or Page Number Transitions { string tmpss = Print(_MyItem as ProcedureInfo, pdfFolder, makePlacekeeper, makeContinuousActionSummary, makeTimeCriticalActionSummary); if (!BatchPrint && ForcedPaginations != null && ForcedPaginations.Count > 0) // B2020-159: Forced Pagination Reporting { ReportForcedPaginations(); ForcedPaginations.Clear(); } return tmpss; } } else // B2017-186 Page Number Transitions { string retstr; // if the plant uses transition modifiers and/or page num in transition format, // need to do two passes. First pass, sets the pagenumbers for each item, // 2nd pass fills in the page numbers in transitions. DirectoryInfo di = new DirectoryInfo(pdfFolder + @"\PageNumberPass1"); if (!di.Exists) di.Create(); if ((_MyItem as ProcedureInfo).ProcHasSupInfoData)// B2017-186 Adds Facing Page Code to Page Number Transition Code { // Use two passes to print facing page supplemental information for SAMGs // First pass gets pagination for the step sections and stores the associated supplemental information ids in // the list StepSectPageBreaksForSupInfo off of the section object // Second pass prints the supplemental information pdfs using the list generated in the first pass to know where to do the // supplemental information page breaks & then merges in those pages when printing the step sections. SupInfoPrintType = E_SupInfoPrintType.DoPageBreaks; retstr = Print(_MyItem as ProcedureInfo, pdfFolder, false, false, false); if (retstr == null) return null; SupInfoPrintType = E_SupInfoPrintType.Merge; } retstr = Print(_MyItem as ProcedureInfo, pdfFolder + @"\PageNumberPass1", false, false, false); if (retstr == null) return null; BeforePageNumberPdf = retstr; BeforePageNumber2Pdf = null; // C2017-018 Initialize Pass 2 PDF Name ProcedureInfo.RefreshPageNumTransitions(_MyItem as ProcedureInfo); _MyReaderHelper = new ReaderHelper(this); retstr = Print(_MyItem as ProcedureInfo, pdfFolder, makePlacekeeper, makeContinuousActionSummary, makeTimeCriticalActionSummary); if (!BatchPrint && ForcedPaginations != null && ForcedPaginations.Count > 0) // B2020-159: Forced Pagination Reporting { ReportForcedPaginations(); ForcedPaginations.Clear(); } if (TransPageNumProblems.Count > 0) { if (BatchPrint || (MessageBox.Show("Page Number Transitions may be fixed if a second pass is performed. Do you want to perform a second pass?", "Page Number Transition Errors", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)) { DirectoryInfo di1 = new DirectoryInfo(pdfFolder + @"\PageNumberPass2"); if (!di1.Exists) di1.Create(); FileInfo fi = new FileInfo(retstr); // Save Pass 2 PDF Name FileInfo fidest = new FileInfo(BeforePageNumber2Pdf = di1.FullName + @"\" + fi.Name); if (fidest.Exists) fidest.Delete(); try { fi.MoveTo(di1.FullName + @"\" + fi.Name); } catch (Exception ex) { _MyLog.ErrorFormat("{0} During Save {1}", ex.GetType().FullName, ex.Message); } ClearTransPageNumProblems(); RefreshPageNumber(_MyItem); ProcedureInfo.RefreshPageNumTransitions(_MyItem as ProcedureInfo); _MyReaderHelper = new ReaderHelper(this); retstr = Print(_MyItem as ProcedureInfo, pdfFolder, makePlacekeeper, makeContinuousActionSummary, makeTimeCriticalActionSummary); } } return retstr; } } return ""; } private void RefreshPageNumber(ItemInfo _MyItem) { // set all page numbers for all items under the passed in procedure item _MyItem.PageNumber = _MyItem.PageNumberNextPass; if (_MyItem.MyContent.ContentPartCount > 0) { foreach (PartInfo pi in _MyItem.MyContent.ContentParts) { foreach (ItemInfo ii in pi.MyItems) RefreshPageNumber(ii); } } } internal string BuildMSWordPDF(SectionInfo section) { DateTime tStart = DateTime.Now; string MSWordFile = null; if (section.MyContent.ContentEntryCount == 1) { MSWordToPDF.DocReplace = DocReplace; MSWordFile = MSWordToPDF.GetDocPdf(section, PrintOverride.TextColor); MSWordToPDF.DocReplace = null; //GC.Collect(); // memory garbage collection OnStatusChanged("MSWord converted to PDF " + MSWordFile, PromsPrinterStatusType.MSWordToPDF); } return MSWordFile; } private static void AddImportedPageToLayer(PdfContentByte cb, PdfLayer layer, PdfImportedPage page, float xOff, float yOff) { cb.BeginLayer(layer); iTextSharp.text.Image image = iTextSharp.text.Image.GetInstance(page); // the following checks if the content of page is landscape and rotates because we're printing on // a portrait page. If, at some point, we print a total landscape page, not a mix, this will need // re-evaluated. if (page.Height < page.Width) { if (cb.PdfDocument.PageSize.Height > cb.PdfDocument.PageSize.Width) image.RotationDegrees = 90F; } image.SetAbsolutePosition(xOff, yOff); cb.AddImage(image); cb.EndLayer(); } private PdfLayer _TextLayer; private PdfLayer _BackgroundLayer; private PdfLayer _MSWordLayer; private PdfLayer _PagelistLayer; private PdfLayer _DebugLayer; private PdfLayer _WatermarkLayer; private PdfLayer _PROMSVersionLayer; //C2018-009 print PROMS version private void CreateLayers(PdfContentByte cb) { if (DebugOutput) { _BackgroundLayer = new PdfLayer("16-Bit", cb.PdfWriter); _MSWordLayer = new PdfLayer("32-Bit MSWord", cb.PdfWriter); _PagelistLayer = new PdfLayer("32-Bit Pagelist", cb.PdfWriter); _TextLayer = new PdfLayer("32-Bit Text", cb.PdfWriter); _DebugLayer = new PdfLayer("Debug", cb.PdfWriter); _WatermarkLayer = new PdfLayer("Watermark", cb.PdfWriter); _WatermarkLayer.SetPrint("Watermark", true); _PROMSVersionLayer = new PdfLayer("PROMS Version", cb.PdfWriter); //C2018-009 print PROMS version _PROMSVersionLayer.SetPrint("PROMS Version", true); } } public static int ProcPageCount = -1; // Used to count page numbers in each pdf. This will be used if/when pdfs are merged together. private void CloseDocument(string fileName) { int profileDepth = ProfileTimer.Push(">>>> CloseDocument"); try { ProcPageCount = MyContentByte.PdfWriter.CurrentPageNumber - 1; MyContentByte.PdfDocument.Close(); MyContentByte = null; } catch (Exception ex) { Console.WriteLine("{0} - {1}", ex.GetType().Name, ex.Message); } OnStatusChanged("CloseDocument", PromsPrinterStatusType.CloseDocument); if (OpenPDF) System.Diagnostics.Process.Start(fileName); OnStatusChanged("OpenPDF", PromsPrinterStatusType.OpenPDF); ProfileTimer.Pop(profileDepth); } private PdfContentByte _MyContentByte = null; public PdfContentByte MyContentByte { get { return _MyContentByte; } set { _MyContentByte = value; if (value != null) MyReaderHelper.LoadTree(MyItem); } } public void NewPage() { //string path = Volian.Base.Library.vlnStackTrace.StackToStringLocal(2, 1); NewPage(false); } public void NewPage(bool sectionChange) { // the first time we come in here we already printed the first page. // we are now setting up the page size for the next page iTextSharp.text.Rectangle rct = MyReaderHelper.MyPromsPrinter.MyContentByte.PdfDocument.PageSize; // B2019-166 Only check for Word margins if the section changed // - was getting next section's page margins prematurely (while still processing the a multi-page section) // - this was changing the margins/orientation of other pages in the same Word section. if (_MyHelper != null && sectionChange) { SectionInfo currentSection = _MyHelper.MySection; SectionInfo nextSection = GetNextSection(currentSection); // _MyHelper.MySection.NextItem as SectionInfo; //SectionConfig sc = (nextSection ?? currentSection).MyConfig as SectionConfig; // If we are changing to a different section, then get the config from the nextSection // else we are still printing in the same section so get the config from the current section SectionConfig sc = ((!sectionChange || nextSection == null) ? currentSection.MyConfig : nextSection.MyConfig) as SectionConfig; bool wordMargins = (sc != null && sc.Section_WordMargin == "Y"); if (wordMargins) { if (nextSection != null && (sectionChange) && !nextSection.IsStepSection) //change to a word section { LastWordSection = nextSection; _PageCountOfWordSection = 1; MyReaderHelper.MySectionInfo = LastWordSection; //ShowNeedForPageSize(); rct = MyReaderHelper.GetSize(LastWordSection, 1); } else if (!currentSection.IsStepSection) //current section is a word section { if (currentSection != LastWordSection) { LastWordSection = currentSection; _PageCountOfWordSection = 1; } else if (!_MyHelper.OnBlankPage) _PageCountOfWordSection++; // B2018-049: supplemental printing throws off page sizes MyReaderHelper.MySectionInfo = LastWordSection; //ShowNeedForPageSize(); rct = MyReaderHelper.GetSize(LastWordSection, _PageCountOfWordSection); } } else // don't use Word margins { rct = PDFPageSize.UsePaperSize(MyItem.ActiveFormat.PlantFormat.FormatData.PDFPageSize.PaperSize); // C2020-002 paper size is now set in the format files } // C2018-004 create meta file for baseline compares if (currentSection.IsStepSection) Volian.Base.Library.BaselineMetaFile.WriteLine("{0}", rct); else Volian.Base.Library.BaselineMetaFile.WriteLine("UseWordMargins={0} {1} ", (wordMargins) ? "Y" : "N", rct); //Console.WriteLine("\"{0}\"\t\"{1}\"\t\"{2}\"\t\"{3}\"\t{4}" // , currentSection == null ? "" : currentSection.DisplayNumber + " " + currentSection.DisplayText // , nextSection == null ? "" : nextSection.DisplayNumber + " " + nextSection.DisplayText // , sectionChange, wordMargins,_PageCountOfWordSection); } // Bug Fix: B2016-135 when using a large page size word section, that large page size was being used for the foldout page // save this for the next section which will be the large page size word section. // Bug Fix: B2018-049 supplemental printing throws off page sizes (need to do same thing as fix for foldouts) // Bug Fix: B2019-165 (Byron/Braidwood) also don't do this if the current section has Don't Include Duplex Foldout checkbox checked (automation tab of section properties) if ((_MyFoldoutReader.Count > 0) && (_MyHelper != null && !((_MyHelper.MySection.MyConfig as SectionConfig).Section_DontIncludeDuplexFoldout) || (_MyHelper != null && _MyHelper.MySection != null && _MyHelper.MySection.MyProcedure.ProcHasSupInfoData))) MyContentByte.PdfDocument.SetPageSize(NxtRCT); else MyContentByte.PdfDocument.SetPageSize(rct); NxtRCT = rct; MyContentByte.PdfDocument.NewPage(); } private static bool _BaselineTesting = false; // Is the baseline being run (/P Commandline Parameter) public static bool BaselineTesting { get { return PromsPrinter._BaselineTesting; } set { PromsPrinter._BaselineTesting = value; } } private iTextSharp.text.Rectangle NxtRCT = PageSize.LETTER; private SectionInfo GetNextSection(SectionInfo currentSection) { if (currentSection.Sections != null) return currentSection.Sections[0].GetSectionInfo(); if (currentSection.NextItem != null) return currentSection.NextItem.GetSectionInfo(); ItemInfo parent = currentSection.ActiveParent as ItemInfo; while (parent != null && parent.NextItem == null) parent = parent.ActiveParent as ItemInfo; if (parent == null) return null; return parent.NextItem.GetSectionInfo(); } private SectionInfo _PreviousWordSection = null; public SectionInfo PreviousWordSection { get { return _PreviousWordSection; } set { _PreviousWordSection = value; } } private SectionInfo _LastWordSection = null; public SectionInfo LastWordSection { get { return _LastWordSection; } set { PreviousWordSection = _LastWordSection; _LastWordSection = value; } } private int _PageCountOfWordSection = 0; //private void ShowNeedForPageSize() //{ // if(PreviousWordSection != null && PreviousWordSection != LastWordSection) // Console.WriteLine("Switch From {0}.{1} ",PreviousWordSection.DisplayNumber,PreviousWordSection.DisplayText); // Console.WriteLine("To {0}.{1} Page: {2}",LastWordSection.DisplayNumber,LastWordSection.DisplayText,_PageCountOfWordSection); // PreviousWordSection = LastWordSection; //} private PdfContentByte OpenDoc(ref string outputFileName, iTextSharp.text.Rectangle rect) { try { PdfWriter writer = null; iTextSharp.text.Document document = new iTextSharp.text.Document(rect); try { if (File.Exists(outputFileName) && !OverWrite && !outputFileName.Contains("PageNumberPass") && !outputFileName.Contains("Foldout")) { if (!BaselineTesting && !SaveLinks) // B2024-031 don't do if creating PDF hyperlinks { String tmpExt = System.IO.Path.GetExtension(outputFileName); String tmpPTH = System.IO.Path.GetDirectoryName(outputFileName); String tmpFN = System.IO.Path.GetFileName(outputFileName); String tmpFNNoExt = System.IO.Path.GetFileNameWithoutExtension(outputFileName); var files = new HashSet(Directory.GetFiles(tmpPTH, "*.pdf")); //string baseName = Path.Combine(scpath, "Screenshot_"); string filename; int i = 0; do { filename = tmpPTH + @"\" + tmpFNNoExt + "_" + ++i + ".pdf"; } while (files.Contains(filename)); outputFileName = filename; } // B2024-031 if doing PDF hyperlinks (Create RO and Transition Hyperlinks) then try to // delete the old PDF file before creating the new one if (SaveLinks) File.Delete(outputFileName); } writer = PdfWriter.GetInstance(document, new FileStream(outputFileName, FileMode.Create)); // PDFA1B does not allow layers, so this is disabled for now // If enabled, CreateLayers will need to be skipped. //writer.PDFXConformance = PdfWriter.PDFA1B; } catch (Exception ex) { StringBuilder sb = new StringBuilder(); sb.AppendLine("Could not create"); sb.AppendLine(); sb.AppendLine(outputFileName + "."); sb.AppendLine(); sb.AppendLine("If it is open, close and retry."); MessageBox.Show(sb.ToString(), "Error on CreatePdf", MessageBoxButtons.OK, MessageBoxIcon.Warning); //MessageBox.Show("Could not create " + outputFileName + ". If it is open, close and retry.", "Error on CreatePdf"); return MyContentByte = null; } document.Open(); // Create Layers CreateLayers(writer.DirectContent); MSWordToPDF.DebugStatus = DebugOutput ? 1 : 0; PrintOverride.Reset(); if (DebugOutput) { PrintOverride.TextColor = System.Drawing.Color.Red; PrintOverride.SvgColor = System.Drawing.Color.LawnGreen; PrintOverride.BoxColor = System.Drawing.Color.Red; PrintOverride.ChangeBarColor = System.Drawing.Color.Red; PrintOverride.DebugColor = System.Drawing.Color.CadetBlue; } MyContentByte = writer.DirectContent; return MyContentByte; } catch (Exception ex) { StringBuilder sb = new StringBuilder(); sb.AppendLine("Error occured when creating Print PDF"); sb.AppendLine(); sb.AppendLine(outputFileName + "."); sb.AppendLine(); sb.AppendLine("If it is open, close and retry."); MessageBox.Show(sb.ToString(), "Error Creating PDF", MessageBoxButtons.OK, MessageBoxIcon.Warning); //MessageBox.Show("Could not create " + outputFileName + ". If it is open, close and retry.", "Error on CreatePdf"); return MyContentByte = null; } } //private string CreateFileName(string procNumber, string sectNumber, string sectTitle) //{ // return FixFileName(procNumber + "_" + ((sectNumber ?? "") != "" ? sectNumber : sectTitle)); //} //private string CreateFileName(string procNumber) //{ // return FixFileName(procNumber); //} //private string FixFileName(string name) //{ // return Regex.Replace(name, "[ .,/]", "_") + ".pdf"; //} int _StepPageNumber = 0; private VlnSvgPageHelper _MyHelper = null; //private static PdfReader _MyFoldoutReader = null; //private static SectionInfo _MyFoldoutSection = null; private static List _MyFoldoutReader = null; public static List MyFoldoutReader { get { return PromsPrinter._MyFoldoutReader; } set { PromsPrinter._MyFoldoutReader = value; } } private static List _MyFoldoutSection = null; private string Print(ProcedureInfo myProcedure, string pdfFolder, bool makePlacekeeper, bool makeContinuousActionSummary, bool makeTimeCriticalAction) { if (_ForcedPaginations != null) _ForcedPaginations.Clear(); int profileDepth = ProfileTimer.Push(">>>> PromsPrinter.Print"); if (_TransPageNumProblems == null) _TransPageNumProblems = new List(); if (_MyFoldoutReader != null) _MyFoldoutReader.Clear(); else _MyFoldoutReader = new List(); if (_MyFoldoutSection != null) _MyFoldoutSection.Clear(); else _MyFoldoutSection = new List(); VlnItextFont.RegisterFontFolder(); //B2019-174 register the font folder so that symbols will display on auto TOC if (myProcedure.Sections != null) { int cnt = 0; foreach (SectionInfo mySection in myProcedure.Sections) { if (PrtSectID == -1 || (PrtSectID > -1 && mySection.ItemID == PrtSectID)) { if (!mySection.MyDocStyle.IsStepSection && !mySection.IsAutoTOCSection) { VEPROMS.CSLA.Library.Document.ConvertWordSectionToDOCX((ItemInfo)mySection); // B2023-093 Convert a Word section to the DOCX Word format if needed before printing } //C2019-042 Section_IsFoldout checks Section Number, Section Title, and use of check box if ((myProcedure.ActiveFormat.PlantFormat.FormatData.PrintData.SectionLevelFoldouts && (mySection.MyConfig as SectionConfig).Section_IsFoldout == "Y") || (myProcedure.ActiveFormat.PlantFormat.FormatData.PrintData.AlternateFloatingFoldout && (mySection.MyConfig as SectionConfig).Section_IsFoldout == "Y")) { // if floating foldouts, need a list of sections & foldoutreaders. Just do first for now. _MyFoldoutSection.Add(mySection); string foldoutPdf = PrintProcedureOrFoldout(myProcedure, mySection, Volian.Base.Library.VlnSettings.TemporaryFolder + @"\Foldout" + cnt.ToString() + @".pdf", false, false, false); _MyFoldoutReader.Add(foldoutPdf != null ? new PdfReader(foldoutPdf) : null); cnt++; } } } } OnStatusChanged("Print " + myProcedure.DisplayNumber, PromsPrinterStatusType.Start); string outputFileName = pdfFolder + "\\" + Prefix + PDFFile; // RHM20150506 Multiline ItemID TextBox string retval = PrintProcedureOrFoldout(myProcedure, null, outputFileName, makePlacekeeper, makeContinuousActionSummary, makeTimeCriticalAction); ProfileTimer.Pop(profileDepth); return retval; } // See if the last non Foldout Section has a Final Message. private int GetFinalMessageSectionID(ProcedureInfo myProcedure, bool doingFoldout) { if (!doingFoldout) { // Work backwards because we don't want to include any foldouts. Find the last section that is not // a foldout. If it has a final message, this is the 'last section' and should have a final message. // If it doesn't have a final message, then none should be printed. int cntSect = myProcedure.Sections.Count; for (int i = cntSect - 1; i >= 0; i--) { SectionInfo mySection = myProcedure.Sections[i] as SectionInfo; if (!((mySection.MyConfig as SectionConfig).Section_IsFoldout == "Y")) //C2019-042 Section_IsFoldout checks Section Number, Section Title, and use of check box { if (mySection.MyDocStyle.Final != null && mySection.MyDocStyle.Final.Message != null && mySection.MyDocStyle.Final.Message.Length > 0) return mySection.ItemID; else return -1; } } } return -1; } private bool _AllowAllWatermarks = false; public bool AllowAllWatermarks { get { return _AllowAllWatermarks; } set { _AllowAllWatermarks = value; } } private string _WatermarkOverrideColor = "BLUE"; // C2021-019: Override watermark color public string WatermarkOverrideColor { get { return _WatermarkOverrideColor; } set { _WatermarkOverrideColor = value; } } //private PdfReader readerWord; private string PrintProcedureOrFoldout(ProcedureInfo myProcedure, SectionInfo myFoldoutSection, string outputFileName, bool makePlacekeeper, bool makeContinuousActionSummary, bool makeTimeCriticalActionSummary) { int profileDepth = ProfileTimer.Push(">>>> PrintProcedureOrFoldout"); Rtf2Pdf.TextAtCounter = 0; bool doingFoldout = myFoldoutSection != null; // B2023-024 PROMS was using old cached PDF data when printing Word sections. if (doingFoldout) { PdfInfo.RemovePDFFromCache((ItemInfo)myFoldoutSection); // remove cached PDF info to force getting of new data if (File.Exists(outputFileName)) File.Delete(outputFileName); // delete the temporary FOLDOUT file } // The following line accounts for 16bit OverrideLeftMargin when the 'Absolute' attribute is used in the genmac. // We don't want to use the OverrideLeftMargin when 'Absolute' is used in the genmac. // It's set here because the Volian.Svg.Library cannot call back to Volian.Print.Library to get this value. Svg.Library.Svg.AbsoluteOffset = new PointF(2 * 72F / (float)myProcedure.ActiveFormat.PlantFormat.FormatData.Font.CPI, -9.5F); // Create an MSWord Pdf // Setup a pdf Document for printing OnStatusChanged("Before OpenDoc", PromsPrinterStatusType.Before); iTextSharp.text.Rectangle rct = PDFPageSize.UsePaperSize(myProcedure.ActiveFormat.PlantFormat.FormatData.PDFPageSize.PaperSize); // C2020-002 paper size is now set in the format files if (myProcedure.Sections != null && !myProcedure.Sections[0].IsStepSection && !doingFoldout) // B2019-166 also don't do this if doing a foldout else it will use the margins set in word { SectionInfo si = myProcedure.Sections[0] as SectionInfo; SectionConfig sc = si.MyConfig as SectionConfig; bool wordMargins = (sc != null && sc.Section_WordMargin == "Y"); if (wordMargins) { string pdfFile = BuildMSWordPDF(si); try { PdfReader reader = new PdfReader(pdfFile); rct = reader.GetPageSizeWithRotation(1); } catch (Exception ex) { MessageBox.Show(ex.Message, ex.GetType().FullName, MessageBoxButtons.OK, MessageBoxIcon.Warning); _MyLog.Warn(string.Format("Error of ReadPDf [{0}],{1}.{2}", si.ItemID, si.DisplayNumber, si.DisplayText), ex); throw new Exception("Error in readPDF", ex); } } } PdfContentByte cb = OpenDoc(ref outputFileName, rct); if (cb == null) { ProfileTimer.Pop(profileDepth); return null; } SetupProperties(cb.PdfDocument, myProcedure); OnStatusChanged("Before NewPage", PromsPrinterStatusType.Before); NewPage(); // Start of print //_MyLog.InfoFormat("NewPage 1 {0}", cb.PdfWriter.CurrentPageNumber); OnStatusChanged("After NewPage", PromsPrinterStatusType.NewPage); if (myProcedure.Sections == null) { MessageBox.Show("This procedure has no content and will not be printed.", "Empty Procedure", MessageBoxButtons.OK, MessageBoxIcon.Information); ProfileTimer.Pop(profileDepth); // B2024-062 Added check for EmptyProcedure. This is to prevent the Try Again message // from appearing after the user clicks on the OK button from the Empty Procedure message _EmptyProcedure = true; return null; } OnStatusChanged(myProcedure.DisplayNumber, PromsPrinterStatusType.ProgressSetup, myProcedure.Sections.Count); int progress = 0; int finalMessageSectionID = GetFinalMessageSectionID(myProcedure, doingFoldout); string LastFmtName = null; int lastDocStyle = -1; bool firstStepSec = true; // C2021-019: Override the watermark color, stored in the PSI - color list set as combobox in PSI with list of colors ProcedureConfig pc = myProcedure.MyConfig as ProcedureConfig; WatermarkOverrideColor = _WatermarkColor; if (pc != null) { string clr = pc.GetValue("PSI", "WATERMARKOVERRIDECLR"); if (clr != null && clr != "") WatermarkOverrideColor = clr.ToUpper().Contains("DEFAULT") ? "BLUE" : clr; } foreach (SectionInfo mySection in myProcedure.Sections) { if(PrtSectID == -1 || (PrtSectID > -1 && mySection.ItemID == PrtSectID) ) { // B2020-115 Calculate maximum available space on a page for figures vlnParagraph.hMax = ((float)mySection.MyDocStyle.Layout.PageLength); vlnParagraph.wMax = ((float)mySection.MyDocStyle.Layout.PageWidth) - ((float)mySection.MyDocStyle.Layout.LeftMargin - 12); NeedSupInfoBreak = true; bool isFoldoutSection = (mySection.MyConfig as SectionConfig).Section_IsFoldout == "Y"; //C2019-042 Section_IsFoldout checks Section Number, Section Title, and use of check box if (((isFoldoutSection && myProcedure.ActiveFormat.PlantFormat.FormatData.PrintData.SectionLevelFoldouts) || (myProcedure.ActiveFormat.PlantFormat.FormatData.PrintData.AlternateFloatingFoldout && isFoldoutSection)) != doingFoldout) continue; if (myFoldoutSection != null && myFoldoutSection.ItemID != mySection.ItemID) continue; PrintOverride.CompressSuper = mySection.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.CompressHPSuper; PrintOverride.CompressSub = mySection.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.CompressHPSub; PrintOverride.CompressPropSubSup = mySection.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.CompressPropSubSup; OnStatusChanged((mySection.DisplayNumber ?? "") == "" ? mySection.DisplayText : mySection.DisplayNumber, PromsPrinterStatusType.Progress, progress++); // Set up Helper for the particular Section if (_MyHelper == null) { LastFmtName = mySection.ActiveFormat.Name; lastDocStyle = (int)mySection.MyDocStyle.Index; string hlsText = ""; int hlsItemId = 0; if (mySection.IsStepSection && mySection.Steps != null && mySection.Steps.Count > 0) { hlsItemId = mySection.Steps[0].ItemID; hlsText = mySection.Steps[0].DisplayText; // save the High level step text for use in the page list } cb.PdfWriter.PageEvent = _MyHelper = new VlnSvgPageHelper(mySection, this, hlsText, hlsItemId, PrtSectID); _MyHelper.AllowAllWatermarks = AllowAllWatermarks; _MyHelper.MyPdfWriter = cb.PdfWriter; _MyHelper.CreatingFoldoutPage = doingFoldout; _MyHelper.CreatingSupInfoPage = false; _MyHelper.MyPdfContentByte = cb; if (!mySection.IsStepSection) _MyHelper.PageBookmarks.Add((ItemInfo)mySection, ((mySection.DisplayNumber ?? "") == "" ? "" : mySection.DisplayNumber + " - ") + mySection.DisplayText, null); if (DebugOutput) { // 16-bit background string procedureFileName = BackgroundFolder + "\\" + PDFFile.Trim();//CreateFileName(myProcedure.DisplayNumber); FileInfo VEPromsFile = new FileInfo(procedureFileName); if (VEPromsFile.Exists && !doingFoldout) { _MyHelper.BackgroundFile = procedureFileName; float x = 0; float y = 0; if (!(mySection.ActiveFormat.Name.ToUpper().StartsWith("WST") || _MyHelper.Back32BitPROMS)) // compare folder contains PROMS generated PDF { // X value below = 16-bit pdf OverrideLeftMargin = -2 characters at the plant format's default Font's // characters per inch. // 16bit OverrideLeftMargin, defined as -2 in driver\drvin.rtf, - override took 2 characters out, so we're adding it back in: // These values adjust if overlay is 16bit versus 32bit. This is // used so that the comparison is an exact match. Use 0,0 if comparing to another 32bit // pdf, i.e. won't process the following 2 lines of code. x = 2 * 72F / (float)myProcedure.ActiveFormat.PlantFormat.FormatData.Font.CPI; y = -9.5F; } x += 72F * VlnSettings.GetCommandFloat("X", 0); y -= 72F * VlnSettings.GetCommandFloat("Y", 0); _MyHelper.BackgroundOffset = new PointF(x, y); _MyHelper.BackgroundPageOffset = 0; } _MyHelper.WatermarkLayer = _WatermarkLayer; _MyHelper.PageListLayer = _PagelistLayer; _MyHelper.TextLayer = _TextLayer; _MyHelper.BackgroundLayer = _BackgroundLayer; _MyHelper.DebugLayer = _DebugLayer; _MyHelper.PROMSVersionLayer = _PROMSVersionLayer; //C2018-009 print PROMS version } _MyHelper.Rev = _Rev; _MyHelper.Watermark = _Watermark; // C2019-039 Remove automatic zoom on Overlay PDFs //_MyHelper.DoZoomOMatic = DebugOutput; _MyHelper.OriginalPageBreak = OriginalPageBreak; _MyHelper.PROMSVersion = _PromsVersion; //C2018-009 print PROMS version _MyHelper.WatermarkColor = WatermarkOverrideColor; // C2021-019: save the override for the watermark color OnStatusChanged("After Set PageEvent", PromsPrinterStatusType.SetPageEvent); } else { //added by jcb to reset mygaps when changing a section. found old section gaps were carrying over to new section _MyHelper.MyGaps.Clear(); //Console.WriteLine("'{0}' PromsPrinter", mySection.DisplayText); // if pagination is separate or we've changed the format, we have not done the first page // (if this format has the useonfirst page docstyle flag) if (LastFmtName != mySection.ActiveFormat.Name || lastDocStyle != (int)mySection.MyDocStyle.Index || mySection.SectionConfig.Section_Pagination == SectionConfig.SectionPagination.Separate) { _MyHelper.DidFirstPageDocStyle = false; lastDocStyle = (int)mySection.MyDocStyle.Index; LastFmtName = mySection.ActiveFormat.Name; } if (!mySection.IsStepSection) _MyHelper.PageBookmarks.Add((ItemInfo)mySection, ((mySection.DisplayNumber ?? "") == "" ? "" : mySection.DisplayNumber + " - ") + mySection.DisplayText, null); _MyHelper.MySection = mySection; //OnStatusChanged("After Set Svg", PromsPrinterStatusType.SetSVG); } // if this format uses phonelists, see if this section has one. We need to know the number //of lines to adjust the pagelength for pagination and printing. _MyHelper.PhoneListHeight = 0; if (mySection.ActiveFormat.PlantFormat.FormatData.SectData.PrintPhoneList) { // get config for section SectionConfig sc = mySection.MyConfig as SectionConfig; if (sc.Section_PhoneList != null && sc.Section_PhoneList == "Y") { DocVersionConfig dvc = mySection.MyDocVersion.MyConfig as DocVersionConfig; if (dvc != null) { string phlist = dvc.Print_PhoneList; if (phlist != null && phlist != "") { // count lines: int cl = 0; int indx = phlist.IndexOf("\n"); while (indx > 0) { cl++; if (indx + 1 > phlist.Length) indx = -1; else indx = phlist.IndexOf("\n", indx + 1); } if (cl == 0) cl = 1; // phone list is a single line without an ending newline _MyHelper.PhoneListHeight = cl * vlnPrintObject.SixLinesPerInch; } } } } _MyHelper.FinalMessageSectionID = finalMessageSectionID; // set VlnSvgPageHelper with the finalMessageSectionID if (mySection.IsAutoTOCSection) { if (((cb.PdfWriter.CurrentPageNumber % 2 == 0 && !_MyHelper.CreatingFoldoutPage) || myProcedure.ProcHasSupInfoData) && InsertBlankPages) // Bug Fix: B2016-181 - add blank page if page count is even { InsertBlankPage(cb); } GenerateTOC(mySection, myProcedure, cb, _TextLayer); // B2020-102: extra page printing after auto TOC for summer. Retested for Farley and was successful also // F2018-033: Farley Auto TOC. If doing foldouts or supinfo facing pages, add a blank page after also: //if (myProcedure.ProcHasSupInfoData && InsertBlankPages) // Bug Fix: B2016-181 - add blank page if page count is even //{ // InsertBlankPage(cb); //} } else { if (mySection.IsStepSection) { if (mySection.Steps != null && mySection.Steps.Count > 0) { // get first step to send to floating foldout indx.&& MyItemInfo.FoldoutIndex>-1) ItemInfo firstStep = mySection.Steps[0]; // B2019-165 Don't print the duplex foldout (on the back of the page) if the Don't Include Duplex Foldout check box was checked in section properties if (firstStep.FoldoutIndex() > -1 && (mySection.MyPrevious == null || !((mySection.MyPrevious.MyConfig as SectionConfig).Section_DontIncludeDuplexFoldout))) DoFoldoutPage(cb, "Beginning of Step Section", _TextLayer, _MyHelper, firstStep.FoldoutIndex(), InsertBlankPages); else if (!_MyHelper.CreatingFoldoutPage && _MyFoldoutReader.Count > 0 && InsertBlankPages) { // if it is continuous pagination, don't do blank page - not sure if this is correct place for this: // C2023-001 if the previous section has the flag to not insert blank pages, // then don't insert a blank page between the previous section and this one if (mySection.SectionConfig.Section_Pagination == SectionConfig.SectionPagination.Separate && !mySection.SearchPrev.MyDocStyle.DontInsertBlankPages) { // only insert a blank page if this section does not have a foldout (but the procedure as a whole does) // and the checkbox on the print dialog to add blank pages is checked InsertBlankPage(cb); //_MyLog.InfoFormat("NewPage Begin Step Sect blank {0}", cb.PdfWriter.CurrentPageNumber); } } else { // If the procedure has supplemental facing pages, but this section does not & it's the first section, // need a blank 'facing page'. Sections after the first will get blank 'facing page' in print's pagination logic if (SupInfoPrintType == E_SupInfoPrintType.Merge && !mySection.HasSupInfoSteps && firstStepSec && InsertBlankPages) { InsertBlankPage(cb); } } } firstStepSec = false; CreateStepPdf(mySection, cb); } else { CreateWordDocPdf(cb, mySection); } } _MyHelper.PrintedSectionPage = 0; } } if (_MyHelper != null && _MyHelper.BackgroundFile != null) { _MyHelper.MySvg = null; while (cb.PdfWriter.CurrentPageNumber <= _MyHelper.BackgroundPageCount) { iTextSharp.text.Font font = FontFactory.GetFont("Arial", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 30, iTextSharp.text.Font.NORMAL, new iTextSharp.text.Color(PrintOverride.TextColor)); PrintTextMessage(cb, "No Proms Output", _TextLayer, font); NewPage(); //_MyLog.InfoFormat("NewPage 2 {0}", cb.PdfWriter.CurrentPageNumber); if (DebugPagination.IsOpen) DebugPagination.WriteLine("{0:D6},'{1}'", _MyHelper.MyPdfContentByte.PdfWriter.CurrentPageNumber, "No PROMS Output"); //C2018-015 add debug pagination to meta file if (BaselineMetaFile.IsOpen) BaselineMetaFile.WriteLine("!! {0:D6},'{1}'", _MyHelper.MyPdfContentByte.PdfWriter.CurrentPageNumber, "No PROMS Output"); } } OnStatusChanged(myProcedure.DisplayNumber + " PDF Creation Completed", PromsPrinterStatusType.Progress, progress); if (DebugPagination.IsOpen) DebugPagination.TotalPages += cb.PdfWriter.CurrentPageNumber; CloseDocument(outputFileName); if (_MyHelper != null && makePlacekeeper) { // Setting the default font to Arial since that is what Calvert is currently using for their Placekeeper pages VE_Font pkFont = new VE_Font("Arial", 11, E_Style.None, 12); // default font info. // Ideally, we should grab the font from the DocStyle used for the Placekeeper. // Note that Calvert has two Placekeeper docSyles (EOPs and AOPs) in the same format. // Both Placekeeper DocStyles use Arial 11 pt font, so there is no need to grab it from the format file. //foreach (DocStyle ds in _MyHelper.MySection.ActiveFormat.PlantFormat.DocStyles.DocStyleList) //{ // note that this will get the last Placekeeper font setting // if (ds.StructureStyle.Style == E_DocStructStyle.Placekeeper) // pkFont = ds.Font; //} if (_MyHelper.MyPlacekeepers.Count > 0) MyPlacekeeper = new Placekeeper(_MyHelper.MyPlacekeepers, pkFont); } // Generate the Continuous Action Summary if (_MyHelper != null && makeContinuousActionSummary) { // Set the font for the Continue Action Summary VE_Font pkFont = myProcedure.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.ContinuousActionSummaryData.Font; // uses font defined in the format or uses what is definced in BaseAll format if (_MyHelper.MyContActSteps.Count > 0) MyContActSummary = new ContinuousActionSummary(_MyHelper.MyContActSteps, pkFont); } // F2022-024 Generate the Time Critical Action Summary // we use the ContinuousActionSummary generator to create the Time Critical summay if (_MyHelper != null && makeTimeCriticalActionSummary) { // Set the font for the Time Critical Action Summary VE_Font pkFont = myProcedure.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.ContinuousActionSummaryData.Font; // uses font defined in the format or uses what is definced in BaseAll format if (_MyHelper.MyTimeCriticalActionSteps.Count > 0) MyTimeCriticalActSummary = new ContinuousActionSummary(_MyHelper.MyTimeCriticalActionSteps, pkFont, true); } _MyHelper = null; ProfileTimer.Pop(profileDepth); return outputFileName; } public void InsertBlankPage(PdfContentByte cb, bool backOfLastPageOfSection = false) { _MyHelper.OnBlankPage = true; string baselinemsg = "==Insert Blank Page=="; if (_BlankPageText != null && _BlankPageText != "") // C2019-004: Allow user to define duplex blank page text { if (_MyHelper.IsLandscape) // rotate the blank page message on landscaped pages { cb.SaveState(); System.Drawing.Drawing2D.Matrix myMatrix = new System.Drawing.Drawing2D.Matrix(0, 1, -1, 0, cb.PdfDocument.PageSize.Height, 0); cb.Transform(myMatrix); } string bsfontfamily = MyItem.ActiveFormat.PlantFormat.FormatData.StepDataList[0].Font.Family; int bsfontsize = 26; iTextSharp.text.Font font = FontFactory.GetFont(bsfontfamily, BaseFont.IDENTITY_H, BaseFont.EMBEDDED, bsfontsize, iTextSharp.text.Font.NORMAL, new iTextSharp.text.Color(PrintOverride.TextColor)); PrintTextMessage(cb, _BlankPageText, _TextLayer, font); if (_MyHelper.IsLandscape) cb.RestoreState(); // B2019-019: Add the blank page message to the baseline output file. baselinemsg = string.Format("==Insert Blank Page With Message \"{0}\"==", _BlankPageText); } else cb.PdfDocument.Add(new iTextSharp.text.Table(1)); NewPage(backOfLastPageOfSection); // backOfLastPageOfSection = true means that next page starts next section and to use the next section's page size Volian.Base.Library.BaselineMetaFile.WriteLine(baselinemsg); } private void SetupProperties(PdfDocument document, ProcedureInfo myProcedure) { document.AddTitle(string.Format("{0} {1}", myProcedure.DisplayNumber, myProcedure.DisplayText)); document.AddSubject(myProcedure.SearchDVPath); document.AddCreator(string.Format("{0} {1}", Application.ProductName, Application.ProductVersion)); document.AddAuthor(Volian.Base.Library.VlnSettings.UserID); } private Placekeeper _MyPlacekeeper = null; public Placekeeper MyPlacekeeper { get { return _MyPlacekeeper; } set { _MyPlacekeeper = value; } } private ContinuousActionSummary _MyContActSummary = null; public ContinuousActionSummary MyContActSummary { get { return _MyContActSummary; } set { _MyContActSummary = value; } } //F2022-024 Time Critical Action Summary private ContinuousActionSummary _MyTimeCriticalActSummary = null; public ContinuousActionSummary MyTimeCriticalActSummary { get { return _MyTimeCriticalActSummary; } set { _MyTimeCriticalActSummary = value; } } public void CreateWordDocPdf(PdfContentByte cb, SectionInfo mySection) { if (mySection.PageNumber == -1) // If page num transition goes to a section, need the pagenumber of section. { if ((mySection.MyDocStyle.StructureStyle.Style & E_DocStructStyle.DontCountFoldoutPgs) == E_DocStructStyle.DontCountFoldoutPgs) mySection.PageNumber = _MyHelper.CurrentPageNumberNoFoldouts; else mySection.PageNumber = _MyHelper.CurrentPageNumber; } else { if ((mySection.MyDocStyle.StructureStyle.Style & E_DocStructStyle.DontCountFoldoutPgs) == E_DocStructStyle.DontCountFoldoutPgs) mySection.PageNumberNextPass = _MyHelper.CurrentPageNumberNoFoldouts; else mySection.PageNumberNextPass = _MyHelper.CurrentPageNumber; } int profileDepth = ProfileTimer.Push(">>>> CreateWordDocPdf"); if (_MyHelper.MySection != mySection) // C2018-004 create meta file for baseline compares - assigning MySection again will reprocess the Page List _MyHelper.MySection = mySection; try { OnStatusChanged("Get Section", PromsPrinterStatusType.GetSection); int sectPageCount = 0; float locEndOfWordDoc = 0; float pdfSize = 0; using (PdfInfo myPdf = PdfInfo.Get(mySection, false)) { if (myPdf == null) // B2017-218 Handle invalid word sections { cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED), 20); cb.SetTextMatrix(cb.PdfDocument.Left + 200, cb.PdfDocument.Bottom + cb.PdfDocument.Top / 2); cb.ShowText("Word Section Invalid"); // C2018-004 create meta file for baseline compares Volian.Base.Library.BaselineMetaFile.WriteLine("!!Word Section Invalid: ID {0} \"{1}\" \"{2}\"", mySection.ItemID, mySection.DisplayNumber, mySection.DisplayText); } else { sectPageCount = (int)(Math.Ceiling(myPdf.PageCount)); locEndOfWordDoc = (float)(myPdf.PageCount - (sectPageCount - 1)) * 100; pdfSize = (float)myPdf.PageCount; } } string tocKey = string.Format("TOC{0}", mySection.ItemID); if (_MyHelper.MyTOCPageCounts.ContainsKey(tocKey)) { PageCount pc = _MyHelper.MyTOCPageCounts[tocKey]; // add 1 because have not hit end page yet. pc.Total = _MyHelper.CurrentTOCPageNumber + 1; pc.DrawTemplates(); } else { // see if this section will be added to the table of content. This is for sections that // precede tha actual Automated TOC section. SectionConfig sc = mySection.MyConfig as SectionConfig; if (sc != null && sc.Section_TOC == "Y") { string tockey = "TOC" + mySection.ItemID.ToString(); if (!_MyHelper.MyTOCPageNums.ContainsKey(tockey)) // increment the CurrentTOCPageNumber by 1 since we haven't hit an // endpage yet, and that's what increments it to the current page number. _MyHelper.MyTOCPageNums.Add(tockey, _MyHelper.CurrentTOCPageNumber + 1); } } float origYoff = (float)mySection.MyDocStyle.Layout.TopMargin; bool resetSvgForCont = true; for (int ii = 0; ii < sectPageCount; ii++) { int pageNumber = 1 + ii; bool didFoldout = false; if (((mySection.MyDocStyle.StructureStyle.Style ?? 0) & E_DocStructStyle.UseSectionFoldout) != 0) { // B2022-009: if doing a foldout & this is a landscape section, turn landscape off for the foldout if (mySection.MyDocStyle.LandscapePageList) _MyHelper.IsLandscape = false; didFoldout = true; DoFoldoutPage(cb, "Word Document", _TextLayer, _MyHelper, 0, false); if (mySection.MyDocStyle.LandscapePageList) _MyHelper.IsLandscape = true; // B2022-009: turn landscape back on for the foldout } if (this.MyReaderHelper != null) { bool doimport2 = true; PdfImportedPage fgPage = null; try { fgPage = this.MyReaderHelper.GetPage(mySection, ii + 1); } catch (Exception ex) { Console.WriteLine(ex); doimport2 = false; } OnStatusChanged("Read MSWord", PromsPrinterStatusType.ReadMSWord); if (doimport2) { if (((!didFoldout && _MyFoldoutReader.Count > 0) || mySection.MyProcedure.ProcHasSupInfoData) && cb.PdfWriter.CurrentPageNumber > 1 && InsertBlankPages) { bool doInsertBlankPage = true; SectionInfo currentSection = _MyHelper.MySection; iTextSharp.text.Rectangle curRecSize = cb.PdfDocument.PageSize; // save current PDF Doc Page Size // only insert a blank page if this section does not have a foldout (but the procedure as a whole does) // and the checkbox on the print dialog to add blank pages is checked if (mySection.MyDocStyle.DontInsertBlankPages) { // C2023-001 we are going to print the first page of a section that we don't insert blank pages // but we do need to insert a blank page for the back of the previous section's last page doInsertBlankPage = (pageNumber == 1); } if (doInsertBlankPage) { // get the current and previous section page size // pass in whether the page sizes are different so when a new page is started // we use the correct page size SectionInfo prevSection = (mySection.MyPrevious != null) ? (SectionInfo)mySection.MyPrevious : currentSection; iTextSharp.text.Rectangle prevRecSize = MyReaderHelper.GetSize(prevSection, 1); curRecSize = MyReaderHelper.GetSize(currentSection, 1); if (prevSection != currentSection) { _MyHelper.MySection = prevSection; // make sure the back page that we are inserting is the correct page size } InsertBlankPage(cb, (prevRecSize != curRecSize)); if (prevSection != currentSection) { _MyHelper.MySection = currentSection; } } //_MyLog.InfoFormat("NewPage 3 blank {0}", cb.PdfWriter.CurrentPageNumber); } float yoff = 0; if (_MyHelper.DidFirstPageDocStyle) yoff = origYoff - (float)mySection.MyDocStyle.Layout.TopMargin; // C2018-004 create meta file for baseline compares Volian.Base.Library.BaselineMetaFile.WriteLine("WD Height={0} Width={1} scPgCnt={2} locEnd={3} pdfSz={4} xOff={5} yOff={6} ScPgNum {7}", fgPage.Height, fgPage.Width, sectPageCount, locEndOfWordDoc, pdfSize, (float)(mySection.MyDocStyle.Layout.MSWordXAdj ?? 0.0), (float)(mySection.MyDocStyle.Layout.MSWordYAdj ?? 0.0) + yoff, pageNumber); AddImportedPageToLayer(cb.PdfWriter.DirectContent, _MSWordLayer, fgPage, (float)(mySection.MyDocStyle.Layout.MSWordXAdj ?? 0), (float)(mySection.MyDocStyle.Layout.MSWordYAdj ?? 0) + yoff); // B2019-102 Handle PDF Destinations for Word Sections if (ii == 0 && _MyHelper.MyPromsPrinter.SaveLinks) { ColumnText ct = new ColumnText(cb); iTextSharp.text.Font font = FontFactory.GetFont("Arial", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12, iTextSharp.text.Font.NORMAL, new iTextSharp.text.Color(PrintOverride.TextColor)); Chunk chk = new Chunk(mySection.DisplayNumber, font); chk.SetLocalDestination(string.Format("ItemID={0}", mySection.ItemID)); //Console.WriteLine("++++ ItemID={0}", mySection.ItemID); //float x1 = cb.PdfDocument.PageSize.Width / 2; //float y1 = cb.PdfDocument.PageSize.Height ; // 1.8f; float height = 12 * 1.5F; // B2019-102 Locate the chunk below the bottom of the page ct.SetSimpleColumn(0, -height, chk.GetWidthPoint() * 1.01F, -2 * height); ct.AddElement(new Phrase(chk)); cb.SetColorFill(new iTextSharp.text.Color(PrintOverride.TextColor)); int status = ct.Go(); if (status > 1) _MyLog.WarnFormat("\r\n.-.-.-. Failed to add a PDF destination for {0}", mySection.ShortPath); } if (DebugPagination.IsOpen) DebugPagination.WriteLine("{0:D6},'{1}',{2}", _MyHelper.MyPdfContentByte.PdfWriter.CurrentPageNumber, mySection.ShortPath, pageNumber); } if (ii == sectPageCount - 1) { // if there's and end message, add it to the appropriate location on the last page of // the word document: if ((mySection.MyDocStyle.End.Message ?? "") != "") { float ylocation = cb.PdfDocument.PageSize.Height - ((float)mySection.MyDocStyle.Layout.TopMargin + locEndOfWordDoc * 72); // 72 - pts per inch. iTextSharp.text.Font fnt = VolianPdf.GetFont(mySection.MyDocStyle.End.Font.WindowsFont); fnt.Color = new iTextSharp.text.Color(PrintOverride.OverrideTextColor(System.Drawing.Color.Black)); // F2022-017: For Word Document sections, allow the message to have the '{Section Number}' token that // gets resolved by the section's DisplayNumber. string myMsg = mySection.MyDocStyle.End.FixedMessage; if (myMsg.Contains("{Section Number}")) myMsg = myMsg.Replace("{Section Number}", mySection.DisplayNumber).Trim(); iTextSharp.text.Paragraph para = new Paragraph(myMsg, fnt); float wtpm = (float)mySection.MyDocStyle.Layout.PageWidth - (float)mySection.MyDocStyle.Layout.LeftMargin; float centerpos = (float)mySection.MyDocStyle.Layout.LeftMargin + (wtpm - (myMsg.Length * (float)mySection.MyDocStyle.End.Font.CharsToTwips)) / 2; float yBottomMargin = Math.Max(0, (float)mySection.MyDocStyle.Layout.TopMargin - (float)mySection.MyDocStyle.Layout.PageLength - 2 * vlnPrintObject.SixLinesPerInch); float adjMsgY = 0; if ((float)mySection.MyDocStyle.End.Flag < 0) // Adjust this many lines down the page. { adjMsgY = (float)(-(float)mySection.MyDocStyle.End.Flag * vlnPrintObject.SixLinesPerInch); if (ylocation - adjMsgY > mySection.MyDocStyle.Layout.FooterLength) ylocation = ylocation - adjMsgY; } bool landscape = (mySection.MyDocStyle.StructureStyle.Style & E_DocStructStyle.DSS_WordContentLandscaped) == E_DocStructStyle.DSS_WordContentLandscaped; if (landscape) { cb.SaveState(); System.Drawing.Drawing2D.Matrix myMatrix = new System.Drawing.Drawing2D.Matrix(0, 1, -1, 0, cb.PdfDocument.PageSize.Height, 0); cb.Transform(myMatrix); } Rtf2Pdf.TextAt(cb, para, centerpos, ylocation + 6, 200, 12, "", yBottomMargin); if (landscape) cb.RestoreState(); } } OnStatusChanged("Merge MSWord", PromsPrinterStatusType.MergeMSWord); } OnStatusChanged("Before NewPage", PromsPrinterStatusType.Before); if (mySection.MyDocStyle.LandscapePageList)// && mySection.MyDocStyle.Layout.PageWidth > mySection.MyDocStyle.Layout.PageLength) _MyHelper.IsLandscape = true; else _MyHelper.IsLandscape = false; // C2023-001 put in for Beaver Valley, DontInsertBlankPages is use on their CAS sections, which inlucde a hard page break // with text saying that page folds out. Some procedures also have a PROMS foldout section that is printed on the // back of the procedure step pages. When we are printing duplex with blank pages, we don't want to insert a blank // page between the two pages of the CAS section // "pageForNextSection" is used to tell NewPage() if it needs to check the page size of the next section before starting the // next page - this fixes an issue where the page we inserted for the backside of the previous section was the larger page // size (11x17 vs 8x11) bool pageForNextSection = (ii == sectPageCount - 1 && !InsertBlankPages); _MyHelper.SpecialCAS = false; if (mySection.MyDocStyle.DontInsertBlankPages && ii == sectPageCount - 1) // C2023-001 beaver valley CAS section type (has hard page break for back of page) { pageForNextSection = true; _MyHelper.SpecialCAS = true; // B2023-005: Beaver Valley - flag to rotate watermark on 2nd CAS page } NewPage(pageForNextSection); // Word Document //_MyLog.InfoFormat("NewPage 3 {0}", cb.PdfWriter.CurrentPageNumber); // if this document style has another style that is for pages other than first, we need to // reset the document style off of this section AND reset docstyle values used. _MyHelper.DidFirstPageDocStyle = true; // Calvert has a case in their stp landscape word docs where a ' (Continued)' message // appears in their pagelist item for printing the section title, {ATTACHTITLECONT} pagelist // However, other sections use this token, but do NOT have the continue message - so // the docstyle was added to flag the difference between them (STP-Landscape Attachments has it, // STP-Attachments do not) if (resetSvgForCont && (mySection.MyDocStyle.StructureStyle.Style & E_DocStructStyle.DSS_PageListAddSectCont) == E_DocStructStyle.DSS_PageListAddSectCont) { _MyHelper.ResetSvg(); resetSvgForCont = false; // only need to reset it once (for all pages after 1st page) } //DebugPagination.WriteLine("CreateWordDocPdf"); if ((mySection.MyDocStyle.StructureStyle.Where & E_DocStyleUse.UseOnFirstPage) > 0) _MyHelper.MySection = mySection; // this resets the docstyle/pagestyle in pagehelper OnStatusChanged("After NewPage", PromsPrinterStatusType.NewPage); } } catch (Exception ex) { NewPage(); // can we put out 'error on page'? _MyLog.InfoFormat("NewPage error on page {0}", cb.PdfWriter.CurrentPageNumber); } finally { _MyHelper.IsLandscape = false; // reset landscape mode to false } ProfileTimer.Pop(profileDepth); } // B2019-152: AddMergedLandscapePage adds entries to the dictionary that keeps track of what pages in a pdf are landscape // so that if merge is done, the pages that are landscaped can have landscaped page numbers placed on them public static void AddMergedLandscapePage(VlnSvgPageHelper _MyHelper, string PDFFile) { string tmp = PDFFile.Substring(0, PDFFile.IndexOf(".pdf")); if (MergedLandscapePages == null) MergedLandscapePages = new Dictionary>(); if (MergedLandscapePages.ContainsKey(tmp)) MergedLandscapePages[tmp].Add(_MyHelper.CurrentPageNumber); else { // Create the list & add this to dictionary, pdf file with page list. List children = new List(); children.Add(_MyHelper.CurrentPageNumber); MergedLandscapePages.Add(tmp, children); } } private void GenerateTOC(SectionInfo tocSection, ProcedureInfo myProcedure, PdfContentByte cb, PdfLayer textLayer) { iTextSharp.text.pdf.PdfWriter writer = cb.PdfWriter; float _PointsPerPage = PDFPageSize.PaperSizePoints(myProcedure.ActiveFormat.PlantFormat.FormatData.PDFPageSize.PaperSize); // C2020-002 paper size is now set in the format files float yTopMargin = _PointsPerPage - (float)tocSection.MyDocStyle.Layout.TopMargin; float yBottomMargin = Math.Max(0, yTopMargin - (float)tocSection.MyDocStyle.Layout.PageLength); // - 2 * vlnPrintObject.SixLinesPerInch); if (textLayer != null) cb.BeginLayer(textLayer); TableOfContentsData tOfC = tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData; //ItemInfo procItem = ItemInfo.Get(myProcedure.ItemID); ItemInfo procItem = myProcedure; lastyLocation = 0; AddSectionToTOC(tocSection, procItem, tOfC, cb, yTopMargin, 0, false); // add all of the marked sections and sub-sections to the table of contents if (textLayer != null) cb.EndLayer(); NewPage(); //_MyLog.InfoFormat("NewPage 4 {0}", cb.PdfWriter.CurrentPageNumber); _NoBreakYOffset = 0; } float lastyLocation = 0; private string GetRtfToC(string txt, TableOfContentsData tOfCData) { return GetRtfToC(txt, tOfCData, null); } private string GetRtfToC(string txt, TableOfContentsData tOfCData, VE_Font overrideFont) { StringBuilder _RtfSB = new StringBuilder(); VEPROMS.CSLA.Library.DisplayText toctxt = new VEPROMS.CSLA.Library.DisplayText(txt, tOfCData.Font, false); System.Drawing.Font myFont = toctxt.TextFont.WindowsFont; if (overrideFont != null) { toctxt = new VEPROMS.CSLA.Library.DisplayText(txt, overrideFont, false); myFont = overrideFont.WindowsFont;//toctxt.TextFont.WindowsFont; } _RtfSB.Append(vlnPrintObject.AddFontTable(myFont)); _RtfSB.Append(toctxt.StartText); _RtfSB.Append("}"); return _RtfSB.ToString(); } private float AddSectionToTOC(SectionInfo tocSection, ItemInfo ii, TableOfContentsData tOfC, PdfContentByte cb, float yPageStart, float yLocation, bool didSubSecLineSpacing) { // The following adjustments are for WCN1: // for xAdjNumber: 6 is for 1 char less in 16bit (\promsnt\exe\print\tabocont.c, method 'howMany'. // 1.2 is a tweak to get output to match between 16 & 32 bit. // for xAdjTitle: .8 & 1.2 are both tweaks to get output to match. // for xAdjTitleIndent: 6 is for 1 char less. 2.4 is a tweak float xAdjNumber = -6 + 1.2F; float xAdjTitle = .8F + 1.2F; float xAdjTitleIndent = -6 + 2.4F; float yadj = 0.5F; // tweak to get 16 & 32 bit output to match. float yPageStartAdj = yPageStart - yadj; float leftMargin = (float)tocSection.MyDocStyle.Layout.LeftMargin; float secNumPos = (float)tOfC.TofCSecNumPos + xAdjNumber; float secTitlePos = (float)tOfC.TofCSecTitlePos + xAdjTitle; float secPagePos = (float)tOfC.TofCPageNumPos + xAdjNumber; float height = tOfC.Font.WindowsFont.Size * 1.5F; string lastTOCGroupHeading = ""; bool firstGroupHeading = true; if (ii.Sections != null) { int sectCnt = 0; // keep count of which section/sub-section we are processing bool doSubY = false; int level = 0; // C2018-004 create meta file for baseline compares Volian.Base.Library.BaselineMetaFile.WriteLine("TOC Ystart {0} LeftMar {1} ScNmPos {2} ScTtlPos {3} ScPgPos {4}", yPageStart, leftMargin, secNumPos, secTitlePos, secPagePos); bool inGroup = false; foreach (SectionInfo mySection in ii.Sections) { if (PrtSectID == -1 || (PrtSectID > -1 && mySection.ItemID == PrtSectID)) { sectCnt++; SectionConfig sc = mySection.MyConfig as SectionConfig; if ((mySection.MyDocStyle != null && mySection.MyDocStyle.IncludeInTOC && (sc == null || sc.Section_TOC != "Y")) || ((mySection.MyDocStyle == null || !mySection.MyDocStyle.IncludeInTOC) && (sc != null && sc.Section_TOC == "Y"))) { // for indenting of subsections, count up tree. Only start indenting // at third level, i.e. not indent on 1.0 and 1.1, but indent on 1.1.1: level = 0; ItemInfo iilvl = mySection as ItemInfo; while (!iilvl.IsProcedure) { level++; iilvl = iilvl.MyParent; } // check what level the plant wants the auto ToC indented: int tofCNumLevels = tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCNumLevels ?? 0; if (tofCNumLevels > 0 && level > tofCNumLevels) return yLocation; int startIndentAfterLevel = tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCStartIndentAfterLevel ?? 2; // level = level <= startIndentAfterLevel ? 0 : level - startIndentAfterLevel; float indentOffset = (level * (secTitlePos - secNumPos)); if (didSubSecLineSpacing && sectCnt == 1 && indentOffset > 0) // processing first sub-section and we are indenting B2016-205 { // processed last sub-section so use the main section line spacing (most times is double spaced) yLocation -= (float)(tOfC.TofCLineSpacingSub ?? 1) * vlnPrintObject.SixLinesPerInch; yLocation += (float)(tOfC.TofCLineSpacing ?? 1) * vlnPrintObject.SixLinesPerInch; } // The Group Title logic was added for V.C. Summer and Farley(new writer guide format) // It allows the user to enter a Table of Contents Grouping Title on the Section Property (under automation) // The grouping title will print if the same title wasn't last printed string tocGrpHeading = sc.Section_TOC_Group_Title; // returns a "" if no title was entered float yBottomMargin = yPageStart - (float)tocSection.MyDocStyle.Layout.PageLength + (2 * vlnPrintObject.SixLinesPerInch); bool doGroupHeading = false; if (tocGrpHeading != lastTOCGroupHeading && tocGrpHeading.Length > 0) { doGroupHeading = true; inGroup = true; // B2020-033: for single spacing, add an extra line before a grouping title: float tmpspc = (float)(tOfC.TofCLineSpacing ?? 1); if (lastTOCGroupHeading != "" || (firstGroupHeading && tmpspc == 1)) { yLocation += vlnPrintObject.SixLinesPerInch; firstGroupHeading = false; } lastTOCGroupHeading = tocGrpHeading; } // need to do the section number, section title & page number. Page number // has to be put on at end after number of page is known, so use a Template. bool didGrp = false; string tmptxt = mySection.MyContent.Number == null || mySection.MyContent.Number == "" ? " " : mySection.DisplayNumber;// B2017-019 - process "" in section number if (tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCRemoveGrpNameInSects && tocGrpHeading.Length > 0 && tmptxt.ToUpper().Contains(tocGrpHeading)) { tmptxt = tmptxt.Replace(tocGrpHeading, ""); didGrp = true; } if (tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCRemoveGrpNameInSects && lastTOCGroupHeading.Length > 0 && tmptxt.ToUpper().Contains(lastTOCGroupHeading)) { tmptxt = tmptxt.Replace(lastTOCGroupHeading, ""); didGrp = true; } string rtfText = GetRtfToC(tmptxt, tOfC); Paragraph myparagraphSecNum = vlnPrintObject.RtfToParagraph(rtfText); float width = 0; foreach (Chunk chkt in myparagraphSecNum.Chunks) width += chkt.GetWidthPoint(); float numwidth = width; Rtf2Pdf.Offset = new PointF(0, 2.5F); // if the starting column of text would be in 'middle of' the number, just put it // a few spaces after the number. The '18' below represents 3 chars. float adjSecTitlePos = secTitlePos + indentOffset + (level * 6); if (secNumPos + numwidth + indentOffset > secTitlePos + indentOffset) adjSecTitlePos = secNumPos + numwidth + 18 - xAdjTitleIndent + indentOffset; // logic put in for V.C. Summer who wants to their auto table of contents to print "OPERATOR ACTIONS" (set in the format) instead of "Procedure Steps" // - note that Summer didn't want to change the section title because they want transition to say "procedure step" for the section title. 03/08/2016 string tocSecTitle = mySection.FormattedDisplayText;// B2017-019 - process "" in section title tocSecTitle = ConvertSpecialChars(tocSecTitle, ii.ActiveFormat.PlantFormat.FormatData); // B2019-172 process symbol characters if (tocSecTitle.ToUpper() == "PROCEDURE STEPS" && tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCProcedureStepsTitle != "") tocSecTitle = tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCProcedureStepsTitle; // Do the title first since it may wrap to 2nd line and this is an issue for // doing a pagebreak, i.e. may cause a page break when the number on a single line // would not. //if (tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData if (level == 0 && tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCUnderlineFirstLevelTitle) { VE_Font ovrFont = new VE_Font(tOfC.Font.Family, (int)tOfC.Font.Size, (E_Style)tOfC.Font.Style | E_Style.Underline, (float)tOfC.Font.CPI); rtfText = GetRtfToC(tocSecTitle, tOfC, ovrFont); } else rtfText = GetRtfToC(tocSecTitle, tOfC); Paragraph myparagraphSecTitle = vlnPrintObject.RtfToParagraph(rtfText); // section title width = secPagePos - adjSecTitlePos - ((tOfC.TofCPageNumAlign.ToUpper() == "RIGHT") ? 18 : 6);// B2021-109 proper adjustment of section title width - copied from leader dots lenth calcuation below float savTitleWid = width; // for South Texas Table of Contents // if there is no section number (we put a blank in tmptxt) then move the section title over to the section number position if (doGroupHeading) // see if the group title and the first item under it will fit on the page B2016-216 { VE_Font grpingFont = new VE_Font(tOfC.Font.Family, (int)tOfC.Font.Size, (E_Style)tOfC.Font.Style | E_Style.Underline, (float)tOfC.Font.CPI); string rtfGrpingText = GetRtfToC(tocGrpHeading, tOfC, grpingFont); Paragraph myparagraphGrpTitle = vlnPrintObject.RtfToParagraph(rtfGrpingText); float w = secPagePos - 6; // F2018-033: Farley Auto TOC. If line spacing is less than 0, then spacing is double spaced if on section items (such as // Purpose, Proc Steps; and spacing is single spaced if within groups. If not less than 0, just use what is in format file. int lnaftergroup = 1; float lnsp = tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCLineSpacing ?? 0; if (lnsp < 0) lnaftergroup = ((tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCLineSpacingSub ?? 0) > 0) ? 1 : 2; float yLocAfterGrpTitleAndFirstItem = yLocation + (lnaftergroup * vlnPrintObject.SixLinesPerInch) + vlnPrintObject.GetHeight(cb, myparagraphGrpTitle, string.Empty, w, false) + vlnPrintObject.GetHeight(cb, myparagraphSecTitle, string.Empty, savTitleWid, false); // if group heading and first item under it does not fit on the page, do a page break if ((yPageStart - yLocAfterGrpTitleAndFirstItem) < yBottomMargin) { NewPage(); _MyHelper.ResetSvg(); // needed to reset so that PSNotFirst pagelist justify flag gets used for BGE yLocation = lastyLocation = 0; } //if (yLocation > 0) yLocation += vlnPrintObject.SixLinesPerInch;// commented out for B2016-200 // for two blank line between last TOC title and the new grouping title float rtnval = Rtf2Pdf.TextAt(cb, myparagraphGrpTitle, leftMargin + secNumPos, yPageStart - yLocation, w, height, "", yBottomMargin); // print the Group Title yLocation += vlnPrintObject.SixLinesPerInch; // for two blank line between last TOC title and the new grouping title - added for B2016-200 if (lnsp > -1) yLocation += (lnaftergroup * vlnPrintObject.SixLinesPerInch); // new line } // Print the section title float retval = Rtf2Pdf.TextAt(cb, myparagraphSecTitle, leftMargin + ((tOfC.TofCSecNumPos == tOfC.TofCSecTitlePos && tmptxt.Equals(" ")) ? secNumPos : adjSecTitlePos), yPageStart - yLocation, width, height, "", yBottomMargin); if (retval == 0) // couldn't fit, flags need for a page break. { NewPage(); _MyHelper.ResetSvg(); // needed to reset so that PSNotFirst pagelist justify flag gets used for BGE yLocation = lastyLocation = 0; retval = Rtf2Pdf.TextAt(cb, myparagraphSecTitle, leftMargin + adjSecTitlePos, yPageStart - yLocation, width, height, "", yBottomMargin); } float ttlRetval = retval; float savTitleFillWidth = Rtf2Pdf.FillWidth; // Now do the section number. Retval is the ylocation on page after the text // is put out. retval = Rtf2Pdf.TextAt(cb, myparagraphSecNum, leftMargin + secNumPos + indentOffset, yPageStart - yLocation, numwidth * 1.3F, height, "", yBottomMargin); //float lSpace = (float)(tOfC.TofCLineSpacing ?? 1) * vlnPrintObject.SixLinesPerInch; // adjust ylocation for pagenumber - the ylocation will get reset if the // section title split on 2 lines and the page number needs to be on the 2nd line. // Only do this if the title is not empty (the code was meant for if title spans more than a line) if (retval != ttlRetval && mySection.MyContent.Text != null && mySection.MyContent.Text != "") yLocation += (retval - ttlRetval); // retval = the minimum (further down the page) between section number and // title - this accounts for multi line title. retval = Math.Min(retval, ttlRetval); lastyLocation = retval; // check that the page number should be in the TOC (some BGE sections do not have the page number) if (!((mySection.MyDocStyle.StructureStyle.Style & E_DocStructStyle.DontNumberInTOC) == E_DocStructStyle.DontNumberInTOC)) { // add a template for the page number: // if the space character is not null & is not a space, the we've got to put out // these between the section title & the page number if (tOfC.TofCSpaceChar != null && tOfC.TofCSpaceChar != "" && tOfC.TofCSpaceChar != " ") { width = 0; foreach (Chunk chkt in myparagraphSecTitle.Chunks) width += chkt.GetWidthPoint(); // get height - if two lines high, need width of 2nd line for adding // space characters float heightTtl = vlnPrintObject.GetHeight(cb, myparagraphSecTitle, string.Empty, savTitleWid, false); string spaceStr = ""; float startSpace = 0; // the '6's in the next few code lines & in the 'while' loop below allows the placement of the dots (or other space character) // to be not too close to section title & page number. if (heightTtl > vlnPrintObject.SixLinesPerInch) { // this title is 2 or more lines, find start of leader dots by determining length of text // on 2nd line (the savTitleFillWidth is the widest line of the split title) float secondLineWidth = width - savTitleFillWidth; startSpace = leftMargin + adjSecTitlePos + secondLineWidth + 6; } else startSpace = leftMargin + adjSecTitlePos + width + 6; // If the page number is Right justified, need less space characters ('.') so that page number // doesn't have the last space character overwriting the 1st digit: int endJust = (tOfC.TofCPageNumAlign.ToUpper() == "RIGHT") ? 18 : 6; float endSpace = leftMargin + secPagePos - endJust; float numSpace = endSpace - startSpace; rtfText = GetRtfToC(tOfC.TofCSpaceChar, tOfC); Paragraph tmpmyparagraph = vlnPrintObject.RtfToParagraph(rtfText); float spacchrwid = 0; foreach (Chunk chkt in tmpmyparagraph.Chunks) spacchrwid += chkt.GetWidthPoint(); while (numSpace > 6) { spaceStr = spaceStr + tOfC.TofCSpaceChar; numSpace -= spacchrwid; } rtfText = GetRtfToC(spaceStr, tOfC); // out the leader dots Paragraph myparagraphs = vlnPrintObject.RtfToParagraph(rtfText); retval = Rtf2Pdf.TextAt(cb, myparagraphs, startSpace, yPageStart - yLocation, endSpace - startSpace, height, "", yBottomMargin); } // if in the pre-table of content list, just put the page number out as text. // otherwise, add a template _MyHelper.MyTOCPageCounts string key = "TOC" + mySection.ItemID.ToString(); if (mySection.IsAutoTOCSection || _MyHelper.MyTOCPageNums.ContainsKey(key)) { string pnum = _MyHelper.MyTOCPageNums.ContainsKey(key) ? _MyHelper.MyTOCPageNums[key].ToString() : (_MyHelper.CurrentTOCPageNumber + 1).ToString(); rtfText = GetRtfToC(pnum, tOfC); Paragraph myparagraphp = vlnPrintObject.RtfToParagraph(rtfText); width = pnum.Length * 6; retval = Rtf2Pdf.TextAt(cb, myparagraphp, leftMargin + secPagePos, yPageStart - yLocation, width, height, "", yBottomMargin); } else AddTemplateTOCPageCounts(tOfC, yLocation, yPageStartAdj, leftMargin, secPagePos, height, key); } //If the format wants single spacing within subsections (2nd level and lower), then need to figure out // spacing (vcb1 table of contents) //bool dosuby = (((tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCLineSpacingSub ?? 1) > 0) // && (mySection.MyParent.IsSection && ((mySection.Sections != null && mySection.Sections.Count > 0) || (level > 0 && mySection.NextItem != null)))) // || didGrp; doSubY = (((tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCLineSpacingSub ?? 1) > 0) && (mySection.MyParent.IsSection && (/* mySection.Sections == null && */ sectCnt < ii.Sections.Count))) // B2016-205 || didGrp; //float spc = (float)(tOfC.TofCLineSpacing ?? 1); if (doSubY) { float spcs = (float)(tOfC.TofCLineSpacingSub ?? 1); if (spcs == -1) // F2018-033: Allow for single & double spacing for Farley (if in group or not) { if (inGroup) // Do not require a title to be placed on each section in group for Farley. If in group from above, single space spcs = 1; else spcs = 2; } yLocation += (spcs * vlnPrintObject.SixLinesPerInch); } else { float spc = (float)(tOfC.TofCLineSpacing ?? 1); if (spc == -1) // F2018-033: Allow for single & double spacing for Farley (if in group or not) { if (inGroup) // Do not require a title to be placed on each section in group for Farley. If in group from above, single space spc = 1; else spc = 2; } yLocation += (spc * vlnPrintObject.SixLinesPerInch); } // C2021-015: Barakah High Level Steps in Table of Contents - check to see if we need to add the high level steps on // table of contents for this section, format flag & section has steps: if (mySection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCAllowHLS && mySection.IsStepSection && mySection.Steps != null && mySection.Steps.Count > 0) yLocation = AddHLSToTOC(tocSection, mySection, tOfC, cb, yPageStart, yLocation, doSubY, level + 1, adjSecTitlePos); } float savYLoc = yLocation; yLocation = AddSectionToTOC(tocSection, mySection, tOfC, cb, yPageStart, yLocation, doSubY); if (savYLoc == yLocation) // nothing was outputted i.e. the subsections are not included on the TOC // B2016-205 { if (doSubY && sectCnt == ii.Sections.Count) { // processed last sub-section so use the main section line spacing (most times is double spaced) yLocation -= (float)(tOfC.TofCLineSpacingSub ?? 1) * vlnPrintObject.SixLinesPerInch; yLocation += (float)(tOfC.TofCLineSpacing ?? 1) * vlnPrintObject.SixLinesPerInch; } } } } } return yLocation; } // C2021-015: Barakah High Level Steps in Table of Contents private float AddHLSToTOC(SectionInfo tocSection, ItemInfo ii, TableOfContentsData tOfC, PdfContentByte cb, float yPageStart, float yLocation, bool didSubSecLineSpacing, int level, float parTitleXOff) { // if section is not in TOC, don't add the HLSs SectionConfig sc = ii.ActiveSection.MyConfig as SectionConfig; if (!((ii.ActiveSection.MyDocStyle != null && ii.ActiveSection.MyDocStyle.IncludeInTOC && (sc == null || sc.Section_TOC != "Y")) || ((ii.ActiveSection.MyDocStyle == null || !ii.ActiveSection.MyDocStyle.IncludeInTOC) && (sc != null && sc.Section_TOC == "Y")))) return yLocation; float xAdjNumber = -6 + 1.2F; float xAdjTitle = .8F + 1.2F; float yadj = 0.5F; // tweak to get 16 & 32 bit output to match. float yPageStartAdj = yPageStart - yadj; float leftMargin = (float)tocSection.MyDocStyle.Layout.LeftMargin; float stepNumPos = (float)tOfC.TofCSecNumPos + xAdjNumber; float stepTitlePos = (float)tOfC.TofCSecTitlePos + xAdjTitle; float stepPagePos = (float)tOfC.TofCPageNumPos + xAdjNumber; float height = tOfC.Font.WindowsFont.Size * 1.5F; // C2018-004 create meta file for baseline compares Volian.Base.Library.BaselineMetaFile.WriteLine("TOC Ystart {0} LeftMar {1} ScNmPos {2} ScTtlPos {3} ScPgPos {4}", yPageStart, leftMargin, stepNumPos, stepTitlePos, stepPagePos); int tofCNumLevels = tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCNumLevels ?? 0; if (tofCNumLevels > 0 && level > tofCNumLevels) return yLocation; // don't go down more than number of levels as defined in format // figure out whether to indent. If not, set indentOffset to 0, otherwise indent number to where parent's text was (input parTitleXOff) int startIndentAfterLevel = tocSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCStartIndentAfterLevel ?? 2; // level = level <= startIndentAfterLevel ? 0 : level - startIndentAfterLevel; // The parTitleXOff is passed in, it is the xoffset for the title of the section. This is where the number should // start, if indenting. Without this, the xoffset was not correct: float indentOffset = level == 0 ? 0 : parTitleXOff; float numwidth = 0; foreach (ItemInfo hls in ii.Steps) // Ouput each HLS that has config flag to include it. { StepConfig stc = hls.MyConfig as StepConfig; if (stc != null && stc.Step_IncludeInTOC) { float yBottomMargin = yPageStart - (float)tocSection.MyDocStyle.Layout.PageLength + (2 * vlnPrintObject.SixLinesPerInch); // need to do the step number, title & page number. Page number has to be put on at end after number of page is known, so use a Template. string tmptxt = hls.MyTab.CleanText; if (tmptxt == null || tmptxt == "") tmptxt = " "; // if no number set to blanks. Without this, low level printing gets confused string rtfText = GetRtfToC(tmptxt, tOfC); Paragraph myparagraphStepNum = vlnPrintObject.RtfToParagraph(rtfText); float width = 0; foreach (Chunk chkt in myparagraphStepNum.Chunks) width += chkt.GetWidthPoint(); if (numwidth == 0) numwidth = width; // keep same xoffset for step text unless overwriting by number (see below) Rtf2Pdf.Offset = new PointF(0, 2.5F); // if the starting column of text would be in 'middle of' the number, just put it // a few spaces after the number. The '182' below represents 2 chars. float adjStepTitlePos = indentOffset + numwidth + 6; if (width + indentOffset > adjStepTitlePos) { adjStepTitlePos = numwidth + 12 + indentOffset; numwidth = width; } string tocStepTitle = hls.FormattedDisplayText; tocStepTitle = ConvertSpecialChars(tocStepTitle, ii.ActiveFormat.PlantFormat.FormatData); // B2019-172 process symbol characters rtfText = GetRtfToC(tocStepTitle, tOfC); Paragraph myparagraphStepTitle = vlnPrintObject.RtfToParagraph(rtfText); width = stepPagePos - adjStepTitlePos - ((tOfC.TofCPageNumAlign.ToUpper() == "RIGHT") ? 18 : 6);// B2021-109 proper adjustment of section title width - copied from leader dots lenth calcuation below float savTitleWid = width; // Print the step text float retval = Rtf2Pdf.TextAt(cb, myparagraphStepTitle, leftMargin + ((tOfC.TofCSecNumPos == tOfC.TofCSecTitlePos && tmptxt.Equals(" ")) ? stepNumPos : adjStepTitlePos), yPageStart - yLocation, width, height, "", yBottomMargin); if (retval == 0) // couldn't fit, flags need for a page break. { NewPage(); _MyHelper.ResetSvg(); // needed to reset so that PSNotFirst pagelist justify flag gets used for BGE yLocation = lastyLocation = 0; retval = Rtf2Pdf.TextAt(cb, myparagraphStepTitle, leftMargin + adjStepTitlePos, yPageStart - yLocation, width, height, "", yBottomMargin); } float ttlRetval = retval; float savTitleFillWidth = Rtf2Pdf.FillWidth; // Now do the step number. Retval is the ylocation on page after the text is put out retval = Rtf2Pdf.TextAt(cb, myparagraphStepNum, leftMargin + indentOffset, yPageStart - yLocation, numwidth * 1.3F, height, "", yBottomMargin); if (retval != ttlRetval) yLocation += (retval - ttlRetval); // retval = the minimum (further down the page) between step number & text - accounts for multi line step retval = Math.Min(retval, ttlRetval); lastyLocation = retval; // add a template for the page number: // if the space character is not null & is not a space, the we've got to put out // these between the section title & the page number if (tOfC.TofCSpaceChar != null && tOfC.TofCSpaceChar != "" && tOfC.TofCSpaceChar != " ") { width = 0; foreach (Chunk chkt in myparagraphStepTitle.Chunks) width += chkt.GetWidthPoint(); // get height - if two lines high, need width of 2nd line for adding // space characters float heightTtl = vlnPrintObject.GetHeight(cb, myparagraphStepTitle, string.Empty, savTitleWid, false); string spaceStr = ""; float startSpace = 0; // the '6's in the next few code lines & in the 'while' loop below allows the placement of the dots (or other space character) // to be not too close to section title & page number. if (heightTtl > vlnPrintObject.SixLinesPerInch) { // this title is 2 or more lines, find start of leader dots by determining length of text // on 2nd line (the savTitleFillWidth is the widest line of the split title) float secondLineWidth = width - savTitleFillWidth; startSpace = leftMargin + adjStepTitlePos + secondLineWidth + 6; } else startSpace = leftMargin + adjStepTitlePos + width + 6; // If the page number is Right justified, need less space characters ('.') so that page number // doesn't have the last space character overwriting the 1st digit: int endJust = (tOfC.TofCPageNumAlign.ToUpper() == "RIGHT") ? 18 : 6; float endSpace = leftMargin + stepPagePos - endJust; float numSpace = endSpace - startSpace; rtfText = GetRtfToC(tOfC.TofCSpaceChar, tOfC); Paragraph tmpmyparagraph = vlnPrintObject.RtfToParagraph(rtfText); float spacchrwid = 0; foreach (Chunk chkt in tmpmyparagraph.Chunks) spacchrwid += chkt.GetWidthPoint(); while (numSpace > 6) { spaceStr = spaceStr + tOfC.TofCSpaceChar; numSpace -= spacchrwid; } rtfText = GetRtfToC(spaceStr, tOfC); // out the leader dots Paragraph myparagraphs = vlnPrintObject.RtfToParagraph(rtfText); retval = Rtf2Pdf.TextAt(cb, myparagraphs, startSpace, yPageStart - yLocation, endSpace - startSpace, height, "", yBottomMargin); } // if in the pre-table of content list, just put the page number out as text. // otherwise, add a template _MyHelper.MyTOCPageCounts string key = "TOC" + hls.ItemID.ToString(); if (ii.IsAutoTOCSection || _MyHelper.MyTOCPageNums.ContainsKey(key)) { string pnum = _MyHelper.MyTOCPageNums.ContainsKey(key) ? _MyHelper.MyTOCPageNums[key].ToString() : (_MyHelper.CurrentTOCPageNumber + 1).ToString(); rtfText = GetRtfToC(pnum, tOfC); Paragraph myparagraphp = vlnPrintObject.RtfToParagraph(rtfText); width = pnum.Length * 6; retval = Rtf2Pdf.TextAt(cb, myparagraphp, leftMargin + stepPagePos, yPageStart - yLocation, width, height, "", yBottomMargin); } else AddTemplateTOCPageCounts(tOfC, yLocation, yPageStartAdj, leftMargin, stepPagePos, height, key); float spc = (float)(tOfC.TofCLineSpacing ?? 1); yLocation += (spc * vlnPrintObject.SixLinesPerInch); } } return yLocation; } //B2019-172 This will add the RTF font commands around the symbol characters // This was copied for DisplaySearch and modified - added the FormatData parameter private string ConvertSpecialChars(string str, FormatData fmtData) { string rtnVal = str; rtnVal = rtnVal.Replace("\u00A0", @"\u160?"); //convert \u00A0 to a hard space (\u00A0 shows as a blank in the search text field) rtnVal = rtnVal.Replace("\n", @"\line "); //B2018-020 SQL content record has "\line " for the hard return // Bug fix B2014-057 // if we are searching for a symbol character in all procedure sets MyDocVersion is null // when MyDocVersion is null, get the symbol list directly from the PROMS base format (BaseAll.xml) if (fmtData != null && fmtData.SymbolList != null) { SymbolList sl = fmtData.SymbolList; if (sl != null) { foreach (Symbol sym in sl) { string rplace = string.Format(sym.Unicode < 256 ? @"\'{0:X2}" : @"\u{0}?", sym.Unicode); // bug fix B2014-057 we were not including the ? in the replace rtnVal = rtnVal.Replace(((char)sym.Unicode).ToString(), rplace); } } } return rtnVal; } private void AddTemplateTOCPageCounts(TableOfContentsData tOfC, float yLocation, float yPageStartAdj, float leftMargin, float secPagePos, float height, string key) { PdfTemplate tmp = _MyHelper.MyTOCPageCounts.AddToTemplateList(key, _MyHelper.MyPdfWriter, "{TOCPAGE}", tOfC.Font.WindowsFont, (tOfC.TofCPageNumAlign.ToUpper() == "RIGHT") ? Element.ALIGN_RIGHT : Element.ALIGN_LEFT, PrintOverride.TextColor); _MyHelper.MyPdfContentByte.AddTemplate(tmp, leftMargin + secPagePos, (yPageStartAdj - yLocation) - height / 2); } //private string GetRtfPlacekeeper(string txt, PlacekeeperData pkCData) //{ // StringBuilder _RtfSB = new StringBuilder(); // Volian.Controls.Library.DisplayText pktxt = new Volian.Controls.Library.DisplayText(txt, pkCData.Font, false); // System.Drawing.Font myFont = pktxt.TextFont.WindowsFont; // _RtfSB.Append(vlnPrintObject.AddFontTable(myFont)); // _RtfSB.Append(pktxt.StartText); // _RtfSB.Append("}"); // return _RtfSB.ToString(); //} //private float AddSectionToPK(SectionInfo pkSection, ItemInfo ii, PlacekeeperData pkData, PdfContentByte cb, float yPageStart, float yLocation) //{ // SectionConfig pkSC = pkSection.MyConfig as SectionConfig; // if (pkSC.Section_Placekeeper != "Y") return yLocation; // automatic generation of Placekeeper turned off // float xAdjNumber = -6 + 1.2F; // float xAdjTitle = .8F + 1.2F; // float xAdjTitleIndent = -6 + 2.4F; // float yadj = 0.5F; // tweak to get 16 & 32 bit output to match. // float yPageStartAdj = yPageStart - yadj; // float leftMargin = (float)pkSection.MyDocStyle.Layout.LeftMargin; // //float secNumPos = (float)tOfC.TofCSecNumPos + xAdjNumber; // //float secTitlePos = (float)tOfC.TofCSecTitlePos + xAdjTitle; // //float secPagePos = (float)tOfC.TofCPageNumPos + xAdjNumber; // //float height = tOfC.Font.WindowsFont.Size * 1.5F; // if (ii.Sections != null) // { // foreach (SectionInfo mySection in ii.Sections) // { // SectionConfig sc = mySection.MyConfig as SectionConfig; // if (mySection.ItemID != pkSection.ItemID && sc != null && sc.Section_Placekeeper == "Y") // { // // Look at each step in the section and build a Placekeeper for those steps that are marked // // need to do the section number, section title & page number. Page number // // has to be put on at end after number of page is known, so use a Template. // //string tmptxt = mySection.MyContent.Number == null || mySection.MyContent.Number == "" ? " " : mySection.MyContent.Number; // //string rtfText = GetRtfPlacekeeper(tmptxt, pkData);//GetRtfToC(tmptxt, tOfC); // //Paragraph myparagraphn = vlnPrintObject.RtfToParagraph(rtfText); // float width = 0; // //foreach (Chunk chkt in myparagraphn.Chunks) // // width += chkt.GetWidthPoint(); // float numwidth = width; // float yBottomMargin = yPageStart - (float)pkSection.MyDocStyle.Layout.PageLength + (2 * vlnPrintObject.SixLinesPerInch); // Rtf2Pdf.Offset = new PointF(0, 2.5F); // //// for indenting of subsections, count up tree. Only start indenting // //// at third level, i.e. not indent on 1.0 and 1.1, but indent on 1.1.1: // //int level = 0; // //ItemInfo iilvl = mySection as ItemInfo; // //while (!iilvl.IsProcedure) // //{ // // level++; // // iilvl = iilvl.MyParent; // //} // //// check what level the plant wants the auto ToC indented: // //int tofCNumLevels = pkSection.ActiveFormat.PlantFormat.FormatData.SectData.AccSectionData.TableOfContentsData.TofCNumLevels ?? 0; // //if (tofCNumLevels > 0 && level > tofCNumLevels) return yLocation; // //level = level <= 2 ? 0 : level - 2; // no indenting until third level // //float indentOffset = (level * (secTitlePos - secNumPos)); // // if the starting column of text would be in 'middle of' the number, just put it // // a few spaces after the number. The '18' below represents 3 chars. // //float adjSecTitlePos = secTitlePos + indentOffset + (level * 6); // //if (secNumPos + numwidth + indentOffset > secTitlePos + indentOffset) // // adjSecTitlePos = secNumPos + numwidth + 18 - xAdjTitleIndent + indentOffset; // //rtfText = GetRtfToC(mySection.MyContent.Text, tOfC); // //Paragraph myparagrapht = vlnPrintObject.RtfToParagraph(rtfText); // //width = secPagePos - adjSecTitlePos - 6; // //float retval = Rtf2Pdf.TextAt(cb, myparagrapht, leftMargin + adjSecTitlePos, yPageStart - yLocation, width, height, "", yBottomMargin); // //float ttlRetval = retval; // //if (retval == 0) // //{ // // cb.PdfDocument.NewPage(); // // //_MyLog.InfoFormat("NewPage 5 {0}", cb.PdfWriter.CurrentPageNumber); // // yLocation = lastyLocation = 0; // // retval = Rtf2Pdf.TextAt(cb, myparagrapht, leftMargin + adjSecTitlePos, yPageStart - yLocation, width, height, "", yBottomMargin); // // ttlRetval = retval; // //} // //retval = Rtf2Pdf.TextAt(cb, myparagraphn, leftMargin + secNumPos + indentOffset, yPageStart - yLocation, width * 1.3F, height, "", yBottomMargin); // //if (retval == 0) // do a newpage, it couldn't fit on current page. // //{ // // cb.PdfDocument.NewPage(); // // //_MyLog.InfoFormat("NewPage 5 {0}", cb.PdfWriter.CurrentPageNumber); // // yLocation = lastyLocation = 0; // // retval = Rtf2Pdf.TextAt(cb, myparagraphn, leftMargin + secNumPos + indentOffset, yPageStart - yLocation, width * 1.3F, height, "", yBottomMargin); // //} // //retval = Math.Min(retval, ttlRetval); // //float lSpace = (tOfC.TofCLineSpacing == 2) ? 2 * vlnPrintObject.SixLinesPerInch : vlnPrintObject.SixLinesPerInch; // //if (lastyLocation != 0 && ((lastyLocation - retval) > lSpace)) // // yLocation += (lastyLocation - retval - lSpace); // //lastyLocation = retval; // //// check that the page number should be in the TOC (some BGE sections do not have the page number) // //if (!((mySection.MyDocStyle.StructureStyle.Style & E_DocStructStyle.DontNumberInTOC) == E_DocStructStyle.DontNumberInTOC)) // //{ // // // add a template for the page number: // // // if the space character is not null & is not a space, the we've got to put out // // // these between the section title & the page number // // if (tOfC.TofCSpaceChar != null && tOfC.TofCSpaceChar != "" && tOfC.TofCSpaceChar != " ") // // { // // width = 0; // // foreach (Chunk chkt in myparagrapht.Chunks) // // width += chkt.GetWidthPoint(); // // float startSpace = leftMargin + adjSecTitlePos + width; // // float endSpace = leftMargin + secPagePos; // // float numSpace = endSpace - startSpace; // // string spaceStr = ""; // // rtfText = GetRtfToC(tOfC.TofCSpaceChar, tOfC); // // Paragraph tmpmyparagraph = vlnPrintObject.RtfToParagraph(rtfText); // // float spacchrwid = 0; // // foreach (Chunk chkt in tmpmyparagraph.Chunks) // // spacchrwid += chkt.GetWidthPoint(); // // while (numSpace > 0) // // { // // spaceStr = spaceStr + tOfC.TofCSpaceChar; // // numSpace -= spacchrwid; // // } // // rtfText = GetRtfToC(spaceStr, tOfC); // // Paragraph myparagraphs = vlnPrintObject.RtfToParagraph(rtfText); // // retval = Rtf2Pdf.TextAt(cb, myparagraphs, startSpace, yPageStart - yLocation, endSpace - startSpace, height, "", yBottomMargin); // // } // // // if in the pre-table of content list, just put the page number out as text. // // // otherwise, add a template _MyHelper.MyTOCPageCounts // // string key = "TOC" + mySection.ItemID.ToString(); // // if (mySection.IsAutoTOCSection || _MyHelper.MyTOCPageNums.ContainsKey(key)) // // { // // string pnum = _MyHelper.MyTOCPageNums.ContainsKey(key) ? _MyHelper.MyTOCPageNums[key].ToString() : (_MyHelper.CurrentTOCPageNumber + 1).ToString(); // // rtfText = GetRtfToC(pnum, tOfC); // // Paragraph myparagraphp = vlnPrintObject.RtfToParagraph(rtfText); // // width = pnum.Length * 6; // // retval = Rtf2Pdf.TextAt(cb, myparagraphp, leftMargin + secPagePos, yPageStart - yLocation, width, height, "", yBottomMargin); // // } // // else // // AddTemplateTOCPageCounts(tOfC, yLocation, yPageStartAdj, leftMargin, secPagePos, height, mySection); // //} // yLocation += vlnPrintObject.SixLinesPerInch; // } // //yLocation = AddSectionToTOC(tocSection, mySection, tOfC, cb, yPageStart, yLocation); // } // } // return yLocation; //} //private void AddTemplatePKPageCounts(TableOfContentsData tOfC, float yLocation, float yPageStartAdj, float leftMargin, float secPagePos, float height, SectionInfo mySection) //{ // string key = "PK" + mySection.ItemID.ToString(); // PdfTemplate tmp = _MyHelper.MyTOCPageCounts.AddToTemplateList(key, _MyHelper.MyPdfWriter, "{TOCPAGE}", tOfC.Font.WindowsFont, Element.ALIGN_LEFT, PrintOverride.TextColor); // _MyHelper.MyPdfContentByte.AddTemplate(tmp, leftMargin + secPagePos, (yPageStartAdj - yLocation) - height / 2); //} private static void PrintTextMessage(PdfContentByte cb, string message, PdfLayer textLayer, iTextSharp.text.Font font) { if (textLayer != null) cb.BeginLayer(textLayer); float fontSize = font.Size; ColumnText ct = new ColumnText(cb); //iTextSharp.text.Font font = FontFactory.GetFont("Arial", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, fontSize,iTextSharp.text.Font.NORMAL, new iTextSharp.text.Color(PrintOverride.TextColor)); Chunk chk = new Chunk(message, font); float xCenter = cb.PdfDocument.PageSize.Width / 2; float yCenter = cb.PdfDocument.PageSize.Height / 2; // 1.8f; float width = chk.GetWidthPoint() * 1.01F; float height = fontSize * 1.5F; ct.SetSimpleColumn(xCenter - width / 2, yCenter - height / 2, xCenter + width / 2, yCenter + height / 2); Phrase ph = new Phrase(chk); ct.AddElement(ph); cb.SetColorFill(new iTextSharp.text.Color(PrintOverride.TextColor)); ct.Go(); if (textLayer != null) cb.EndLayer(); } private float _NoBreakYOffset = 0; private float _NoBreakYPageStart = 0; private void CreateStepPdf(SectionInfo section, PdfContentByte cb) { int profileDepth = ProfileTimer.Push(">>>> CreateStepPdf"); //PrintTimer pt = new PrintTimer(); iTextSharp.text.pdf.PdfWriter writer = cb.PdfWriter; ItemInfo myItemInfo = section as ItemInfo; if (SupInfoPrintType == E_SupInfoPrintType.DoPageBreaks) { section.StepSectPageBreaksForSupInfo.Clear(); section.StepSectPageBreaks.Clear(); } // 792: 72 * 11 inches - TopRow - Top is high value for 8.5x11 paper, 842 for A4 paper float _PointsPerPage = PDFPageSize.PaperSizePoints(myItemInfo.ActiveFormat.PlantFormat.FormatData.PDFPageSize.PaperSize); // C2020-002 paper size is now set in the format files float yTopMargin = _PointsPerPage - (float)myItemInfo.MyDocStyle.Layout.TopMargin; yTopMargin -= _MyHelper.AdjustTopMarginForMultiLinePageListItems; // the following line was modified to comment out the - 2 * SixLinesPerInch. this fixed a pagination problem with WCN EMG E-3. // The change mentioned above caused the bottom message to print without a line separating it from step text (if the text was long // enough to go to the bottom of page). A fix for this problem, B2017-103, is to use a format variable to define // how many lines will go above the continue message. It was done this way so that it fixes the format/data that the problem // was found in, i.e. Ginna Rev2 SAMG, SAG-3 step 7. This change won't affect other plants/formats but if the problem occurs // the format value can be used to adjust the location of bottom message. However, this could adversely affect pagination and // each plant/format needs to be done on a case by base basis. float yBottomMargin = Math.Max(0, yTopMargin - (float)myItemInfo.MyDocStyle.Layout.PageLength - (myItemInfo.MyDocStyle.Continue.Bottom.LocAdj == null ? 0 : (int)myItemInfo.MyDocStyle.Continue.Bottom.LocAdj)); vlnParagraph.PathPrefix = myItemInfo.Path; //Rtf2Pdf.PdfDebug = true; Rtf2Pdf.Offset = new PointF(0, 2.5F); if (section.MyDocStyle.LandscapePageList) { if ((section.MyConfig as SectionConfig).Section_Pagination == SectionConfig.SectionPagination.Separate) { System.Drawing.Drawing2D.Matrix myMatrix = new System.Drawing.Drawing2D.Matrix(0, 1, -1, 0, cb.PdfDocument.PageSize.Height, 0); cb.Transform(myMatrix); _MyHelper.IsWatermarkLandscape = true; // B2019-145: watermark on landscape page } } else _MyHelper.IsLandscape = false; _MyHelper.ChangeBarDefinition = MyChangeBarDefinition; //pt.Description = "vlnParagrph"; vlnParagraph myParagraph = new vlnParagraph(null, cb, myItemInfo, (float)myItemInfo.MyDocStyle.Layout.LeftMargin, _NoBreakYOffset, 0, myItemInfo.ColumnMode, myItemInfo.ActiveFormat, null, null, 0, true, this); //pt.Description = "After vlnParagrph"; //if (myParagraph.MyItemInfo.InList(15906)) //{ // CPSGen myGen = new CPSGen(myParagraph); // myGen.Generate("TestCPSGEN.xml"); //} float localYPageStart = 0; float yPageStart = yTopMargin; if (myItemInfo.HasChildren || myItemInfo.MyDocVersion.DocVersionConfig.SelectedSlave > 0 || (myItemInfo.ActiveFormat.PlantFormat.FormatData.Express && myItemInfo.MyDocStyle.OptionalSectionContent)) { // if there is supplemental information associated with this section... generate the pdf. This pdf will be used to place // supplemental information on facing pages // B2017-267 Put in the DocStyle setting "AdjustTopMarginOnStepContinuePages" for Farley who uses the "PSOnlyFirst" in some of their pagelists so that a section title is printed only on the first page of the section. // This will adjust the top margin when the section title is not printed on the other pages. yTopMargin += myItemInfo.MyDocStyle.AdjustTopMarginOnStepContinuePages; if (SupInfoPrintType == E_SupInfoPrintType.Merge && myParagraph.SupInfoSection != null && myParagraph.SupInfoSection.ChildrenBelow != null && myParagraph.SupInfoSection.ChildrenBelow.Count > 0) GenerateSuppInfoPdf(myParagraph.SupInfoSection, yTopMargin, yBottomMargin); localYPageStart = myParagraph.ToPdf(cb, yPageStart, ref yTopMargin, ref yBottomMargin); if (myParagraph.MyPlaceKeeper != null) _MyHelper.MyPlacekeepers.Add(myParagraph.MyPlaceKeeper); // add this step text to the PlaceKeeper (Calvert Cliffs) if (myParagraph.MyContAct != null) _MyHelper.MyContActSteps.Add(myParagraph.MyContAct); // add this step to the Continuous Action Summary // F2022-024 Time Critical Action if (myParagraph.MyTimeCriticalAction != null) _MyHelper.MyTimeCriticalActionSteps.Add(myParagraph.MyTimeCriticalAction); // add this step to the Time Critical Action Summary } else if (!myItemInfo.MyDocStyle.OptionalSectionContent) { SectionInfo si = myItemInfo.ActiveSection as SectionInfo; if (si.IsSeparatePagination()) // Don't output this message for continuous sections { // This was causing overlap of this message with previous sections iTextSharp.text.Font font = FontFactory.GetFont("Arial", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 30, iTextSharp.text.Font.NORMAL, new iTextSharp.text.Color(PrintOverride.TextColor)); PrintTextMessage(cb, "No Section Content", _TextLayer, font); } } SectionConfig.SectionPagination sp = SectionConfig.SectionPagination.Separate; // always the default ItemInfo nxtItem = section.NextItem; if (nxtItem != null) { // if this section & the next section are not accessory pages, see if there is continuous pagination, // i.e. no page break between them. if (section.IsStepSection && nxtItem.IsStepSection) { SectionInfo si = nxtItem as SectionInfo; // B2017-201 if si is null we need to get it via the ItemID if (si == null) si = SectionInfo.Get(nxtItem.ItemID); //ActiveSection as SectionInfo; SectionConfig sc = si.SectionConfig; try { if (sc != null) sp = sc.Section_Pagination; } catch (Exception ex) { sp = SectionConfig.SectionPagination.Separate; } } } if (sp == SectionConfig.SectionPagination.Separate) { if (section.MyDocStyle.LandscapePageList) { _MyHelper.IsLandscape = false; } NewPage(true); // end of step section //_MyLog.InfoFormat("NewPage 6 {0}", cb.PdfWriter.CurrentPageNumber); _NoBreakYOffset = 0; yPageStart = yTopMargin; } else { if (nxtItem != null && nxtItem.MyPrevious != null && nxtItem.ActiveFormat.PlantFormat.FormatData.SectData.SectionHeader.Level0Big) localYPageStart -= 12; if (_MyHelper.BottomMessage != null && _MyHelper.BottomMessage.Count != 0) { _MyHelper.DrawBottomMessage(cb); localYPageStart += 12; myParagraph.YBottomMost += 24; // two lines. } float tmplocal = localYPageStart; _NoBreakYOffset = myParagraph.YBottomMost; float yOffset = localYPageStart - yTopMargin; _NoBreakYOffset -= yOffset; yPageStart = localYPageStart; Volian.Base.Library.DebugText.WriteLine("NOBREAK:{0},'{1}','{2}','{3}','{4}','{5}'", section.ItemID, tmplocal, yTopMargin, localYPageStart, _NoBreakYOffset, myParagraph.YBottomMost); } OnStatusChanged("StepSection converted to PDF " + section.ShortPath, PromsPrinterStatusType.BuildStep); //pt.Description = "End"; ProfileTimer.Pop(profileDepth); _MyHelper.IsWatermarkLandscape = false; } private void GenerateSuppInfoPdf(vlnParagraph vlnParagraph, float yTopMargin, float yBottomMargin) { ReaderHelper savMyReaderHelper = MyReaderHelper; PdfContentByte savMyContentByte = MyContentByte; VlnSvgPageHelper savMyHelper = _MyHelper; if (SupInfoPdfPage == null) SupInfoPdfPage = new Dictionary(); else SupInfoPdfPage.Clear(); string SupInfoPdfName = Volian.Base.Library.VlnSettings.TemporaryFolder + @"\SupInfo" + vlnParagraph.MyItemInfo.ItemID.ToString() + @".pdf"; iTextSharp.text.Rectangle pageSize = PDFPageSize.UsePaperSize(MyItem.ActiveFormat.PlantFormat.FormatData.PDFPageSize.PaperSize); // C2020-002 paper size is now set in the format files PdfContentByte cb = OpenDoc(ref SupInfoPdfName, pageSize); if (cb == null) return; VlnSvgPageHelper myPageHelper = new VlnSvgPageHelper(vlnParagraph.MyItemInfo as SectionInfo, this, null, 0, _prtSectID); cb.PdfWriter.PageEvent = myPageHelper; myPageHelper.AllowAllWatermarks = AllowAllWatermarks; myPageHelper.MyPdfWriter = cb.PdfWriter; myPageHelper.CreatingFoldoutPage = false; myPageHelper.CreatingSupInfoPage = true; myPageHelper.MyPdfContentByte = cb; myPageHelper.ChangeBarDefinition = MyChangeBarDefinition; float yPageStart = yTopMargin; vlnParagraph._yPageStartForSupInfo = yTopMargin; SupInfoAjustGroupings(vlnParagraph, cb); SupInfoPrintType = E_SupInfoPrintType.SupInfoPdfPrint; vlnParagraph.ToPdf(cb, yPageStart, ref yTopMargin, ref yBottomMargin); SupInfoPrintType = E_SupInfoPrintType.Merge; try { cb.PdfDocument.Close(); cb = null; } catch (Exception ex) { Console.WriteLine("{0} - {1}", ex.GetType().Name, ex.Message); } _MyHelper = savMyHelper; MyContentByte = savMyContentByte; MyReaderHelper = savMyReaderHelper; } #region ShrinkSupInfoGroupAtATime private Dictionary _AdjustForParagraphShrinkage; private float _LastOffset; private float _LastBottom; /// /// Adjust the font size so that the Supplemental Information will fit on a single page. /// /// supinfo paragraphs /// pdfcontentbyte private void SupInfoAjustGroupings(vlnParagraph vlnParagraph, PdfContentByte cb) { // Get groups of SupInfo paragraphs List> supInfoGroups = FindSupInfoGroups(vlnParagraph); float pageLength = (float)vlnParagraph.MyItemInfo.MyDocStyle.Layout.PageLength; // Process Each Group of SupInfo paragraphs foreach (List grp in supInfoGroups) { // Initialize font decrement float decrement = 2f; float fontSize = grp[0].IParagraph.Leading; // current font size float mpglen = MeasureSupInfoGroupLength(grp, cb, pageLength); // Measure the length of the group of supinfo paragraphs // If the group does not fit on a page then reduce the font size if (mpglen > pageLength) { float newFontSize = fontSize; // reduce the font size by _Decrement until the entire grouping fits on one page while (mpglen > pageLength) // While the measured supinfo group length is greatere than the page length { newFontSize = fontSize - decrement; float scaler = newFontSize / fontSize;// Calculate the reduction ReduceSupInfoGroupFontSize(grp, scaler, cb); // Apply the smaller font size fontSize = newFontSize; // adjust the base font size mpglen = MeasureSupInfoGroupLength(grp, cb, pageLength);// Measure the length of the group of supinfo paragraphs } // Binary Search to find proper font size within .1 while (decrement > .1f) { decrement = decrement / 2f; //Reduce the decrement by half if (mpglen > pageLength) // If the measured length is too big newFontSize -= decrement;// Reduce the font size else newFontSize += decrement;//Otherwise increase the font size float scaler = newFontSize / fontSize;// Calculate the reduction ReduceSupInfoGroupFontSize(grp, scaler, cb);// apply the new font size fontSize = newFontSize;// adjust the base font size mpglen = MeasureSupInfoGroupLength(grp, cb, pageLength);// Measure the length of the group of supinfo paragraphs } if (mpglen > pageLength) //If the measured lenght is greater than the page length reduce the font size { newFontSize -= decrement;// Reduce the font size float scaler = newFontSize / fontSize;// Calculate the reduction ReduceSupInfoGroupFontSize(grp, scaler, cb);// apply the new font size fontSize = newFontSize;// adjust the base font size mpglen = MeasureSupInfoGroupLength(grp, cb, pageLength);// Measure the length of the group of supinfo paragraphs } } } } /// /// Method to apply a multiplier to the font size to a group of supinfo paragraphs /// /// group of SupInfo paragraphs /// multiplier for font size /// The PDFContentByte which is needed to calculate the adjusted the paragraph height private void ReduceSupInfoGroupFontSize(List grp, float scaler, PdfContentByte cb) { foreach (vlnParagraph pg in grp) // For each paragraph and children apply the scaler { // When adjusting the font size if the change causes the paragraph to change due to the number of characters // per line these changes will impact the paragraphs below the affected paragraph // This is kept in a dictionary. The key is the vertical offset to the affected paragraph. // The value is the impact on the paragraph height. _AdjustForParagraphShrinkage = new Dictionary(); // Apply the scaler to each paragraph ReduceSupInfoGroupFontSize(pg, scaler, cb); } } /// /// Method to apply a multiplier to the font size to a supinfo paragraph and children /// /// supinfo paragraph /// font multiplier /// pdfcontentbyte private void ReduceSupInfoGroupFontSize(vlnParagraph pg, float scaler, PdfContentByte cb) { float hBefore = pg.Height;// Save initial paragraph height AdjustSupInfoTable(pg, scaler, cb); NewSupInfoFixChunks(pg, scaler);// Apply multiplier to font size pg.IParagraph.Leading = scaler * pg.IParagraph.Leading; // Adjust leading (line spacing) // B2017-112: Don't do the font size change if images. // B2017-116: Don;t do the font size change for RTF Raw if (pg.ImageText == null && !pg.MyItemInfo.IsRtfRaw) { float hAfter = pg.GetParagraphHeight(cb, pg.IParagraph, "", pg.Width); // Calculate new paragraph height pg.Height = hAfter; // Save new Height; pg.YBottomMost += hAfter - hBefore; // Adjust YbottomMost for font size and leading float hleading = hBefore * scaler; // Calcuate leading change // If the change in font size effects the paragraph height due to word wrapping save the impact to adjust // Offsets below if ((hleading > hAfter + 1 && scaler < 1f) || (hleading < hAfter + 1 && scaler > 1f)) { if (!_AdjustForParagraphShrinkage.ContainsKey(pg.YOffset)) { _AdjustForParagraphShrinkage.Add(pg.YOffset, hleading - hAfter); } } } else // Adjust the Image Size B2017-112 And B2017-116 { pg.ImageScaler *= scaler; pg.Height *= scaler; pg.Width *= scaler; } float yoBefore = pg.YOffset;// Save the offset before float yoAfter = pg.YOffset = NewSupInfoFixOffset(pg, scaler);// Calculate the offset after pg.YBottomMost += yoAfter - yoBefore;// Adjust YbottomMost for changes to yOffset foreach (vlnParagraph cpg in pg.ChildrenAbove)// Process Children Above ReduceSupInfoGroupFontSize(cpg, scaler, cb); foreach (vlnParagraph cpg in pg.ChildrenBelow)// Process Children Below ReduceSupInfoGroupFontSize(cpg, scaler, cb); foreach (vlnParagraph cpg in pg.ChildrenLeft)// Process Children Left ReduceSupInfoGroupFontSize(cpg, scaler, cb); foreach (vlnParagraph cpg in pg.ChildrenRight)// Process Children Right ReduceSupInfoGroupFontSize(cpg, scaler, cb); foreach (vlnPrintObject po in pg.PartsAbove) // Process Parts Above (headers) NewSupInfoFixParts(po, scaler); foreach (vlnPrintObject po in pg.PartsBelow) // Process Parts Below (unknown) NewSupInfoFixParts(po, scaler); foreach (vlnPrintObject po in pg.PartsLeft) // Process Parts Left (Tabs) NewSupInfoFixParts(po, scaler); foreach (vlnPrintObject po in pg.PartsRight) // Process Parts Right (unknown) NewSupInfoFixParts(po, scaler); } /// /// Adjust the Table Fonts to shrink the sup info text /// /// vlnParagraph potentially containing a vlnTable /// multiplier /// pdfContentByte private void AdjustSupInfoTable(vlnParagraph pg, float scaler, PdfContentByte cb) { // Adjust the table if it exists if (pg.MyGrid != null) { // Create lists of heights before and after shrinking sup info Dictionary beforeRowHeight = new Dictionary(); Dictionary afterRowHeight = new Dictionary(); for (int i = 0; i < pg.MyGrid.RowTop.GetLength(0) - 1; i++) { beforeRowHeight.Add(i, scaler * (pg.MyGrid.RowTop[i + 1] - pg.MyGrid.RowTop[i])); afterRowHeight.Add(i, 0); } // Adjust font size for each cell. foreach (vlnCell cell in pg.MyGrid.MyCells) { float x = cell.MyTable.ColLeft[cell.c1]; float w = cell.MyTable.ColLeft[cell.c2 + 1] - x; float hBefore = scaler * pg.GetParagraphHeight(cb, cell.MyPara, "", w); foreach (Chunk chk in cell.MyPara.Chunks) chk.Font.Size = scaler * chk.Font.Size; float hAfter = 1.075F * pg.GetParagraphHeight(cb, cell.MyPara, "", w);// 1.075 is a magic number that worked for Ginna. Otherwise decenders overlapped the bottom line of the cell. cell.HContent = hAfter; // Save tthe height after adjusting the font size to account for changes in word wrap. afterRowHeight[cell.r1] = Math.Max(hAfter, afterRowHeight[cell.r1]); } for (int i = 0; i < pg.MyGrid.RowTop.GetLength(0) - 1; i++) { pg.MyGrid.RowTop[i + 1] = scaler * pg.MyGrid.RowTop[i + 1];// Adjust for leading pg.MyGrid.RowTop[i + 1] += (afterRowHeight[i] - beforeRowHeight[i]); //Adjust for word wrapping } } } /// /// Adjust YOffset for Print Objects /// /// Print Object (tab, paragraph, etc.) /// multiplier /// private float NewSupInfoFixOffset(vlnPrintObject vpo, float scaler) { float fixOffSet = 0; // determine impact of paragraphs above due to word-wrap foreach (float off in _AdjustForParagraphShrinkage.Keys) if (off < vpo.YOffset) fixOffSet += _AdjustForParagraphShrinkage[off]; // combine this impact with the scaler on the YOffset return scaler * vpo.YOffset - fixOffSet; } /// /// Adjust Font Size and offset for Parts /// /// PrintObject /// Multiplier private void NewSupInfoFixParts(vlnPrintObject po, float scaler) { if (po.IParagraph == null) return; // B2017-132 check for NULL reference (happend printing Bryon SAMGS with sup info pages) // Adjust the font size by the multiplier foreach (Chunk chk in po.IParagraph.Chunks) chk.Font.Size = scaler * chk.Font.Size; po.IParagraph.Leading = scaler * po.IParagraph.Leading;// Apply the multiplier to the leading (line spacing) po.YOffset = NewSupInfoFixOffset(po, scaler);// Adjust the YOffset } /// /// Adjust the Font Size /// /// Paragraph /// Multiplier private void NewSupInfoFixChunks(vlnParagraph vlnParagraph, float scaler) { // Adjust the font size by the multiplier foreach (Chunk chk in vlnParagraph.IParagraph.Chunks) chk.Font.Size = scaler * chk.Font.Size; } /// /// Measure the Length of the Group /// /// Group of SupInfo Paragraphs /// pdfContentByte /// Page Length /// Length private float MeasureSupInfoGroupLength(List grp, PdfContentByte cb, float pagelength) { _LastBottom = 0; _LastOffset = 12;// 24; // account for the Sup Info header (two lines) float fontSize = grp[0].IParagraph.Leading; // current font size // For each SupInfo Paragraph calculate length foreach (vlnParagraph pg in grp) { MeasureSupInfoGroupLength(pg, cb);// Measure a SupInfo Paragraph and children _LastOffset += _LastBottom; _LastBottom = 0; } //Console.WriteLine("MeasureSupInfoGroupLength {0} {1} {2} {3}", grp[0].MyItemInfo.ItemID, _LastOffset, fontSize, pagelength); return _LastOffset; } /// /// Measure the height of a supinfo paragraph and children /// /// SupInfo paragraph /// pdfContentByte private void MeasureSupInfoGroupLength(vlnParagraph pg, PdfContentByte cb) { _LastBottom = pg.YBottomMost; foreach (vlnParagraph cpg in pg.ChildrenAbove) // Measure Children Above MeasureSupInfoGroupLength(cpg, cb); foreach (vlnParagraph cpg in pg.ChildrenBelow) // Measure Children Below MeasureSupInfoGroupLength(cpg, cb); foreach (vlnParagraph cpg in pg.ChildrenLeft) // Measure Children Left MeasureSupInfoGroupLength(cpg, cb); foreach (vlnParagraph cpg in pg.ChildrenRight) // Measure Children Right MeasureSupInfoGroupLength(cpg, cb); } /// /// Find Groups of SupInfo Paragraphs /// /// SupInfo Paragraph /// List of supinfo paragraph groups private List> FindSupInfoGroups(vlnParagraph vlnParagraph) { List> supInfoGroups; supInfoGroups = new List>(); foreach (vlnParagraph pg in vlnParagraph.ChildrenBelow) { SectionInfo supInfoSect = (pg.MyItemInfo.ActiveSection != null) ? pg.MyItemInfo.ActiveSection as SectionInfo : null; if (supInfoSect != null && supInfoSect.StepSectPageBreaksForSupInfo != null && supInfoSect.StepSectPageBreaksForSupInfo.Contains(pg.MyItemInfo.ItemID)) // C2018-003 fixed use of getting the active section supInfoGroups.Add(new List()); // add a grouping since a page break exists if (supInfoGroups.Count == 0) { supInfoGroups.Add(new List()); // add a grouping since a page break exists _MyLog.WarnFormat("SupInfo Print Groups: {0}, {1}", pg.MyItemInfo.ItemID, pg.MyItemInfo.ShortPath); } supInfoGroups[supInfoGroups.Count - 1].Add(pg); // add paragraph to the last grouping } return supInfoGroups; } #endregion //ShrinkSupInfoGroupAtATime public void DoSupInfoPage(PdfContentByte cb, string str, PdfLayer textLayer, VlnSvgPageHelper myPageHelper, int itemid, bool insertBlankPages) { // see if the ID is in the facing page pdf - if so, get the page: if (SupInfoPdfPage == null || SupInfoPdfPage.Count < 1) return; int getpage = 0; if (SupInfoPdfPage.ContainsKey(itemid)) getpage = SupInfoPdfPage[itemid]; if (getpage < 0) { InsertBlankPage(cb); return; } PdfImportedPage sipage = null; try { string SupInfoPdfName = Volian.Base.Library.VlnSettings.TemporaryFolder + @"\SupInfo" + myPageHelper.MySection.ItemID.ToString() + @".pdf"; PdfReader pdfreader = new PdfReader(SupInfoPdfName); sipage = cb.PdfWriter.GetImportedPage(pdfreader, getpage + 1); // F2023-035: WCN - allow for change in left margin for supplemental information pages by // setting a value in the DocStyle for the adjustment. When importing the vlnParagraph page // use this adjustment (note that page list items are done in VlnSvgPageHelper) float lmargadj = myPageHelper.MySection.MyDocStyle.SupInfoMargAdj == null ? 0 : (float)myPageHelper.MySection.MyDocStyle.SupInfoMargAdj; AddImportedPageToLayer(cb.PdfWriter.DirectContent, textLayer, sipage, lmargadj, 0); DoingFacingPage = true; myPageHelper.ResetSvg(); NewPage(); DoingFacingPage = false; myPageHelper.ResetSvg(); pdfreader.Close(); } catch (Exception ex) { Console.WriteLine(ex); } } public void DoFoldoutPage(PdfContentByte cb, string str, PdfLayer textLayer, VlnSvgPageHelper myPageHelper, int foldoutindx, bool insertBlankPages) { if (_MyFoldoutSection == null || _MyFoldoutSection.Count == 0) return; // if the very first page to be output is a 'foldout', treat this as a special case, since // if duplex printing is on, the foldout should always be on the left side, i.e. or behind // the document text. The option PROMS provides is to either: // 1) Skip doing the foldout before the first page, if the 'InsertBlankPages' isn't set, by not checking // the checkbox on the print dialog. // 2) Insert a blank page as the first page, if the 'InsertBlankPages' is set. if (!myPageHelper.PrintedAPage && !insertBlankPages) return; if (!myPageHelper.PrintedAPage) { // only insert a blank page if this is the very first page printed & section has a foldout // and the checkbox on the print dialog to add blank pages is checked. This will put out a blank page as // as the first page so that duplex printing is correct for this condition. InsertBlankPage(cb); } SectionInfo saveSect = myPageHelper.MySection; myPageHelper.MySection = _MyFoldoutSection[foldoutindx]; myPageHelper.OnFoldoutPage = true; if (_MyFoldoutReader != null) { bool doimport2 = true; PdfImportedPage fgPage = null; try { // read saved foldout page fgPage = cb.PdfWriter.GetImportedPage(_MyFoldoutReader[foldoutindx], 1); } catch (Exception ex) { Console.WriteLine(ex); doimport2 = false; } if (doimport2) {// put the saved foldout page into the PDF AddImportedPageToLayer(cb.PdfWriter.DirectContent, textLayer, fgPage, 0, 0); foreach (iTextSharp.text.pdf.PdfAnnotation.PdfImportedLink il in _MyFoldoutReader[foldoutindx].GetLinks(1)) { if (!il.IsInternal()) cb.PdfWriter.AddAnnotation(il.CreateAnnotation(cb.PdfWriter)); } if (DebugPagination.IsOpen) DebugPagination.WriteLine("{0:D6},'{1}',{2}", myPageHelper.MyPdfContentByte.PdfWriter.CurrentPageNumber, "Foldout", 1); //if (BaselineMetaFile.IsOpen) BaselineMetaFile.WriteLine("!! {0:D6},'{1}',{2}", //myPageHelper.MyPdfContentByte.PdfWriter.CurrentPageNumber, "Foldout", 1); } } //_MyLog.InfoFormat("DoFoldoutPage {0}", cb.PdfWriter.CurrentPageNumber); //PrintTextMessage(cb, "Foldout for: " + str, textLayer); NewPage();// Temporary for foldout/16bit-32bit page alignment //_MyLog.InfoFormat("NewPage 8 {0}", cb.PdfWriter.CurrentPageNumber); myPageHelper.MySection = saveSect; myPageHelper.OnFoldoutPage = false; } public static byte[] WatermarkPDF(byte[] buffer, string watermark) { PdfReader reader = new PdfReader(buffer); MemoryStream ms = new MemoryStream(); PdfStamper stamper = new PdfStamper(reader, ms); PdfLayer layer = new PdfLayer("Watermark", stamper.Writer); layer.SetPrint("Print", true); for (int i = 1; i <= reader.NumberOfPages; i++) { PdfContentByte cb = stamper.GetUnderContent(i); cb.SaveState(); cb.BeginLayer(layer); SvgWatermark wm = new SvgWatermark(cb, watermark, System.Drawing.Color.Blue, .15F); //wm.SetSquareDotPattern(.7F); //wm.SetTextPattern(8, 3); //wm.SetTextPattern2(8); //wm.SetHashPattern(1, 6); //wm.SetDotPattern(5, 2); wm.Draw(); cb.EndLayer(); cb.RestoreState(); } stamper.Close(); /* cb.SaveState(); if (_WatermarkLayer != null) cb.BeginLayer(_WatermarkLayer); SvgWatermark myWatermark = new SvgWatermark(cb, Watermark, System.Drawing.Color.Blue, .15F); myWatermark.SetSquareDotPattern(.7F); myWatermark.Draw(); if (_WatermarkLayer != null) cb.EndLayer(); cb.RestoreState(); */ return ms.ToArray(); } // C2021-010: Remove trailing returns/spaces & manual page breaks & allow save. public void SavePaginationFixes() { // If manual page breaks were removed (during pagination), save those if (RemoveManualPageBreaks != null && RemoveManualPageBreaks.Count > 0) { foreach (int iid in RemoveManualPageBreaks) { ItemInfo ii = ItemInfo.Get(iid); StepConfig sc = ii.MyConfig as StepConfig; if (sc != null) { using (Item itm = Item.Get(iid)) { if (!HasManPagAnnot(ii, "Removed Manual Page Break")) VEPROMS.CSLA.Library.Annotation.MakeAnnotation(itm, AnnotationType.GetByNameOrCreate("Manual Pagination Issues"), null, "Removed Manual Page Break", null); sc.Step_NewManualPagebreak = false; // reset the flag that was set in the config if (sc.Step_SubStepPagebreak) sc.Step_SubStepPagebreak = false; // C2023-018: remove substep page break flags itm.MyContent.Config = sc.ToString(); itm.MyContent.DTS = DateTime.Now; itm.MyContent.UserID = Volian.Base.Library.VlnSettings.UserID; itm.MyContent.Save(); } } } } // If trailing newlines and/or spages were removed, save those if (RemoveTrailingHardReturnsAndSpaces != null && RemoveTrailingHardReturnsAndSpaces.Count > 0) { foreach (int iid in RemoveTrailingHardReturnsAndSpaces) { // first check if it exists, if the step was empty, it may have been deleted. ItemInfo iitmp = ItemInfo.Get(iid); if (iitmp != null) { string annot = "Removed Trailing Newlines and Spaces"; if (RemoveManualPageBreaks != null && RemoveManualPageBreaks.Contains(iid)) annot = "Removed Trailing Newlines, Spaces and ManualPageBreak"; string tmp = Regex.Replace(iitmp.MyContent.Text, "(\\\\line|\r|\n|\\\\u160\\?| )+$", ""); // if the step ends up empty, set the text to a space (if null or "") step gets deleted. // B2021-028: removed code that set a step config item flagging an empty step. This had been put in to print // the same way before save but that printing was wrong and was fixed for this bug. if (tmp == null || tmp == "") { tmp = " "; annot = "Empty step. Consider deleting to restore standard pagination."; } bool alreadyHasAnnot = HasManPagAnnot(iitmp, annot); // don't add annotation if it exists. using (Item itm = Item.Get(iid)) { if (!alreadyHasAnnot) VEPROMS.CSLA.Library.Annotation.MakeAnnotation(itm, AnnotationType.GetByNameOrCreate("Manual Pagination Issues"), null, annot, null); itm.MyContent.Text = tmp; itm.MyContent.DTS = DateTime.Now; itm.MyContent.UserID = Volian.Base.Library.VlnSettings.UserID; itm.MyContent.Save(); } } } } } private bool HasManPagAnnot(ItemInfo iitmp, string annotstr) { if (iitmp.ItemAnnotations != null && iitmp.ItemAnnotationCount > 0) { // check each annotation to see if this manual pagination issue exists - so it won't get added again. foreach (AnnotationInfo ai in iitmp.ItemAnnotations) { if (ai.MyAnnotationType.Name.StartsWith("Manual Pagination Issues")) if (ai.SearchText.StartsWith(annotstr)) return true; } } return false; } } public class ReaderHelper { private static readonly log4net.ILog _MyLog = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private Dictionary dicPage = new Dictionary(); private Dictionary dicSize = new Dictionary(); private PromsPrinter _MyPromsPrinter; public PromsPrinter MyPromsPrinter { get { return _MyPromsPrinter; } set { _MyPromsPrinter = value; } } private SectionInfo _MySectionInfo; public SectionInfo MySectionInfo { get { return _MySectionInfo; } set { if (_MySectionInfo == null || value.ItemID != _MySectionInfo.ItemID) { _MySectionInfo = value; //MyPdfFile = _MyPromsPrinter.BuildMSWordPDF(MySectionInfo); //try //{ // MyReader = MyPdfFile != null ? new PdfReader(MyPdfFile) : null; // string key = string.Empty; // for (int i = 1; i <= MyReader.NumberOfPages; i++) // { // key = string.Format("{0}.{1}", MySectionInfo.ItemID, i); // dicPage.Add(key, MyPromsPrinter.MyContentByte.PdfWriter.GetImportedPage(MyReader, i)); // dicSize.Add(key, MyReader.GetPageSizeWithRotation(i)); // } //} //catch (Exception ex) //{ // MyReader = null; //} } } } private string _MyPdfFile; public string MyPdfFile { get { return _MyPdfFile; } set { _MyPdfFile = value; } } private int _DocID; public int DocID { get { return _DocID; } set { _DocID = value; } } private PdfReader _MyReader; public PdfReader MyReader { get { return _MyReader; } set { _MyReader = value; } } public ReaderHelper(PromsPrinter pp) { MyPromsPrinter = pp; //this.LoadTree(pp.MyItem); } public PdfImportedPage GetPage(SectionInfo sectInfo, int pageNumber) { string key = string.Format("{0}.{1}", sectInfo.ItemID, pageNumber); return dicPage[key]; } public iTextSharp.text.Rectangle GetSize(SectionInfo sectInfo, int pageNumber) { string key = string.Format("{0}.{1}", sectInfo.ItemID, pageNumber); if (dicSize.ContainsKey(key)) return dicSize[key]; return PDFPageSize.UsePaperSize(sectInfo.ActiveFormat.PlantFormat.FormatData.PDFPageSize.PaperSize); // C2020-002 paper size is now set in the format files } public void LoadTree(ItemInfo ii) { dicPage = new Dictionary(); dicSize = new Dictionary(); LoadSectionTree(ii); } private void LoadSectionTree(ItemInfo ii) { if (ii.Sections == null) return; // B2021-067 crash on null reference foreach (SectionInfo si in ii.Sections) { if (MyPromsPrinter.PrtSectID == -1 || (MyPromsPrinter.PrtSectID > -1 && (ii.ItemID == MyPromsPrinter.PrtSectID || si.ItemID == MyPromsPrinter.PrtSectID))) { if (si.IsStepSection) { if (si.Sections != null) LoadSectionTree(si); } else { MyPdfFile = _MyPromsPrinter.BuildMSWordPDF(si); if (MyPdfFile != null) { try { FileInfo fi = new FileInfo(MyPdfFile); if (fi.Length == 0) // B2017-218 Handle invalid word sections { if (!PromsPrinter.BaselineTesting) //B2018-071 Output a message box unless baseline testing is being performed. MessageBox.Show(si.DisplayNumber + " " + si.DisplayText + " is not valid", "Invalid Word Section", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } MyReader = MyPdfFile != null ? new PdfReader(MyPdfFile) : null; string key = string.Empty; for (int i = 1; i <= MyReader.NumberOfPages; i++) { key = string.Format("{0}.{1}", si.ItemID, i); dicPage.Add(key, MyPromsPrinter.MyContentByte.PdfWriter.GetImportedPage(MyReader, i)); iTextSharp.text.Rectangle rectgl = MyReader.GetPageSizeWithRotation(i); // If the word page is set to landscape, but the document style is not landscape, then flip the height and width (put back to portrait) if (!((si.MyDocStyle.StructureStyle.Style & E_DocStructStyle.DSS_WordContentLandscaped) == E_DocStructStyle.DSS_WordContentLandscaped) && rectgl.Height < rectgl.Width) rectgl = new iTextSharp.text.Rectangle(rectgl.Height, rectgl.Width); dicSize.Add(key, rectgl); //dicSize.Add(key, MyReader.GetPageSizeWithRotation(i)); } } catch (Exception ex) { if (_MyPromsPrinter.DocReplace == null) MessageBox.Show(ex.Message, ex.GetType().FullName, MessageBoxButtons.OK, MessageBoxIcon.Error); _MyLog.Warn(string.Format("Error in LoadSectionTree [{0}],{1}.{2}", si.ItemID, si.DisplayNumber, si.DisplayText), ex); } } } } } } } //public class PrintTimer //{ // private DateTime _StartTime = DateTime.Now; // public DateTime StartTime // { // get { return _StartTime; } // set { _StartTime = value; } // } // private string _Description = "Start"; // public string Description // { // get { return _Description; } // set // { // DateTime dtNext = DateTime.Now; // //Console.WriteLine("{0},'{1}'", TimeSpan.FromTicks(dtNext.Ticks - LastTime.Ticks).TotalSeconds, Description); // _Description = value; // _LastTime = dtNext; // } // } // private DateTime _LastTime = DateTime.Now; // public DateTime LastTime // { // get { return _LastTime; } // set { _LastTime = value; } // } //} }