Rich f9ef66880b B2018-034 Use _IParagraph when PDF Link info is added
B2018-034 Copy the attributes from old to new when IParagraph is refreshed (IsCompressed)
2018-03-14 13:41:42 +00:00

576 lines
20 KiB
C#

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();
// B2018-034 Attributes were being lost when the IsCompressed flag was true.
// The following variable was changed to a public so that it could be used
// for setting PDF Links without creating IParagraph when IsCompressed was true.
public 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 ");
if (myRtf.Contains(@"\pard\line ")) // Bug fix: B2016-145 for VC.Summer End Message
myRtf = myRtf.Replace(@"\pard\line ", @"\par ");
// B2018-034 Attributes were being lost when the IsCompressed flag was true.
// CopyAttributesToNewIParagraph retains any attribute which have been set
Paragraph oldParagraph = _IParagraph;
_IParagraph = RtfToParagraph(myRtf, HasIndent);
// B2018-034 Attributes were being lost when the IsCompressed flag was true.
// CopyAttributesToNewIParagraph retains any attribute which have been set
CopyAttributesToNewIParagraph(oldParagraph, _IParagraph);
ProfileTimer.Pop(profileDepth);
}
return _IParagraph;
}
set { _IParagraph = value; }
}
// B2018-034 Attributes were being lost when the IsCompressed flag was true.
// CopyAttributesToNewIParagraph retains any attribute which have been set
private void CopyAttributesToNewIParagraph(Paragraph oldParagraph, Paragraph newParagraph)
{
if (oldParagraph == null) return;
if (oldParagraph.Chunks.Count != newParagraph.Chunks.Count) return;
for (int i = 0; i < oldParagraph.Chunks.Count; i++)
{
System.Collections.Hashtable oldTable = (oldParagraph.Chunks[i] as Chunk).Attributes;
System.Collections.Hashtable newTable = (newParagraph.Chunks[i] as Chunk).Attributes;
if (oldTable.Count == newTable.Count) return;
foreach (System.Collections.DictionaryEntry de in oldTable)
if (!newTable.ContainsKey(de.Key))
newTable.Add(de.Key, de.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)
{
// B2016-142: If the paragraph won't fit on page and indenting is on, remove
// indenting & try again:
if (iParagraph.FirstLineIndent < 0)
{
iParagraph.FirstLineIndent = 0;
iParagraph.IndentationLeft = 0;
return GetHeight(cb, iParagraph, suffix, width, 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()
{
}
/// <summary>
/// No conversion necessary: twips -> twips
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int ToInt(int value)
{
return value;
}
/// <summary>
/// No conversion necessary: int? twips -> int twips
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int ToInt(int? value)
{
return ToInt((int)value);
}
/// <summary>
/// No conversion necessary: string twips -> int twips
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int ToInt(string value)
{
return ToInt((int)Convert.ToSingle(value));
}
/// <summary>
/// No conversion necessary: value from a list in a string, twips -> int twips
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
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 " + Volian.Base.Library.vlnFont.ProportionalSymbolFont + @";}}{\colortbl ;\red255\green0\blue0;}"); // C2017-036 get best available proportional font for symbols
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 " + Volian.Base.Library.vlnFont.ProportionalSymbolFont + @";}}{\colortbl ;\red255\green0\blue0;}"); // C2017-036 get best available proportional font for symbols
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(FixNumber(match.Groups[1].Value)) / 20;
float li = float.Parse(FixNumber(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 (object obj in para)//Fix the code to check for chunks before assuming chunks
{
Chunk chk = obj as Chunk;
if (chk != null && (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;
}
private static string FixNumber(string num)
{
if (num == "") return ("0");
return num;
}
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<vlnPrintObject>
{
public float ToPdf(PdfContentByte cb, float yPageStart, ref float yTopMargin, ref float yBottomMargin)
{
foreach (vlnPrintObject part in this)
{
// F2018-013 Replace Symbols as necesasary on tabs (consistent bullets)
if(part.Rtf != null) part.Rtf = vlnParagraph.FixRTFToPrint(part.MyParent.MyItemInfo, part.Rtf);
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
}
}