using System; using System.Collections.Generic; using System.Text; using System.Drawing; using iTextSharp.text.pdf; using iTextSharp.text; using Itenso.Rtf; using Itenso.Rtf.Parser; using Itenso.Rtf.Interpreter; using Itenso.Rtf.Support; using Volian.Controls.Library; using VEPROMS.CSLA.Library; using Volian.Base.Library; using System.Text.RegularExpressions; namespace Volian.Print.Library { public abstract partial class vlnPrintObject { // Used for debugging: private static int _UniqueDebugId = 0; public static int UniqueDebugId { get { return _UniqueDebugId++; } set { _UniqueDebugId = value; } } private int _DebugId = UniqueDebugId; public int DebugId { get { return _DebugId; } set { _DebugId = value; } } protected static float _WidthAdjust = 1; // 1 Point - adjusted to match 16-bit line wrapping. For editting it's 3. protected static float _WidthAdjustBox = 6; private static float _SixLinesPerInch = 12; // twips public static float SixLinesPerInch { get { return vlnPrintObject._SixLinesPerInch; } set { vlnPrintObject._SixLinesPerInch = value; } } protected static float _SevenLinesPerInch = 10.1F;//72F / 7F; protected float _XOffset; public float XOffset { get { return _XOffset; } set {_XOffset = value; } } protected float _YOffset; public float YOffset { get { return _YOffset; } set { _YOffset = value; } } protected vlnParagraph _MyParent; public vlnParagraph MyParent { get { return _MyParent; } set { _MyParent = value; if (_MyParent != null) { } } } private bool _HasIndent = false; public bool HasIndent { get { return _HasIndent; } set { _HasIndent = value; } } private bool _IsCompressed = false; public bool IsCompressed { get { return _IsCompressed; } set { _IsCompressed = value; } } private string _ImageText; // ro definition, value part of #Link in case of image/figure public string ImageText { get { return _ImageText; } set { _ImageText = value; } } protected string _Rtf; // may become iTextSharp paragraph public virtual string Rtf { get { return _Rtf; } set { _Rtf = value; } } public class VlnSplitCharacter : ISplitCharacter { public bool IsSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) { return (cc[current] == ' '); } public bool IsSplitCharacter(char c) { return (c == ' '); } } public static VlnSplitCharacter mySplitter = new VlnSplitCharacter(); iTextSharp.text.Paragraph _IParagraph; public iTextSharp.text.Paragraph IParagraph { get { // If the paragraph iscompressed, it may have been created as not compressed since compression // is not set when paragraph is initially created, it is set during pagination tests. //if (_IParagraph == null || IsCompressed) if (Rtf != null && (_IParagraph == null || IsCompressed)) { int profileDepth = ProfileTimer.Push(">>>> VlnPrintObject.IParagraph"); string myRtf = Rtf; // Add a printable character (hard space) between multiple newlines // this asssures that the blank line will be printed if (myRtf.Contains(@"\line \line ")) myRtf= myRtf.Replace(@"\line \line ", @"\line \u160? \line "); _IParagraph = RtfToParagraph(myRtf, HasIndent); ProfileTimer.Pop(profileDepth); } return _IParagraph; } set { _IParagraph = value; } } private float _Width; public float Width { get { return _Width; } set { _Width = value; } } protected float _Height; public virtual float Height { get { int profileDepth = ProfileTimer.Push(">>>> vlnPrintObject.Height"); if (_Height == 0) _Height = GetParagraphHeight(MyContentByte, IParagraph, string.Empty, Width); ProfileTimer.Pop(profileDepth); return _Height; } set { _Height = value; } } public float GetParagraphHeight(PdfContentByte cb, Paragraph iParagraph, string suffix, float width) { return GetParagraphHeight(cb, iParagraph, suffix, width, true); } public float GetParagraphHeight(PdfContentByte cb, Paragraph iParagraph, string suffix, float width, bool throwException) { VlnSvgPageHelper ph = null; if(cb != null) ph = cb.PdfWriter.PageEvent as VlnSvgPageHelper; float heightAll = GetHeight(cb, iParagraph, suffix, width, throwException); if (ph != null && ph.MyPromsPrinter.DebugOutput && !MyPageHelper.Back32BitPROMS) { vlnParagraph myParagraph = this as vlnParagraph; if (myParagraph != null) { //if (myParagraph.MyItemInfo.FormatStepData != null && myParagraph.MyItemInfo.FormatStepData.Font.Family == "Prestige Elite Tall") //if (!myParagraph.MyItemInfo.ActiveFormat.PlantFormat.FormatData.SectData.StepSectionData.WrapSameAsEdit) //{ // Chunk chk = RemoveLastCharacter(iParagraph); // float heightAllButOne = GetHeight(cb, iParagraph, width, throwException); // if (heightAll != heightAllButOne) // return heightAllButOne; // if (chk != null) // RestoreLastCharacter(iParagraph, chk); //} } } return heightAll; } private static void RestoreLastCharacter(Paragraph iParagraph, Chunk chk) { iParagraph.RemoveAt(iParagraph.Count - 1); iParagraph.Add(chk); } private static Chunk RemoveLastCharacter(Paragraph iParagraph) { if (iParagraph.Count == 0) return null; object obj = iParagraph[iParagraph.Count-1]; Chunk chk = obj as Chunk; if (chk == null) return null; string s = chk.Content; if (s.Length > 1 || iParagraph.Count > 1) // don't remove last character if it's the only one { iParagraph.RemoveAt(iParagraph.Count - 1); if (s.Length > 0) { if (s == "\xA0") // If this is a space at the end put it back { iParagraph.Add(chk); return null; } else { s = s.Substring(0, s.Length - 1); iParagraph.Add(new Chunk(s, chk.Font)); } } } return chk; } public static float GetHeight(PdfContentByte cb, Paragraph iParagraph, string suffix, float width, bool throwException) { ColumnText myColumnText = new ColumnText(cb); myColumnText.SetSimpleColumn(0, 792F, width, 0); // Bottom margin if (suffix != string.Empty) { Chunk chk = iParagraph.Chunks[0] as Chunk; iParagraph.Add(new Chunk(suffix, chk.Font)); myColumnText.AddText(iParagraph); } else { myColumnText.AddElement(iParagraph); } //myColumnText.UseAscender = true;// Adjusts to the top of the text box. int status = myColumnText.Go(true); // Check to see if it will fit on the page. if (ColumnText.HasMoreText(status) && throwException) //throw (new Exception("Paragraph longer than a page")); Console.WriteLine("Paragraph longer than a page"); return 792F - myColumnText.YLine; // This gives the height of the Paragraph } public float GetTableWidth(PdfContentByte cb, Paragraph iParagraph, float? maxWidth) { int iWidth = (int)(maxWidth ?? 72 * 8.5F); float h = GetParagraphHeight(cb, iParagraph, string.Empty, iWidth); int iWidthMax = iWidth; // maximum width in Characters int iDelta = iWidth / 2; iWidth = iWidth / 2; while (iDelta > 0) { float h2 = GetParagraphHeight(cb, iParagraph, string.Empty, iWidth,false); iDelta = iDelta / 2; if (h2 == h) iWidthMax = iWidth; iWidth += (h2>h ? 1: -1) * iDelta; } return (float) iWidthMax; } private PdfContentByte _MyContentByte; public PdfContentByte MyContentByte { get { return _MyContentByte; } set { _MyContentByte = value; } } private VlnSvgPageHelper _MyPageHelper; public VlnSvgPageHelper MyPageHelper { get { if(_MyPageHelper == null) _MyPageHelper = MyContentByte.PdfWriter.PageEvent as VlnSvgPageHelper; return _MyPageHelper; } set { _MyPageHelper = value; } } public vlnPrintObject() { } /// /// No conversion necessary: twips -> twips /// /// /// public static int ToInt(int value) { return value; } /// /// No conversion necessary: int? twips -> int twips /// /// /// public static int ToInt(int? value) { return ToInt((int)value); } /// /// No conversion necessary: string twips -> int twips /// /// /// public static int ToInt(string value) { return ToInt((int)Convert.ToSingle(value)); } /// /// No conversion necessary: value from a list in a string, twips -> int twips /// /// /// public static int ToInt(string value, int i) { string s = (value.Contains(",")) ? value.Split(",".ToCharArray())[i] : value; return ToInt(s); } public static string GetRtf(string text, VE_Font vFont) { StringBuilder rtfSB = new StringBuilder(); //DisplayText vlntxt = new DisplayText(text.TrimStart(" ".ToCharArray()), vFont, false); DisplayText vlntxt = new DisplayText(text, vFont, false); //rtfSB.Append(AddFontTable(vlntxt.TextFont.WindowsFont)); //rtfSB.Append(AddFontTable(vFont.WindowsFont)); rtfSB.Append(AddFontTable(vFont)); rtfSB.Append(vlntxt.StartText); rtfSB.Append("}"); return rtfSB.ToString(); } public static string AddFontTable(VE_Font vfont) { System.Drawing.Font font = vfont.WindowsFont; StringBuilder rtfSB = new StringBuilder(); StringBuilder sbbeg = new StringBuilder(); StringBuilder sbend = new StringBuilder(); if ((vfont.Style & E_Style.Bold) > 0) { sbbeg.Append(@"\b"); sbend.Append(@"\b0"); } if ((vfont.Style & E_Style.Underline) > 0) { sbbeg.Append(@"\ul"); sbend.Insert(0, @"\ulnone"); } if ((vfont.Style & E_Style.Italics) > 0) { sbbeg.Append(@"\i"); sbend.Insert(0, @"\i0"); } rtfSB.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset2 " + font.FontFamily.Name + @";}"); //}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}"; if (!FontIsFixed(font)) rtfSB.Append(@"{\f1\fnil\fcharset0 Arial Unicode MS;}}{\colortbl ;\red255\green0\blue0;}"); else rtfSB.Append(@"{\f1\fnil\fcharset0 VESymbFix;}}{\colortbl ;\red255\green0\blue0;}"); rtfSB.Append("\r\n"); // use styles to construct rtf commands to insert into next line (where \b, etc is) rtfSB.Append(@"\viewkind4\uc1\pard\sl-240\slmult0" + sbbeg.ToString() + @"\fs" + Convert.ToInt32(font.SizeInPoints * 2).ToString() + @" "); // \f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}"; return rtfSB.ToString(); } public static string AddFontTable(System.Drawing.Font font) { StringBuilder rtfSB = new StringBuilder(); StringBuilder sbbeg = new StringBuilder(); StringBuilder sbend = new StringBuilder(); if (font.Bold) { sbbeg.Append(@"\b"); sbend.Append(@"\b0"); } if (font.Underline) { sbbeg.Append(@"\ul"); sbend.Insert(0, @"\ulnone"); } if (font.Italic) { sbbeg.Append(@"\i"); sbend.Insert(0, @"\i0"); } rtfSB.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset2 " + font.FontFamily.Name + @";}"); //}\f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}}"; if (!FontIsFixed(font)) rtfSB.Append(@"{\f1\fnil\fcharset0 Arial Unicode MS;}}{\colortbl ;\red255\green0\blue0;}"); else rtfSB.Append(@"{\f1\fnil\fcharset0 VESymbFix;}}{\colortbl ;\red255\green0\blue0;}"); rtfSB.Append("\r\n"); // use styles to construct rtf commands to insert into next line (where \b, etc is) rtfSB.Append(@"\viewkind4\uc1\pard\sl-240\slmult0" + sbbeg.ToString() + @"\fs" + Convert.ToInt32(font.SizeInPoints * 2).ToString() + @" "); // \f0\fs" + this.Font.SizeInPoints * 2 + @" " + myDisplayTextElement.Text + @"}"; return rtfSB.ToString(); } private static bool FontIsFixed(System.Drawing.Font font) { iTextSharp.text.Font iFont = Volian.Svg.Library.VolianPdf.GetFont(font); float fW = iFont.BaseFont.GetWidthPointKerned("W", 12); float fE = iFont.BaseFont.GetWidthPointKerned("!", 12); return fW == fE; } public static iTextSharp.text.Paragraph RtfToParagraph(string rtf) { return RtfToParagraph(rtf, false); } public static iTextSharp.text.Paragraph RtfToParagraph(string rtf, bool hasIndent) { if (hasIndent) { hasIndent = rtf.Contains("\x05"); if (hasIndent && rtf.Contains(@"\par \par")) rtf = rtf.Replace(@"\par \par", @"\par \u160? \par"); if (hasIndent && rtf.Contains(@"\par\par")) rtf = rtf.Replace(@"\par\par", @"\par \u160? \par"); } IRtfDocument rtfDoc = RtfInterpreterTool.BuildDoc(rtf); Rtf2iTextSharp rtf2IText = new Rtf2iTextSharp(rtfDoc); rtf2IText.HasIndent = hasIndent; iTextSharp.text.Paragraph para = rtf2IText.Convert(); para.SetLeading(_SixLinesPerInch, 0); if (rtf.Contains("\x05")) // note that this is for existing customer data as of August 2015. { // if there is a hanging indent, the iTextSharp paragraph properties must be set // to print the indent. Replace the indent 'token' with a non-used symbol, this will // create a chunk with that symbol. Then loop through all of the chunks until we find // this symbol, adding up the widths to that point. This width is the value that // needs to be used to set the indent. // Notes: // A hard return will reset the chkW (indent width) back to zero. // We jump out of the processing loop after the first indent token is found and ignor any other ones float chkW = CalculateHangingIndent(rtf); para.IndentationLeft = chkW; para.FirstLineIndent = -chkW; } Match match = Regex.Match(rtf, @"\\fi([-0-9]*) ?\\li([0-9]*)"); if (match.Success) { float fi = float.Parse(match.Groups[1].Value) / 20; float li = float.Parse(match.Groups[2].Value) / 20; // if there is a hanging indent, the iTextSharp paragraph properties must be set // to print the indent. para.IndentationLeft = li; para.FirstLineIndent = fi; } // Change the chunks to only split on spaces rather than spaces and hyphens foreach (Chunk chk in para) { if (chk.Attributes==null || !chk.Attributes.ContainsKey("NoSplit")) { if (chk.Attributes == null) chk.Attributes = new System.Collections.Hashtable(); chk.SetSplitCharacter(mySplitter); chk.Attributes.Add("NoSplit", false); } } return para; } public static float CalculateHangingIndent(string rtf) { float chkW=0; IRtfDocument rtfDoc2 = RtfInterpreterTool.BuildDoc(rtf.Replace("\x05", @"\f1 \u9999? \f0 ")); Rtf2iTextSharp rtf2IText2 = new Rtf2iTextSharp(rtfDoc2); iTextSharp.text.Paragraph para2 = rtf2IText2.Convert(); foreach (Chunk chk in para2.Chunks) { if (chk.Content[0] == 9999) break; if (chk.Content.Contains("\u270f")) { int i = chk.Content.IndexOf('\u270F'); int n = chk.Content.Length; chkW += chk.GetWidthPoint() * i / (n - i); break; } if (chk.Content.Contains("\n")) chkW = 0; //hard return - reset chkW (indent start) chkW += chk.GetWidthPoint(); } return chkW; } public abstract float ToPdf(PdfContentByte cb, float yPageStart, ref float yTopMargin, ref float yBottomMargin); protected float CalculateYOffset(float yPageStart, float yTopMargin) { float yLocation = yPageStart - YOffset; if (MyPageHelper.YMultiplier != 1) { yLocation = -1 + yTopMargin - (yTopMargin - yLocation) * MyPageHelper.YMultiplier; if (Rtf != null) IParagraph.Leading = _SevenLinesPerInch; } return yLocation; } protected float CalculateYLocation(float yLocation, float yTopMargin) { if (MyPageHelper.YMultiplier != 1) yLocation = -1 + yTopMargin - (yTopMargin - yLocation) * MyPageHelper.YMultiplier; return yLocation; } public void DebugPdf(PdfContentByte cb, float left, float top) { VlnSvgPageHelper _MyPageHelper = cb.PdfWriter.PageEvent as VlnSvgPageHelper; PdfLayer debugLayer = _MyPageHelper == null ? null : _MyPageHelper.DebugLayer; if (debugLayer == null) return; cb.SaveState(); cb.BeginLayer(debugLayer); ColumnText ct = new ColumnText(cb); ct.SetSimpleColumn(left, top, left+50, top - 50); iTextSharp.text.Font font = FontFactory.GetFont("Arial", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 2); Chunk chk = new Chunk(DebugId.ToString(), font); Phrase ph = new Phrase(chk); ct.AddElement(ph); cb.SetColorFill(new iTextSharp.text.Color(PrintOverride.OverrideDebugColor(System.Drawing.Color.Gray))); ct.Go(); cb.EndLayer(); cb.RestoreState(); } public virtual float YBottom { get { return YOffset + Height;} } } public partial class vlnPrintObjects : List { public float ToPdf(PdfContentByte cb, float yPageStart, ref float yTopMargin, ref float yBottomMargin) { foreach (vlnPrintObject part in this) { yPageStart = part.ToPdf(cb, yPageStart, ref yTopMargin, ref yBottomMargin); } return yPageStart; } } public enum PartLocation : int { None = 0, // Non-printable Below = 1, // RNO Separator Above = 2, // Tab Headers, Separator? Right = 3, // Change Bars Left = 4, // Tabs, Checkoffs? (maybe part of tab) Container = 5 // Box }; public enum ChildLocation : int { None = 0, Below = 1, Above = 2, Right = 3, // RNO Left = 4 } }