// =========================================================================== //  ©2013-2026 WebSupergoo. All rights reserved. // //  This source code is for use exclusively with the ABCpdf product with //  which it is distributed, under the terms of the license for that //  product. Details can be found at // //    http://www.websupergoo.com/ // //  This copyright notice must not be deleted and must be reproduced alongside //  any sections of code extracted from this module. // =========================================================================== using System; using System.Drawing; using System.Collections.Generic; using System.Globalization; using System.Diagnostics; using WebSupergoo.ABCpdf14; namespace PDFTableExamples {   /// <summary>   /// PDFTable class is used to add table aligned content to pdf files   /// </summary>   public class PDFTable {     /// <summary>The cell padding property</summary>     public double CellPadding {       get { return mCellPadding; }       set { mCellPadding = value; }     }     /// <summary>Vertical alignment of the row</summary>     public double VerticalAlignment  {       get { return mVerticalAlignment; }       set {         if (value > 1)           mVerticalAlignment = 1;         else if (value < 0)           mVerticalAlignment = 0;         else           mVerticalAlignment = value;       }     }     /// <summary>Horizontal alignment. This property is taken     /// directly from Doc.TextStyle.HPos</summary>     public double HorizontalAlignment {       get { return mDoc.TextStyle.HPos; }       set { mDoc.TextStyle.HPos = value; }     }     /// <summary>Number of lines to be repeated on every page</summary>     public int HeaderLinesCount {       get { return mHeaderLinesCount; }       set { mHeaderLinesCount = value; }     }     /// <summary>If true, header is repeated on every new page</summary>     public bool RepeatHeader {       get { return mRepeatHeader; }       set { mRepeatHeader = value; }     }     /// <summary>If true, header is framed</summary>     public bool FrameHeader {       get { return mFrameHeader; }       set { mFrameHeader = value; }     }     /// <summary>The current page number - cached for speed</summary>     public int PageNumber {       get {         int thePageID = mDoc.Page;         if (thePageID == 0)           return 1;         int thePageNum;         if (!mPageNos.TryGetValue(thePageID, out thePageNum)) {           thePageNum = mDoc.PageNumber;           mPageNos.Add(thePageID, thePageNum);           mPageIDs[thePageNum] = thePageID;         }         return thePageNum;       }       set {         int thePageID;         if (!mPageIDs.TryGetValue(value, out thePageID)) {           thePageID = mDoc.Page;           if (value == 1 && thePageID == 0)             return;           mDoc.PageNumber = value;           mPageNos[thePageID] = value;           mPageIDs.Add(value, thePageID);         } else {           mDoc.Page = thePageID;         }       }     }     private double mCellPadding = 0;     private double mVerticalAlignment = 0;     private int mHeaderLinesCount = 1;     private bool mRepeatHeader = false;     private bool mFrameHeader = false;     private SortedDictionary<int, int> mPageNos = new SortedDictionary<int, int>();     private SortedDictionary<int, int> mPageIDs = new SortedDictionary<int, int>();     /// <summary>Associated Doc</summary>     private Doc mDoc = null;     /// <summary>Number of pre-existing layers</summary>     private SortedDictionary<int, int> mInitialLayerCounts =  new SortedDictionary<int, int>();     /// <summary>Current cell</summary>     private Point mPos = new Point();     /// <summary>Bounds of the table</summary>     private XRect mBounds = new XRect();     /// <summary>Relative widths of the columns</summary>     private double[] mWidths = new double[0];     private PDFTable mParentTable;     /// <summary>PagePos class includes vertical position and page number</summary>     private sealed class PagePos {       public PagePos(PDFTable inTable) {         mPageHeight = inTable.mDoc.MediaBox.Height;         PosY = inTable.mDoc.Rect.Top;               PageNr = inTable.PageNumber;       }       public PagePos(PagePos p) {         mPageHeight = p.mPageHeight;         PosY = p.PosY;         PageNr = p.PageNr;       }       public static bool operator <(PagePos p1, PagePos p2) {         return (p1.PageNr > p2.PageNr) || (p1.PageNr == p2.PageNr && p1.PosY < p2.PosY);       }       public static bool operator <=(PagePos p1, PagePos p2) {         return (p1.PageNr > p2.PageNr) || (p1.PageNr == p2.PageNr && p1.PosY <= p2.PosY);       }       public static bool operator >(PagePos p1, PagePos p2) {         return !(p1.PageNr > p2.PageNr) || (p1.PageNr == p2.PageNr && p1.PosY < p2.PosY);       }       public static bool operator >=(PagePos p1, PagePos p2) {         return !(p1.PageNr > p2.PageNr) || (p1.PageNr == p2.PageNr && p1.PosY <= p2.PosY);       }       public static PagePos operator -(PagePos p1, double val) {         PagePos pn = new PagePos(p1);         pn.PosY -= val;         return pn;       }       public static PagePos operator +(PagePos p1, double val) {         PagePos pn = new PagePos(p1);         pn.PosY += val;         return pn;       }       public override bool Equals(System.Object obj) {         return Equals(obj as PagePos);       }       public bool Equals(PagePos p) {         if ((object)p == null)           return false;         const double tolerance = 0.01; // arbitrary value in points         return (PageNr == p.PageNr) && (Math.Abs(PosY - p.PosY) <= tolerance) && (Math.Abs(mPageHeight - p.mPageHeight) <= tolerance);       }       public override int GetHashCode() {         return PageNr.GetHashCode() + PosY.GetHashCode() + mPageHeight.GetHashCode();       }       /// <summary>Page number</summary>       public int PageNr = 1;       /// <summary>Vertical position</summary>       public double PosY = 0;       /// <summary>Page height</summary>       private double mPageHeight;     }     /// <summary>RowVerticalBounds class includes row top and bottom coordinates</summary>     private sealed class RowVerticalBounds {       public PagePos Top;       public PagePos Bottom;       public override bool Equals(System.Object obj) {         return Equals(obj as RowVerticalBounds);       }       public bool Equals(RowVerticalBounds p) {         if ((object)p == null)           return false;         return (Top.Equals(p.Top)) && (Bottom.Equals(p.Bottom));       }       public override int GetHashCode() {         return Top.GetHashCode() + Bottom.GetHashCode();       }     }     /// <summary>Bounds of the all existing rows</summary>     private RowVerticalBounds[] mRowPositions = new RowVerticalBounds[0];     /// <summary>Top of the current row</summary>     private PagePos RowTop;     /// <summary>Bottom of the current row</summary>     private PagePos mRowBottom;     /// <summary>Row bottom</summary>     private PagePos RowBottom {       get { return mRowBottom; }       set {         mRowBottom = value;         if (mParentTable != null) {           if (mParentTable.RowBottom > mRowBottom - CellPadding)             mParentTable.RowBottom = mRowBottom - CellPadding;         }       }     }     /// <summary>RowObject class describes row object (string or image)</summary>     private sealed class RowObject {       public RowObject() { }       public RowObject(RowObject inObj) {         id = inObj.id;         obj = inObj.obj;         rect.String = inObj.rect.String;         textStyle = inObj.textStyle;         pageNr = inObj.pageNr;         font = inObj.font;       }       /// <summary>Pdf object id</summary>       public int id = 0;       /// <summary>String or image object</summary>       public object obj = new object();       /// <summary>Bounding rectangle</summary>       public XRect rect = new XRect();       /// <summary>Text style</summary>       public string textStyle = "";       /// <summary>Page number</summary>       public int pageNr = 0;       /// <summary>Font ID</summary>       public int font = 0;     }     /// <summary>Contains current row objects added directly via AddTextStyled or AddImage methods</summary>     private List<RowObject> mOwnRowObjects = new List<RowObject>();     /// <summary>Contains current row objects added to the child tables</summary>     private List<RowObject> mChildRowObjects = new List<RowObject>();     /// <summary>Save object description</summary>     /// <param name="id">id of the added object</param>     /// <param name="obj">string or XImage</param>     /// <returns></returns>     private string SaveRowObject(int id, object obj) {       RowObject newObj = new RowObject();       newObj.id = id;       newObj.obj = obj;       newObj.rect.String = mDoc.GetInfo(id, "rect");       newObj.textStyle = mDoc.TextStyle.String;       newObj.pageNr = PageNumber;       newObj.font = mDoc.Font;       mOwnRowObjects.Add(newObj);       if (mParentTable != null)         mParentTable.SaveChildRowObject(newObj);       return newObj.rect.String;     }     private void SaveChildRowObject(RowObject obj) {       RowObject newObj = new RowObject(obj);       newObj.rect.String = obj.rect.String;       newObj.rect.Inset(-CellPadding, -CellPadding);       mChildRowObjects.Add(newObj);       if (mParentTable != null)         mParentTable.SaveChildRowObject(newObj);     }     /// <summary>Header position</summary>     private PagePos mHeaderPos;     /// <summary>Header content</summary>     private List<RowObject> mHeaderObjects = new List<RowObject>();     /// <summary>Focus on the document and assign the relevant number of columns</summary>     /// <param name="doc">Parent Doc</param>     /// <param name="columns">Number of columns</param>     public PDFTable(Doc doc, int columns) {       mDoc = doc;       RowTop = new PagePos(this);       mRowBottom = new PagePos(this);       SetRect(mDoc.Rect.String);       SetColumns(columns);     }     /// <summary>Focus on the document and assign the relevant number of columns and padding</summary>     /// <param name="doc">Parent Doc</param>     /// <param name="columns">Number of columns</param>     /// <param name="padding">Padding amount</param>     public PDFTable(Doc doc, int columns, double padding) {       mDoc = doc;       RowTop = new PagePos(this);       mRowBottom = new PagePos(this);       SetRect(mDoc.Rect.String);       SetColumns(columns);       CellPadding = padding;     }     /// <summary>Assign a new table rectangle and reset the current table position</summary>     /// <param name="rect">New table rectangle</param>     protected void SetRect(string rect) {       mDoc.Rect.String = rect;       mInitialLayerCounts[mDoc.Page] = mDoc.LayerCount;       mBounds.String = rect;       mPos.Y = -1;       mPos.X = -1;     }     /// <summary>Change the number of columns in the table</summary>     /// <param name="inNum">Number of columns</param>     private void SetColumns(int inNum) {       if (inNum > 0) {         mWidths = new double[inNum];         for(int i = 0; i < mWidths.Length; i++)           mWidths[i] = 1;       }     }     /// <summary>Get the current row - a zero based index</summary>     public int Row { get { return mPos.Y; } }     /// <summary>Get the current column - a zero based index</summary>     public int Column { get { return mPos.X; } }     /// <summary>Change a column width</summary>     /// <param name="i">Column index</param>     /// <param name="inWidth">Relative width of the column</param>     public void SetColumnWidth(int i, double inWidth) {       if ((i >= 0) && (i < mWidths.Length))         mWidths[i] = inWidth;     }     /// <summary>Change column widths</summary>     /// <param name="inWidths">Array of column widths</param>     public void SetColumnWidths(double[] inWidths) {       inWidths.CopyTo(mWidths, 0);     }     /// <summary>Move to the next cell</summary>     public void NextCell() {       if (mPos.Y == -1)         NextRow();       mPos.X = mPos.X + 1;       if (mPos.X >= mWidths.Length) {         NextRow();         mPos.X = 0;       }       if (mPos.X < 0)         mPos.X = 0;       SelectCurrentCell();     }     /// <summary>Move by the number of cells</summary>     /// <param name="count"> Number of cells to move by</param>     public void NextCell(int count)  {       for (int i =0; i < count; i++)         NextCell();     }     /// <summary>Move to the next row</summary>     public void NextRow() {         FixRowPosition();       if (VerticalAlignment > 0)         AlignVertically();              if (RepeatHeader && mPos.Y == 0 && HeaderLinesCount != 0) {         mHeaderObjects = new List<RowObject>();         mHeaderPos = RowTop;       }       if (RepeatHeader && mPos.Y >= 0 && mPos.Y < HeaderLinesCount) {         for (int i = 0; i < mOwnRowObjects.Count; i++)           mHeaderObjects.Add(mOwnRowObjects[i]);       }       if (RepeatHeader && FrameHeader && mPos.Y == HeaderLinesCount - 1) {         FrameCells(0, 0, mWidths.Length - 1, HeaderLinesCount - 1);       }       if (mPos.Y >= 0)         RowTop = RowBottom - CellPadding;       else         RowTop = RowBottom;       if (RowTop.PosY < mBounds.Bottom)         RowTop.PosY = mBounds.Bottom;       mDoc.Rect.String = mBounds.String;       RowBottom = RowTop;       mDoc.Rect.Top = RowTop.PosY;       mChildRowObjects.Clear();       mOwnRowObjects.Clear();       mPos.Y++;       mPos.X = -1;     }     /// <summary>Add text to the current cell</summary>     /// <param name="inText">Text of the cell</param>     /// <returns>id of the created pdf object</returns>     public int AddText(string inText) {       while (inText.Length > 0 && Char.IsWhiteSpace(inText[inText.Length - 1]))         inText = inText.Remove(inText.Length -1 , 1);       string theRect = mDoc.Rect.String;       mDoc.Rect.Inset (CellPadding, CellPadding);       PageNumber = RowTop.PageNr;       int id = mDoc.AddText(inText);       int theDrawn = 0;       if (id > 0)         theDrawn = mDoc.GetInfoInt(id, "Characters");       if (theDrawn < inText.Length) {         if (id != 0)           mDoc.Delete(id);         if (mRowPositions.Length >= 2)           mRowPositions[mRowPositions.Length - 1].Top.PosY             = mRowPositions[mRowPositions.Length - 2].Bottom.PosY;         MoveRowToNextPage();         mDoc.Rect.String = theRect;         mDoc.Rect.Top = RowTop.PosY;         AddText(inText);       } else {         XRect drawnRect = new XRect();         drawnRect.String = SaveRowObject(id, inText);         PagePos thePos = new PagePos(this);         thePos.PosY = (double)(mDoc.Pos.Y - mDoc.FontSize);         if (thePos < RowBottom) RowBottom = thePos;         mDoc.Rect.String = theRect;       }       return id;     }     /// <summary>Add text to the current cell</summary>     /// <param name="inText">Text of the cell</param>     /// <param name="inFontSize">Font size</param>     /// <returns>id of the created text object</returns>     public int AddText(string inText, int inFontSize) {       int oldFontSize = mDoc.FontSize;       mDoc.FontSize = inFontSize;       int id = AddText(inText);       mDoc.FontSize = oldFontSize;       return id;     }     /// <summary>Add enumerable data (strings or images) to the table</summary>     /// <param name="data">Enumerable collection of strings and images</param>     public void AddEnumerableData(System.Collections.IEnumerable data) {       System.Collections.IEnumerator it = data.GetEnumerator();       it.Reset();       bool bNoneEmpty = it.MoveNext();       while (bNoneEmpty) {         NextCell();         if (it.Current is string)           AddTextStyled((string)it.Current);         else if (it.Current is XImage)           AddImage((XImage)it.Current, true);         bNoneEmpty = it.MoveNext();       }     }     /// <summary>Add enumerable html data</summary>     /// <param name="data">Enumerable collection of strings and images</param>     public void AddTextStyled(System.Collections.IEnumerable data) {       AddEnumerableData(data);     }     /// <summary>Fill table with rectangular array of strings</summary>     /// <param name="inTextArrays">Rectangular array of strings</param>     public void AddTextStyled(string [][] inTextArrays) {       if (inTextArrays.Length == mWidths.Length) {         bool bLast = false;         int RowCount =0;         while (! bLast) {           NextRow();           bLast = true;           for (int i = 0; i < inTextArrays.Length; i++) {             NextCell();             if (inTextArrays[i].Length > RowCount+1)               bLast = false;             if (inTextArrays[i].Length > RowCount)               AddTextStyled(inTextArrays[i][RowCount]);           }           RowCount++;         }       }     }     /// <summary>Depth of recursion into AddTextStyled function</summary>     private int HtmlDepth = 0;     /// <summary>Maximum allowed depth of recursion into AddTextStyled function</summary>     private const int MaxHtmlDepth = 10;     /// <summary>Add Html to the current cell</summary>     /// <param name="inHtml">Html to put into the cell</param>     /// <returns>id of the created pdf object</returns>     public int AddTextStyled(string inHtml) {       return AddTextStyled(inHtml, true);     }     private int AddTextStyled(string inHtml, bool inMoveRows) {       int id = 0;       HtmlDepth++;       if (HtmlDepth < MaxHtmlDepth) {         inHtml = inHtml.TrimEnd(null);         if (inHtml.Length > 0) {           string theRect = mDoc.Rect.String;           mDoc.Rect.Inset (CellPadding, CellPadding);           PageNumber = RowTop.PageNr;           if (mDoc.Rect.Top >= mDoc.Rect.Bottom)             id = mDoc.AddTextStyled(inHtml);           if ((id == 0) || (mDoc.Chainable(id))) {             if (inMoveRows) {               if (id != 0)                 mDoc.Delete(id);               if (mRowPositions.Length >= 2)                 mRowPositions[mRowPositions.Length - 1].Top.PosY                   = mRowPositions[mRowPositions.Length - 2].Bottom.PosY;               MoveRowToNextPage();               mDoc.Rect.String = theRect;               mDoc.Rect.Top = RowTop.PosY;               id = AddTextStyled(inHtml);             }           } else {             XRect drawnRect = new XRect();             drawnRect.String = SaveRowObject(id, inHtml);                PagePos thePos = new PagePos(this);             thePos.PosY = drawnRect.Bottom;             if (thePos < RowBottom)               RowBottom = thePos;             mDoc.Rect.String = theRect;           }         }       }       HtmlDepth--;       return id;     }     /// <summary>Add nested table with the specified number of columns</summary>     /// <param name="inColumns">Number of columns</param>     /// <returns></returns>     public PDFTable AddTable(int inColumns)  {       PageNumber = RowTop.PageNr;       string theRect = mDoc.Rect.String;       mDoc.Rect.Bottom = mBounds.Bottom;       PDFTable nestedTable = new PDFTable(mDoc, inColumns);       nestedTable.mParentTable = this;       nestedTable.CellPadding = CellPadding;       nestedTable.VerticalAlignment = VerticalAlignment;       mDoc.Rect.String = theRect;       return nestedTable;     }     /// <summary>Add nested table with the specified number of columns and cellpadding</summary>     /// <param name="inColumns">Number of columns</param>     /// <param name="padding">Padding amount</param>     /// <returns>Created PDFTable object</returns>     public PDFTable AddTable(int inColumns, double padding)  {       PDFTable result = AddTable(inColumns);       result.CellPadding = padding;       return result;     }     /// <summary>Add image</summary>     /// <param name="inImage">XImage to put into the cell</param>     /// <param name="bStretch">If bStretch is true inserted image will have the same width as the cell.</param>     /// <returns>The ID of the image. Zero if no image added.</returns>     public int AddImage(XImage inImage, bool inStretch) {       return AddImage(inImage, inStretch, true);     }     /// <summary>Add image</summary>     /// <param name="inImage">XImage to put into the cell</param>     /// <param name="bStretch">If bStretch is true inserted image will have the same width as the cell.</param>     /// <returns>The ID of the image. Zero if no image added.</returns>     public int AddImage(XImage inImage, bool inStretch, bool allowMoveToNextPage){       int id = 0;       if ((inImage != null) && (inImage.Width > 0) && (inImage.Height > 0)) {         string theRect = mDoc.Rect.String;         mDoc.Rect.Inset (CellPadding, CellPadding);         double scale = 1;         if (inStretch) {           scale =  mDoc.Rect.Width / inImage.Width;           if (!allowMoveToNextPage) {             double horizontalScale = mDoc.Rect.Height / inImage.Height;             scale = Math.Min(scale, horizontalScale);           }         }         mDoc.Rect.Width = inImage.Width * scale;         PageNumber = RowTop.PageNr;         if (mDoc.Rect.Top - inImage.Height * scale >= mBounds.Bottom) {           mDoc.Rect.Bottom = mDoc.Rect.Top - inImage.Height * scale;           PagePos thePos = new PagePos(this);           id = mDoc.AddImage(inImage);           thePos -= inImage.Height*scale;           if (thePos < RowBottom) RowBottom = thePos;           SaveRowObject(id, inImage);         } else if (allowMoveToNextPage) {           MoveRowToNextPage();           mDoc.Rect.String = theRect;           mDoc.Rect.Top = RowTop.PosY;           id = AddImage(inImage, inStretch, false);         }         mDoc.Rect.String = theRect;       }       return id;     }     /// <summary>Moves vertically the input rect of the cell</summary>     /// <param name="advance">Advance amount</param>     public void Advance(double advance) {       if (mDoc.Rect.Top - advance >= mBounds.Bottom)         mDoc.Rect.Top -= advance;       else         mDoc.Rect.Top = mBounds.Bottom;       if (RowBottom.PosY - advance >= mBounds.Bottom)         RowBottom -= advance;       else         RowBottom.PosY = mBounds.Bottom;       UnfixRowPosition();     }     /// <summary>Returns top bound of the top parent table</summary>     /// <returns>top bound of the top parent table</returns>     private double GetParentTopBounds()  {       if (mParentTable == null)         return mBounds.Top;       else         return mParentTable.GetParentTopBounds();     }     /// <summary>Align content vertically</summary>     private void AlignVertically() {       List<RowObject> objectList = new List<RowObject>(mOwnRowObjects);       mOwnRowObjects.Clear();       for (int i = 0; i < objectList.Count; i++) {         RowObject oldObj = objectList[i];         double bottom = Math.Round(RowBottom.PosY - CellPadding, 6);         XRect bbox = new XRect();         bbox.String = mDoc.GetInfo(oldObj.id, "rect");         bbox.Inset(-CellPadding, -CellPadding);         if (bbox.Bottom > bottom && oldObj.pageNr == RowBottom.PageNr) {           double alpha = VerticalAlignment;           if (VerticalAlignment > 1)             alpha = 1;           double offset = alpha * (bbox.Bottom - bottom);           double width = 0;           if (oldObj.obj is XImage)             width = Double.Parse(mDoc.GetInfo(oldObj.id, "Width"), new CultureInfo("en-US"));           mDoc.Delete(oldObj.id);           XRect newRect =new XRect();           newRect.String = bbox.String;           newRect.Move(0, -offset);           if (newRect.Bottom > bottom)             newRect.Bottom = bottom;           mDoc.Rect.String = newRect.String;           string theTextStyle = mDoc.TextStyle.String;           mDoc.TextStyle.String = oldObj.textStyle;           int iFont = mDoc.Font;           mDoc.Font = oldObj.font;           if (oldObj.obj is string)             AddTextStyled((string)oldObj.obj);           else if (oldObj.obj is XImage) {             XImage image = (XImage)oldObj.obj;             if (width == image.Width)               AddImage(image, false);             else               AddImage(image, true);           }           mDoc.Font = iFont;           mDoc.TextStyle.String = theTextStyle;         } else           mOwnRowObjects.Add(oldObj);       }     }     /// <summary>Move row content to the next page</summary>     private void MoveRowToNextPage() {       List<RowObject> oldRowObjects = new List<RowObject>(mOwnRowObjects);       oldRowObjects.AddRange(mChildRowObjects);       mOwnRowObjects.Clear();       mChildRowObjects.Clear();       UnfixRowPosition();       if (PageNumber == mDoc.PageCount)         mDoc.Page = mDoc.AddPage();       else         PageNumber = PageNumber + 1;       if (!mInitialLayerCounts.ContainsKey(mDoc.Page))         mInitialLayerCounts[mDoc.Page] = mDoc.LayerCount;       mDoc.Rect.String = mBounds.String;       mDoc.Rect.Top = GetParentTopBounds();            PagePos oldRowTop = RowTop;       RowTop = new PagePos(this);       RowBottom = RowTop;       double headerOffset = 0;       if (RepeatHeader) {         int posX = mPos.X;         if (mHeaderPos != null) {           MoveRowToNextPage(mHeaderObjects, GetParentTopBounds() - mHeaderPos.PosY, false);           if (FrameHeader)             FrameRow(mPos.Y);         }         NextRow();         mPos.X = posX;         var position = mPos.Y > 0 ? mRowPositions[mPos.Y - 1] : null;         headerOffset = position != null ? position.Top.PosY - position.Bottom.PosY : 0;       }       MoveRowToNextPage(oldRowObjects, GetParentTopBounds() - oldRowTop.PosY - headerOffset, true);     }     private bool MoveRowToNextPage(List<RowObject> objectList, double offset, bool bRemove) {       bool ok = true;       for (int i = 0; i < objectList.Count; i++) {         RowObject oldObj = objectList[i];         double width = 0;         if (oldObj.obj is XImage)           width = Double.Parse(mDoc.GetInfo(oldObj.id, "Width"), new CultureInfo("en-US"));         mDoc.Rect.String = mDoc.GetInfo(oldObj.id, "rect");         if (bRemove)           mDoc.Delete(oldObj.id);         mDoc.Rect.Move(0, offset);         mDoc.Rect.Inset(-CellPadding, -CellPadding);         string theTextStyle = mDoc.TextStyle.String;         mDoc.TextStyle.String = oldObj.textStyle;         mDoc.Rect.Bottom -= mDoc.TextStyle.LineSpacing;         int iFont = mDoc.Font;         mDoc.Font = oldObj.font;         if (oldObj.obj is string) {           if (AddTextStyled((string)oldObj.obj) == 0)             ok = false;         } else if (oldObj.obj is XImage) {           XImage image = (XImage)oldObj.obj;           if (AddImage(image, width != image.Width) == 0)             ok = false;         }         mDoc.Font = iFont;         mDoc.TextStyle.String = theTextStyle;       }       return ok;     }          /// <summary>Frame specified rectangle</summary>     /// <param name="inX1">Bottom left X coordinate</param>     /// <param name="inY1">Bottom left Y coordinate</param>     /// <param name="inX2">Top right X coordinate</param>     /// <param name="inY2">Top right Y coordinate</param>     public void FrameCells(int inX1, int inY1, int inX2, int inY2) {       FrameCells(inX1, inY1, inX2, inY2, true, true, true, true);     }     /// <summary>Frame specified rectangle</summary>     /// <param name="inX1">Bottom left X coordinate</param>     /// <param name="inY1">Bottom left Y coordinate</param>     /// <param name="inX2">Top right X coordinate</param>     /// <param name="inY2">Top right Y coordinate</param>     /// <param name="inTop">If true, line top side</param>     /// <param name="inBottom">If true, line bottom side</param>     /// <param name="inLeft">If true, line left side</param>     /// <param name="inRight">If true, line right side</param>     public void FrameCells(int inX1, int inY1, int inX2, int inY2, bool inTop, bool inBottom, bool inLeft, bool inRight) {       // check inputs       if (inX1 > inX2)         return;       if (inY1 > inY2)         return;       double theTotal = 0;       double left = 0, right = 0;       for (int i = 0; i < inX1; i++)         left += mWidths[i];       for (int i = 0; i <= inX2; i++)         right += mWidths[i];       for (int i = 0; i < mWidths.Length; i++)         theTotal += mWidths[i];       left = mBounds.Left + left * mBounds.Width / theTotal;       right = mBounds.Left +  right * mBounds.Width / theTotal;       PagePos top = mRowPositions[inY1].Top;       PagePos bottom = mRowPositions[inY2].Bottom;       int pageNr = PageNumber;       double tempTop = top.PosY;       double tempBottom = 0;       int curPageNr = top.PageNr;       do {         if (curPageNr == bottom.PageNr)           tempBottom = bottom.PosY;         else           tempBottom = mBounds.Bottom;         if (curPageNr == top.PageNr)           tempTop = top.PosY;         else           tempTop = GetParentTopBounds();         PageNumber = curPageNr++;                  if (inLeft)           mDoc.AddLine(left, tempBottom, left, tempTop);         if (inTop)           mDoc.AddLine(left, tempTop, right, tempTop);         if (inRight)           mDoc.AddLine(right, tempTop, right, tempBottom);         if (inBottom)           mDoc.AddLine(right, tempBottom, left, tempBottom);       } while (curPageNr <= bottom.PageNr);       PageNumber = pageNr;     }     /// <summary>Frame table</summary>     public void Frame()  {       FixRowPosition();       FrameCells(0, 0, mWidths.Length - 1, mRowPositions.Length - 1);     }     /// <summary>Frame Row</summary>     /// <param name="index">Row index</param>     public void FrameRow(int index) {       FixRowPosition();       FrameCells(0, index, mWidths.Length - 1, index);     }     /// <summary>Frame all rows</summary>     public void FrameRows() {       FixRowPosition();       for (int i = 0; i< mRowPositions.Length; i++)         FrameRow(i);     }        /// <summary>Frame column</summary>     /// <param name="index">Column index</param>     public void FrameColumn(int index) {       FixRowPosition();       FrameCells(index, 0, index, mRowPositions.Length - 1);     }     /// <summary>Frame all columns</summary>     public void FrameColumns() {       FixRowPosition();       for (int i = 0; i < mWidths.Length; i++)         FrameCells(i, 0, i, mRowPositions.Length - 1);     }     /// <summary>Fill rectangle with specified color</summary>     /// <param name="inColor">Fill color</param>     /// <param name="inX1">Bottom left X coordinate</param>     /// <param name="inY1">Bottom left Y coordinate</param>     /// <param name="inX2">Top right X coordinate</param>     /// <param name="inY2">Top right Y coordinate</param>     public void FillCells(string inColor, int inX1, int inY1, int inX2, int inY2) {       // check inputs       if (inX1 > inX2)         return;       if (inY1 > inY2)         return;       double theTotal = 0;       double left = 0, right = 0;       for (int i = 0; i < inX1; i++)         left += mWidths[i];       for (int i = 0; i <= inX2; i++)         right += mWidths[i];       for (int i = 0; i < mWidths.Length; i++)         theTotal += mWidths[i];       left = mBounds.Left + left * mBounds.Width / theTotal;       right = mBounds.Left +  right * mBounds.Width / theTotal;       PagePos top = mRowPositions[inY1].Top;       PagePos bottom = mRowPositions[inY2].Bottom;       int pageNr = PageNumber;       double tempTop = top.PosY;       double tempBottom = 0;       int curPageNr = top.PageNr;       do {         if (curPageNr == bottom.PageNr)           tempBottom = bottom.PosY;         else           tempBottom = mBounds.Bottom;         if (curPageNr == top.PageNr)           tempTop = top.PosY;         else           tempTop = GetParentTopBounds();         PageNumber = curPageNr++;         mDoc.Rect.Left = left;         mDoc.Rect.Right = right;         mDoc.Rect.Top = tempTop;         mDoc.Rect.Bottom = tempBottom;         int theLayer = mDoc.Layer;         string theColor = mDoc.Color.String;         int layer = 0;         mInitialLayerCounts.TryGetValue(mDoc.Page, out layer);         mDoc.Layer = mDoc.LayerCount - layer + 1;         mDoc.Color.String = inColor;         int id = mDoc.FillRect();         mDoc.Color.String = theColor;         mDoc.Layer = theLayer;       } while (curPageNr <= bottom.PageNr);       PageNumber = pageNr;     }     /// <summary>Fill table with specified color</summary>     /// <param name="inColor">Fill color</param>     public void Fill(string inColor) {       FixRowPosition();       FillCells(inColor, 0, 0, mWidths.Length - 1, mRowPositions.Length - 1);     }     /// <summary>Fill column with specified color</summary>     /// <param name="inColor">Fill color</param>     /// <param name="index">Column index</param>     public void FillColumn(string inColor, int index) {       FixRowPosition();       FillCells(inColor, index, 0, index, mRowPositions.Length - 1);     }     /// <summary>Fill row with specified color</summary>     /// <param name="inColor">Fill color</param>     /// <param name="index">Row index</param>     public void FillRow(string inColor, int index) {       FixRowPosition();       FillCells(inColor, 0, index, mWidths.Length - 1, index);     }     /// <summary>Sets the height of the current row.</summary>     public void SetRowHeight(double inHeight) {       PagePos thePos = new PagePos(this);       thePos.PosY = thePos.PosY - inHeight;       RowBottom = thePos;     }     /// <summary>Fix current row position</summary>     private void FixRowPosition() {       if (mPos.Y >= 0) {         if (mPos.Y > mRowPositions.Length - 1) {           RowVerticalBounds[] theCopy = new RowVerticalBounds[mPos.Y + 1];           mRowPositions.CopyTo(theCopy, 0);           mRowPositions = theCopy;         }         if (RowBottom.PosY < mBounds.Bottom)           RowBottom.PosY = mBounds.Bottom;         RowVerticalBounds theBounds = new RowVerticalBounds();         theBounds.Top = RowTop;         theBounds.Bottom = RowBottom - CellPadding;         // If the row position is not null then the height of row has already been fixed.         // This generally indicates a programming error. A typical thing that might cause it would be         // to add some content, add a frame round part of the row, add some more content that might         // well change the height and thus mean that the frame had been drawn incorrectly.         Debug.Assert((mRowPositions[mPos.Y] == null) || (mRowPositions[mPos.Y].Equals(theBounds)));         mRowPositions[mPos.Y] = theBounds;       }     }     /// <summary>Unfix current row position so that it can be fixed again.</summary>     private void UnfixRowPosition() {       if ((mPos.Y >= 0) && (mPos.Y < mRowPositions.Length))         mRowPositions[mPos.Y] = null;     }     /// <summary>Select current cell</summary>     private void SelectCurrentCell() {       double theTotal = 0, thePos = 0;       if ((mPos.X >= 0) && (mPos.X < mWidths.Length)) {         // get the x offset and width of the cell         for (int i = 0; i < mWidths.Length; i++) {           theTotal = theTotal + mWidths[i];           if (i < mPos.X) thePos = thePos + mWidths[i];         }         thePos = thePos * (mBounds.Width / theTotal);         double theWidth = mWidths[mPos.X] * (mBounds.Width / theTotal);         // position the cell         mDoc.Rect.Top = RowTop.PosY;         mDoc.Rect.Left = mBounds.Left + thePos;         mDoc.Rect.Width = theWidth;       }     }     /// <summary>Delete all the objects drawn part of the current row</summary>     public void DeleteLastRow() {       for (int i = 0; i < mOwnRowObjects.Count; i++)         mDoc.Delete(mOwnRowObjects[i].id);       for (int i = 0; i < mChildRowObjects.Count; i++)         mDoc.Delete(mChildRowObjects[i].id);     }     /// <summary>Select a cell specified by row and column. This function fixes     /// the row height so you must have already added your content.</summary>     public void SelectCell(int row, int column, bool applyPadding) {       FixRowPosition();       mPos.Y = row;       mPos.X = column;       SelectCurrentCell();       if (row < mRowPositions.Length)         mDoc.Rect.Bottom = mRowPositions[row].Bottom.PosY;       if (applyPadding)         mDoc.Rect.Inset(CellPadding, CellPadding);     }     public void SelectionRotate() {       mDoc.Transform.Rotate(90, mDoc.Rect.Left, mDoc.Rect.Bottom);       mDoc.Transform.Translate(mDoc.Rect.Height, 0);       mDoc.Rect.Resize(mDoc.Rect.Height, mDoc.Rect.Width, XRect.Corner.TopLeft);     }     public void SelectionReset() {       mDoc.Transform.Reset();     }   } }